Compare commits

...

9 Commits

18 changed files with 89 additions and 54 deletions

View File

@ -146,7 +146,7 @@ class StaffForm(forms.ModelForm):
)
class Meta:
model = Staff
fields = ["first_name","last_name", "arabic_name", "phone_number", "address", "logo", "group"]
@ -177,7 +177,7 @@ class DealerForm(forms.ModelForm):
"""
phone_number = SaudiPhoneNumberField(label=_("Phone Number"))
# phone_number = SaudiPhoneNumberField(label=_("Phone Number"))
class Meta:
model = Dealer
@ -193,7 +193,7 @@ class DealerForm(forms.ModelForm):
class CustomerForm(forms.ModelForm):
phone_number = SaudiPhoneNumberField(label=_("Phone Number"))
# phone_number = SaudiPhoneNumberField(label=_("Phone Number"))
class Meta:
model = Customer
@ -563,7 +563,7 @@ class VendorForm(forms.ModelForm):
:type Meta: Type[VendorForm.Meta]
"""
phone_number = SaudiPhoneNumberField(label=_("Phone Number"))
# phone_number = SaudiPhoneNumberField(label=_("Phone Number"))
contact_person = forms.CharField(label=_("Contact Person"))
class Meta:
@ -652,7 +652,7 @@ class RepresentativeForm(forms.ModelForm):
:type Meta.fields: list of str
"""
phone_number = SaudiPhoneNumberField(label=_("Phone Number"))
# phone_number = SaudiPhoneNumberField(label=_("Phone Number"))
class Meta:
model = Representative
@ -1077,7 +1077,7 @@ class LeadForm(forms.ModelForm):
:type id_car_model: ModelChoiceField
"""
phone_number = SaudiPhoneNumberField(label=_("Phone Number"))
# phone_number = SaudiPhoneNumberField(label=_("Phone Number"))
# email = forms.EmailField(
# label=_("Email"),

View File

@ -6,8 +6,7 @@ logger = logging.getLogger(__name__)
def check_create_coa_accounts(task):
logger.info("Checking if all accounts are created")
instance_id = task.args[0]
instance = Dealer.objects.get(pk=instance_id)
instance = task.kwargs["dealer"]
entity = instance.entity
coa = entity.get_default_coa()
@ -19,4 +18,6 @@ def check_create_coa_accounts(task):
create_account(entity, coa, account_data)
def print_results(task):
print(task.kwargs.get("dealer"))
dealer= task.kwargs["dealer"]
print("HOOK: ",dealer)
print("HOOK: ",dealer.pk)

View File

@ -6,4 +6,4 @@ class Command(BaseCommand):
def handle(self, *args, **kwargs):
from inventory.models import Dealer
instance = Dealer.objects.first()
async_task(name="test_task_test",func="inventory.tasks.test_task",dealer=instance,hook="inventory.hooks.print_results")
async_task(func="inventory.tasks.test_task",dealer=instance,hook="inventory.hooks.print_results")

View File

@ -1306,7 +1306,7 @@ class Dealer(models.Model, LocalizedNameMixin):
)
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
name = models.CharField(max_length=255, verbose_name=_("English Name"))
phone_number = models.CharField(max_length=255, verbose_name=_("Phone Number"),validators=[SaudiPhoneNumberValidator])
phone_number = models.CharField(max_length=255, verbose_name=_("Phone Number"),validators=[SaudiPhoneNumberValidator()])
address = models.CharField(
max_length=200, blank=True, null=True, verbose_name=_("Address")
)
@ -1432,7 +1432,7 @@ class Staff(models.Model):
last_name = models.CharField(max_length=255, verbose_name=_("Last Name"))
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
phone_number = models.CharField(max_length=255, verbose_name=_("Phone Number"),validators=[SaudiPhoneNumberValidator])
phone_number = models.CharField(max_length=255, verbose_name=_("Phone Number"),validators=[SaudiPhoneNumberValidator()])
staff_type = models.CharField(
choices=StaffTypes.choices, max_length=255, verbose_name=_("Staff Type")
)
@ -1826,7 +1826,7 @@ class Organization(models.Model, LocalizedNameMixin):
)
vrn = models.CharField(max_length=15, verbose_name=_("VAT Registration Number"))
email = models.EmailField(verbose_name=_("Email"))
phone_number = models.CharField(max_length=255, verbose_name=_("Phone Number"),validators=[SaudiPhoneNumberValidator])
phone_number = models.CharField(max_length=255, verbose_name=_("Phone Number"),validators=[SaudiPhoneNumberValidator()])
address = models.CharField(
max_length=200, blank=True, null=True, verbose_name=_("Address")
)
@ -1960,7 +1960,7 @@ class Representative(models.Model, LocalizedNameMixin):
id_number = models.CharField(
max_length=10, unique=True, verbose_name=_("ID Number")
)
phone_number = models.CharField(max_length=255, verbose_name=_("Phone Number"),validators=[SaudiPhoneNumberValidator])
phone_number = models.CharField(max_length=255, verbose_name=_("Phone Number"),validators=[SaudiPhoneNumberValidator()])
email = models.EmailField(max_length=255, verbose_name=_("Email Address"))
address = models.CharField(
max_length=200, blank=True, null=True, verbose_name=_("Address")
@ -1980,7 +1980,7 @@ class Lead(models.Model):
first_name = models.CharField(max_length=50, verbose_name=_("First Name"))
last_name = models.CharField(max_length=50, verbose_name=_("Last Name"))
email = models.EmailField(verbose_name=_("Email"))
phone_number = models.CharField(max_length=255, verbose_name=_("Phone Number"),validators=[SaudiPhoneNumberValidator])
phone_number = models.CharField(max_length=255, verbose_name=_("Phone Number"),validators=[SaudiPhoneNumberValidator()])
address = models.CharField(
max_length=200, blank=True, null=True, verbose_name=_("Address")
)
@ -2668,7 +2668,7 @@ class Vendor(models.Model, LocalizedNameMixin):
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
name = models.CharField(max_length=255, verbose_name=_("English Name"))
contact_person = models.CharField(max_length=100, verbose_name=_("Contact Person"))
phone_number = models.CharField(max_length=255, verbose_name=_("Phone Number"),validators=[SaudiPhoneNumberValidator])
phone_number = models.CharField(max_length=255, verbose_name=_("Phone Number"),validators=[SaudiPhoneNumberValidator()])
email = models.EmailField(max_length=255, verbose_name=_("Email Address"))
address = models.CharField(max_length=200, verbose_name=_("Address"))
logo = models.ImageField(

View File

@ -1,7 +1,6 @@
from datetime import datetime, timedelta
from decimal import Decimal
from django.urls import reverse
from inventory.tasks import create_coa_accounts, create_make_accounts
from django.contrib.auth.models import Group
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
@ -178,7 +177,7 @@ def create_ledger_entity(sender, instance, created, **kwargs):
entity.create_uom(name=u[1], unit_abbr=u[0])
# Create COA accounts, background task
async_task(name="create_coa_accounts",func=create_coa_accounts,dealer_pk=instance.pk,hook="inventory.hooks.check_create_coa_accounts")
async_task(func="inventory.tasks.create_coa_accounts",dealer=instance,hook="inventory.hooks.check_create_coa_accounts")
# async_task('inventory.tasks.check_create_coa_accounts', instance, schedule_type='O', schedule_time=timedelta(seconds=20))
# create_settings(instance.pk)

View File

@ -50,7 +50,7 @@ def create_settings(pk):
def create_coa_accounts(**kwargs):
logger.info("creating all accounts are created")
instance = Dealer.objects.get(pk=kwargs['dealer_pk'])
instance = kwargs.get("dealer")
entity = instance.entity
coa = entity.get_default_coa()
@ -927,5 +927,5 @@ def remove_reservation_by_id(reservation_id):
except Exception as e:
logger.error(f"Error removing reservation with ID {reservation_id}: {e}")
def test_task(instance):
print(instance.pk)
def test_task(**kwargs):
print("TASK : ",kwargs.get("dealer"))

View File

@ -1571,13 +1571,13 @@ def _post_sale_and_cogs(invoice, dealer):
# calc = CarFinanceCalculator(invoice)
data = get_finance_data(invoice,dealer)
car = data.get("car")
cash_acc = entity.get_all_accounts().filter(role_default=True, role=roles.ASSET_CA_CASH).first()
ar_acc = entity.get_all_accounts().filter(role_default=True, role=roles.ASSET_CA_RECEIVABLES).first()
vat_acc = entity.get_all_accounts().filter(role_default=True, role=roles.LIABILITY_CL_TAXES_PAYABLE).first()
car_rev = entity.get_all_accounts().filter(role_default=True, role=roles.INCOME_OPERATIONAL).first()
add_rev = entity.get_all_accounts().filter(role_default=True, role=roles.INCOME_OPERATIONAL).first()
cogs_acc = entity.get_all_accounts().filter(role_default=True, role=roles.COGS).first()
inv_acc = entity.get_all_accounts().filter(role_default=True, role=roles.ASSET_CA_INVENTORY).first()
cash_acc = entity.get_default_coa_accounts().filter(role_default=True, role=roles.ASSET_CA_CASH).first()
ar_acc = entity.get_default_coa_accounts().filter(role_default=True, role=roles.ASSET_CA_RECEIVABLES).first()
vat_acc = entity.get_default_coa_accounts().filter(role_default=True, role=roles.LIABILITY_CL_TAXES_PAYABLE).first()
car_rev = entity.get_default_coa_accounts().filter(role_default=True, role=roles.INCOME_OPERATIONAL).first()
add_rev = entity.get_default_coa_accounts().filter(code="4020").first()
cogs_acc = entity.get_default_coa_accounts().filter(role_default=True, role=roles.COGS).first()
inv_acc = entity.get_default_coa_accounts().filter(role_default=True, role=roles.ASSET_CA_INVENTORY).first()
# for car_data in data['cars']:
# car = invoice.get_itemtxs_data()[0].filter(
@ -1588,7 +1588,7 @@ def _post_sale_and_cogs(invoice, dealer):
net_car_price = Decimal(data['discounted_price'])
net_additionals_price = Decimal(data['additional_services']['total'])
vat_amount = Decimal(data['vat_amount'])
grand_total = net_car_price + net_additionals_price + vat_amount
grand_total = net_car_price + car.get_additional_services_amount_ + vat_amount
cost_total = Decimal(car.cost_price)
discount_amount =Decimal(data['discount_amount'])
@ -1608,7 +1608,8 @@ def _post_sale_and_cogs(invoice, dealer):
journal_entry=je_sale,
account=cash_acc,
amount=grand_total,
tx_type='debit'
tx_type='debit',
description='Debit to Cash on Hand'
)
# # Cr A/R (clear the receivable)
@ -1624,7 +1625,8 @@ def _post_sale_and_cogs(invoice, dealer):
journal_entry=je_sale,
account=vat_acc,
amount=vat_amount,
tx_type='credit'
tx_type='credit',
description="Credit to Tax Payable"
)
# Cr Sales Car
@ -1632,16 +1634,26 @@ def _post_sale_and_cogs(invoice, dealer):
journal_entry=je_sale,
account=car_rev,
amount=net_car_price,
tx_type='credit'
tx_type='credit',
description=" Credit to Car Sales"
)
if net_additionals_price > 0:
if car.get_additional_services_amount > 0:
# Cr Sales Additional Services
TransactionModel.objects.create(
journal_entry=je_sale,
account=add_rev,
amount=net_additionals_price,
tx_type='credit'
amount=car.get_additional_services_amount,
tx_type='credit',
description="Credit to After-Sales Services"
)
TransactionModel.objects.create(
journal_entry=je_sale,
account=vat_acc,
amount=car.get_additional_services_vat,
tx_type='credit',
description="Credit to Tax Payable (Additional Services)"
)
# ------------------------------------------------------------------
@ -1660,7 +1672,7 @@ def _post_sale_and_cogs(invoice, dealer):
journal_entry=je_cogs,
account=cogs_acc,
amount=cost_total,
tx_type='debit'
tx_type='debit',
)
# Cr Inventory

View File

@ -1,10 +1,14 @@
from django.core.validators import RegexValidator
from django.utils.translation import gettext_lazy as _
import re
class SaudiPhoneNumberValidator(RegexValidator):
def __init__(self):
def __init__(self, *args, **kwargs):
super().__init__(
regex=r"^(\+9665|05)[0-9]{8}$",
message=_("Enter a valid Saudi phone number (05XXXXXXXX or +9665XXXXXXXX)"),
)
def __call__(self, value):
# Remove any whitespace, dashes, or other separators
cleaned_value = re.sub(r'[\s\-\(\)\.]', '', str(value))
super().__call__(cleaned_value)

View File

@ -4,6 +4,13 @@ from decimal import Decimal
def run():
# Create quotas first
free_quota = Quota.objects.create(
codename="free_quota",
name="Free Features",
description="Free plan features",
is_boolean=True,
url="pricing",
)
basic_quota = Quota.objects.create(
codename="basic_quota",
name="Basic Features",
@ -27,7 +34,19 @@ def run():
is_boolean=True,
url="pricing",
)
# Create the plans
free_plan = Plan.objects.create(
name="Free",
description="Free plan with limited features",
price=Decimal("0.00"), # 0
period=7, # 1 week
default=True,
available=True,
visible=True,
order=1,
)
free_plan.quotas.add(free_quota)
# Create the plans
basic_plan = Plan.objects.create(
name="Basic",

View File

@ -138,7 +138,8 @@ html[dir="rtl"] .form-icon-container .form-control {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 10;
z-index: 9999;
pointer-events: none;
}
#spinner-bg {
@ -150,11 +151,14 @@ html[dir="rtl"] .form-icon-container .form-control {
background-color: rgba(255, 255, 255, 0.7);
opacity: 0;
transition: opacity 500ms ease-in;
z-index: 5;
visibility: hidden;
z-index: 10000;
pointer-events: none;
}
#spinner-bg.htmx-request {
opacity: .8;
visibility: visible;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 MiB

View File

@ -8,7 +8,7 @@
{% trans "Aging Inventory" %}
<i class="fas fa-box-open text-danger ms-2"></i>
</h2>
<h4 class="text-muted mb-3 ">{% trans "Aging Inventory Total" %} :: <span class=" text-danger">{{total_aging_inventory_value}}<span class="icon-saudi_riyal"></span></span></h4>
<h4 class="text-muted mb-3 ">{% trans "Aging Inventory Total" %} :: <span class=" text-danger">{{total_aging_inventory_value|default:0.00}}<span class="icon-saudi_riyal"></span></span></h4>
<p class="text-muted mb-0">{% trans "Cars in inventory for more than 60 days." %}</p>
</div>

View File

@ -138,11 +138,11 @@
</div> {% endcomment %}
<div class="d-flex justify-content-end gap-2">
{% if not dealer.user.userplan %}
<a href="{% url 'pricing_page' request.dealer.slug %}" class="btn btn-success"><span class="fas fa-cart-plus me-2"></span>{{ _("Subscribe Now") }}</a>
<a href="{% url 'pricing_page' request.dealer.slug %}" class="btn btn-outline-primary"><span class="fas fa-cart-plus me-2"></span>{{ _("Subscribe Now") }}</a>
{% elif dealer.user.userplan.is_expired %}
<a href="{% url 'pricing_page' request.dealer.slug %}" class="btn btn-warning"><span class="fas fa-redo-alt me-2"></span>{{ _("Renew") }}</a>
<a href="{% url 'pricing_page' request.dealer.slug %}" class="btn btn-outline-warning"><span class="fas fa-redo-alt me-2"></span>{{ _("Renew") }}</a>
{% elif dealer.user.userplan.plan.name != "Enterprise" %}
<a href="{% url 'pricing_page' request.dealer.slug %}" class="btn btn-primary"><span class="fas fa-rocket me-2"></span>{{ _("Upgrade Plan") }}</a>
<a href="{% url 'pricing_page' request.dealer.slug %}" class="btn btn-outline-primary"><span class="fas fa-rocket me-2"></span>{{ _("Upgrade Plan") }}</a>
{% endif %}
</div>
</div>

View File

@ -425,7 +425,7 @@
aria-label="Toggle Navigation">
<span class="navbar-toggle-icon"><span class="toggle-line"></span></span>
</button>
<a class="navbar-brand me-1 me-sm-3" href="{% url 'home' %}">
<a class="navbar-brand me-1 me-sm-3" href="{% url 'home'%}">
<div class="d-flex align-items-center">
<img class="logo-img d-dark-none" src="{% static 'images/logos/logo-d.png' %}" alt="haikal" width="27" />
<img class="logo-img d-light-none" src="{% static 'images/logos/logo.png' %}" alt="haikal" width="27" />

View File

@ -15,7 +15,7 @@
}
.moving-car {
.moving-tenhal {
position: absolute;
font-size: 3rem;
font-weight: bold;
@ -39,14 +39,10 @@
{% if inventory.total_cars > 0 %}
<div class="row justify-content-between">
<div class="col-sm-12 ">
<div class="card border h-100 w-100 p-lg-10">
{% comment %} <div class="road-container">
<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSY0ESBb8625a12EguCyY8j4eL93sY3ibEAuQ&s" alt="Car" id="car" class="img-fluid" style="height:30px; width:30px;">
</div> {% endcomment %}
<div class="card border h-100 w-100 p-lg-10" style="">
<div class="road">
<p class="moving-car ">Powered By Tenhal&nbsp;&nbsp;&nbsp;&nbsp;</p>
<p class="moving-tenhal ">&nbsp;&nbsp;&nbsp;&nbsp;{% trans "Powered By Tenhal" %}&nbsp;&nbsp;&nbsp;&nbsp;</p>
</div>
<div class="bg-holder bg-card"

View File

@ -10,7 +10,7 @@
{% endblock %}
{% block content %}
<main class="d-flex align-items-center justify-content-center min-vh-100 py-5">
<main class="d-flex align-items-center justify-content-center min-vh-50 py-5">
<div class="col-12 col-lg-8 col-xl-7">
<div class="card shadow-lg border-0 rounded-4 overflow-hidden animate__animated animate__fadeInUp">
<div class="card-header bg-gradient py-4 border-0 rounded-top-4">