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]) + "/"
breadcrumbs.append({"name": path[i].capitalize(), "url": url})
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")
if "Accountant" in staff.groups.values_list("name", flat=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
if "Sales" in staff.groups.values_list("name", flat=True):
elif "Sales" in staff.groups.values_list("name", flat=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
except Exception:
pass

View File

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

View File

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

View File

@ -1,4 +1,5 @@
import logging
from .models import Dealer
from django.core.exceptions import ImproperlyConfigured,ValidationError
from django.contrib.auth.mixins import LoginRequiredMixin,PermissionRequiredMixin
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.edit import UpdateView
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__)
@ -319,7 +321,12 @@ class BasePurchaseOrderActionActionView(LoginRequiredMixin,
f"while performing action '{self.action_name}' on Purchase Order ID: {po_model.pk}. "
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
class BillModelDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
@ -642,3 +649,41 @@ class BaseBillActionView(LoginRequiredMixin,PermissionRequiredMixin, RedirectVie
level=messages.ERROR,
extra_tags='is-danger')
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"""
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>.
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,
)
from django_ledger.forms.account import AccountModelCreateForm, AccountModelUpdateForm
from django_ledger.views.inventory import InventoryListView as InventoryListViewBase
from django_ledger.views.entity import (
EntityModelDetailBaseView,
EntityModelDetailHandlerView,
@ -143,6 +142,7 @@ from .override import (
BillModelDetailView as BillModelDetailViewBase,
BillModelUpdateView as BillModelUpdateViewBase,
BaseBillActionView as BaseBillActionViewBase,
InventoryListView as InventoryListViewBase,
)
from django_ledger.models import (
@ -10253,9 +10253,11 @@ def upload_cars(request, dealer_slug, pk=None):
car_make = get_make(manufacturer_name)
car_model = get_model(model_name, car_make)
if (
not all([car_make, car_model])
not all([car_make])
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(
f"User {user_username} uploaded CSV with VIN '{row['vin']}' "
@ -10368,12 +10370,3 @@ def bulk_update_car_price(request):
class InventoryListView(InventoryListViewBase):
template_name = "inventory/list.html"
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
anyio==4.9.0
arrow==1.3.0
asgiref==3.8.1
attrs==25.3.0
Babel==2.15.0
beautifulsoup4==4.13.4
blessed==1.21.0
cattrs==24.1.3
certifi==2025.1.31
cffi==1.17.1
charset-normalizer==3.4.1
click==8.2.1
colorama==0.4.6
crispy-bootstrap5==2024.10
cryptography==44.0.2
cssbeautifier==1.15.4
defusedxml==0.7.1
diff-match-patch==20241021
distro==1.9.0
Django==5.2.3
django-allauth==65.6.0
django-appointment==3.8.0
django-background-tasks==1.2.8
django-bootstrap5==25.1
django-ckeditor==6.7.2
django-cors-headers==4.7.0
django-countries==7.6.1
django-crispy-forms==2.3
django-easy-audit==1.3.7
django-extensions==3.2.3
django-filter==25.1
django-import-export==4.3.7
django-js-asset==3.1.2
django-ledger==0.7.7
django-manager-utils==3.1.5
django-next-url-mixin==0.4.0
django-ordered-model==3.7.4
django-phonenumber-field==8.0.0
django-picklefield==3.3
django-plans==2.0.0
django-q2==1.8.0
django-query-builder==3.2.0
django-schema-graph==3.1.0
django-sequences==3.0
django-tables2==2.7.5
django-treebeard==4.7.1
django-widget-tweaks==1.5.0
djangorestframework==3.15.2
djhtml==3.0.7
djlint==1.36.4
docopt==0.6.2
EditorConfig==0.17.0
Faker==37.3.0
fleming==0.7.0
fonttools==4.57.0
fpdf==1.7.2
fpdf2==2.8.3
greenlet==3.2.2
h11==0.14.0
httpcore==1.0.7
httpx==0.28.1
icalendar==6.1.2
idna==3.10
jiter==0.9.0
jsbeautifier==1.15.4
json5==0.12.0
jsonpatch==1.33
jsonpointer==3.0.0
jwt==1.3.1
langchain==0.3.25
langchain-core==0.3.61
langchain-ollama==0.3.3
langchain-text-splitters==0.3.8
langsmith==0.3.42
luhnchecker==0.0.12
Markdown==3.8
markdown-it-py==3.0.0
mdurl==0.1.2
num2words==0.5.14
numpy==2.2.4
ofxtools==0.9.5
ollama==0.4.8
openai==1.68.2
opencv-python==4.11.0.86
orjson==3.10.18
packaging==24.2
pandas==2.2.3
pathspec==0.12.1
phonenumbers==8.13.42
pillow==11.2.1
pycparser==2.22
pydantic==2.10.6
pydantic_core==2.27.2
Pygments==2.19.1
python-dateutil==2.9.0.post0
python-slugify==8.0.4
python-stdnum==1.20
pytz==2025.2
pyvin==0.0.2
PyYAML==6.0.2
pyzbar==0.1.9
redis==3.5.3
regex==2024.11.6
requests==2.32.3
requests-toolbelt==1.0.0
rich==14.0.0
ruff==0.11.10
setuptools==80.3.0
six==1.17.0
sniffio==1.3.1
soupsieve==2.7
SQLAlchemy==2.0.41
sqlparse==0.5.3
suds==1.2.0
swapper==1.3.0
tablib==3.8.0
tenacity==9.1.2
text-unidecode==1.3
tqdm==4.67.1
types-python-dateutil==2.9.0.20250516
typing_extensions==4.13.0
tzdata==2025.2
urllib3==2.3.0
wcwidth==0.2.13
zstandard==0.23.0
annotated-types
anyio
arrow
asgiref
attrs
Babel
beautifulsoup4
blessed
cattrs
certifi
cffi
charset-normalizer
click
colorama
crispy-bootstrap5
cryptography
cssbeautifier
defusedxml
diff-match-patch
distro
Django
django-allauth
django-appointment
django-background-tasks
django-bootstrap5
django-ckeditor
django-cors-headers
django-countries
django-crispy-forms
django-easy-audit
django-extensions
django-filter
django-import-export
django-js-asset
django-ledger
django-manager-utils
django-next-url-mixin
django-ordered-model
django-phonenumber-field
django-picklefield
django-plans
django-q2
django-query-builder
django-schema-graph
django-sequences
django-tables2
django-treebeard
django-widget-tweaks
djangorestframework
djhtml
djlint
docopt
EditorConfig
Faker
fleming
fonttools
fpdf
fpdf2
greenlet
h11
httpcore
httpx
icalendar
idna
jiter
jsbeautifier
json5
jsonpatch
jsonpointer
jwt
langchain
langchain-core
langchain-ollama
langchain-text-splitters
langsmith
luhnchecker
Markdown
markdown-it-py
mdurl
num2words
numpy
ofxtools
ollama
openai
opencv-python
orjson
packaging
pandas
pathspec
phonenumbers
pillow
pycparser
pydantic
pydantic_core
Pygments
python-dateutil
python-slugify
python-stdnum
pytz
pyvin
PyYAML
pyzbar
redis
regex
requests
requests-toolbelt
rich
ruff
setuptools
six
sniffio
soupsieve
SQLAlchemy
sqlparse
suds
swapper
tablib
tenacity
text-unidecode
tqdm
types-python-dateutil
typing_extensions
tzdata
urllib3
wcwidth
zstandard

2
t1.py
View File

@ -19,4 +19,4 @@ def get_models_for_make():
models = get_models_for_make()
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="row g-2">
<!-- Bill Form -->
<div class="col-12">

View File

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

View File

@ -64,6 +64,13 @@
</div>
</a>
</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 %}
</ul>

View File

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