710 lines
34 KiB
HTML
710 lines
34 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}{% if object %}Edit{% else %}New{% endif %} 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>
|
|
{% if object %}
|
|
<li class="breadcrumb-item"><a href="{% url 'pharmacy:inventory_detail' object.pk %}">{{ object.medication.name }}</a></li>
|
|
<li class="breadcrumb-item active">Edit</li>
|
|
{% else %}
|
|
<li class="breadcrumb-item active">New</li>
|
|
{% endif %}
|
|
</ol>
|
|
<!-- END breadcrumb -->
|
|
|
|
<!-- BEGIN page-header -->
|
|
<h1 class="page-header">
|
|
{% if object %}Edit Inventory{% else %}New Inventory{% endif %}
|
|
<small>{% if object %}{{ object.medication.name }}{% else %}Add medication to inventory{% endif %}</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 Details</h4>
|
|
<div class="panel-heading-btn">
|
|
<button type="button" class="btn btn-xs btn-info me-2" onclick="calculateReorderLevel()">
|
|
<i class="fa fa-calculator"></i> Calculate Reorder
|
|
</button>
|
|
<button type="button" class="btn btn-xs btn-secondary me-2" onclick="saveDraft()">
|
|
<i class="fa fa-save"></i> Save Draft
|
|
</button>
|
|
<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">
|
|
<form method="post" id="inventory-form" novalidate>
|
|
{% csrf_token %}
|
|
|
|
<!-- Medication Selection -->
|
|
<div class="row mb-3">
|
|
<div class="col-md-8">
|
|
<div class="form-floating">
|
|
{{ form.medication }}
|
|
<label for="{{ form.medication.id_for_label }}">Medication <span class="text-danger">*</span></label>
|
|
{% if form.medication.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.medication.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
<div class="form-text">Select the medication for this inventory record.</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-floating">
|
|
{{ form.location }}
|
|
<label for="{{ form.location.id_for_label }}">Location <span class="text-danger">*</span></label>
|
|
{% if form.location.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.location.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
<div class="form-text">Storage location.</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Medication Information Display -->
|
|
<div id="medication-info" class="card border-info mb-3" style="display: none;">
|
|
<div class="card-header bg-info text-white">
|
|
<h6 class="card-title mb-0">
|
|
<i class="fa fa-pills me-2"></i>Medication Information
|
|
</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div id="med-generic-name" class="mb-2"></div>
|
|
<div id="med-brand-name" class="mb-2"></div>
|
|
<div id="med-strength" class="mb-2"></div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div id="med-dosage-form" class="mb-2"></div>
|
|
<div id="med-manufacturer" class="mb-2"></div>
|
|
<div id="med-special-properties" class="mb-2"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stock Levels -->
|
|
<div class="card border-success mb-3">
|
|
<div class="card-header bg-success text-white">
|
|
<h6 class="card-title mb-0">
|
|
<i class="fa fa-boxes me-2"></i>Stock Levels
|
|
</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-3">
|
|
<div class="form-floating mb-3">
|
|
{{ form.current_stock }}
|
|
<label for="{{ form.current_stock.id_for_label }}">Current Stock <span class="text-danger">*</span></label>
|
|
{% if form.current_stock.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.current_stock.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
<div class="form-text">Current quantity on hand.</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="form-floating mb-3">
|
|
{{ form.minimum_stock }}
|
|
<label for="{{ form.minimum_stock.id_for_label }}">Minimum Stock <span class="text-danger">*</span></label>
|
|
{% if form.minimum_stock.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.minimum_stock.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
<div class="form-text">Minimum safe stock level.</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="form-floating mb-3">
|
|
{{ form.reorder_level }}
|
|
<label for="{{ form.reorder_level.id_for_label }}">Reorder Level <span class="text-danger">*</span></label>
|
|
{% if form.reorder_level.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.reorder_level.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
<div class="form-text">Trigger point for reordering.</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="form-floating mb-3">
|
|
{{ form.maximum_stock }}
|
|
<label for="{{ form.maximum_stock.id_for_label }}">Maximum Stock</label>
|
|
{% if form.maximum_stock.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.maximum_stock.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
<div class="form-text">Maximum storage capacity.</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Location and Storage -->
|
|
<div class="card border-warning mb-3">
|
|
<div class="card-header bg-warning text-dark">
|
|
<h6 class="card-title mb-0">
|
|
<i class="fa fa-warehouse me-2"></i>Location and Storage
|
|
</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-floating mb-3">
|
|
{{ form.bin_location }}
|
|
<label for="{{ form.bin_location.id_for_label }}">Bin/Shelf Location</label>
|
|
{% if form.bin_location.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.bin_location.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
<div class="form-text">Specific bin or shelf location.</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-floating mb-3">
|
|
{{ form.storage_conditions }}
|
|
<label for="{{ form.storage_conditions.id_for_label }}">Storage Conditions</label>
|
|
{% if form.storage_conditions.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.storage_conditions.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
<div class="form-text">Special storage requirements.</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Cost and Pricing -->
|
|
<div class="card border-primary mb-3">
|
|
<div class="card-header bg-primary text-white">
|
|
<h6 class="card-title mb-0">
|
|
<i class="fa fa-dollar-sign me-2"></i>Cost and Pricing
|
|
</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-4">
|
|
<div class="form-floating mb-3">
|
|
{{ form.unit_cost }}
|
|
<label for="{{ form.unit_cost.id_for_label }}">Unit Cost <span class="text-danger">*</span></label>
|
|
{% if form.unit_cost.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.unit_cost.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
<div class="form-text">Cost per unit.</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-floating mb-3">
|
|
{{ form.wholesale_cost }}
|
|
<label for="{{ form.wholesale_cost.id_for_label }}">Wholesale Cost</label>
|
|
{% if form.wholesale_cost.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.wholesale_cost.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
<div class="form-text">Wholesale acquisition cost.</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-floating mb-3">
|
|
<input type="text" class="form-control" id="total_value" readonly>
|
|
<label for="total_value">Total Value</label>
|
|
</div>
|
|
<div class="form-text">Calculated total value.</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Lot and Expiration -->
|
|
<div class="card border-info mb-3">
|
|
<div class="card-header bg-info text-white">
|
|
<h6 class="card-title mb-0">
|
|
<i class="fa fa-calendar me-2"></i>Lot and Expiration
|
|
</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-4">
|
|
<div class="form-floating mb-3">
|
|
{{ form.lot_number }}
|
|
<label for="{{ form.lot_number.id_for_label }}">Lot Number</label>
|
|
{% if form.lot_number.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.lot_number.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
<div class="form-text">Manufacturer lot number.</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-floating mb-3">
|
|
{{ form.expiration_date }}
|
|
<label for="{{ form.expiration_date.id_for_label }}">Expiration Date</label>
|
|
{% if form.expiration_date.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.expiration_date.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
<div class="form-text">Product expiration date.</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-floating mb-3">
|
|
{{ form.manufacture_date }}
|
|
<label for="{{ form.manufacture_date.id_for_label }}">Manufacture Date</label>
|
|
{% if form.manufacture_date.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.manufacture_date.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
<div class="form-text">Product manufacture date.</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Supplier Information -->
|
|
<div class="card border-secondary mb-3">
|
|
<div class="card-header bg-secondary text-white">
|
|
<h6 class="card-title mb-0">
|
|
<i class="fa fa-truck me-2"></i>Supplier Information
|
|
</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-floating mb-3">
|
|
{{ form.supplier }}
|
|
<label for="{{ form.supplier.id_for_label }}">Supplier</label>
|
|
{% if form.supplier.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.supplier.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
<div class="form-text">Primary supplier for this medication.</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-floating mb-3">
|
|
{{ form.lead_time_days }}
|
|
<label for="{{ form.lead_time_days.id_for_label }}">Lead Time (Days)</label>
|
|
{% if form.lead_time_days.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.lead_time_days.errors.0 }}</div>
|
|
{% endif %}
|
|
</div>
|
|
<div class="form-text">Typical delivery lead time.</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Notes -->
|
|
<div class="mb-3">
|
|
<label for="{{ form.notes.id_for_label }}" class="form-label">Notes</label>
|
|
{{ form.notes }}
|
|
{% if form.notes.errors %}
|
|
<div class="invalid-feedback d-block">{{ form.notes.errors.0 }}</div>
|
|
{% endif %}
|
|
<div class="form-text">Additional notes or comments.</div>
|
|
</div>
|
|
|
|
<!-- Form Actions -->
|
|
<div class="d-flex justify-content-between">
|
|
<div>
|
|
<a href="{% if object %}{% url 'pharmacy:inventory_detail' object.pk %}{% else %}{% url 'pharmacy:inventory_list' %}{% endif %}" class="btn btn-secondary">
|
|
<i class="fa fa-arrow-left me-2"></i>Cancel
|
|
</a>
|
|
</div>
|
|
<div>
|
|
<button type="button" class="btn btn-info me-2" onclick="saveDraft()">
|
|
<i class="fa fa-save me-2"></i>Save Draft
|
|
</button>
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fa fa-check me-2"></i>{% if object %}Update{% else %}Create{% endif %} Inventory
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</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">Inventory Guidelines</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="alert alert-info">
|
|
<h6 class="alert-heading">Best Practices</h6>
|
|
<ul class="mb-0 small">
|
|
<li>Set minimum stock above safety stock</li>
|
|
<li>Reorder level should account for lead time</li>
|
|
<li>Maximum stock should consider storage capacity</li>
|
|
<li>Include lot numbers for tracking</li>
|
|
<li>Monitor expiration dates closely</li>
|
|
<li>Update costs regularly</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="card border-warning mb-3">
|
|
<div class="card-header bg-warning text-dark">
|
|
<h6 class="card-title mb-0">Validation Checks</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<ul class="list-unstyled mb-0 small">
|
|
<li id="stock-check"><i class="fa fa-times text-danger me-2"></i>Stock levels validation pending</li>
|
|
<li id="cost-check"><i class="fa fa-times text-danger me-2"></i>Cost validation pending</li>
|
|
<li id="expiration-check"><i class="fa fa-times text-danger me-2"></i>Expiration date pending</li>
|
|
<li id="location-check"><i class="fa fa-times text-danger me-2"></i>Location validation pending</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END panel -->
|
|
|
|
<!-- 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-outline-info" onclick="calculateReorderLevel()">
|
|
<i class="fa fa-calculator me-2"></i>Calculate Reorder Level
|
|
</button>
|
|
<button type="button" class="btn btn-outline-warning" onclick="checkDuplicates()">
|
|
<i class="fa fa-search me-2"></i>Check Duplicates
|
|
</button>
|
|
<button type="button" class="btn btn-outline-primary" onclick="loadTemplate()">
|
|
<i class="fa fa-copy me-2"></i>Load Template
|
|
</button>
|
|
<button type="button" class="btn btn-outline-secondary" onclick="clearForm()">
|
|
<i class="fa fa-refresh me-2"></i>Clear Form
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END panel -->
|
|
|
|
<!-- BEGIN panel -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Form Status</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<div id="form-status">
|
|
<div class="alert alert-secondary">
|
|
<i class="fa fa-info-circle me-2"></i>
|
|
<span id="status-text">Draft inventory record</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="small text-muted">
|
|
<div>Last saved: <span id="last-saved">Never</span></div>
|
|
<div>Auto-save: <span class="text-success">Enabled</span></div>
|
|
<div>Validation: <span id="validation-status" class="text-warning">Pending</span></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END panel -->
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block js %}
|
|
<script>
|
|
var autoSaveTimer;
|
|
|
|
$(document).ready(function() {
|
|
// Initialize form behavior
|
|
initializeFormBehavior();
|
|
|
|
// Auto-save functionality
|
|
$('#inventory-form input, #inventory-form select, #inventory-form textarea').on('change input', function() {
|
|
clearTimeout(autoSaveTimer);
|
|
autoSaveTimer = setTimeout(function() {
|
|
saveDraft();
|
|
}, 5000);
|
|
});
|
|
|
|
// Form validation
|
|
$('#inventory-form').on('submit', function(e) {
|
|
if (!validateForm()) {
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
});
|
|
|
|
function initializeFormBehavior() {
|
|
// Medication selection change
|
|
$('#{{ form.medication.id_for_label }}').on('change', function() {
|
|
var medicationId = $(this).val();
|
|
if (medicationId) {
|
|
loadMedicationInfo(medicationId);
|
|
checkDuplicates();
|
|
} else {
|
|
$('#medication-info').hide();
|
|
}
|
|
});
|
|
|
|
// Stock level calculations
|
|
$('#{{ form.current_stock.id_for_label }}, #{{ form.unit_cost.id_for_label }}').on('change', function() {
|
|
calculateTotalValue();
|
|
validateStockLevels();
|
|
});
|
|
|
|
// Cost calculations
|
|
$('#{{ form.unit_cost.id_for_label }}, #{{ form.wholesale_cost.id_for_label }}').on('change', function() {
|
|
validateCosts();
|
|
});
|
|
|
|
// Expiration date validation
|
|
$('#{{ form.expiration_date.id_for_label }}').on('change', function() {
|
|
validateExpirationDate();
|
|
});
|
|
|
|
// Location validation
|
|
$('#{{ form.location.id_for_label }}, #{{ form.bin_location.id_for_label }}').on('change', function() {
|
|
validateLocation();
|
|
});
|
|
|
|
// Initial calculations
|
|
calculateTotalValue();
|
|
}
|
|
|
|
function loadMedicationInfo(medicationId) {
|
|
$.ajax({
|
|
url: '{% url "pharmacy:get_medication_info" 0 %}'.replace('0', medicationId),
|
|
method: 'GET',
|
|
data: {'medication_id': medicationId},
|
|
success: function(response) {
|
|
var med = response.medication;
|
|
$('#med-generic-name').html('<strong>Generic Name:</strong> ' + med.generic_name);
|
|
$('#med-brand-name').html('<strong>Brand Name:</strong> ' + (med.brand_name || 'Not specified'));
|
|
$('#med-strength').html('<strong>Strength:</strong> ' + med.strength);
|
|
$('#med-dosage-form').html('<strong>Dosage Form:</strong> ' + med.dosage_form);
|
|
$('#med-manufacturer').html('<strong>Manufacturer:</strong> ' + (med.manufacturer || 'Not specified'));
|
|
|
|
var properties = [];
|
|
if (med.is_controlled) properties.push('<span class="badge bg-warning text-dark">Controlled</span>');
|
|
if (med.is_high_alert) properties.push('<span class="badge bg-danger">High Alert</span>');
|
|
if (med.requires_refrigeration) properties.push('<span class="badge bg-info">Refrigeration</span>');
|
|
|
|
$('#med-special-properties').html('<strong>Properties:</strong> ' + (properties.length ? properties.join(' ') : 'None'));
|
|
$('#medication-info').show();
|
|
},
|
|
error: function() {
|
|
toastr.error('Failed to load medication information');
|
|
}
|
|
});
|
|
}
|
|
|
|
function calculateTotalValue() {
|
|
var currentStock = parseFloat($('#{{ form.current_stock.id_for_label }}').val()) || 0;
|
|
var unitCost = parseFloat($('#{{ form.unit_cost.id_for_label }}').val()) || 0;
|
|
var totalValue = currentStock * unitCost;
|
|
|
|
$('#total_value').val('$' + totalValue.toFixed(2));
|
|
}
|
|
|
|
function validateForm() {
|
|
var isValid = true;
|
|
var errors = [];
|
|
|
|
// Required field validation
|
|
var requiredFields = [
|
|
'{{ form.medication.id_for_label }}',
|
|
'{{ form.location.id_for_label }}',
|
|
'{{ form.current_stock.id_for_label }}',
|
|
'{{ form.minimum_stock.id_for_label }}',
|
|
'{{ form.reorder_level.id_for_label }}',
|
|
'{{ form.unit_cost.id_for_label }}'
|
|
];
|
|
|
|
requiredFields.forEach(function(fieldId) {
|
|
var field = $('#' + fieldId);
|
|
if (!field.val()) {
|
|
field.addClass('is-invalid');
|
|
errors.push(field.closest('.form-floating, .mb-3').find('label').text().replace(' *', '') + ' is required');
|
|
isValid = false;
|
|
} else {
|
|
field.removeClass('is-invalid');
|
|
}
|
|
});
|
|
|
|
// Stock level validation
|
|
var currentStock = parseFloat($('#{{ form.current_stock.id_for_label }}').val()) || 0;
|
|
var minimumStock = parseFloat($('#{{ form.minimum_stock.id_for_label }}').val()) || 0;
|
|
var reorderLevel = parseFloat($('#{{ form.reorder_level.id_for_label }}').val()) || 0;
|
|
var maximumStock = parseFloat($('#{{ form.maximum_stock.id_for_label }}').val()) || 0;
|
|
|
|
if (minimumStock > reorderLevel) {
|
|
errors.push('Minimum stock cannot be greater than reorder level');
|
|
isValid = false;
|
|
}
|
|
|
|
if (maximumStock > 0 && currentStock > maximumStock) {
|
|
errors.push('Current stock cannot exceed maximum stock');
|
|
isValid = false;
|
|
}
|
|
|
|
// Cost validation
|
|
var unitCost = parseFloat($('#{{ form.unit_cost.id_for_label }}').val()) || 0;
|
|
if (unitCost <= 0) {
|
|
errors.push('Unit cost must be greater than zero');
|
|
isValid = false;
|
|
}
|
|
|
|
if (!isValid) {
|
|
toastr.error('Please fix the following errors:\n' + errors.join('\n'));
|
|
$('#validation-status').text('Failed').removeClass('text-success text-warning').addClass('text-danger');
|
|
} else {
|
|
$('#validation-status').text('Passed').removeClass('text-danger text-warning').addClass('text-success');
|
|
}
|
|
|
|
return isValid;
|
|
}
|
|
|
|
function validateStockLevels() {
|
|
var currentStock = parseFloat($('#{{ form.current_stock.id_for_label }}').val()) || 0;
|
|
var minimumStock = parseFloat($('#{{ form.minimum_stock.id_for_label }}').val()) || 0;
|
|
var reorderLevel = parseFloat($('#{{ form.reorder_level.id_for_label }}').val()) || 0;
|
|
var maximumStock = parseFloat($('#{{ form.maximum_stock.id_for_label }}').val()) || 0;
|
|
|
|
if (currentStock >= 0 && minimumStock >= 0 && reorderLevel >= 0) {
|
|
if (minimumStock <= reorderLevel && (maximumStock === 0 || currentStock <= maximumStock)) {
|
|
$('#stock-check').html('<i class="fa fa-check text-success me-2"></i>Stock levels valid');
|
|
} else {
|
|
$('#stock-check').html('<i class="fa fa-exclamation-triangle text-warning me-2"></i>Check stock level relationships');
|
|
}
|
|
} else {
|
|
$('#stock-check').html('<i class="fa fa-times text-danger me-2"></i>Invalid stock levels');
|
|
}
|
|
}
|
|
|
|
function validateCosts() {
|
|
var unitCost = parseFloat($('#{{ form.unit_cost.id_for_label }}').val()) || 0;
|
|
var wholesaleCost = parseFloat($('#{{ form.wholesale_cost.id_for_label }}').val()) || 0;
|
|
|
|
if (unitCost > 0) {
|
|
if (wholesaleCost === 0 || unitCost >= wholesaleCost) {
|
|
$('#cost-check').html('<i class="fa fa-check text-success me-2"></i>Cost validation passed');
|
|
} else {
|
|
$('#cost-check').html('<i class="fa fa-exclamation-triangle text-warning me-2"></i>Unit cost less than wholesale cost');
|
|
}
|
|
} else {
|
|
$('#cost-check').html('<i class="fa fa-times text-danger me-2"></i>Invalid unit cost');
|
|
}
|
|
}
|
|
|
|
function validateExpirationDate() {
|
|
var expirationDate = $('#{{ form.expiration_date.id_for_label }}').val();
|
|
|
|
if (expirationDate) {
|
|
var expDate = new Date(expirationDate);
|
|
var today = new Date();
|
|
var daysDiff = Math.ceil((expDate - today) / (1000 * 60 * 60 * 24));
|
|
|
|
if (daysDiff < 0) {
|
|
$('#expiration-check').html('<i class="fa fa-times text-danger me-2"></i>Medication is expired');
|
|
} else if (daysDiff <= 30) {
|
|
$('#expiration-check').html('<i class="fa fa-exclamation-triangle text-warning me-2"></i>Expires within 30 days');
|
|
} else {
|
|
$('#expiration-check').html('<i class="fa fa-check text-success me-2"></i>Expiration date valid');
|
|
}
|
|
} else {
|
|
$('#expiration-check').html('<i class="fa fa-info-circle text-info me-2"></i>No expiration date specified');
|
|
}
|
|
}
|
|
|
|
function validateLocation() {
|
|
var location = $('#{{ form.location.id_for_label }}').val();
|
|
var binLocation = $('#{{ form.bin_location.id_for_label }}').val();
|
|
|
|
if (location) {
|
|
$('#location-check').html('<i class="fa fa-check text-success me-2"></i>Location specified');
|
|
} else {
|
|
$('#location-check').html('<i class="fa fa-times text-danger me-2"></i>Location required');
|
|
}
|
|
}
|
|
|
|
function calculateReorderLevel() {
|
|
var leadTimeDays = parseFloat($('#{{ form.lead_time_days.id_for_label }}').val()) || 7;
|
|
var minimumStock = parseFloat($('#{{ form.minimum_stock.id_for_label }}').val()) || 0;
|
|
|
|
// Simple calculation: minimum stock + (average daily usage * lead time)
|
|
// For now, use minimum stock * 1.5 as a basic calculation
|
|
var suggestedReorderLevel = Math.ceil(minimumStock * 1.5);
|
|
|
|
$('#{{ form.reorder_level.id_for_label }}').val(suggestedReorderLevel);
|
|
toastr.info('Suggested reorder level: ' + suggestedReorderLevel + ' (based on minimum stock and lead time)');
|
|
validateStockLevels();
|
|
}
|
|
|
|
{#function checkDuplicates() {#}
|
|
{# var medicationId = $('#{{ form.medication.id_for_label }}').val();#}
|
|
{# var locationId = $('#{{ form.location.id_for_label }}').val();#}
|
|
{# #}
|
|
{# if (!medicationId || !locationId) {#}
|
|
{# return;#}
|
|
{# }#}
|
|
{# #}
|
|
{# $.ajax({#}
|
|
{# url: '{% url "pharmacy:check_inventory_duplicates" %}',#}
|
|
{# method: 'POST',#}
|
|
{# data: {#}
|
|
{# 'medication_id': medicationId,#}
|
|
{# 'location_id': locationId,#}
|
|
{# {% if object %}'exclude_id': {{ object.pk }},{% endif %}#}
|
|
{# 'csrfmiddlewaretoken': '{{ csrf_token }}'#}
|
|
{# },#}
|
|
{# success: function(response) {#}
|
|
{# if (response.exists) {#}
|
|
{# toastr.warning('Inventory record already exists for this medication at this location');#}
|
|
{# }#}
|
|
{# },#}
|
|
{# error: function() {#}
|
|
{# toastr.error('Failed to check for duplicates');#}
|
|
{# }#}
|
|
{# });#}
|
|
{# }#}
|
|
|
|
{#function saveDraft() {#}
|
|
{# var formData = $('#inventory-form').serialize();#}
|
|
{# #}
|
|
{# $.ajax({#}
|
|
{# url: '{% url "pharmacy:save_inventory_draft" %}',#}
|
|
{# method: 'POST',#}
|
|
{# data: formData,#}
|
|
{# success: function(response) {#}
|
|
{# updateFormStatus('Draft saved', 'success');#}
|
|
{# $('#last-saved').text(new Date().toLocaleTimeString());#}
|
|
{# },#}
|
|
{# error: function() {#}
|
|
{# updateFormStatus('Failed to save draft', 'danger');#}
|
|
{# }#}
|
|
{# });#}
|
|
{# }#}
|
|
|
|
function updateFormStatus(message, type) {
|
|
var alertClass = 'alert-' + type;
|
|
$('#form-status').html('<div class="alert ' + alertClass + '"><i class="fa fa-info-circle me-2"></i>' + message + '</div>');
|
|
}
|
|
|
|
function loadTemplate() {
|
|
// Implementation for loading inventory templates
|
|
toastr.info('Template loading feature coming soon');
|
|
}
|
|
|
|
function clearForm() {
|
|
if (confirm('Are you sure you want to clear all form data?')) {
|
|
$('#inventory-form')[0].reset();
|
|
$('#medication-info').hide();
|
|
$('#total_value').val('');
|
|
updateFormStatus('Form cleared', 'info');
|
|
}
|
|
}
|
|
</script>
|
|
{% endblock %}
|
|
|