Marwan Alwali b9b8c69129 update
2025-08-31 10:47:23 +03:00

573 lines
28 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends "base.html" %}
{% load static %}
{% block title %}{% if object %}Edit Stock{% else %}New Stock Entry{% endif %}{% endblock %}
{% block content %}
<div class="d-flex align-items-center mb-3">
<div>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'inventory:dashboard' %}">Inventory</a></li>
<li class="breadcrumb-item"><a href="{% url 'inventory:stock_list' %}">Stock</a></li>
<li class="breadcrumb-item active">{% if object %}Edit{% else %}New{% endif %}</li>
</ol>
<h1 class="page-header mb-0">
{% if object %}Edit Stock - {{ object.item.name }}{% else %}Create Stock Entry{% endif %}
</h1>
</div>
<div class="ms-auto">
<a href="{% url 'inventory:stock_list' %}" class="btn btn-secondary">
<i class="fas fa-arrow-left me-2"></i>Back to Stock List
</a>
</div>
</div>
<div class="row">
<div class="col-xl-8">
<div class="panel panel-inverse" data-sortable-id="index-1">
<div class="panel-heading">
<h4 class="panel-title"><i class="fas fa-boxes me-2"></i> {{ _("Stock Information")}}</h4>
<div class="panel-heading-btn">
{# <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-success" data-toggle="panel-reload"><i class="fa fa-redo"></i></a>#}
{# <a href="javascript:;" class="btn btn-xs btn-icon btn-warning" data-toggle="panel-collapse"><i class="fa fa-minus"></i></a>#}
{# <a href="javascript:;" class="btn btn-xs btn-icon btn-danger" data-toggle="panel-remove"><i class="fa fa-times"></i></a>#}
</div>
</div>
<div class="panel-body">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
<form method="post" novalidate>
{% csrf_token %}
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.item.id_for_label }}" class="form-label">Item *</label>
<select class="form-select {% if form.item.errors %}is-invalid{% endif %}"
id="{{ form.item.id_for_label }}"
name="{{ form.item.name }}"
required>
<option value="">Select Item</option>
{% for choice in form.item.field.choices %}
{% if choice.0 %}
<option value="{{ choice.0 }}" {% if choice.0 == form.item.value %}selected{% endif %}>
{{ choice.1 }}
</option>
{% endif %}
{% endfor %}
</select>
{% if form.item.errors %}
<div class="invalid-feedback">
{{ form.item.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.location.id_for_label }}" class="form-label">Location *</label>
<select class="form-select {% if form.location.errors %}is-invalid{% endif %}"
id="{{ form.location.id_for_label }}"
name="{{ form.location.name }}"
required>
<option value="">Select Location</option>
{% for choice in form.location.field.choices %}
{% if choice.0 %}
<option value="{{ choice.0 }}" {% if choice.0 == form.location.value %}selected{% endif %}>
{{ choice.1 }}
</option>
{% endif %}
{% endfor %}
</select>
{% if form.location.errors %}
<div class="invalid-feedback">
{{ form.location.errors.0 }}
</div>
{% endif %}
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="mb-3">
<label for="{{ form.current_quantity.id_for_label }}" class="form-label">Current Quantity *</label>
<input type="number"
class="form-control {% if form.current_quantity.errors %}is-invalid{% endif %}"
id="{{ form.current_quantity.id_for_label }}"
name="{{ form.current_quantity.name }}"
value="{{ form.current_quantity.value|default:'' }}"
min="0"
step="0.01"
placeholder="Enter current quantity"
required>
{% if form.current_quantity.errors %}
<div class="invalid-feedback">
{{ form.current_quantity.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label for="{{ form.minimum_quantity.id_for_label }}" class="form-label">Minimum Quantity *</label>
<input type="number"
class="form-control {% if form.minimum_quantity.errors %}is-invalid{% endif %}"
id="{{ form.minimum_quantity.id_for_label }}"
name="{{ form.minimum_quantity.name }}"
value="{{ form.minimum_quantity.value|default:'' }}"
min="0"
step="0.01"
placeholder="Enter minimum quantity"
required>
{% if form.minimum_quantity.errors %}
<div class="invalid-feedback">
{{ form.minimum_quantity.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label for="{{ form.reorder_level.id_for_label }}" class="form-label">Reorder Level *</label>
<input type="number"
class="form-control {% if form.reorder_level.errors %}is-invalid{% endif %}"
id="{{ form.reorder_level.id_for_label }}"
name="{{ form.reorder_level.name }}"
value="{{ form.reorder_level.value|default:'' }}"
min="0"
step="0.01"
placeholder="Enter reorder level"
required>
{% if form.reorder_level.errors %}
<div class="invalid-feedback">
{{ form.reorder_level.errors.0 }}
</div>
{% endif %}
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="mb-3">
<label for="{{ form.maximum_quantity.id_for_label }}" class="form-label">Maximum Quantity</label>
<input type="number"
class="form-control {% if form.maximum_quantity.errors %}is-invalid{% endif %}"
id="{{ form.maximum_quantity.id_for_label }}"
name="{{ form.maximum_quantity.name }}"
value="{{ form.maximum_quantity.value|default:'' }}"
min="0"
step="0.01"
placeholder="Enter maximum quantity">
{% if form.maximum_quantity.errors %}
<div class="invalid-feedback">
{{ form.maximum_quantity.errors.0 }}
</div>
{% endif %}
<div class="form-text">
Optional: Maximum storage capacity for this location
</div>
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label for="{{ form.unit_cost.id_for_label }}" class="form-label">Unit Cost *</label>
<div class="input-group">
<span class="input-group-text">$</span>
<input type="number"
class="form-control {% if form.unit_cost.errors %}is-invalid{% endif %}"
id="{{ form.unit_cost.id_for_label }}"
name="{{ form.unit_cost.name }}"
value="{{ form.unit_cost.value|default:'' }}"
min="0"
step="0.01"
placeholder="0.00"
required>
</div>
{% if form.unit_cost.errors %}
<div class="invalid-feedback">
{{ form.unit_cost.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label class="form-label">Total Value</label>
<div class="input-group">
<span class="input-group-text">$</span>
<input type="text"
class="form-control"
id="totalValue"
readonly
placeholder="0.00">
</div>
<div class="form-text">
Automatically calculated: Quantity × Unit Cost
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.batch_number.id_for_label }}" class="form-label">Batch Number</label>
<input type="text"
class="form-control {% if form.batch_number.errors %}is-invalid{% endif %}"
id="{{ form.batch_number.id_for_label }}"
name="{{ form.batch_number.name }}"
value="{{ form.batch_number.value|default:'' }}"
placeholder="Enter batch number">
{% if form.batch_number.errors %}
<div class="invalid-feedback">
{{ form.batch_number.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.expiry_date.id_for_label }}" class="form-label">Expiry Date</label>
<input type="date"
class="form-control {% if form.expiry_date.errors %}is-invalid{% endif %}"
id="{{ form.expiry_date.id_for_label }}"
name="{{ form.expiry_date.name }}"
value="{{ form.expiry_date.value|default:'' }}">
{% if form.expiry_date.errors %}
<div class="invalid-feedback">
{{ form.expiry_date.errors.0 }}
</div>
{% endif %}
</div>
</div>
</div>
<div class="mb-3">
<label for="{{ form.notes.id_for_label }}" class="form-label">Notes</label>
<textarea class="form-control {% if form.notes.errors %}is-invalid{% endif %}"
id="{{ form.notes.id_for_label }}"
name="{{ form.notes.name }}"
rows="4"
placeholder="Enter any additional notes about this stock entry...">{{ form.notes.value|default:'' }}</textarea>
{% if form.notes.errors %}
<div class="invalid-feedback">
{{ form.notes.errors.0 }}
</div>
{% endif %}
</div>
<div class="d-flex justify-content-between">
<div>
{% if object %}
<a href="{% url 'inventory:stock_detail' object.pk %}" class="btn btn-secondary">
<i class="fas fa-times me-2"></i>Cancel
</a>
{% else %}
<a href="{% url 'inventory:stock_list' %}" class="btn btn-secondary">
<i class="fas fa-times me-2"></i>Cancel
</a>
{% endif %}
</div>
<div>
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-2"></i>
{% if object %}Update Stock{% else %}Create Stock Entry{% endif %}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="col-xl-4">
<!-- Stock Level Guidelines -->
<div class="panel panel-inverse" data-sortable-id="index-2">
<div class="panel-heading">
<h4 class="panel-title"><i class="fas fa-info-circle me-2"></i> {{ _("Stock Level Guidelines")}}</h4>
<div class="panel-heading-btn">
{# <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-success" data-toggle="panel-reload"><i class="fa fa-redo"></i></a>#}
{# <a href="javascript:;" class="btn btn-xs btn-icon btn-warning" data-toggle="panel-collapse"><i class="fa fa-minus"></i></a>#}
{# <a href="javascript:;" class="btn btn-xs btn-icon btn-danger" data-toggle="panel-remove"><i class="fa fa-times"></i></a>#}
</div>
</div>
<div class="panel-body">
<div class="mb-3">
<h6><i class="fas fa-exclamation-triangle text-danger me-2"></i>Minimum Quantity</h6>
<p class="small text-muted">
The lowest acceptable stock level. When stock reaches this level,
immediate action is required to prevent stockouts.
</p>
</div>
<div class="mb-3">
<h6><i class="fas fa-bell text-warning me-2"></i>Reorder Level</h6>
<p class="small text-muted">
The stock level that triggers a reorder. Should be set higher than
minimum quantity to account for lead times.
</p>
</div>
<div class="mb-3">
<h6><i class="fas fa-check-circle text-success me-2"></i>Maximum Quantity</h6>
<p class="small text-muted">
The maximum storage capacity for this location. Helps prevent
overstocking and storage issues.
</p>
</div>
<div class="alert alert-info">
<small>
<strong>Recommended:</strong> Reorder Level should be 2-3 times
the Minimum Quantity to ensure adequate buffer stock.
</small>
</div>
</div>
</div>
<!-- Item Information -->
<div class="card mb-4" id="itemInfoCard" style="display: none;">
<div class="card-header">
<h5 class="card-title">
<i class="fas fa-cube me-2"></i>
Item Information
</h5>
</div>
<div class="card-body" id="itemInfoContent">
<!-- Item details will be loaded here -->
</div>
</div>
<!-- Location Information -->
<div class="card mb-4" id="locationInfoCard" style="display: none;">
<div class="card-header">
<h5 class="card-title">
<i class="fas fa-map-marker-alt me-2"></i>
Location Information
</h5>
</div>
<div class="card-body" id="locationInfoContent">
<!-- Location details will be loaded here -->
</div>
</div>
<!-- Quick Actions -->
<div class="panel panel-inverse" data-sortable-id="index-3">
<div class="panel-heading">
<h4 class="panel-title"><i class="fas fa-bolt me-2"></i> {{ _("Quick Actions")}}</h4>
<div class="panel-heading-btn">
{# <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-success" data-toggle="panel-reload"><i class="fa fa-redo"></i></a>#}
{# <a href="javascript:;" class="btn btn-xs btn-icon btn-warning" data-toggle="panel-collapse"><i class="fa fa-minus"></i></a>#}
{# <a href="javascript:;" class="btn btn-xs btn-icon btn-danger" data-toggle="panel-remove"><i class="fa fa-times"></i></a>#}
</div>
</div>
<div class="panel-body">
<div class="d-grid gap-2">
<button type="button" class="btn btn-outline-primary" onclick="calculateRecommendedLevels()">
<i class="fas fa-calculator me-2"></i>Calculate Recommended Levels
</button>
<button type="button" class="btn btn-outline-secondary" onclick="copyFromExisting()">
<i class="fas fa-copy me-2"></i>Copy from Existing Stock
</button>
<button type="button" class="btn btn-outline-info" onclick="viewItemHistory()">
<i class="fas fa-history me-2"></i>View Item History
</button>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const itemSelect = document.getElementById('{{ form.item.id_for_label }}');
const locationSelect = document.getElementById('{{ form.location.id_for_label }}');
const currentQuantityInput = document.getElementById('{{ form.current_quantity.id_for_label }}');
const unitCostInput = document.getElementById('{{ form.unit_cost.id_for_label }}');
const totalValueInput = document.getElementById('totalValue');
const minimumQuantityInput = document.getElementById('{{ form.minimum_quantity.id_for_label }}');
const reorderLevelInput = document.getElementById('{{ form.reorder_level.id_for_label }}');
// Load item information when item changes
itemSelect.addEventListener('change', function() {
const itemId = this.value;
if (itemId) {
loadItemInfo(itemId);
} else {
document.getElementById('itemInfoCard').style.display = 'none';
}
});
// Load location information when location changes
locationSelect.addEventListener('change', function() {
const locationId = this.value;
if (locationId) {
loadLocationInfo(locationId);
} else {
document.getElementById('locationInfoCard').style.display = 'none';
}
});
// Calculate total value when quantity or unit cost changes
function calculateTotalValue() {
const quantity = parseFloat(currentQuantityInput.value) || 0;
const unitCost = parseFloat(unitCostInput.value) || 0;
const totalValue = quantity * unitCost;
totalValueInput.value = totalValue.toFixed(2);
}
currentQuantityInput.addEventListener('input', calculateTotalValue);
unitCostInput.addEventListener('input', calculateTotalValue);
// Validate stock levels
function validateStockLevels() {
const minimum = parseFloat(minimumQuantityInput.value) || 0;
const reorder = parseFloat(reorderLevelInput.value) || 0;
if (reorder <= minimum && reorder > 0) {
showWarning('Reorder level should be higher than minimum quantity');
}
}
minimumQuantityInput.addEventListener('blur', validateStockLevels);
reorderLevelInput.addEventListener('blur', validateStockLevels);
// Initial calculation
calculateTotalValue();
});
function loadItemInfo(itemId) {
// Simulate loading item information
const itemInfoCard = document.getElementById('itemInfoCard');
const itemInfoContent = document.getElementById('itemInfoContent');
itemInfoContent.innerHTML = `
<div class="text-center">
<div class="spinner-border spinner-border-sm text-primary mb-2" role="status"></div>
<div class="small">Loading item information...</div>
</div>
`;
itemInfoCard.style.display = 'block';
// Simulate API call
setTimeout(() => {
itemInfoContent.innerHTML = `
<div class="mb-2">
<strong>SKU:</strong> ITM-001
</div>
<div class="mb-2">
<strong>Category:</strong> Medical Supplies
</div>
<div class="mb-2">
<strong>Unit:</strong> Each
</div>
<div class="mb-2">
<strong>Current Stock:</strong> 150 units
</div>
<div>
<strong>Avg. Monthly Usage:</strong> 45 units
</div>
`;
}, 1000);
}
function loadLocationInfo(locationId) {
// Simulate loading location information
const locationInfoCard = document.getElementById('locationInfoCard');
const locationInfoContent = document.getElementById('locationInfoContent');
locationInfoContent.innerHTML = `
<div class="text-center">
<div class="spinner-border spinner-border-sm text-primary mb-2" role="status"></div>
<div class="small">Loading location information...</div>
</div>
`;
locationInfoCard.style.display = 'block';
// Simulate API call
setTimeout(() => {
locationInfoContent.innerHTML = `
<div class="mb-2">
<strong>Zone:</strong> A
</div>
<div class="mb-2">
<strong>Aisle:</strong> 3
</div>
<div class="mb-2">
<strong>Shelf:</strong> B2
</div>
<div class="mb-2">
<strong>Capacity:</strong> 500 units
</div>
<div>
<strong>Temperature:</strong> Room temperature
</div>
`;
}, 1000);
}
function calculateRecommendedLevels() {
// In a real implementation, this would calculate based on usage patterns
const avgMonthlyUsage = 45; // This would come from actual data
const leadTimeDays = 7; // This would be configurable
const dailyUsage = avgMonthlyUsage / 30;
const recommendedMinimum = Math.ceil(dailyUsage * leadTimeDays);
const recommendedReorder = Math.ceil(recommendedMinimum * 2);
document.getElementById('{{ form.minimum_quantity.id_for_label }}').value = recommendedMinimum;
document.getElementById('{{ form.reorder_level.id_for_label }}').value = recommendedReorder;
showSuccess(`Recommended levels calculated: Minimum: ${recommendedMinimum}, Reorder: ${recommendedReorder}`);
}
function copyFromExisting() {
// In a real implementation, this would open a modal to select existing stock
alert('Copy from existing stock functionality would be implemented here.');
}
function viewItemHistory() {
// In a real implementation, this would show item usage history
alert('Item history functionality would be implemented here.');
}
function showWarning(message) {
// Simple warning display - in a real implementation, use a proper notification system
const alertDiv = document.createElement('div');
alertDiv.className = 'alert alert-warning alert-dismissible fade show';
alertDiv.innerHTML = `
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.querySelector('.card-body').insertBefore(alertDiv, document.querySelector('form'));
}
function showSuccess(message) {
// Simple success display - in a real implementation, use a proper notification system
const alertDiv = document.createElement('div');
alertDiv.className = 'alert alert-success alert-dismissible fade show';
alertDiv.innerHTML = `
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.querySelector('.card-body').insertBefore(alertDiv, document.querySelector('form'));
}
</script>
{% endblock %}