This commit is contained in:
Marwan Alwali 2025-08-31 12:21:16 +03:00
parent b9b8c69129
commit 1f0a6bff5f
11 changed files with 3145 additions and 684 deletions

Binary file not shown.

View File

@ -38,6 +38,7 @@ DJANGO_APPS = [
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.humanize',
]
THIRD_PARTY_APPS = [

View File

@ -341,26 +341,26 @@ class InventoryLocationDetailView(LoginRequiredMixin, DetailView):
# Stock items at this location
context['stock_items'] = InventoryStock.objects.filter(
tenant=self.request.user.tenant,
inventory_item__tenant=self.request.user.tenant,
location=location
).select_related('item').order_by('item__item_name')
).select_related('inventory_item').order_by('inventory_item__item_name')
# Location statistics
context['total_items'] = InventoryStock.objects.filter(
tenant=self.request.user.tenant,
inventory_item__tenant=self.request.user.tenant,
location=location
).count()
context['total_quantity'] = InventoryStock.objects.filter(
tenant=self.request.user.tenant,
inventory_item__tenant=self.request.user.tenant,
location=location
).aggregate(total=Sum('quantity'))['total'] or 0
).aggregate(total=Sum('quantity_available'))['total'] or 0
context['total_value'] = InventoryStock.objects.filter(
tenant=self.request.user.tenant,
inventory_item__tenant=self.request.user.tenant,
location=location
).aggregate(
total_value=Sum(F('quantity') * F('unit_cost'))
total_value=Sum(F('quantity_available') * F('unit_cost'))
)['total_value'] or 0
return context
@ -503,27 +503,27 @@ class InventoryItemDetailView(LoginRequiredMixin, DetailView):
# Stock information across all locations
context['stock_locations'] = InventoryStock.objects.filter(
tenant=self.request.user.tenant,
item=item
inventory_item__tenant=self.request.user.tenant,
inventory_item=item
).select_related('location').order_by('location__location_name')
# Item statistics
context['total_stock'] = InventoryStock.objects.filter(
tenant=self.request.user.tenant,
item=item
).aggregate(total=Sum('quantity'))['total'] or 0
inventory_item__tenant=self.request.user.tenant,
inventory_item=item
).aggregate(total=Sum('quantity_available'))['total'] or 0
context['total_value'] = InventoryStock.objects.filter(
tenant=self.request.user.tenant,
item=item
inventory_item__tenant=self.request.user.tenant,
inventory_item=item
).aggregate(
total_value=Sum(F('quantity') * F('unit_cost'))
total_value=Sum(F('quantity_available') * F('unit_cost'))
)['total_value'] or 0
# Recent purchase orders for this item
context['recent_orders'] = PurchaseOrderItem.objects.filter(
purchase_order__tenant=self.request.user.tenant,
item=item
inventory_item=item
).select_related('purchase_order').order_by('-purchase_order__order_date')[:5]
return context

File diff suppressed because it is too large Load Diff

View File

@ -4,9 +4,9 @@
{% block title %}{{ object.item_name }} - Inventory Item Details{% endblock %}
{% block css %}
<link href="{% static 'assets/plugins/datatables.net-bs5/css/dataTables.bootstrap5.min.css' %}" rel="stylesheet" />
<link href="{% static 'assets/plugins/datatables.net-responsive-bs5/css/responsive.bootstrap5.min.css' %}" rel="stylesheet" />
<link href="{% static 'assets/plugins/chart.js/dist/Chart.min.css' %}" rel="stylesheet" />
<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/chart.js/dist/Chart.min.css' %}" rel="stylesheet" />
{% endblock %}
{% block content %}
@ -96,11 +96,11 @@
</tr>
<tr>
<td class="fw-bold">Unit Cost:</td>
<td class="fw-bold text-success">${{ object.unit_cost }}</td>
<td class="fw-bold text-success"><span class="symbol">&#xea;</span>{{ object.unit_cost|floatformat:'2g' }}</td>
</tr>
<tr>
<td class="fw-bold">List Price:</td>
<td>${{ object.list_price }}</td>
<td><span class="symbol">&#xea;</span>{{ object.list_price|floatformat:'2g' }}</td>
</tr>
</table>
</div>
@ -237,7 +237,7 @@
</div>
<div class="col-md-3">
<div class="text-center p-3 bg-success text-white rounded">
<div class="fs-24px fw-bold">${{ object.total_value|floatformat:2 }}</div>
<div class="fs-24px fw-bold"><span class="symbol">&#xea;</span>{{ object.total_value|floatformat:'2g' }}</div>
<div class="small">Total Value</div>
</div>
</div>
@ -303,10 +303,15 @@
<span class="text-muted">No expiration</span>
{% endif %}
</td>
<td>
<span class="badge bg-{{ stock.get_quality_status_color }}">
{{ stock.get_quality_status_display }}
</span>
{% 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 href="{% url 'inventory:stock_detail' stock.pk %}" class="btn btn-xs btn-outline-primary">
@ -336,7 +341,7 @@
<div class="panel-heading">
<h4 class="panel-title">Recent Transactions</h4>
<div class="panel-heading-btn">
<a href="{% url 'inventory:transaction_list' %}?item={{ object.pk }}" class="btn btn-xs btn-outline-secondary me-2">
<a href="#" class="btn btn-xs btn-outline-secondary me-2">
<i class="fa fa-list"></i> View All
</a>
<a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-expand"><i class="fa fa-expand"></i></a>
@ -405,7 +410,7 @@
<i class="fa fa-plus me-2"></i>Add Stock
</a>
<a href="{% url 'inventory:stock_adjustment_create' %}?item={{ object.pk }}" class="btn btn-warning">
<a href="#" class="btn btn-warning">
<i class="fa fa-adjust me-2"></i>Adjust Stock
</a>
@ -594,32 +599,32 @@
{% endblock %}
{% block js %}
<script src="{% static 'assets/plugins/datatables.net/js/jquery.dataTables.min.js' %}"></script>
<script src="{% static 'assets/plugins/datatables.net-bs5/js/dataTables.bootstrap5.min.js' %}"></script>
<script src="{% static 'assets/plugins/chart.js/dist/Chart.min.js' %}"></script>
<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/chart.js/dist/chart.js' %}"></script>
<script>
var stockChart;
function printItemLabel() {
window.open('{% url "inventory:item_label_print" object.pk %}', '_blank');
}
{#function printItemLabel() {#}
{# window.open('{% url "inventory:item_label_print" object.pk %}', '_blank');#}
{# }#}
function showStockChart() {
$('#stockChartModal').modal('show');
loadStockChart();
}
function loadStockChart() {
$.ajax({
url: '{% url "inventory:item_stock_history" object.pk %}',
success: function(data) {
renderStockChart(data);
},
error: function() {
toastr.error('Failed to load stock history data');
}
});
}
{#function loadStockChart() {#}
{# $.ajax({#}
{# url: '{% url "inventory:item_stock_history" object.pk %}',#}
{# success: function(data) {#}
{# renderStockChart(data);#}
{# },#}
{# error: function() {#}
{# toastr.error('Failed to load stock history data');#}
{# }#}
{# });#}
{# }#}
function renderStockChart(data) {
var ctx = document.getElementById('stock-chart').getContext('2d');

View File

@ -4,9 +4,9 @@
{% block title %}{% if object %}Edit {{ object.item_name }}{% else %}Add New Item{% endif %} - Inventory{% endblock %}
{% block css %}
<link href="{% static 'assets/plugins/select2/dist/css/select2.min.css' %}" rel="stylesheet" />
<link href="{% static 'assets/plugins/bootstrap-datepicker/dist/css/bootstrap-datepicker.min.css' %}" rel="stylesheet" />
<link href="{% static 'assets/plugins/jquery-file-upload/css/jquery.fileupload.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" />
{#<link href="{% static 'plugins/jquery-file-upload/css/jquery.fileupload.css' %}" rel="stylesheet" />#}
{% endblock %}
{% block content %}
@ -642,30 +642,30 @@ function toggleConditionalFields() {
}
}
function generateItemCode() {
var category = $('#id_category').val();
var itemType = $('#id_item_type').val();
if (!category) {
toastr.warning('Please select a category first');
return;
}
$.ajax({
url: '{% url "inventory:generate_item_code" %}',
data: {
'category': category,
'item_type': itemType
},
success: function(response) {
$('#id_item_code').val(response.item_code);
toastr.success('Item code generated: ' + response.item_code);
},
error: function() {
toastr.error('Failed to generate item code');
}
});
}
{#function generateItemCode() {#}
{# var category = $('#id_category').val();#}
{# var itemType = $('#id_item_type').val();#}
{# #}
{# if (!category) {#}
{# toastr.warning('Please select a category first');#}
{# return;#}
{# }#}
{# #}
{# $.ajax({#}
{# url: '{% url "inventory:generate_item_code" %}',#}
{# data: {#}
{# 'category': category,#}
{# 'item_type': itemType#}
{# },#}
{# success: function(response) {#}
{# $('#id_item_code').val(response.item_code);#}
{# toastr.success('Item code generated: ' + response.item_code);#}
{# },#}
{# error: function() {#}
{# toastr.error('Failed to generate item code');#}
{# }#}
{# });#}
{# }#}
function scanBarcode() {
// Implementation for barcode scanning

File diff suppressed because it is too large Load Diff

View File

@ -465,10 +465,10 @@
{% endif %}
</td>
<td>
<a class="me-3" href="{% url 'inventory:stock_detail' stock.id %}">
<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="me-3" href="{% url 'inventory:stock_update' stock.id %}">
<a class="btn btn-xs btn-outline-secondary" href="{% url 'inventory:stock_update' stock.id %}">
<i class="fas fa-edit"></i>
</a>
</td>

View File

@ -301,44 +301,7 @@
<!-- Pagination -->
{% if is_paginated %}
<div class="pagination-wrapper">
<div class="row">
<div class="col-sm-12 col-md-5">
<div class="dataTables_info">
Showing {{ page_obj.start_index }} to {{ page_obj.end_index }} of {{ paginator.count }} entries
</div>
</div>
<div class="col-sm-12 col-md-7">
<div class="dataTables_paginate">
<ul class="pagination">
{% if page_obj.has_previous %}
<li class="paginate_button page-item previous">
<a href="?page={{ page_obj.previous_page_number }}" class="page-link">Previous</a>
</li>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<li class="paginate_button page-item active">
<a href="#" class="page-link">{{ num }}</a>
</li>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<li class="paginate_button page-item">
<a href="?page={{ num }}" class="page-link">{{ num }}</a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="paginate_button page-item next">
<a href="?page={{ page_obj.next_page_number }}" class="page-link">Next</a>
</li>
{% endif %}
</ul>
</div>
</div>
</div>
</div>
{% include 'partial/pagination.html' %}
{% endif %}
</div>
</div>