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.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'django.contrib.humanize',
] ]
THIRD_PARTY_APPS = [ THIRD_PARTY_APPS = [

View File

@ -341,26 +341,26 @@ class InventoryLocationDetailView(LoginRequiredMixin, DetailView):
# Stock items at this location # Stock items at this location
context['stock_items'] = InventoryStock.objects.filter( context['stock_items'] = InventoryStock.objects.filter(
tenant=self.request.user.tenant, inventory_item__tenant=self.request.user.tenant,
location=location location=location
).select_related('item').order_by('item__item_name') ).select_related('inventory_item').order_by('inventory_item__item_name')
# Location statistics # Location statistics
context['total_items'] = InventoryStock.objects.filter( context['total_items'] = InventoryStock.objects.filter(
tenant=self.request.user.tenant, inventory_item__tenant=self.request.user.tenant,
location=location location=location
).count() ).count()
context['total_quantity'] = InventoryStock.objects.filter( context['total_quantity'] = InventoryStock.objects.filter(
tenant=self.request.user.tenant, inventory_item__tenant=self.request.user.tenant,
location=location location=location
).aggregate(total=Sum('quantity'))['total'] or 0 ).aggregate(total=Sum('quantity_available'))['total'] or 0
context['total_value'] = InventoryStock.objects.filter( context['total_value'] = InventoryStock.objects.filter(
tenant=self.request.user.tenant, inventory_item__tenant=self.request.user.tenant,
location=location location=location
).aggregate( ).aggregate(
total_value=Sum(F('quantity') * F('unit_cost')) total_value=Sum(F('quantity_available') * F('unit_cost'))
)['total_value'] or 0 )['total_value'] or 0
return context return context
@ -503,27 +503,27 @@ class InventoryItemDetailView(LoginRequiredMixin, DetailView):
# Stock information across all locations # Stock information across all locations
context['stock_locations'] = InventoryStock.objects.filter( context['stock_locations'] = InventoryStock.objects.filter(
tenant=self.request.user.tenant, inventory_item__tenant=self.request.user.tenant,
item=item inventory_item=item
).select_related('location').order_by('location__location_name') ).select_related('location').order_by('location__location_name')
# Item statistics # Item statistics
context['total_stock'] = InventoryStock.objects.filter( context['total_stock'] = InventoryStock.objects.filter(
tenant=self.request.user.tenant, inventory_item__tenant=self.request.user.tenant,
item=item inventory_item=item
).aggregate(total=Sum('quantity'))['total'] or 0 ).aggregate(total=Sum('quantity_available'))['total'] or 0
context['total_value'] = InventoryStock.objects.filter( context['total_value'] = InventoryStock.objects.filter(
tenant=self.request.user.tenant, inventory_item__tenant=self.request.user.tenant,
item=item inventory_item=item
).aggregate( ).aggregate(
total_value=Sum(F('quantity') * F('unit_cost')) total_value=Sum(F('quantity_available') * F('unit_cost'))
)['total_value'] or 0 )['total_value'] or 0
# Recent purchase orders for this item # Recent purchase orders for this item
context['recent_orders'] = PurchaseOrderItem.objects.filter( context['recent_orders'] = PurchaseOrderItem.objects.filter(
purchase_order__tenant=self.request.user.tenant, purchase_order__tenant=self.request.user.tenant,
item=item inventory_item=item
).select_related('purchase_order').order_by('-purchase_order__order_date')[:5] ).select_related('purchase_order').order_by('-purchase_order__order_date')[:5]
return context 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 title %}{{ object.item_name }} - Inventory Item Details{% endblock %}
{% block css %} {% block css %}
<link href="{% static 'assets/plugins/datatables.net-bs5/css/dataTables.bootstrap5.min.css' %}" rel="stylesheet" /> <link href="{% static '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 '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/chart.js/dist/Chart.min.css' %}" rel="stylesheet" />
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@ -96,11 +96,11 @@
</tr> </tr>
<tr> <tr>
<td class="fw-bold">Unit Cost:</td> <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>
<tr> <tr>
<td class="fw-bold">List Price:</td> <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> </tr>
</table> </table>
</div> </div>
@ -237,7 +237,7 @@
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
<div class="text-center p-3 bg-success text-white rounded"> <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 class="small">Total Value</div>
</div> </div>
</div> </div>
@ -303,10 +303,15 @@
<span class="text-muted">No expiration</span> <span class="text-muted">No expiration</span>
{% endif %} {% endif %}
</td> </td>
<td> <td>
<span class="badge bg-{{ stock.get_quality_status_color }}"> {% if stock.quality_status == 'GOOD' %}
{{ stock.get_quality_status_display }} <span class="badge bg-success">{{ stock.get_quality_status_display }}</span>
</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>
<td> <td>
<a href="{% url 'inventory:stock_detail' stock.pk %}" class="btn btn-xs btn-outline-primary"> <a href="{% url 'inventory:stock_detail' stock.pk %}" class="btn btn-xs btn-outline-primary">
@ -336,7 +341,7 @@
<div class="panel-heading"> <div class="panel-heading">
<h4 class="panel-title">Recent Transactions</h4> <h4 class="panel-title">Recent Transactions</h4>
<div class="panel-heading-btn"> <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 <i class="fa fa-list"></i> View All
</a> </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-expand"><i class="fa fa-expand"></i></a>
@ -405,7 +410,7 @@
<i class="fa fa-plus me-2"></i>Add Stock <i class="fa fa-plus me-2"></i>Add Stock
</a> </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 <i class="fa fa-adjust me-2"></i>Adjust Stock
</a> </a>
@ -594,32 +599,32 @@
{% endblock %} {% endblock %}
{% block js %} {% block js %}
<script src="{% static 'assets/plugins/datatables.net/js/jquery.dataTables.min.js' %}"></script> <script src="{% static 'plugins/datatables.net/js/dataTables.min.js' %}"></script>
<script src="{% static 'assets/plugins/datatables.net-bs5/js/dataTables.bootstrap5.min.js' %}"></script> <script src="{% static '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/chart.js/dist/chart.js' %}"></script>
<script> <script>
var stockChart; var stockChart;
function printItemLabel() { {#function printItemLabel() {#}
window.open('{% url "inventory:item_label_print" object.pk %}', '_blank'); {# window.open('{% url "inventory:item_label_print" object.pk %}', '_blank');#}
} {# }#}
function showStockChart() { function showStockChart() {
$('#stockChartModal').modal('show'); $('#stockChartModal').modal('show');
loadStockChart(); loadStockChart();
} }
function loadStockChart() { {#function loadStockChart() {#}
$.ajax({ {# $.ajax({#}
url: '{% url "inventory:item_stock_history" object.pk %}', {# url: '{% url "inventory:item_stock_history" object.pk %}',#}
success: function(data) { {# success: function(data) {#}
renderStockChart(data); {# renderStockChart(data);#}
}, {# },#}
error: function() { {# error: function() {#}
toastr.error('Failed to load stock history data'); {# toastr.error('Failed to load stock history data');#}
} {# }#}
}); {# });#}
} {# }#}
function renderStockChart(data) { function renderStockChart(data) {
var ctx = document.getElementById('stock-chart').getContext('2d'); 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 title %}{% if object %}Edit {{ object.item_name }}{% else %}Add New Item{% endif %} - Inventory{% endblock %}
{% block css %} {% block css %}
<link href="{% static 'assets/plugins/select2/dist/css/select2.min.css' %}" rel="stylesheet" /> <link href="{% static '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 '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/jquery-file-upload/css/jquery.fileupload.css' %}" rel="stylesheet" />#}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@ -642,30 +642,30 @@ function toggleConditionalFields() {
} }
} }
function generateItemCode() { {#function generateItemCode() {#}
var category = $('#id_category').val(); {# var category = $('#id_category').val();#}
var itemType = $('#id_item_type').val(); {# var itemType = $('#id_item_type').val();#}
{# #}
if (!category) { {# if (!category) {#}
toastr.warning('Please select a category first'); {# toastr.warning('Please select a category first');#}
return; {# return;#}
} {# }#}
{# #}
$.ajax({ {# $.ajax({#}
url: '{% url "inventory:generate_item_code" %}', {# url: '{% url "inventory:generate_item_code" %}',#}
data: { {# data: {#}
'category': category, {# 'category': category,#}
'item_type': itemType {# 'item_type': itemType#}
}, {# },#}
success: function(response) { {# success: function(response) {#}
$('#id_item_code').val(response.item_code); {# $('#id_item_code').val(response.item_code);#}
toastr.success('Item code generated: ' + response.item_code); {# toastr.success('Item code generated: ' + response.item_code);#}
}, {# },#}
error: function() { {# error: function() {#}
toastr.error('Failed to generate item code'); {# toastr.error('Failed to generate item code');#}
} {# }#}
}); {# });#}
} {# }#}
function scanBarcode() { function scanBarcode() {
// Implementation for barcode scanning // Implementation for barcode scanning

File diff suppressed because it is too large Load Diff

View File

@ -465,10 +465,10 @@
{% endif %} {% endif %}
</td> </td>
<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> <i class="fas fa-eye"></i>
</a> </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> <i class="fas fa-edit"></i>
</a> </a>
</td> </td>

View File

@ -301,44 +301,7 @@
<!-- Pagination --> <!-- Pagination -->
{% if is_paginated %} {% if is_paginated %}
<div class="pagination-wrapper"> {% include 'partial/pagination.html' %}
<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>
{% endif %} {% endif %}
</div> </div>
</div> </div>