free plans in set_plans

This commit is contained in:
Faheedkhan 2025-08-24 16:00:45 +03:00
commit 68a67caab5
6 changed files with 62 additions and 27 deletions

View File

@ -1306,7 +1306,7 @@ class Dealer(models.Model, LocalizedNameMixin):
) )
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name")) arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
name = models.CharField(max_length=255, verbose_name=_("English 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( address = models.CharField(
max_length=200, blank=True, null=True, verbose_name=_("Address") 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")) last_name = models.CharField(max_length=255, verbose_name=_("Last Name"))
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic 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( staff_type = models.CharField(
choices=StaffTypes.choices, max_length=255, verbose_name=_("Staff Type") 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")) vrn = models.CharField(max_length=15, verbose_name=_("VAT Registration Number"))
email = models.EmailField(verbose_name=_("Email")) 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( address = models.CharField(
max_length=200, blank=True, null=True, verbose_name=_("Address") max_length=200, blank=True, null=True, verbose_name=_("Address")
) )
@ -1960,7 +1960,7 @@ class Representative(models.Model, LocalizedNameMixin):
id_number = models.CharField( id_number = models.CharField(
max_length=10, unique=True, verbose_name=_("ID Number") 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")) email = models.EmailField(max_length=255, verbose_name=_("Email Address"))
address = models.CharField( address = models.CharField(
max_length=200, blank=True, null=True, verbose_name=_("Address") 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")) arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
name = models.CharField(max_length=255, verbose_name=_("English Name")) name = models.CharField(max_length=255, verbose_name=_("English Name"))
contact_person = models.CharField(max_length=100, verbose_name=_("Contact Person")) 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")) email = models.EmailField(max_length=255, verbose_name=_("Email Address"))
address = models.CharField(max_length=200, verbose_name=_("Address")) address = models.CharField(max_length=200, verbose_name=_("Address"))
logo = models.ImageField( logo = models.ImageField(

View File

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

View File

@ -1,10 +1,14 @@
from django.core.validators import RegexValidator from django.core.validators import RegexValidator
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
import re
class SaudiPhoneNumberValidator(RegexValidator): class SaudiPhoneNumberValidator(RegexValidator):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__( super().__init__(
regex=r"^(\+9665|05)[0-9]{8}$", regex=r"^(\+9665|05)[0-9]{8}$",
message=_("Enter a valid Saudi phone number (05XXXXXXXX or +9665XXXXXXXX)"), 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(): def run():
# Create quotas first # 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( basic_quota = Quota.objects.create(
codename="basic_quota", codename="basic_quota",
name="Basic Features", name="Basic Features",
@ -27,7 +34,19 @@ def run():
is_boolean=True, is_boolean=True,
url="pricing", 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 # Create the plans
basic_plan = Plan.objects.create( basic_plan = Plan.objects.create(
name="Basic", name="Basic",

View File

@ -138,11 +138,11 @@
</div> {% endcomment %} </div> {% endcomment %}
<div class="d-flex justify-content-end gap-2"> <div class="d-flex justify-content-end gap-2">
{% if not dealer.user.userplan %} {% 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 %} {% 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" %} {% 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 %} {% endif %}
</div> </div>
</div> </div>

View File

@ -39,7 +39,7 @@
{% if inventory.total_cars > 0 %} {% if inventory.total_cars > 0 %}
<div class="row justify-content-between"> <div class="row justify-content-between">
<div class="col-sm-12 "> <div class="col-sm-12 ">
<div class="card border h-100 w-100 p-lg-10"> <div class="card border h-100 w-100 p-lg-10" style="">
<div class="road"> <div class="road">
<p class="moving-tenhal ">&nbsp;&nbsp;&nbsp;&nbsp;{% trans "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>