some fixes and improvements

This commit is contained in:
ismail 2025-07-10 19:20:46 +03:00
parent 19b2913a4e
commit db751f7837
15 changed files with 657 additions and 892 deletions

BIN
dbtest.sqlite3 Normal file

Binary file not shown.

View File

@ -40,3 +40,46 @@ def breadcrumbs(request):
url = "/" + "/".join(path[: i + 1]) + "/" url = "/" + "/".join(path[: i + 1]) + "/"
breadcrumbs.append({"name": path[i].capitalize(), "url": url}) breadcrumbs.append({"name": path[i].capitalize(), "url": url})
return {"breadcrumbs": breadcrumbs} return {"breadcrumbs": breadcrumbs}
def user_types(request):
"""
Sets various flags indicating the user's role types.
The flags are:
- request.is_dealer
- request.is_staff
- request.is_manager
- request.is_accountant
- request.is_sales
- request.is_inventory
:param request: The request object to set the flags upon.
:type request: HttpRequest
"""
request.is_dealer = False
request.is_staff = False
request.is_manager = False
request.is_accountant = False
request.is_sales = False
request.is_inventory = False
if hasattr(request.user, "dealer"):
request.is_dealer = True
elif hasattr(request.user, "staffmember"):
request.is_staff = True
staff = getattr(request.user.staffmember, "staff")
if "Accountant" in staff.groups.values_list("name", flat=True):
request.is_accountant = True
return {"is_accountant": True}
elif "Manager" in staff.groups.values_list("name", flat=True):
request.is_manager = True
return {"is_manager": True}
elif "Sales" in staff.groups.values_list("name", flat=True):
request.is_sales = True
return {"is_sales": True}
elif "Inventory" in staff.groups.values_list("name", flat=True):
request.is_inventory = True
return {"is_inventory": True}
return {}

View File

@ -107,11 +107,11 @@ class InjectDealerMiddleware:
staff = getattr(request.user.staffmember, "staff") staff = getattr(request.user.staffmember, "staff")
if "Accountant" in staff.groups.values_list("name", flat=True): if "Accountant" in staff.groups.values_list("name", flat=True):
request.is_accountant = True request.is_accountant = True
if "Manager" in staff.groups.values_list("name", flat=True): elif "Manager" in staff.groups.values_list("name", flat=True):
request.is_manager = True request.is_manager = True
if "Sales" in staff.groups.values_list("name", flat=True): elif "Sales" in staff.groups.values_list("name", flat=True):
request.is_sales = True request.is_sales = True
if "Inventory" in staff.groups.values_list("name", flat=True): elif "Inventory" in staff.groups.values_list("name", flat=True):
request.is_inventory = True request.is_inventory = True
except Exception: except Exception:
pass pass

View File

@ -19,7 +19,7 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
('appointment', '0001_initial'), ('appointment', '__first__'),
('auth', '0012_alter_user_first_name_max_length'), ('auth', '0012_alter_user_first_name_max_length'),
('contenttypes', '0002_remove_content_type_name'), ('contenttypes', '0002_remove_content_type_name'),
('django_ledger', '0021_alter_bankaccountmodel_account_model_and_more'), ('django_ledger', '0021_alter_bankaccountmodel_account_model_and_more'),

View File

@ -1199,17 +1199,20 @@ class Staff(models.Model, LocalizedNameMixin):
self.clear_groups() self.clear_groups()
try: try:
self.user.groups.add(group) self.user.groups.add(group)
if "accountant" in group.name.lower() or "manager" in group.name.lower(): if "accountant" in group.name.lower():
self.add_superuser_permission() self.add_superuser_permission()
except Exception as e: except Exception as e:
print(e) print(e)
def add_superuser_permission(self): def add_superuser_permission(self):
pass entity = self.dealer.entity
# self.dealer.entity.managers.add(self.user) if entity.managers.count() == 0:
entity.managers.add(self.user)
def remove_superuser_permission(self): def remove_superuser_permission(self):
pass entity = self.dealer.entity
# self.dealer.entity.managers.remove(self.user) if self.user in entity.managers.all():
entity.managers.remove(self.user)
class Meta: class Meta:
verbose_name = _("Staff") verbose_name = _("Staff")
@ -2708,7 +2711,8 @@ class CustomGroup(models.Model):
"tasks", "tasks",
"activity", "activity",
"payment", "payment",
'vendor'], "vendor",
],
other_perms=[ other_perms=[
"view_car", "view_car",
"view_carlocation", "view_carlocation",

View File

@ -1,4 +1,5 @@
import logging import logging
from .models import Dealer
from django.core.exceptions import ImproperlyConfigured,ValidationError from django.core.exceptions import ImproperlyConfigured,ValidationError
from django.contrib.auth.mixins import LoginRequiredMixin,PermissionRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin,PermissionRequiredMixin
from django_ledger.forms.bill import ( from django_ledger.forms.bill import (
@ -30,7 +31,8 @@ from django_ledger.models import PurchaseOrderModel,EstimateModel,BillModel
from django.views.generic.detail import SingleObjectMixin from django.views.generic.detail import SingleObjectMixin
from django.views.generic.edit import UpdateView from django.views.generic.edit import UpdateView
from django.views.generic.base import RedirectView from django.views.generic.base import RedirectView
from .models import Dealer from django.views.generic.list import ListView
from django.utils.translation import gettext_lazy as _
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -319,7 +321,12 @@ class BasePurchaseOrderActionActionView(LoginRequiredMixin,
f"while performing action '{self.action_name}' on Purchase Order ID: {po_model.pk}. " f"while performing action '{self.action_name}' on Purchase Order ID: {po_model.pk}. "
f"Error: {e}" f"Error: {e}"
) )
print(e) except AttributeError as e:
logger.warning(
f"User {user_username} encountered an AttributeError "
f"while performing action '{self.action_name}' on Purchase Order ID: {po_model.pk}. "
f"Error: {e}"
)
return response return response
class BillModelDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): class BillModelDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
@ -642,3 +649,41 @@ class BaseBillActionView(LoginRequiredMixin,PermissionRequiredMixin, RedirectVie
level=messages.ERROR, level=messages.ERROR,
extra_tags='is-danger') extra_tags='is-danger')
return response return response
class InventoryListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
template_name = 'django_ledger/inventory/inventory_list.html'
context_object_name = 'inventory_list'
http_method_names = ['get']
def get_context_data(self, *, object_list=None, **kwargs):
context = super(InventoryListView, self).get_context_data(**kwargs)
qs = self.get_queryset()
# evaluates the queryset...
context['qs_count'] = qs.count()
# ordered inventory...
ordered_qs = qs.is_ordered()
context['inventory_ordered'] = ordered_qs
# in transit inventory...
in_transit_qs = qs.in_transit()
context['inventory_in_transit'] = in_transit_qs
# on hand inventory...
received_qs = qs.is_received()
context['inventory_received'] = received_qs
context['page_title'] = _('Inventory')
context['header_title'] = _('Inventory Status')
context['header_subtitle'] = _('Ordered/In Transit/On Hand')
context['header_subtitle_icon'] = 'ic:round-inventory'
return context
def get_queryset(self):
if self.queryset is None:
self.queryset = ItemTransactionModel.objects.inventory_pipeline_aggregate(
entity_slug=self.kwargs['entity_slug'],
)
return super().get_queryset()

View File

@ -1092,6 +1092,6 @@ def bill_model_after_approve_notification(sender, instance, created, **kwargs):
message=f""" message=f"""
Bill {instance.bill_number} has been approved. Bill {instance.bill_number} has been approved.
<a href="{reverse('bill-detail', kwargs={'dealer_slug': dealer.slug, 'entity_slug':dealer.entity.slug, 'bill_pk': instance.pk})}" target="_blank">View</a>. <a href="{reverse('bill-detail', kwargs={'dealer_slug': dealer.slug, 'entity_slug':dealer.entity.slug, 'bill_pk': instance.pk})}" target="_blank">View</a>.
please comlete the bill payment. please complete the bill payment.
""" """
) )

File diff suppressed because it is too large Load Diff

View File

@ -88,7 +88,6 @@ from django_ledger.views import (
LedgerModelCreateView as LedgerModelCreateViewBase, LedgerModelCreateView as LedgerModelCreateViewBase,
) )
from django_ledger.forms.account import AccountModelCreateForm, AccountModelUpdateForm from django_ledger.forms.account import AccountModelCreateForm, AccountModelUpdateForm
from django_ledger.views.inventory import InventoryListView as InventoryListViewBase
from django_ledger.views.entity import ( from django_ledger.views.entity import (
EntityModelDetailBaseView, EntityModelDetailBaseView,
EntityModelDetailHandlerView, EntityModelDetailHandlerView,
@ -143,6 +142,7 @@ from .override import (
BillModelDetailView as BillModelDetailViewBase, BillModelDetailView as BillModelDetailViewBase,
BillModelUpdateView as BillModelUpdateViewBase, BillModelUpdateView as BillModelUpdateViewBase,
BaseBillActionView as BaseBillActionViewBase, BaseBillActionView as BaseBillActionViewBase,
InventoryListView as InventoryListViewBase,
) )
from django_ledger.models import ( from django_ledger.models import (
@ -10253,9 +10253,11 @@ def upload_cars(request, dealer_slug, pk=None):
car_make = get_make(manufacturer_name) car_make = get_make(manufacturer_name)
car_model = get_model(model_name, car_make) car_model = get_model(model_name, car_make)
if ( if (
not all([car_make, car_model]) not all([car_make])
or (make.pk != car_make.pk) or (make.pk != car_make.pk)
or (model.pk != car_model.pk) # not all([car_make, car_model])
# or (make.pk != car_make.pk)
# or (model.pk != car_model.pk)
): ):
logger.warning( logger.warning(
f"User {user_username} uploaded CSV with VIN '{row['vin']}' " f"User {user_username} uploaded CSV with VIN '{row['vin']}' "
@ -10368,12 +10370,3 @@ def bulk_update_car_price(request):
class InventoryListView(InventoryListViewBase): class InventoryListView(InventoryListViewBase):
template_name = "inventory/list.html" template_name = "inventory/list.html"
permission_required = ["django_ledger.view_purchaseordermodel"] permission_required = ["django_ledger.view_purchaseordermodel"]
def get_queryset(self):
dealer = get_user_type(self.request)
if self.queryset is None:
self.queryset = ItemTransactionModel.objects.inventory_pipeline_aggregate(
entity_slug=dealer.entity.slug,
)
return super().get_queryset()

View File

@ -1,126 +1,126 @@
annotated-types==0.7.0 annotated-types
anyio==4.9.0 anyio
arrow==1.3.0 arrow
asgiref==3.8.1 asgiref
attrs==25.3.0 attrs
Babel==2.15.0 Babel
beautifulsoup4==4.13.4 beautifulsoup4
blessed==1.21.0 blessed
cattrs==24.1.3 cattrs
certifi==2025.1.31 certifi
cffi==1.17.1 cffi
charset-normalizer==3.4.1 charset-normalizer
click==8.2.1 click
colorama==0.4.6 colorama
crispy-bootstrap5==2024.10 crispy-bootstrap5
cryptography==44.0.2 cryptography
cssbeautifier==1.15.4 cssbeautifier
defusedxml==0.7.1 defusedxml
diff-match-patch==20241021 diff-match-patch
distro==1.9.0 distro
Django==5.2.3 Django
django-allauth==65.6.0 django-allauth
django-appointment==3.8.0 django-appointment
django-background-tasks==1.2.8 django-background-tasks
django-bootstrap5==25.1 django-bootstrap5
django-ckeditor==6.7.2 django-ckeditor
django-cors-headers==4.7.0 django-cors-headers
django-countries==7.6.1 django-countries
django-crispy-forms==2.3 django-crispy-forms
django-easy-audit==1.3.7 django-easy-audit
django-extensions==3.2.3 django-extensions
django-filter==25.1 django-filter
django-import-export==4.3.7 django-import-export
django-js-asset==3.1.2 django-js-asset
django-ledger==0.7.7 django-ledger
django-manager-utils==3.1.5 django-manager-utils
django-next-url-mixin==0.4.0 django-next-url-mixin
django-ordered-model==3.7.4 django-ordered-model
django-phonenumber-field==8.0.0 django-phonenumber-field
django-picklefield==3.3 django-picklefield
django-plans==2.0.0 django-plans
django-q2==1.8.0 django-q2
django-query-builder==3.2.0 django-query-builder
django-schema-graph==3.1.0 django-schema-graph
django-sequences==3.0 django-sequences
django-tables2==2.7.5 django-tables2
django-treebeard==4.7.1 django-treebeard
django-widget-tweaks==1.5.0 django-widget-tweaks
djangorestframework==3.15.2 djangorestframework
djhtml==3.0.7 djhtml
djlint==1.36.4 djlint
docopt==0.6.2 docopt
EditorConfig==0.17.0 EditorConfig
Faker==37.3.0 Faker
fleming==0.7.0 fleming
fonttools==4.57.0 fonttools
fpdf==1.7.2 fpdf
fpdf2==2.8.3 fpdf2
greenlet==3.2.2 greenlet
h11==0.14.0 h11
httpcore==1.0.7 httpcore
httpx==0.28.1 httpx
icalendar==6.1.2 icalendar
idna==3.10 idna
jiter==0.9.0 jiter
jsbeautifier==1.15.4 jsbeautifier
json5==0.12.0 json5
jsonpatch==1.33 jsonpatch
jsonpointer==3.0.0 jsonpointer
jwt==1.3.1 jwt
langchain==0.3.25 langchain
langchain-core==0.3.61 langchain-core
langchain-ollama==0.3.3 langchain-ollama
langchain-text-splitters==0.3.8 langchain-text-splitters
langsmith==0.3.42 langsmith
luhnchecker==0.0.12 luhnchecker
Markdown==3.8 Markdown
markdown-it-py==3.0.0 markdown-it-py
mdurl==0.1.2 mdurl
num2words==0.5.14 num2words
numpy==2.2.4 numpy
ofxtools==0.9.5 ofxtools
ollama==0.4.8 ollama
openai==1.68.2 openai
opencv-python==4.11.0.86 opencv-python
orjson==3.10.18 orjson
packaging==24.2 packaging
pandas==2.2.3 pandas
pathspec==0.12.1 pathspec
phonenumbers==8.13.42 phonenumbers
pillow==11.2.1 pillow
pycparser==2.22 pycparser
pydantic==2.10.6 pydantic
pydantic_core==2.27.2 pydantic_core
Pygments==2.19.1 Pygments
python-dateutil==2.9.0.post0 python-dateutil
python-slugify==8.0.4 python-slugify
python-stdnum==1.20 python-stdnum
pytz==2025.2 pytz
pyvin==0.0.2 pyvin
PyYAML==6.0.2 PyYAML
pyzbar==0.1.9 pyzbar
redis==3.5.3 redis
regex==2024.11.6 regex
requests==2.32.3 requests
requests-toolbelt==1.0.0 requests-toolbelt
rich==14.0.0 rich
ruff==0.11.10 ruff
setuptools==80.3.0 setuptools
six==1.17.0 six
sniffio==1.3.1 sniffio
soupsieve==2.7 soupsieve
SQLAlchemy==2.0.41 SQLAlchemy
sqlparse==0.5.3 sqlparse
suds==1.2.0 suds
swapper==1.3.0 swapper
tablib==3.8.0 tablib
tenacity==9.1.2 tenacity
text-unidecode==1.3 text-unidecode
tqdm==4.67.1 tqdm
types-python-dateutil==2.9.0.20250516 types-python-dateutil
typing_extensions==4.13.0 typing_extensions
tzdata==2025.2 tzdata
urllib3==2.3.0 urllib3
wcwidth==0.2.13 wcwidth
zstandard==0.23.0 zstandard

2
t1.py
View File

@ -19,4 +19,4 @@ def get_models_for_make():
models = get_models_for_make() models = get_models_for_make()
for model in models: for model in models:
print(model["Model_Name"]) print(model["Model_Name"])

View File

@ -9,7 +9,6 @@
<div class="container py-4"> <div class="container py-4">
<div class="row g-2"> <div class="row g-2">
<!-- Bill Form --> <!-- Bill Form -->
<div class="col-12"> <div class="col-12">

View File

@ -46,7 +46,7 @@
<tr class="align-middle"> <tr class="align-middle">
<!-- Item Column --> <!-- Item Column -->
<td> <td>
<div class="d-flex flex-column"> <div class="d-flex flex-column ms-2">
{% for hidden_field in f.hidden_fields %} {% for hidden_field in f.hidden_fields %}
{{ hidden_field }} {{ hidden_field }}
{% endfor %} {% endfor %}

View File

@ -64,6 +64,13 @@
</div> </div>
</a> </a>
</li> </li>
<li class="nav-item">
<a class="nav-link" href="{% url 'inventort_list' request.dealer.slug request.dealer.entity.slug %}">
<div class="d-flex align-items-center">
<span class="nav-link-icon"><span class="fas fa-boxes"></span></span><span class="nav-link-text">{% trans "Inventory List"|capfirst %}</span>
</div>
</a>
</li>
{% endif %} {% endif %}
</ul> </ul>

View File

@ -14,12 +14,11 @@
<tbody> <tbody>
{% for i in inventory_list %} {% for i in inventory_list %}
<tr class="hover-actions-trigger">
<tr class="hover-actions-trigger"> <td class="ps-2 fw-medium">{{ i.item_model__name }}</td>
<td class="ps-2 fw-medium">{{ i.item_model__name }}</td> <td class="text-center">{{ i.item_model__uom__name }}</td>
<td class="text-center">{{ i.item_model__uom__name }}</td> <td class="text-end pe-3">{{ i.total_quantity | floatformat:3 }}</td>
<td class="text-end pe-3">{{ i.total_quantity | floatformat:3 }}</td> <td class="text-end pe-3 fw-bold text-primary">
<td class="text-end pe-3 fw-bold text-primary">
<span class="currency">{{CURRENCY}}</span>{{ i.total_value | currency_format }} <span class="currency">{{CURRENCY}}</span>{{ i.total_value | currency_format }}
</td> </td>
</tr> </tr>
@ -30,7 +29,7 @@
<td colspan="2"></td> <td colspan="2"></td>
<td class="text-end pe-3 fw-bold">{% trans "Total Value" %}</td> <td class="text-end pe-3 fw-bold">{% trans "Total Value" %}</td>
<td class="text-end pe-3 fw-bold text-success"> <td class="text-end pe-3 fw-bold text-success">
<span class="currency">{{CURRENCY}}</span>{{ inventory_total_value | currency_format }} <span class="currency">{{CURRENCY}}</span>{{ inventory_total_value | currency_format }}
</td> </td>
</tr> </tr>
</tfoot> </tfoot>