From 32437a2ec0a6da1adac062d7aba6c16b97cb3544 Mon Sep 17 00:00:00 2001 From: ismail <=> Date: Mon, 5 May 2025 14:40:13 +0300 Subject: [PATCH] update the add colors visuals + more.. --- inventory/forms.py | 96 ++++++++++----- inventory/management/commands/tenhal_plan.py | 2 +- inventory/management/commands/test.py | 19 ++- inventory/management/set_vat.py | 5 + inventory/signals.py | 30 ++--- inventory/tasks.py | 25 ++-- inventory/views.py | 12 +- load_initial_data.sh | 4 + requirements_dev.txt | 5 + templates/inventory/add_colors.html | 116 ++++++++++++++----- templates/users/user_list.html | 4 +- 11 files changed, 220 insertions(+), 98 deletions(-) create mode 100644 inventory/management/set_vat.py diff --git a/inventory/forms.py b/inventory/forms.py index 5132e364..9de89581 100644 --- a/inventory/forms.py +++ b/inventory/forms.py @@ -1,3 +1,4 @@ +import re from django.core.cache import cache from datetime import datetime from luhnchecker.luhn import Luhn @@ -104,6 +105,21 @@ class StaffForm(forms.ModelForm): widget=forms.CheckboxSelectMultiple(attrs={"class": "form-check-input"}), queryset=Service.objects.all(), required=False,) + phone_number = forms.CharField( + required=False, + max_length=10, + min_length=10, + widget=forms.TextInput(attrs={ + 'class': 'form-control', + 'placeholder': _('Your Phone Number'), + 'id': 'phone' + }), + label=_('Phone Number'), + validators=[RegexValidator( + regex=r'^05[0-9]{8}$', + message=_('Enter a valid phone number (8-15 digits, starting with 05)') + )] + ) class Meta: model = Staff fields = ["name", "arabic_name", "phone_number", "staff_type"] @@ -182,21 +198,21 @@ class CustomerForm(forms.Form): last_name = forms.CharField() arabic_name = forms.CharField() email = forms.EmailField() - phone_number = PhoneNumberField( - label=_("Phone Number"), - widget=forms.TextInput( - attrs={ - "placeholder": _("Phone"), - } - ), - region="SA", - error_messages={ - "required": _("This field is required."), - "invalid": _("Phone number must be in the format 05xxxxxxxx"), - }, - required=True, - ) - + # phone_number = PhoneNumberField( + # label=_("Phone Number"), + # widget=forms.TextInput( + # attrs={ + # "placeholder": _("Phone"), + # } + # ), + # region="SA", + # error_messages={ + # "required": _("This field is required."), + # "invalid": _("Phone number must be in the format 05xxxxxxxx"), + # }, + # required=True, + # ) + phone_number = forms.CharField(label=_("Phone Number"),min_length=10,max_length=10,validators=[RegexValidator(regex='^05[0-9]{8}$')], required=True) national_id = forms.CharField(max_length=10,required=False) crn = forms.CharField(required=False) vrn = forms.CharField(required=False) @@ -466,6 +482,8 @@ class VendorForm(forms.ModelForm): :ivar Meta: Inner class to define metadata for the Vendor form. :type Meta: Type[VendorForm.Meta] """ + phone_number = forms.CharField(label=_("Phone Number"),min_length=10,max_length=10,validators=[RegexValidator(regex='^05[0-9]{8}$')], required=True) + contact_person = forms.CharField(label=_("Phone Number"),min_length=10,max_length=10,validators=[RegexValidator(regex='^05[0-9]{8}$')], required=True) class Meta: model = Vendor @@ -755,19 +773,34 @@ class WizardForm2(forms.Form): }, ) - phone_number = PhoneNumberField( - label=_("Phone Number"), - widget=forms.TextInput( - attrs={ - "placeholder": _("Phone"), - } - ), - region="SA", - error_messages={ - "required": _("This field is required."), - "invalid": _("Phone number must be in the format 05xxxxxxxx"), - }, - required=True, + # phone_number = PhoneNumberField( + # label=_("Phone Number"), + # widget=forms.TextInput( + # attrs={ + # "placeholder": _("Phone"), + # } + # ), + # region="SA", + # error_messages={ + # "required": _("This field is required."), + # "invalid": _("Phone number must be in the format 05xxxxxxxx"), + # }, + # required=True, + # ) + phone = forms.CharField( + required=False, + max_length=10, + min_length=10, + widget=forms.TextInput(attrs={ + 'class': 'form-control', + 'placeholder': _('Your Phone Number'), + 'id': 'phone' + }), + label=_('Phone Number'), + validators=[RegexValidator( + regex=r'^05[0-9]{8}$', + message=_('Enter a valid phone number (8-15 digits, starting with 05)') + )] ) @@ -1550,7 +1583,8 @@ class PaymentPlanForm(forms.Form): phone = forms.CharField( required=False, - max_length=20, + max_length=10, + min_length=10, widget=forms.TextInput(attrs={ 'class': 'form-control', 'placeholder': _('Your Phone Number'), @@ -1558,8 +1592,8 @@ class PaymentPlanForm(forms.Form): }), label=_('Phone Number'), validators=[RegexValidator( - regex=r'^\+?[0-9]{8,15}$', - message=_('Enter a valid phone number (8-15 digits, + optional)') + regex=r'^05[0-9]{8}$', + message=_('Enter a valid phone number (8-15 digits, starting with 05)') )] ) diff --git a/inventory/management/commands/tenhal_plan.py b/inventory/management/commands/tenhal_plan.py index 714caf25..ba1ec1d3 100644 --- a/inventory/management/commands/tenhal_plan.py +++ b/inventory/management/commands/tenhal_plan.py @@ -32,7 +32,7 @@ class Command(BaseCommand): ten_users_quota = Quota.objects.create(name='10 users', codename='10 users', unit='number') # Create plans - basic_plan = Plan.objects.create(name='Basic', description='Basic plan', available=True, visible=True) + basic_plan = Plan.objects.create(name='Basic', description='basic plan', available=True, visible=True) pro_plan = Plan.objects.create(name='Pro', description='Pro plan', available=True, visible=True) enterprise_plan = Plan.objects.create(name='Enterprise', description='Enterprise plan', available=True, visible=True) diff --git a/inventory/management/commands/test.py b/inventory/management/commands/test.py index eae6f1b1..889b7d12 100644 --- a/inventory/management/commands/test.py +++ b/inventory/management/commands/test.py @@ -3,15 +3,22 @@ from django.core.mail import send_mail from allauth.account.models import EmailConfirmation from inventory.tasks import send_email from django.contrib.auth import get_user_model +import re +from inventory.tasks import create_coa_accounts +from inventory.models import Dealer User = get_user_model() class Command(BaseCommand): def handle(self, *args, **kwargs): - user = User.objects.last() - print(user.email) - # 2. Force email confirmation - # email = user.emailaddress_set.first() - confirmation = EmailConfirmation.create(user.email) - confirmation.send() + # user = User.objects.last() + # print(user.email) + # # 2. Force email confirmation + # # email = user.emailaddress_set.first() + # confirmation = EmailConfirmation.create(user.email) + # confirmation.send() + # result = re.match(r'^05\d{8}$', '0625252522') + # print(result) + dealer = Dealer.objects.last() + create_coa_accounts(dealer.pk) \ No newline at end of file diff --git a/inventory/management/set_vat.py b/inventory/management/set_vat.py new file mode 100644 index 00000000..a173e3c2 --- /dev/null +++ b/inventory/management/set_vat.py @@ -0,0 +1,5 @@ +from inventory.models import VatRate +from decimal import Decimal +class Command: + def handle(self, *args, **kwargs): + VatRate.objects.get_or_create(rate=Decimal('0.15'), is_active=True) \ No newline at end of file diff --git a/inventory/signals.py b/inventory/signals.py index 63dedd73..4ec99603 100644 --- a/inventory/signals.py +++ b/inventory/signals.py @@ -630,23 +630,23 @@ def create_dealer_settings(sender, instance, created, **kwargs): # current_staff_count = instance.dealer.staff.count() # if current_staff_count > allowed_users: # raise ValidationError(_("You have reached the maximum number of staff users allowed for your plan.")) -@receiver(post_save, sender=models.Dealer) -def create_vat(sender, instance, created, **kwargs): - """ - Signal receiver that listens to the `post_save` signal for the `Dealer` model - and handles the creation of a `VatRate` instance if it does not already exist. +# @receiver(post_save, sender=models.Dealer) +# def create_vat(sender, instance, created, **kwargs): +# """ +# Signal receiver that listens to the `post_save` signal for the `Dealer` model +# and handles the creation of a `VatRate` instance if it does not already exist. - This function ensures that a default VAT rate is created with a specified rate - and is marked as active. It is connected to the Django signals framework and - automatically executes whenever a `Dealer` instance is saved. +# This function ensures that a default VAT rate is created with a specified rate +# and is marked as active. It is connected to the Django signals framework and +# automatically executes whenever a `Dealer` instance is saved. - :param sender: The model class that triggered the signal (in this case, `Dealer`). - :param instance: The instance of the model being saved. - :param created: Boolean indicating whether a new instance was created. - :param kwargs: Additional keyword arguments passed by the signal. - :return: None - """ - VatRate.objects.get_or_create(rate=Decimal('0.15'), is_active=True) +# :param sender: The model class that triggered the signal (in this case, `Dealer`). +# :param instance: The instance of the model being saved. +# :param created: Boolean indicating whether a new instance was created. +# :param kwargs: Additional keyword arguments passed by the signal. +# :return: None +# """ +# VatRate.objects.get_or_create(rate=Decimal('0.15'), is_active=True) @receiver(post_save, sender=models.Dealer) def create_make_ledger_accounts(sender, instance, created, **kwargs): diff --git a/inventory/tasks.py b/inventory/tasks.py index e3fdcb18..ef7747a0 100644 --- a/inventory/tasks.py +++ b/inventory/tasks.py @@ -545,7 +545,7 @@ def create_coa_accounts(pk): entity.create_account(coa_model=coa, code="6303", role=roles.EXPENSE_OTHER, name=_("Foreign Currency Translation"), balance_type="debit", active=True) entity.create_account(coa_model=coa, code="6304", role=roles.EXPENSE_OTHER, name=_("Interest Expenses"), balance_type="debit", active=True) - # create_settings(instance.pk) + create_settings(instance.pk) # @background # def create_groups(instance): @@ -556,26 +556,33 @@ def create_coa_accounts(pk): # group_manager.set_default_permissions() # instance.user.groups.add(group) -@background -def create_accounts_for_make(pk): - instance = Dealer.objects.get(pk=pk) - entity = instance.entity +# @background +def create_accounts_for_make(dealer,makes): + entity = dealer.entity coa = entity.get_default_coa() - for make in CarMake.objects.all(): + for make in makes: last_account = entity.get_all_accounts().filter(role=roles.ASSET_CA_RECEIVABLES).order_by('-created').first() if len(last_account.code) == 4: code = f"{int(last_account.code)}{1:03d}" elif len(last_account.code) > 4: code = f"{int(last_account.code)+1}" - entity.create_account( + + if not entity.get_all_accounts().filter( name=make.name, - code=code, role=roles.ASSET_CA_RECEIVABLES, coa_model=coa, balance_type="credit", active=True - ) + ).exists(): + entity.create_account( + name=make.name, + code=code, + role=roles.ASSET_CA_RECEIVABLES, + coa_model=coa, + balance_type="credit", + active=True + ) diff --git a/inventory/views.py b/inventory/views.py index 25d538ef..d75b637d 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -170,7 +170,7 @@ from .utils import ( set_invoice_payment, CarTransfer, ) -from .tasks import send_email +from .tasks import create_accounts_for_make, send_email logger = logging.getLogger(__name__) @@ -6167,7 +6167,7 @@ class OrderListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): # email @login_required -@permission_required("django_ledger.view_estimate", raise_exception=True) +@permission_required("django_ledger.view_estimatemodel", raise_exception=True) def send_email_view(request, pk): """ View function to send an email for an estimate. This function allows authenticated and @@ -6217,11 +6217,11 @@ def send_email_view(request, pk): {dealer.phone_number} هيكل | Haikal """ - + subject = _("Quotation") send_email( - settings.DEFAULT_FROM_EMAIL, + str(settings.DEFAULT_FROM_EMAIL), estimate.customer.email, - _("Quotation"), + subject, msg, ) @@ -7188,6 +7188,8 @@ def assign_car_makes(request): if request.method == "POST": form = forms.DealersMakeForm(request.POST, dealer=dealer) if form.is_valid(): + makes = form.cleaned_data["car_makes"] + create_accounts_for_make(dealer, makes) form.save() return redirect("dealer_detail", pk=dealer.pk) else: diff --git a/load_initial_data.sh b/load_initial_data.sh index 80f6b64a..295919ca 100755 --- a/load_initial_data.sh +++ b/load_initial_data.sh @@ -22,4 +22,8 @@ python3 manage.py loaddata --app carequipment carequipment_backup.json echo "Populating colors" python3 manage.py populate_colors +python3 manage.py tenhal_plan + +python3 manage.py set_vat + echo "Done" \ No newline at end of file diff --git a/requirements_dev.txt b/requirements_dev.txt index 9c6c1f79..6d97782b 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -66,3 +66,8 @@ tqdm==4.67.1 typing_extensions==4.13.0 tzdata==2025.2 urllib3==2.3.0 +fpdf2 +luhnchecker +requests +django-ckeditor +django-cors-headers \ No newline at end of file diff --git a/templates/inventory/add_colors.html b/templates/inventory/add_colors.html index 4e397b2c..21645c05 100644 --- a/templates/inventory/add_colors.html +++ b/templates/inventory/add_colors.html @@ -8,54 +8,110 @@