Compare commits
10 Commits
d7ba36b773
...
2d20e0e0bf
| Author | SHA1 | Date | |
|---|---|---|---|
| 2d20e0e0bf | |||
| 27d6cce9f3 | |||
| 50ae705e97 | |||
| 81c69f5858 | |||
| 0b98dcb41d | |||
| 0bc0d17f43 | |||
| 832b9b5b1c | |||
| 55593377c3 | |||
| db751f7837 | |||
| 19b2913a4e |
3
.gitignore
vendored
3
.gitignore
vendored
@ -45,6 +45,9 @@ Makefile
|
||||
.idea/**/dbnavigator.xml
|
||||
**/migrations/**
|
||||
|
||||
**haikalbot/migrations/**
|
||||
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
@ -8,13 +8,13 @@
|
||||
"name": "Python Debugger: Django",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"args": [
|
||||
"runserver",
|
||||
"0.0.0.0:8000"
|
||||
],
|
||||
"django": true,
|
||||
"autoStartBrowser": false,
|
||||
"program": "${workspaceFolder}/manage.py"
|
||||
"program": "C:\\Users\\user\\Desktop\\haikal_projects\\venv\\bin\\activate & ${workspaceFolder}/manage.py"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -4,6 +4,8 @@ from django.conf.urls.static import static
|
||||
from django.conf import settings
|
||||
from django.conf.urls.i18n import i18n_patterns
|
||||
from inventory import views
|
||||
# from debug_toolbar.toolbar import debug_toolbar_urls
|
||||
|
||||
|
||||
# import debug_toolbar
|
||||
from schema_graph.views import Schema
|
||||
@ -15,7 +17,7 @@ urlpatterns = [
|
||||
path("api-auth/", include("rest_framework.urls")),
|
||||
path("api/", include("api.urls")),
|
||||
# path('dj-rest-auth/', include('dj_rest_auth.urls')),
|
||||
]
|
||||
]# + debug_toolbar_urls()
|
||||
urlpatterns += i18n_patterns(
|
||||
path("admin/", admin.site.urls),
|
||||
path("switch_language/", views.switch_language, name="switch_language"),
|
||||
@ -32,3 +34,5 @@ urlpatterns += i18n_patterns(
|
||||
)
|
||||
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
||||
|
||||
|
||||
@ -70,6 +70,7 @@ admin.site.register(models.Schedule)
|
||||
admin.site.register(models.Notes)
|
||||
admin.site.register(models.UserActivityLog)
|
||||
admin.site.register(models.DealersMake)
|
||||
admin.site.register(models.ExtraInfo)
|
||||
|
||||
|
||||
@admin.register(models.Car)
|
||||
|
||||
@ -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 {}
|
||||
|
||||
@ -30,6 +30,7 @@ from .models import (
|
||||
Vendor,
|
||||
Schedule,
|
||||
Car,
|
||||
VatRate,
|
||||
CarTransfer,
|
||||
CarFinance,
|
||||
CustomCard,
|
||||
@ -143,7 +144,7 @@ class StaffForm(forms.ModelForm):
|
||||
)
|
||||
class Meta:
|
||||
model = Staff
|
||||
fields = ["name", "arabic_name", "phone_number", "group"]
|
||||
fields = ["name", "arabic_name", "phone_number", "address","image","group"]
|
||||
|
||||
|
||||
# Dealer Form
|
||||
@ -2091,3 +2092,16 @@ class CSVUploadForm(forms.Form):
|
||||
# Reset file pointer for later processing
|
||||
csv_file.file.seek(0)
|
||||
return csv_file
|
||||
|
||||
|
||||
class AdditionalFinancesForm(forms.Form):
|
||||
additional_finances = forms.ModelMultipleChoiceField(
|
||||
queryset=AdditionalServices.objects.all(),
|
||||
widget=forms.CheckboxSelectMultiple(attrs={"class": "form-check-input"}),
|
||||
required=False,
|
||||
)
|
||||
|
||||
class VatRateForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = VatRate
|
||||
fields = ['rate']
|
||||
@ -1,9 +1,9 @@
|
||||
import logging
|
||||
|
||||
from django.http import Http404, HttpResponseForbidden
|
||||
from django.shortcuts import redirect
|
||||
from inventory import models
|
||||
from django.utils import timezone
|
||||
# from django.http import Http404, HttpResponseForbidden
|
||||
# from django.shortcuts import redirect
|
||||
# from inventory import models
|
||||
# from django.utils import timezone
|
||||
|
||||
from inventory.utils import get_user_type
|
||||
|
||||
@ -102,17 +102,24 @@ class InjectDealerMiddleware:
|
||||
request.is_inventory = False
|
||||
if hasattr(request.user, "dealer"):
|
||||
request.is_dealer = True
|
||||
request.dealer = request.user.dealer
|
||||
elif hasattr(request.user, "staffmember"):
|
||||
request.is_staff = True
|
||||
request.staff = request.user.staffmember.staff
|
||||
request.dealer = request.staff.dealer
|
||||
|
||||
staff = getattr(request.user.staffmember, "staff")
|
||||
if "Accountant" in staff.groups.values_list("name", flat=True):
|
||||
staff_groups = staff.groups.values_list("name", flat=True)
|
||||
if "Accountant" in staff_groups:
|
||||
request.is_accountant = True
|
||||
if "Manager" in staff.groups.values_list("name", flat=True):
|
||||
elif "Manager" in staff_groups:
|
||||
request.is_manager = True
|
||||
if "Sales" in staff.groups.values_list("name", flat=True):
|
||||
elif "Sales" in staff_groups:
|
||||
request.is_sales = True
|
||||
if "Inventory" in staff.groups.values_list("name", flat=True):
|
||||
elif "Inventory" in staff_groups:
|
||||
request.is_inventory = True
|
||||
request.entity = request.dealer.entity
|
||||
request.admin = request.dealer.entity.admin
|
||||
except Exception:
|
||||
pass
|
||||
response = self.get_response(request)
|
||||
|
||||
@ -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'),
|
||||
|
||||
@ -43,6 +43,7 @@ from django.core.serializers.json import DjangoJSONEncoder
|
||||
from appointment.models import StaffMember
|
||||
from plans.quota import get_user_quota
|
||||
from plans.models import UserPlan
|
||||
from django.db.models import Q
|
||||
# from plans.models import AbstractPlan
|
||||
# from simple_history.models import HistoricalRecords
|
||||
|
||||
@ -190,6 +191,7 @@ class UnitOfMeasure(models.TextChoices):
|
||||
|
||||
|
||||
class VatRate(models.Model):
|
||||
dealer = models.ForeignKey("Dealer", on_delete=models.CASCADE)
|
||||
rate = models.DecimalField(max_digits=5, decimal_places=2, default=Decimal("0.15"))
|
||||
is_active = models.BooleanField(default=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
@ -243,6 +245,11 @@ class CarMake(models.Model, LocalizedNameMixin):
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Make")
|
||||
indexes = [
|
||||
models.Index(fields=['name'], name='car_make_name_idx'),
|
||||
models.Index(fields=['is_sa_import'], name='car_make_sa_import_idx'),
|
||||
models.Index(fields=['car_type'], name='car_make_type_idx'),
|
||||
]
|
||||
|
||||
|
||||
class CarModel(models.Model, LocalizedNameMixin):
|
||||
@ -272,6 +279,11 @@ class CarModel(models.Model, LocalizedNameMixin):
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Model")
|
||||
indexes = [
|
||||
models.Index(fields=['id_car_make'], name='car_model_make_idx'),
|
||||
models.Index(fields=['name'], name='car_model_name_idx'),
|
||||
models.Index(fields=['id_car_make', 'name'], name='car_model_make_name_idx'),
|
||||
]
|
||||
|
||||
|
||||
class CarSerie(models.Model, LocalizedNameMixin):
|
||||
@ -306,6 +318,12 @@ class CarSerie(models.Model, LocalizedNameMixin):
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Series")
|
||||
indexes = [
|
||||
models.Index(fields=['id_car_model'], name='car_serie_model_idx'),
|
||||
models.Index(fields=['year_begin', 'year_end'], name='car_serie_years_idx'),
|
||||
models.Index(fields=['name'], name='car_serie_name_idx'),
|
||||
models.Index(fields=['generation_name'], name='car_serie_generation_idx'),
|
||||
]
|
||||
|
||||
|
||||
class CarTrim(models.Model, LocalizedNameMixin):
|
||||
@ -339,6 +357,11 @@ class CarTrim(models.Model, LocalizedNameMixin):
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Trim")
|
||||
indexes = [
|
||||
models.Index(fields=['id_car_serie'], name='car_trim_serie_idx'),
|
||||
models.Index(fields=['start_production_year', 'end_production_year'], name='car_trim_prod_years_idx'),
|
||||
models.Index(fields=['name'], name='car_trim_name_idx'),
|
||||
]
|
||||
|
||||
|
||||
class CarEquipment(models.Model, LocalizedNameMixin):
|
||||
@ -359,6 +382,11 @@ class CarEquipment(models.Model, LocalizedNameMixin):
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Equipment")
|
||||
indexes = [
|
||||
models.Index(fields=['id_car_trim'], name='car_equipment_trim_idx'),
|
||||
models.Index(fields=['year_begin'], name='car_equipment_year_idx'),
|
||||
models.Index(fields=['name'], name='car_equipment_name_idx'),
|
||||
]
|
||||
|
||||
|
||||
class CarSpecification(models.Model, LocalizedNameMixin):
|
||||
@ -390,6 +418,10 @@ class CarSpecification(models.Model, LocalizedNameMixin):
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Specification")
|
||||
indexes = [
|
||||
models.Index(fields=['id_parent'], name='car_spec_parent_idx'),
|
||||
models.Index(fields=['name'], name='car_spec_name_idx'),
|
||||
]
|
||||
|
||||
|
||||
class CarSpecificationValue(models.Model):
|
||||
@ -406,6 +438,11 @@ class CarSpecificationValue(models.Model):
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Specification Value")
|
||||
indexes = [
|
||||
models.Index(fields=['id_car_trim'], name='car_spec_val_trim_idx'),
|
||||
models.Index(fields=['id_car_specification'], name='car_spec_val_spec_idx'),
|
||||
models.Index(fields=['id_car_trim', 'id_car_specification'], name='car_spec_val_trim_spec_idx'),
|
||||
]
|
||||
|
||||
|
||||
class CarOption(models.Model, LocalizedNameMixin):
|
||||
@ -437,6 +474,10 @@ class CarOption(models.Model, LocalizedNameMixin):
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Option")
|
||||
indexes = [
|
||||
models.Index(fields=['id_parent'], name='car_option_parent_idx'),
|
||||
models.Index(fields=['name'], name='car_option_name_idx'),
|
||||
]
|
||||
|
||||
|
||||
class CarOptionValue(models.Model):
|
||||
@ -456,6 +497,12 @@ class CarOptionValue(models.Model):
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Option Value")
|
||||
indexes = [
|
||||
models.Index(fields=['id_car_option'], name='car_opt_val_option_idx'),
|
||||
models.Index(fields=['id_car_equipment'], name='car_opt_val_equipment_idx'),
|
||||
models.Index(fields=['is_base'], name='car_opt_val_is_base_idx'),
|
||||
models.Index(fields=['id_car_option', 'id_car_equipment'], name='cov_option_equipment_idx'),
|
||||
]
|
||||
|
||||
|
||||
class CarTransferStatusChoices(models.TextChoices):
|
||||
@ -517,7 +564,7 @@ class AdditionalServices(models.Model, LocalizedNameMixin):
|
||||
|
||||
@property
|
||||
def price_(self):
|
||||
vat = VatRate.objects.filter(is_active=True).first()
|
||||
vat = VatRate.objects.filter(dealer=self.dealer,is_active=True).first()
|
||||
return (
|
||||
Decimal(self.price + (self.price * vat.rate))
|
||||
if self.taxable
|
||||
@ -614,6 +661,26 @@ class Car(Base):
|
||||
class Meta:
|
||||
verbose_name = _("Car")
|
||||
verbose_name_plural = _("Cars")
|
||||
indexes = [
|
||||
models.Index(fields=['vin'], name='car_vin_idx'),
|
||||
models.Index(fields=['year'], name='car_year_idx'),
|
||||
models.Index(fields=['status'], name='car_status_idx'),
|
||||
|
||||
models.Index(fields=['dealer'], name='car_dealer_idx'),
|
||||
models.Index(fields=['vendor'], name='car_vendor_idx'),
|
||||
models.Index(fields=['id_car_make'], name='car_make_idx'),
|
||||
models.Index(fields=['id_car_model'], name='car_model_idx'),
|
||||
models.Index(fields=['id_car_serie'], name='car_serie_idx'),
|
||||
models.Index(fields=['id_car_trim'], name='car_trim_idx'),
|
||||
|
||||
models.Index(fields=['id_car_make', 'id_car_model'], name='car_make_model_idx'),
|
||||
models.Index(fields=['id_car_make', 'year'], name='car_make_year_idx'),
|
||||
models.Index(fields=['dealer', 'status'], name='car_dealer_status_idx'),
|
||||
models.Index(fields=['vendor', 'status'], name='car_vendor_status_idx'),
|
||||
models.Index(fields=['year', 'status'], name='car_year_status_idx'),
|
||||
models.Index(fields=['status'], name='car_active_status_idx',
|
||||
condition=Q(status=CarStatusChoices.AVAILABLE)),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
make = self.id_car_make.name if self.id_car_make else "Unknown Make"
|
||||
@ -823,6 +890,9 @@ class CarFinance(models.Model):
|
||||
selling_price = models.DecimalField(
|
||||
max_digits=14, decimal_places=2, verbose_name=_("Selling Price")
|
||||
)
|
||||
marked_price = models.DecimalField(
|
||||
max_digits=14, decimal_places=2, verbose_name=_("Marked Price")
|
||||
)
|
||||
discount_amount = models.DecimalField(
|
||||
max_digits=14,
|
||||
decimal_places=2,
|
||||
@ -855,7 +925,7 @@ class CarFinance(models.Model):
|
||||
|
||||
@property
|
||||
def vat_amount(self):
|
||||
vat = VatRate.objects.filter(is_active=True).first()
|
||||
vat = VatRate.objects.filter(dealer=self.car.dealer, is_active=True).first()
|
||||
if vat:
|
||||
return (self.total_discount * Decimal(vat.rate)).quantize(Decimal("0.01"))
|
||||
return Decimal("0.00")
|
||||
@ -891,6 +961,12 @@ class CarFinance(models.Model):
|
||||
class Meta:
|
||||
verbose_name = _("Car Financial Details")
|
||||
verbose_name_plural = _("Car Financial Details")
|
||||
indexes = [
|
||||
models.Index(fields=['car'], name='car_finance_car_idx'),
|
||||
models.Index(fields=['cost_price'], name='car_finance_cost_price_idx'),
|
||||
models.Index(fields=['selling_price'], name='car_finance_selling_price_idx'),
|
||||
models.Index(fields=['discount_amount'], name='car_finance_discount_idx'),
|
||||
]
|
||||
|
||||
|
||||
class ExteriorColors(models.Model, LocalizedNameMixin):
|
||||
@ -901,6 +977,10 @@ class ExteriorColors(models.Model, LocalizedNameMixin):
|
||||
class Meta:
|
||||
verbose_name = _("Exterior Colors")
|
||||
verbose_name_plural = _("Exterior Colors")
|
||||
indexes = [
|
||||
models.Index(fields=['name'], name='exterior_color_name_idx'),
|
||||
models.Index(fields=['arabic_name'], name='exterior_color_arabic_name_idx'),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name} ({self.rgb})"
|
||||
@ -914,6 +994,10 @@ class InteriorColors(models.Model, LocalizedNameMixin):
|
||||
class Meta:
|
||||
verbose_name = _("Interior Colors")
|
||||
verbose_name_plural = _("Interior Colors")
|
||||
indexes = [
|
||||
models.Index(fields=['name'], name='interior_color_name_idx'),
|
||||
models.Index(fields=['arabic_name'], name='interior_color_arabic_name_idx'),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name} ({self.rgb})"
|
||||
@ -932,6 +1016,11 @@ class CarColors(models.Model):
|
||||
verbose_name = _("Color")
|
||||
verbose_name_plural = _("Colors")
|
||||
unique_together = ("car", "exterior", "interior")
|
||||
indexes = [
|
||||
models.Index(fields=['exterior'], name='car_colors_exterior_idx'),
|
||||
models.Index(fields=['interior'], name='car_colors_interior_idx'),
|
||||
models.Index(fields=['exterior', 'interior'], name='car_colors_ext_int_combo_idx'),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.car} ({self.exterior.name}) ({self.interior.name})"
|
||||
@ -1105,9 +1194,13 @@ class Dealer(models.Model, LocalizedNameMixin):
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def vat_rate(self):
|
||||
return VatRate.objects.get(dealer=self,is_active=True).rate
|
||||
class Meta:
|
||||
verbose_name = _("Dealer")
|
||||
verbose_name_plural = _("Dealers")
|
||||
indexes = [models.Index(fields=["name"])]
|
||||
# permissions = [
|
||||
# ('change_dealer_type', 'Can change dealer type'),
|
||||
# ]
|
||||
@ -1137,6 +1230,12 @@ class Staff(models.Model, LocalizedNameMixin):
|
||||
staff_type = models.CharField(
|
||||
choices=StaffTypes.choices, max_length=255, verbose_name=_("Staff Type")
|
||||
)
|
||||
address = models.CharField(
|
||||
max_length=200, blank=True, null=True, verbose_name=_("Address")
|
||||
)
|
||||
image = models.ImageField(
|
||||
upload_to="staff/", blank=True, null=True, verbose_name=_("Image")
|
||||
)
|
||||
active = models.BooleanField(default=True, verbose_name=_("Active"))
|
||||
created = models.DateTimeField(auto_now_add=True, verbose_name=_("Created"))
|
||||
updated = models.DateTimeField(auto_now=True, verbose_name=_("Updated"))
|
||||
@ -1188,7 +1287,7 @@ class Staff(models.Model, LocalizedNameMixin):
|
||||
|
||||
@property
|
||||
def groups(self):
|
||||
return CustomGroup.objects.filter(pk__in=[x.customgroup.pk for x in self.user.groups.all()])
|
||||
return CustomGroup.objects.select_related("group").filter(pk__in=[x.customgroup.pk for x in self.user.groups.all()])
|
||||
|
||||
def clear_groups(self):
|
||||
self.remove_superuser_permission()
|
||||
@ -1199,21 +1298,28 @@ 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")
|
||||
verbose_name_plural = _("Staff")
|
||||
indexes = [
|
||||
models.Index(fields=["name"]),
|
||||
models.Index(fields=["staff_type"]),
|
||||
]
|
||||
permissions = []
|
||||
|
||||
def __str__(self):
|
||||
@ -1369,6 +1475,13 @@ class Customer(models.Model):
|
||||
class Meta:
|
||||
verbose_name = _("Customer")
|
||||
verbose_name_plural = _("Customers")
|
||||
indexes = [
|
||||
models.Index(fields=["title"]),
|
||||
models.Index(fields=["first_name"]),
|
||||
models.Index(fields=["last_name"]),
|
||||
models.Index(fields=["email"]),
|
||||
models.Index(fields=["phone_number"]),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
# middle = f" {self.middle_name}" if self.middle_name else ""
|
||||
@ -1507,6 +1620,11 @@ class Organization(models.Model, LocalizedNameMixin):
|
||||
class Meta:
|
||||
verbose_name = _("Organization")
|
||||
verbose_name_plural = _("Organizations")
|
||||
indexes = [
|
||||
models.Index(fields=["name"]),
|
||||
models.Index(fields=["email"]),
|
||||
models.Index(fields=["phone_number"]),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
@ -1696,6 +1814,17 @@ class Lead(models.Model):
|
||||
class Meta:
|
||||
verbose_name = _("Lead")
|
||||
verbose_name_plural = _("Leads")
|
||||
indexes = [
|
||||
models.Index(fields=["dealer"]),
|
||||
models.Index(fields=["customer"]),
|
||||
models.Index(fields=["organization"]),
|
||||
models.Index(fields=["staff"]),
|
||||
models.Index(fields=["first_name"]),
|
||||
models.Index(fields=["last_name"]),
|
||||
models.Index(fields=["email"]),
|
||||
models.Index(fields=["phone_number"]),
|
||||
models.Index(fields=["created"]),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.first_name} {self.last_name}"
|
||||
@ -1870,6 +1999,14 @@ class Schedule(models.Model):
|
||||
|
||||
class Meta:
|
||||
ordering = ["-scheduled_at"]
|
||||
verbose_name = _("Schedule")
|
||||
verbose_name_plural = _("Schedules")
|
||||
indexes = [
|
||||
models.Index(fields=["dealer"]),
|
||||
models.Index(fields=["customer"]),
|
||||
models.Index(fields=["content_type", "object_id"]),
|
||||
models.Index(fields=["scheduled_at"]),
|
||||
]
|
||||
|
||||
|
||||
class LeadStatusHistory(models.Model):
|
||||
@ -2042,6 +2179,14 @@ class Opportunity(models.Model):
|
||||
class Meta:
|
||||
verbose_name = _("Opportunity")
|
||||
verbose_name_plural = _("Opportunities")
|
||||
indexes = [
|
||||
models.Index(fields=["dealer"]),
|
||||
models.Index(fields=["customer"]),
|
||||
models.Index(fields=["car"]),
|
||||
models.Index(fields=["lead"]),
|
||||
models.Index(fields=["organization"]),
|
||||
models.Index(fields=["created"]),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
if self.customer:
|
||||
@ -2066,6 +2211,19 @@ class Notes(models.Model):
|
||||
class Meta:
|
||||
verbose_name = _("Note")
|
||||
verbose_name_plural = _("Notes")
|
||||
indexes = [
|
||||
models.Index(fields=['dealer'], name='note_dealer_idx'),
|
||||
models.Index(fields=['created_by'], name='note_created_by_idx'),
|
||||
models.Index(fields=['content_type'], name='note_content_type_idx'),
|
||||
models.Index(fields=['content_type', 'object_id'], name='note_content_object_idx'),
|
||||
|
||||
models.Index(fields=['created'], name='note_created_date_idx'),
|
||||
models.Index(fields=['updated'], name='note_updated_date_idx'),
|
||||
|
||||
models.Index(fields=['dealer', 'created'], name='note_dealer_created_idx'),
|
||||
models.Index(fields=['content_type', 'object_id', 'created'],
|
||||
name='note_content_obj_created_idx'),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"Note by {self.created_by.first_name} on {self.content_object}"
|
||||
@ -2096,6 +2254,19 @@ class Tasks(models.Model):
|
||||
class Meta:
|
||||
verbose_name = _("Task")
|
||||
verbose_name_plural = _("Tasks")
|
||||
indexes = [
|
||||
models.Index(fields=['dealer'], name='task_dealer_idx'),
|
||||
models.Index(fields=['created_by'], name='task_created_by_idx'),
|
||||
models.Index(fields=['content_type'], name='task_content_type_idx'),
|
||||
models.Index(fields=['content_type', 'object_id'], name='task_content_object_idx'),
|
||||
|
||||
models.Index(fields=['created'], name='task_created_date_idx'),
|
||||
models.Index(fields=['updated'], name='task_updated_date_idx'),
|
||||
|
||||
models.Index(fields=['dealer', 'created'], name='task_dealer_created_idx'),
|
||||
models.Index(fields=['content_type', 'object_id', 'created'],
|
||||
name='task_content_obj_created_idx'),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"Task by {self.created_by.email} on {self.content_object}"
|
||||
@ -2124,6 +2295,17 @@ class Email(models.Model):
|
||||
class Meta:
|
||||
verbose_name = _("Email")
|
||||
verbose_name_plural = _("Emails")
|
||||
indexes = [
|
||||
models.Index(fields=['created_by'], name='email_created_by_idx'),
|
||||
models.Index(fields=['content_type'], name='email_content_type_idx'),
|
||||
models.Index(fields=['content_type', 'object_id'], name='email_content_object_idx'),
|
||||
|
||||
models.Index(fields=['created'], name='email_created_date_idx'),
|
||||
models.Index(fields=['updated'], name='email_updated_date_idx'),
|
||||
|
||||
models.Index(fields=['content_type', 'object_id', 'created'],
|
||||
name='email_content_obj_created_idx'),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"Email by {self.created_by.first_name} on {self.content_object}"
|
||||
@ -2149,6 +2331,17 @@ class Activity(models.Model):
|
||||
class Meta:
|
||||
verbose_name = _("Activity")
|
||||
verbose_name_plural = _("Activities")
|
||||
indexes = [
|
||||
models.Index(fields=['created_by'], name='activity_created_by_idx'),
|
||||
models.Index(fields=['content_type'], name='activity_content_type_idx'),
|
||||
models.Index(fields=['content_type', 'object_id'], name='activity_content_object_idx'),
|
||||
|
||||
models.Index(fields=['created'], name='activity_created_date_idx'),
|
||||
models.Index(fields=['updated'], name='activity_updated_date_idx'),
|
||||
|
||||
models.Index(fields=['content_type', 'object_id', 'created'],
|
||||
name='a_content_obj_created_idx'),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.get_activity_type_display()} by {self.created_by.get_full_name} on {self.content_object}"
|
||||
@ -2167,6 +2360,12 @@ class Notification(models.Model):
|
||||
verbose_name_plural = _("Notifications")
|
||||
ordering = ["-created"]
|
||||
|
||||
indexes = [
|
||||
models.Index(fields=['user'], name='notification_user_idx'),
|
||||
models.Index(fields=['is_read'], name='notification_is_read_idx'),
|
||||
models.Index(fields=['created'], name='notification_created_date_idx'),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return self.message
|
||||
|
||||
@ -2230,6 +2429,12 @@ class Vendor(models.Model, LocalizedNameMixin):
|
||||
class Meta:
|
||||
verbose_name = _("Vendor")
|
||||
verbose_name_plural = _("Vendors")
|
||||
indexes = [
|
||||
models.Index(fields=['slug'], name='vendor_slug_idx'),
|
||||
models.Index(fields=['active'], name='vendor_active_idx'),
|
||||
models.Index(fields=['crn'], name='vendor_crn_idx'),
|
||||
models.Index(fields=['vrn'], name='vendor_vrn_idx'),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
@ -2422,7 +2627,7 @@ class SaleOrder(models.Model):
|
||||
blank=True,
|
||||
)
|
||||
comments = models.TextField(blank=True, null=True)
|
||||
formatted_order_id = models.CharField(max_length=10, unique=True, editable=False)
|
||||
formatted_order_id = models.CharField(max_length=20, unique=True, editable=False)
|
||||
|
||||
# Status and Dates
|
||||
status = models.CharField(
|
||||
@ -2471,9 +2676,21 @@ class SaleOrder(models.Model):
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Sales Order"
|
||||
verbose_name_plural = "Sales Orders"
|
||||
verbose_name = _("Sales Order")
|
||||
verbose_name_plural = _("Sales Orders")
|
||||
ordering = ["-order_date"] # Order by most recent first
|
||||
indexes = [
|
||||
models.Index(fields=["dealer"]),
|
||||
models.Index(fields=["estimate"]),
|
||||
models.Index(fields=["invoice"]),
|
||||
models.Index(fields=["opportunity"]),
|
||||
models.Index(fields=["customer"]),
|
||||
models.Index(fields=["status"]),
|
||||
models.Index(fields=["order_date"]),
|
||||
models.Index(fields=["expected_delivery_date"]),
|
||||
models.Index(fields=["actual_delivery_date"]),
|
||||
models.Index(fields=["cancelled_date"]),
|
||||
]
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.formatted_order_id:
|
||||
@ -2531,6 +2748,14 @@ class CustomGroup(models.Model):
|
||||
group = models.OneToOneField(
|
||||
"auth.Group", verbose_name=_("Group"), on_delete=models.CASCADE
|
||||
)
|
||||
class Meta:
|
||||
verbose_name = _("Custom Group")
|
||||
verbose_name_plural = _("Custom Groups")
|
||||
indexes = [
|
||||
models.Index(fields=["name"]),
|
||||
models.Index(fields=["dealer"]),
|
||||
models.Index(fields=["group"]),
|
||||
]
|
||||
|
||||
@property
|
||||
def entity(self):
|
||||
@ -2666,7 +2891,7 @@ class CustomGroup(models.Model):
|
||||
app="inventory",
|
||||
allowed_models=[
|
||||
"saleorder",
|
||||
"payment",
|
||||
# "payment",
|
||||
"staff",
|
||||
"schedule",
|
||||
"activity",
|
||||
@ -2708,7 +2933,8 @@ class CustomGroup(models.Model):
|
||||
"tasks",
|
||||
"activity",
|
||||
"payment",
|
||||
'vendor'],
|
||||
"vendor",
|
||||
],
|
||||
other_perms=[
|
||||
"view_car",
|
||||
"view_carlocation",
|
||||
@ -2731,7 +2957,6 @@ class CustomGroup(models.Model):
|
||||
"itemmodel",
|
||||
"invoicemodel",
|
||||
"vendormodel",
|
||||
|
||||
"journalentrymodel",
|
||||
"purchaseordermodel",
|
||||
"estimatemodel",
|
||||
@ -2935,6 +3160,13 @@ class PoItemsUploaded(models.Model):
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("PO Items")
|
||||
verbose_name_plural = _("PO Items")
|
||||
indexes = [
|
||||
models.Index(fields=["po"]),
|
||||
models.Index(fields=["item"]),
|
||||
]
|
||||
def get_name(self):
|
||||
return self.item.item.name.split('||')
|
||||
class ExtraInfo(models.Model):
|
||||
@ -2944,7 +3176,13 @@ class ExtraInfo(models.Model):
|
||||
- JSON data storage
|
||||
- Tracking fields
|
||||
"""
|
||||
# Primary GenericForeignKey (main linked object)
|
||||
dealer = models.ForeignKey(
|
||||
Dealer,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="extra_info"
|
||||
)
|
||||
content_type = models.ForeignKey(
|
||||
ContentType,
|
||||
on_delete=models.CASCADE,
|
||||
@ -2986,7 +3224,9 @@ class ExtraInfo(models.Model):
|
||||
models.Index(fields=['content_type', 'object_id']),
|
||||
models.Index(fields=['related_content_type', 'related_object_id']),
|
||||
]
|
||||
verbose_name_plural = "Extra Info"
|
||||
verbose_name_plural = _("Extra Info")
|
||||
verbose_name = _("Extra Info")
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return f"ExtraInfo for {self.content_object} ({self.content_type})"
|
||||
@ -3012,8 +3252,8 @@ class ExtraInfo(models.Model):
|
||||
related_content_type=related_content_type,
|
||||
related_object_id=staff.pk
|
||||
)
|
||||
|
||||
return [x.content_object.sale_orders.first() for x in qs if x.content_object.sale_orders.first()]
|
||||
# qs = qs.select_related("customer","estimate","invoice")
|
||||
return [x.content_object.sale_orders.select_related("customer","estimate","invoice").first() for x in qs if x.content_object.sale_orders.first()]
|
||||
@classmethod
|
||||
def get_invoices(cls, staff=None, is_dealer=False):
|
||||
if not staff and not is_dealer:
|
||||
|
||||
@ -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__)
|
||||
@ -213,7 +215,7 @@ class PurchaseOrderModelUpdateView(LoginRequiredMixin,
|
||||
if form.has_changed():
|
||||
po_items_qs = ItemTransactionModel.objects.for_po(
|
||||
entity_slug=self.kwargs['entity_slug'],
|
||||
user_model=dealer.entity.admin,
|
||||
user_model=self.request.admin,
|
||||
po_pk=po_model.uuid,
|
||||
).select_related('bill_model')
|
||||
|
||||
@ -314,12 +316,23 @@ class BasePurchaseOrderActionActionView(LoginRequiredMixin,
|
||||
)
|
||||
except ValidationError as e:
|
||||
# --- Single-line log for ValidationError ---
|
||||
print(f"User {user_username} encountered a validation error "
|
||||
f"while performing action '{self.action_name}' on Purchase Order ID: {po_model.pk}. "
|
||||
f"Error: {e}")
|
||||
logger.warning(
|
||||
f"User {user_username} encountered a validation error "
|
||||
f"while performing action '{self.action_name}' on Purchase Order ID: {po_model.pk}. "
|
||||
f"Error: {e}"
|
||||
)
|
||||
print(e)
|
||||
except AttributeError as e:
|
||||
print(f"User {user_username} encountered an AttributeError "
|
||||
f"while performing action '{self.action_name}' on Purchase Order ID: {po_model.pk}. "
|
||||
f"Error: {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):
|
||||
@ -406,19 +419,23 @@ class BillModelUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateVie
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
form_class = self.get_form_class()
|
||||
dealer = get_object_or_404(Dealer,slug=self.kwargs['dealer_slug'])
|
||||
entity_model = dealer.entity
|
||||
entity_model = self.request.dealer.entity
|
||||
if self.request.method == 'POST' and self.action_update_items:
|
||||
return form_class(
|
||||
entity_model=entity_model,
|
||||
user_model=dealer.entity.admin,
|
||||
user_model=self.request.admin,
|
||||
instance=self.object
|
||||
)
|
||||
return form_class(
|
||||
form = form_class(
|
||||
entity_model=entity_model,
|
||||
user_model=dealer.entity.admin,
|
||||
user_model=self.request.admin,
|
||||
**self.get_form_kwargs()
|
||||
)
|
||||
try:
|
||||
form.initial['amount_paid'] = self.object.get_itemtxs_data()[1]["total_amount__sum"]
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return form
|
||||
|
||||
def get_form_class(self):
|
||||
bill_model: BillModel = self.object
|
||||
@ -642,3 +659,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()
|
||||
@ -1,10 +1,10 @@
|
||||
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
|
||||
from appointment.models import Service
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.auth import get_user_model
|
||||
@ -672,6 +672,7 @@ def create_dealer_settings(sender, instance, created, **kwargs):
|
||||
:return: None
|
||||
"""
|
||||
if created:
|
||||
models.VatRate.objects.create(dealer=instance)
|
||||
models.DealerSettings.objects.create(
|
||||
dealer=instance,
|
||||
invoice_cash_account=instance.entity.get_all_accounts()
|
||||
@ -941,6 +942,11 @@ def create_po_item_upload(sender,instance,created,**kwargs):
|
||||
dealer = models.Dealer.objects.get(entity=instance.entity)
|
||||
models.PoItemsUploaded.objects.create(dealer=dealer,po=instance, item=item, status="fulfilled")
|
||||
|
||||
@receiver(post_save, sender=models.Staff)
|
||||
def add_service_to_staff(sender,instance,created,**kwargs):
|
||||
if created:
|
||||
for service in Service.objects.all():
|
||||
instance.staff_member.services_offered.add(service)
|
||||
|
||||
##########################################################
|
||||
######################Notification########################
|
||||
@ -1092,6 +1098,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.
|
||||
"""
|
||||
)
|
||||
1137
inventory/tasks.py
1137
inventory/tasks.py
File diff suppressed because it is too large
Load Diff
@ -51,6 +51,11 @@ urlpatterns = [
|
||||
views.DealerUpdateView.as_view(),
|
||||
name="dealer_update",
|
||||
),
|
||||
path(
|
||||
"dealers/<slug:slug>/dealer_vat_rate_update/",
|
||||
views.dealer_vat_rate_update,
|
||||
name="dealer_vat_rate_update",
|
||||
),
|
||||
# path('dealers/<int:pk>/delete/', views.DealerDeleteView.as_view(), name='dealer_delete'),
|
||||
# CRM URLs
|
||||
path(
|
||||
@ -683,6 +688,17 @@ urlpatterns = [
|
||||
views.preview_sale_order,
|
||||
name="preview_sale_order",
|
||||
),
|
||||
path(
|
||||
"<slug:dealer_slug>/sales/estimates/<uuid:pk>/update_estimate_discount/",
|
||||
views.update_estimate_discount,
|
||||
name="update_estimate_discount",
|
||||
),
|
||||
path(
|
||||
"<slug:dealer_slug>/sales/estimates/<uuid:pk>/update_estimate_additionals/",
|
||||
views.update_estimate_additionals,
|
||||
name="update_estimate_additionals",
|
||||
),
|
||||
|
||||
###############################################
|
||||
# Invoice
|
||||
###############################################
|
||||
|
||||
@ -22,6 +22,7 @@ from django_ledger.models.items import ItemModel
|
||||
from django.utils.translation import get_language
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django_ledger.models.transactions import TransactionModel
|
||||
from django_ledger.models.journal_entry import JournalEntryModel
|
||||
|
||||
@ -473,7 +474,7 @@ def set_invoice_payment(dealer, entity, invoice, amount, payment_method):
|
||||
calculator = CarFinanceCalculator(invoice)
|
||||
finance_data = calculator.get_finance_data()
|
||||
|
||||
# handle_account_process(invoice, amount, finance_data)
|
||||
handle_account_process(invoice, amount, finance_data)
|
||||
invoice.make_payment(amount)
|
||||
invoice.save()
|
||||
|
||||
@ -996,13 +997,15 @@ class CarFinanceCalculator:
|
||||
ADDITIONAL_SERVICES_KEY = "additional_services"
|
||||
|
||||
def __init__(self, model):
|
||||
self.dealer = models.Dealer.objects.get(entity=model.entity)
|
||||
self.model = model
|
||||
self.vat_rate = self._get_vat_rate()
|
||||
self.item_transactions = self._get_item_transactions()
|
||||
self.additional_services = self._get_additional_services()
|
||||
self.extra_info = models.ExtraInfo.objects.get(dealer=self.dealer,content_type=ContentType.objects.get_for_model(model),object_id=model.pk)
|
||||
|
||||
def _get_vat_rate(self):
|
||||
vat = models.VatRate.objects.filter(is_active=True).first()
|
||||
vat = models.VatRate.objects.filter(dealer=self.dealer,is_active=True).first()
|
||||
if not vat:
|
||||
raise ObjectDoesNotExist("No active VAT rate found")
|
||||
return vat.rate
|
||||
@ -1025,7 +1028,6 @@ class CarFinanceCalculator:
|
||||
car_finance = self._get_nested_value(item, self.CAR_FINANCE_KEY)
|
||||
car_info = self._get_nested_value(item, self.CAR_INFO_KEY)
|
||||
unit_price = Decimal(car_finance.get("selling_price", 0))
|
||||
print(item.item_model.car.finances)
|
||||
return {
|
||||
"item_number": item.item_model.item_number,
|
||||
"vin": car_info.get("vin"),
|
||||
@ -1037,6 +1039,7 @@ class CarFinanceCalculator:
|
||||
"mileage": car_info.get("mileage"),
|
||||
"cost_price": car_finance.get("cost_price"),
|
||||
"selling_price": car_finance.get("selling_price"),
|
||||
"marked_price": car_finance.get("marked_price"),
|
||||
"discount": car_finance.get("discount_amount"),
|
||||
"quantity": quantity,
|
||||
"unit_price": unit_price,
|
||||
@ -1070,13 +1073,15 @@ class CarFinanceCalculator:
|
||||
Decimal(x.get("price_")) for x in self._get_additional_services()
|
||||
)
|
||||
|
||||
total_discount = sum(
|
||||
Decimal(
|
||||
self._get_nested_value(item, self.CAR_FINANCE_KEY, "discount_amount")
|
||||
)
|
||||
for item in self.item_transactions
|
||||
)
|
||||
total_price_discounted = total_price - total_discount
|
||||
total_discount = self.extra_info.data.get("discount")
|
||||
|
||||
# total_discount = sum(
|
||||
# Decimal(
|
||||
# self._get_nested_value(item, self.CAR_FINANCE_KEY, "discount_amount")
|
||||
# )
|
||||
# for item in self.item_transactions
|
||||
# )
|
||||
total_price_discounted = total_price - Decimal(total_discount)
|
||||
total_vat_amount = total_price_discounted * self.vat_rate
|
||||
|
||||
return {
|
||||
@ -1085,7 +1090,7 @@ class CarFinanceCalculator:
|
||||
), # total_price_before_discount,
|
||||
"total_price": round(total_price_discounted, 2), # total_price_discounted,
|
||||
"total_vat_amount": round(total_vat_amount, 2), # total_vat_amount,
|
||||
"total_discount": round(total_discount, 2),
|
||||
"total_discount": round(Decimal(total_discount)),
|
||||
"total_additionals": round(total_additionals, 2), # total_additionals,
|
||||
"grand_total": round(
|
||||
total_price_discounted + total_vat_amount + total_additionals, 2
|
||||
@ -1139,7 +1144,6 @@ def get_item_transactions(txs):
|
||||
data["customer"] = tx.invoice_model.customer
|
||||
if bool(data):
|
||||
transactions.append(data)
|
||||
print(data)
|
||||
return transactions
|
||||
|
||||
|
||||
@ -1291,7 +1295,7 @@ def handle_account_process(invoice, amount, finance_data):
|
||||
exc_info=True
|
||||
)
|
||||
|
||||
print(e)
|
||||
|
||||
car.finances.is_sold = True
|
||||
car.finances.save()
|
||||
car.item_model.save()
|
||||
@ -1428,7 +1432,7 @@ def handle_payment(request, order):
|
||||
print("Failed to process payment:", data)
|
||||
#
|
||||
data = response.json()
|
||||
print(data)
|
||||
|
||||
# order.status = AbstractOrder.STATUS.NEW
|
||||
order.save()
|
||||
#
|
||||
|
||||
@ -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 (
|
||||
@ -440,8 +440,16 @@ class ManagerDashboard(LoginRequiredMixin, TemplateView):
|
||||
transfer_cars = models.Car.objects.filter(
|
||||
dealer=dealer, status=models.CarStatusChoices.TRANSFER
|
||||
).count()
|
||||
reserved_percentage = reserved_cars / total_cars * 100
|
||||
sold_percentage = sold_cars / total_cars * 100
|
||||
try:
|
||||
reserved_percentage = reserved_cars / total_cars * 100
|
||||
except ZeroDivisionError as e:
|
||||
print(f"error: {e}")
|
||||
try:
|
||||
sold_percentage = sold_cars / total_cars * 100
|
||||
except ZeroDivisionError as e:
|
||||
print(f"error: {e}")
|
||||
|
||||
|
||||
qs = (
|
||||
models.Car.objects.values("id_car_make__name")
|
||||
.annotate(count=Count("id"))
|
||||
@ -1585,7 +1593,6 @@ class CarUpdateView(
|
||||
def get_form(self, form_class=None):
|
||||
form = super().get_form(form_class)
|
||||
dealer = get_user_type(self.request)
|
||||
print(dealer.get_vendors())
|
||||
form.fields["vendor"].queryset = dealer.vendors.all()
|
||||
return form
|
||||
|
||||
@ -2124,12 +2131,16 @@ class DealerDetailView(LoginRequiredMixin, PermissionRequiredMixin,DetailView):
|
||||
context["cars_count"] = cars_count
|
||||
context["allowed_users"] = dealer.user_quota
|
||||
context["allowed_cars"] = dealer.car_quota
|
||||
context["vatform"] = forms.VatRateForm(initial={"rate": dealer.vat_rate})
|
||||
context["quota_display"] = (
|
||||
f"{staff_count}/{dealer.user_quota}" if dealer.user_quota else "0"
|
||||
)
|
||||
|
||||
return context
|
||||
|
||||
def dealer_vat_rate_update(request,slug):
|
||||
dealer = get_object_or_404(models.Dealer,slug=slug)
|
||||
models.VatRate.objects.filter(dealer=dealer).update(rate=request.POST.get("rate"))
|
||||
return redirect("dealer_detail", slug=slug)
|
||||
|
||||
class DealerUpdateView(LoginRequiredMixin,PermissionRequiredMixin, SuccessMessageMixin, UpdateView):
|
||||
"""
|
||||
@ -2516,6 +2527,8 @@ def vendorDetailView(request, dealer_slug,slug):
|
||||
return render(
|
||||
request, template_name="vendors/view_vendor.html", context={"vendor": vendor}
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
class VendorCreateView(
|
||||
@ -4298,11 +4311,14 @@ class EstimateListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
||||
|
||||
if any([self.request.is_dealer ,self.request.is_manager ,self.request.is_accountant]):
|
||||
qs = models.ExtraInfo.objects.filter(
|
||||
dealer=dealer,
|
||||
content_type=ContentType.objects.get_for_model(EstimateModel),
|
||||
related_content_type=ContentType.objects.get_for_model(models.Staff),
|
||||
)
|
||||
print(qs)
|
||||
elif self.request.is_staff and self.request.is_sales:
|
||||
qs = models.ExtraInfo.objects.filter(
|
||||
dealer=dealer,
|
||||
content_type=ContentType.objects.get_for_model(EstimateModel),
|
||||
related_content_type=ContentType.objects.get_for_model(models.Staff),
|
||||
related_object_id=staff.pk,
|
||||
@ -4499,15 +4515,18 @@ def create_estimate(request, dealer_slug, slug=None):
|
||||
|
||||
if staff:=getattr(request.user.staffmember,'staff',None):
|
||||
models.ExtraInfo.objects.create(
|
||||
dealer=dealer,
|
||||
content_object=estimate,
|
||||
related_object=staff,
|
||||
created_by=request.user,
|
||||
)
|
||||
else:
|
||||
models.ExtraInfo.objects.create(
|
||||
dealer=dealer,
|
||||
content_object=estimate,
|
||||
related_object=request.user,
|
||||
created_by=request.user,
|
||||
data={"vat_rate": 0.15,"discount": 0},
|
||||
)
|
||||
|
||||
url = reverse(
|
||||
@ -4612,14 +4631,22 @@ class EstimateDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView
|
||||
permission_required = ["django_ledger.view_estimatemodel"]
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"])
|
||||
estimate = kwargs.get("object")
|
||||
if estimate.get_itemtxs_data():
|
||||
calculator = CarFinanceCalculator(estimate)
|
||||
finance_data = calculator.get_finance_data()
|
||||
print(finance_data)
|
||||
invoice_obj = InvoiceModel.objects.all().filter(ce_model=estimate).first()
|
||||
kwargs["data"] = finance_data
|
||||
kwargs["invoice"] = invoice_obj
|
||||
try:
|
||||
cf = estimate.get_itemtxs_data()[0].first().item_model.car.finances
|
||||
selected_items = cf.additional_services.filter(dealer=dealer)
|
||||
form = forms.AdditionalFinancesForm()
|
||||
form.initial["additional_finances"] = selected_items
|
||||
kwargs["additionals_form"] = form
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
@ -4699,6 +4726,31 @@ def create_sale_order(request, dealer_slug, pk):
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_POST
|
||||
def update_estimate_discount(request,dealer_slug,pk):
|
||||
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
|
||||
estimate = get_object_or_404(EstimateModel, pk=pk)
|
||||
extra_info = models.ExtraInfo.objects.get(dealer=dealer,content_type=ContentType.objects.get_for_model(EstimateModel),object_id=estimate.pk)
|
||||
discount_amount = request.POST.get("discount_amount",0)
|
||||
|
||||
extra_info.data.update({"discount":Decimal(discount_amount)})
|
||||
extra_info.save()
|
||||
return redirect("estimate_detail", dealer_slug=dealer_slug, pk=pk)
|
||||
@login_required
|
||||
@require_POST
|
||||
def update_estimate_additionals(request,dealer_slug,pk):
|
||||
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
|
||||
form = forms.AdditionalFinancesForm(request.POST)
|
||||
if request.method == "POST":
|
||||
if form.is_valid():
|
||||
estimate = get_object_or_404(EstimateModel, pk=pk)
|
||||
car = estimate.get_itemtxs_data()[0].first().item_model.car
|
||||
car.finances.additional_services.set(form.cleaned_data["additional_finances"])
|
||||
car.finances.save()
|
||||
|
||||
return redirect("estimate_detail", dealer_slug=dealer_slug, pk=pk)
|
||||
|
||||
class SaleOrderDetail(LoginRequiredMixin,PermissionRequiredMixin,DetailView):
|
||||
model = models.SaleOrder
|
||||
template_name = "sales/orders/order_details.html"
|
||||
@ -5331,6 +5383,7 @@ def PaymentCreateView(request, dealer_slug, pk):
|
||||
model = invoice if invoice else bill
|
||||
entity = dealer.entity
|
||||
form = forms.PaymentForm()
|
||||
breakpoint()
|
||||
if request.method == "POST":
|
||||
form = forms.PaymentForm(request.POST)
|
||||
|
||||
@ -5590,19 +5643,18 @@ class LeadListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
||||
qs = models.Lead.objects.filter(dealer=dealer).exclude(status="converted")
|
||||
if query:
|
||||
qs = qs.filter(Q(first_name__icontains=query)
|
||||
| Q(last_name__icontains=query)
|
||||
| Q(id_car_make__name__icontains=query)
|
||||
| Q(id_car_model__name__icontains=query)
|
||||
| Q(email__icontains=query)
|
||||
| Q(phone_number__icontains=query)
|
||||
| Q(next_action__icontains=query)
|
||||
| Q(staff__name__icontains=query))
|
||||
|
||||
if self.request.is_dealer:
|
||||
| Q(last_name__icontains=query)
|
||||
| Q(id_car_make__name__icontains=query)
|
||||
| Q(id_car_model__name__icontains=query)
|
||||
| Q(email__icontains=query)
|
||||
| Q(phone_number__icontains=query)
|
||||
| Q(next_action__icontains=query)
|
||||
| Q(staff__name__icontains=query))
|
||||
|
||||
if self.request.is_dealer :#or self.request.is_manager:
|
||||
return qs
|
||||
if self.request.user.is_staff:
|
||||
staff = getattr(self.request.user.staffmember, "staff", None)
|
||||
return qs.filter(staff=staff)
|
||||
if self.request.is_staff:
|
||||
return qs.filter(staff=self.request.staff)
|
||||
return models.Lead.objects.none()
|
||||
|
||||
|
||||
@ -5659,7 +5711,7 @@ class LeadDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
|
||||
context["transfer_form"] = forms.LeadTransferForm()
|
||||
context["transfer_form"].fields[
|
||||
"transfer_to"
|
||||
].queryset = models.Staff.objects.filter(
|
||||
].queryset = models.Staff.objects.select_related("staff_member","staff_member__user").filter(
|
||||
dealer=dealer,staff_member__user__groups__permissions__codename__contains="can_reassign_lead").exclude(staff_member__user=self.request.user).distinct()
|
||||
|
||||
context["activity_form"] = forms.ActivityForm()
|
||||
@ -5779,15 +5831,12 @@ def lead_create(request,dealer_slug):
|
||||
qs = form.fields["id_car_make"].queryset.filter(
|
||||
is_sa_import=True, pk__in=dealer_make_list
|
||||
)
|
||||
form.fields["staff"].queryset = form.fields["staff"].queryset.filter(
|
||||
dealer=dealer,staff_member__user__groups__permissions__codename__contains="add_lead").distinct()
|
||||
form.fields["staff"].queryset = form.fields["staff"].queryset.select_related("staff_member","staff_member__user").filter(dealer=dealer,staff_member__user__groups__permissions__codename__contains="add_lead").distinct()
|
||||
|
||||
|
||||
if hasattr(request.user.staffmember, "staff"):
|
||||
staff = request.user.staffmember.staff
|
||||
form.initial["staff"] = staff
|
||||
if request.is_staff:
|
||||
form.initial["staff"] = request.staff
|
||||
form.fields["staff"].widget.attrs.update({"readonly":"true","required":"true"})
|
||||
form.fields["staff"].queryset = models.Staff.objects.filter(dealer=dealer,pk=staff.pk)
|
||||
form.fields["staff"].queryset = models.Staff.objects.filter(dealer=dealer,pk=request.staff.pk)
|
||||
form.fields["id_car_make"].queryset = qs
|
||||
form.fields["id_car_make"].choices = [
|
||||
(obj.id_car_make, obj.get_local_name()) for obj in qs
|
||||
@ -5801,7 +5850,7 @@ def lead_create(request,dealer_slug):
|
||||
@permission_required("inventory.view_lead", raise_exception=True)
|
||||
def lead_tracking(request,dealer_slug):
|
||||
dealer = get_object_or_404(models.Dealer,slug=dealer_slug)
|
||||
staff = models.Staff.objects.filter(dealer=dealer, staff_member__user=request.user).first()
|
||||
staff = models.Staff.objects.select_related("staff_member","staff_member__user").filter(dealer=dealer, staff_member__user=request.user).first()
|
||||
|
||||
if staff:
|
||||
qs = models.Lead.objects.filter(dealer=dealer,staff=staff)
|
||||
@ -5945,7 +5994,7 @@ class LeadUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
||||
form.fields[
|
||||
"id_car_model"
|
||||
].queryset = form.instance.id_car_make.carmodel_set.all()
|
||||
form.fields["staff"].queryset = form.fields["staff"].queryset.filter(
|
||||
form.fields["staff"].queryset = form.fields["staff"].queryset.select_related("staff_member","staff_member__user").filter(
|
||||
dealer=dealer,staff_member__user__groups__permissions__codename__contains="add_lead").distinct()
|
||||
|
||||
return form
|
||||
@ -6880,8 +6929,8 @@ class ItemServiceCreateView(
|
||||
permission_required = ["inventory.add_additionalservices"]
|
||||
|
||||
def form_valid(self, form):
|
||||
vat = models.VatRate.objects.get(is_active=True)
|
||||
dealer = get_user_type(self.request)
|
||||
vat = models.VatRate.objects.get(dealer=dealer,is_active=True)
|
||||
form.instance.dealer = dealer
|
||||
if form.instance.taxable:
|
||||
form.instance.price = (form.instance.price * vat.rate) + form.instance.price
|
||||
@ -6927,8 +6976,8 @@ class ItemServiceUpdateView(
|
||||
|
||||
|
||||
def form_valid(self, form):
|
||||
vat = models.VatRate.objects.get(is_active=True)
|
||||
dealer = get_user_type(self.request)
|
||||
vat = models.VatRate.objects.get(dealer=dealer,is_active=True)
|
||||
form.instance.dealer = dealer
|
||||
if form.instance.taxable:
|
||||
form.instance.price = (form.instance.price * vat.rate) + form.instance.price
|
||||
@ -7575,7 +7624,7 @@ class OrderListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
||||
|
||||
def get_queryset(self):
|
||||
dealer = get_user_type(self.request)
|
||||
qs = super().get_queryset()
|
||||
qs = super().get_queryset().select_related("customer","estimate","invoice")
|
||||
return qs.filter(estimate__entity=dealer.entity)
|
||||
|
||||
|
||||
@ -8676,12 +8725,13 @@ class LedgerModelListView(LoginRequiredMixin,PermissionRequiredMixin, ListView,
|
||||
show_visible = False
|
||||
allow_empty = True
|
||||
paginate_by = 30
|
||||
permission_required = "django_ledger.view_ledgermodel"
|
||||
|
||||
|
||||
def get_queryset(self):
|
||||
qs = super().get_queryset()
|
||||
dealer = get_user_type(self.request)
|
||||
qs = qs.filter(entity=dealer.entity)
|
||||
# dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"])
|
||||
# dealer = get_user_type(self.request)
|
||||
qs = qs.filter(entity=self.request.entity)
|
||||
qs = qs.select_related("billmodel", "invoicemodel")
|
||||
qs = qs.order_by("-created")
|
||||
if self.show_all:
|
||||
@ -8694,8 +8744,7 @@ class LedgerModelListView(LoginRequiredMixin,PermissionRequiredMixin, ListView,
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
dealer = get_user_type(self.request)
|
||||
context["entity_slug"] = dealer.entity.slug
|
||||
context["entity_slug"] = self.request.dealer.entity.slug
|
||||
return context
|
||||
|
||||
|
||||
@ -8742,16 +8791,14 @@ class LedgerModelCreateView(LedgerModelCreateViewBase):
|
||||
permission_required = ["django_ledger.add_ledgermodel"]
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
dealer = get_user_type(self.request)
|
||||
return LedgerModelCreateForm(
|
||||
entity_slug=dealer.entity.slug,
|
||||
user_model=dealer.entity.admin,
|
||||
entity_slug=self.request.dealer.entity.slug,
|
||||
user_model=self.request.entity.admin,
|
||||
**self.get_form_kwargs(),
|
||||
)
|
||||
|
||||
def form_valid(self, form):
|
||||
dealer = get_user_type(self.request)
|
||||
form.field["entity"] = dealer.entity
|
||||
form.field["entity"] = self.request.dealer.entity
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_success_url(self):
|
||||
@ -8995,7 +9042,7 @@ class JournalEntryModelTXSDetailView(JournalEntryModelTXSDetailViewBase):
|
||||
"""
|
||||
|
||||
template_name = "ledger/journal_entry/journal_entry_txs.html"
|
||||
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required("django_ledger.change_ledgermodel", raise_exception=True)
|
||||
@ -10253,9 +10300,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']}' "
|
||||
@ -10283,12 +10332,20 @@ def upload_cars(request, dealer_slug, pk=None):
|
||||
vendor=vendor,
|
||||
receiving_date=receiving_date,
|
||||
)
|
||||
if po_item:
|
||||
models.CarFinance.objects.create(
|
||||
car=car,
|
||||
cost_price=po_item.item.unit_cost,
|
||||
marked_price=0,
|
||||
selling_price=0
|
||||
)
|
||||
car.add_colors(exterior=exterior, interior=interior)
|
||||
cars_created += 1
|
||||
logger.debug(
|
||||
f"User {user_username} created Car ID: {car.pk} (VIN: {car.vin}). "
|
||||
f"Count: {cars_created}."
|
||||
)
|
||||
|
||||
if po_item:
|
||||
po_item.status = "uploaded"
|
||||
po_item.save()
|
||||
@ -10368,12 +10425,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()
|
||||
|
||||
@ -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
|
||||
|
||||
9
static/css/all.min.css
vendored
Normal file
9
static/css/all.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -45,7 +45,7 @@
|
||||
}
|
||||
|
||||
.form-control, .form-select {
|
||||
text-align: center;
|
||||
/* text-align: center; */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@ -130,4 +130,5 @@ html[dir="rtl"] .form-icon-container .form-control {
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
6
static/js/fontawesome.min.js
vendored
Normal file
6
static/js/fontawesome.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
t1.py
2
t1.py
@ -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"])
|
||||
@ -26,19 +26,19 @@
|
||||
<meta name="msapplication-TileImage" content="{% static 'images/logos/logo-d.png' %}">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<script src="{% static 'vendors/simplebar/simplebar.min.js' %}"></script>
|
||||
{% comment %} <script src="{% static 'vendors/simplebar/simplebar.min.js' %}"></script> {% endcomment %}
|
||||
<script src="{% static 'js/config.js' %}"></script>
|
||||
<script src="{% static 'js/sweetalert2.all.min.js' %}"></script>
|
||||
<link href="{% static 'vendors/mapbox-gl/mapbox-gl.css' %}" rel="stylesheet">
|
||||
<link href="{% static 'vendors/swiper/swiper-bundle.min.css' %}" rel="stylesheet">
|
||||
{% comment %} <link href="{% static 'vendors/mapbox-gl/mapbox-gl.css' %}" rel="stylesheet"> {% endcomment %}
|
||||
{% comment %} <link href="{% static 'vendors/swiper/swiper-bundle.min.css' %}" rel="stylesheet"> {% endcomment %}
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@300;400;600;700;800;900&display=swap" rel="stylesheet">
|
||||
<link href="{% static 'vendors/simplebar/simplebar.min.css' %}" rel="stylesheet">
|
||||
{% comment %} <link href="{% static 'vendors/simplebar/simplebar.min.css' %}" rel="stylesheet"> {% endcomment %}
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@emran-alhaddad/saudi-riyal-font/index.css">
|
||||
<link href="{% static 'vendors/flatpickr/flatpickr.min.css' %}" rel="stylesheet">
|
||||
{% comment %} <link href="{% static 'vendors/flatpickr/flatpickr.min.css' %}" rel="stylesheet"> {% endcomment %}
|
||||
<link href="{% static 'css/custom.css' %}" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://unicons.iconscout.com/release/v4.0.8/css/line.css">
|
||||
{% comment %} <link rel="stylesheet" href="https://unicons.iconscout.com/release/v4.0.8/css/line.css"> {% endcomment %}
|
||||
{% if LANGUAGE_CODE == 'ar' %}
|
||||
<link href="{% static 'css/theme-rtl.min.css' %}" type="text/css" rel="stylesheet" id="style-rtl">
|
||||
<link href="{% static 'css/user-rtl.min.css' %}" type="text/css" rel="stylesheet" id="user-style-rtl">
|
||||
@ -46,9 +46,9 @@
|
||||
<link href="{% static 'css/theme.min.css' %}" type="text/css" rel="stylesheet" id="style-default">
|
||||
<link href="{% static 'css/user.min.css' %}" type="text/css" rel="stylesheet" id="user-style-default">
|
||||
{% endif %}
|
||||
<script src="{% static 'js/main.js' %}"></script>
|
||||
<script src="{% static 'js/jquery.min.js' %}"></script>
|
||||
<script src="{% static 'js/echarts.js' %}"></script>
|
||||
{% comment %} <script src="{% static 'js/main.js' %}"></script> {% endcomment %}
|
||||
{% comment %} <script src="{% static 'js/jquery.min.js' %}"></script> {% endcomment %}
|
||||
{% comment %} <script src="{% static 'js/echarts.js' %}"></script> {% endcomment %}
|
||||
|
||||
{% block customCSS %}
|
||||
|
||||
@ -79,9 +79,9 @@
|
||||
{% include 'footer.html' %}
|
||||
</div>
|
||||
</main>
|
||||
<script src="{% static 'js/djetler.bundle.js' %}"></script>
|
||||
<script src="{% static 'js/js-utils.js' %}"></script>
|
||||
<script src="{% static 'js/modal/show_modal.js' %}"></script>
|
||||
{% comment %} <script src="{% static 'js/djetler.bundle.js' %}"></script>
|
||||
<script src="{% static 'js/js-utils.js' %}"></script> {% endcomment %}
|
||||
{% comment %} <script src="{% static 'js/modal/show_modal.js' %}"></script> {% endcomment %}
|
||||
|
||||
<!-- ===============================================-->
|
||||
<!-- JavaScripts-->
|
||||
@ -90,31 +90,30 @@
|
||||
<!--1-->
|
||||
<script src="{% static 'vendors/bootstrap/bootstrap.min.js' %}"></script>
|
||||
|
||||
<script src="{% static 'vendors/anchorjs/anchor.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/is/is.min.js' %}"></script>
|
||||
{% comment %} <script src="{% static 'vendors/anchorjs/anchor.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/is/is.min.js' %}"></script> {% endcomment %}
|
||||
<!--2-->
|
||||
<script src="{% static 'vendors/fontawesome/all.min.js' %}"></script>
|
||||
|
||||
<script src="{% static 'vendors/lodash/lodash.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/list.js/list.min.js' %}"></script>
|
||||
{% comment %} <script src="{% static 'vendors/list.js/list.min.js' %}"></script> {% endcomment %}
|
||||
<script src="{% static 'vendors/feather-icons/feather.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/dayjs/dayjs.min.js' %}"></script>
|
||||
{% comment %} <script src="{% static 'vendors/dayjs/dayjs.min.js' %}"></script> {% endcomment %}
|
||||
<script src="{% static 'js/phoenix.js' %}"></script>
|
||||
<script src="{% static 'js/apexcharts.js' %}"></script>
|
||||
<script src="{% static 'vendors/echarts/echarts.min.js' %}"></script>
|
||||
<script src="{% static 'js/crm-analytics.js' %}"></script>
|
||||
<script src="{% static 'js/travel-agency-dashboard.js' %}"></script>
|
||||
{% comment %} <script src="{% static 'js/apexcharts.js' %}"></script> {% endcomment %}
|
||||
{% comment %} <script src="{% static 'vendors/echarts/echarts.min.js' %}"></script> {% endcomment %}
|
||||
{% comment %} <script src="{% static 'js/crm-analytics.js' %}"></script> {% endcomment %}
|
||||
{% comment %} <script src="{% static 'js/travel-agency-dashboard.js' %}"></script>
|
||||
<script src="{% static 'js/crm-dashboard.js' %}"></script>
|
||||
<script src="{% static 'js/projectmanagement-dashboard.js' %}"></script>
|
||||
<script src="{% static 'js/projectmanagement-dashboard.js' %}"></script> {% endcomment %}
|
||||
|
||||
<script src="{% static 'vendors/mapbox-gl/mapbox-gl.js' %}"></script>
|
||||
<script src="{% static 'vendors/turf.min.js' %}"></script>
|
||||
{% comment %} <script src="{% static 'vendors/mapbox-gl/mapbox-gl.js' %}"></script> {% endcomment %}
|
||||
{% comment %} <script src="{% static 'vendors/turf.min.js' %}"></script> {% endcomment %}
|
||||
<script src="{% static 'vendors/htmx.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/swiper/swiper-bundle.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/flatpickr/flatpickr.min.js' %}"></script>
|
||||
{% comment %} <script src="{% static 'vendors/swiper/swiper-bundle.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/flatpickr/flatpickr.min.js' %}"></script> {% endcomment %}
|
||||
|
||||
<script>
|
||||
|
||||
{% if entity_slug %}
|
||||
let entitySlug = "{{ view.kwargs.entity_slug }}"
|
||||
{% endif %}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
{% load crispy_forms_filters %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
{% comment %} <div class="row justify-content-center">
|
||||
<div class="col-lg-6">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-light py-3">
|
||||
@ -46,5 +46,63 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> {% endcomment %}
|
||||
|
||||
|
||||
<!---->
|
||||
|
||||
<div class="row justify-content-center mt-5 mb-3">
|
||||
|
||||
<div class="col-lg-8 col-md-10">
|
||||
<div class="card shadow-sm border-0 rounded-3">
|
||||
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
|
||||
<h3 class="mb-0 fs-4 text-center text-white">
|
||||
{% trans 'Create Bill' %}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body bg-light-subtle">
|
||||
|
||||
<form action="{{ form_action_url }}" method="post" id="djl-bill-model-create-form-id">
|
||||
<div class="card-body">
|
||||
|
||||
{% csrf_token %}
|
||||
{% if po_model %}
|
||||
<div class="text-center mb-4">
|
||||
<h3 class="h5">{% trans 'Bill for' %} {{ po_model.po_number }}</h3>
|
||||
<p class="text-muted mb-3">{% trans 'Bill for' %} {{ po_model.po_title }}</p>
|
||||
<div class="d-flex flex-column gap-2">
|
||||
{% for itemtxs in po_itemtxs_qs %}
|
||||
<span class="badge bg-secondary">{{ itemtxs }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="mb-4">
|
||||
{{ form|crispy }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-footer bg-transparent">
|
||||
<div class="d-grid gap-2">
|
||||
<button type="submit"
|
||||
id="djl-bill-create-button"
|
||||
class="btn btn-phoenix-primary btn-lg">{% trans 'Create' %}
|
||||
</button>
|
||||
<a href="{{request.META.HTTP_REFERER}}"
|
||||
id="djl-bill-create-back-button"
|
||||
class="btn btn-phoenix-secondary">{% trans 'Cancel' %}</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!---->
|
||||
{% endblock %}
|
||||
@ -44,12 +44,7 @@
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
{% include 'bill/includes/card_bill.html' with dealer_slug=request.dealer.slug bill=bill entity_slug=view.kwargs.entity_slug style='bill-detail' %}
|
||||
|
||||
<div class="d-grid mt-4">
|
||||
<a href="{% url 'bill_list' request.dealer.slug %}" class="btn btn-phoenix-primary">
|
||||
<i class="fas fa-arrow-left me-1"></i> {% trans 'Bill List' %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -9,7 +9,6 @@
|
||||
<div class="container py-4">
|
||||
<div class="row g-2">
|
||||
|
||||
|
||||
<!-- Bill Form -->
|
||||
<div class="col-12">
|
||||
|
||||
@ -18,11 +17,7 @@
|
||||
<div class="card mb-2">
|
||||
<div class="card-body">
|
||||
{% include 'bill/includes/card_bill.html' with dealer_slug=request.dealer.slug bill=bill_model style='bill-detail' entity_slug=view.kwargs.entity_slug %}
|
||||
<a href="{% url 'bill-detail' dealer_slug=request.dealer.slug entity_slug=view.kwargs.entity_slug bill_pk=bill_model.uuid %}"
|
||||
class="btn btn-phoenix-secondary w-100 mb-2">
|
||||
<i class="fas fa-arrow-left me-2"></i>{% trans 'Back to Bill Detail' %}
|
||||
</a>
|
||||
|
||||
|
||||
<form action="{% url 'bill-update' dealer_slug=request.dealer.slug entity_slug=view.kwargs.entity_slug bill_pk=bill_model.uuid %}" method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
@ -30,20 +25,21 @@
|
||||
{{ form|crispy }}
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-phoenix-primary w-100 mb-2">
|
||||
<button type="submit" class="btn btn-phoenix-primary mb-2 me-2">
|
||||
<i class="fas fa-save me-2"></i>{% trans 'Save Bill' %}
|
||||
</button>
|
||||
<a href="{% url 'bill-detail' dealer_slug=request.dealer.slug entity_slug=view.kwargs.entity_slug bill_pk=bill_model.uuid %}"
|
||||
class="btn btn-phoenix-secondary mb-2">
|
||||
<i class="fas fa-arrow-left me-2"></i>{% trans 'Back to Bill Detail' %}
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
<a href="{% url 'bill_list' request.dealer.slug %}"
|
||||
class="btn btn-phoenix-info w-100 mb-2">
|
||||
<i class="fas fa-list me-2"></i>{% trans 'Bill List' %}
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -51,7 +47,6 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- Bill Item Formset -->
|
||||
<div class="col-12">
|
||||
{% bill_item_formset_table itemtxs_formset %}
|
||||
|
||||
@ -1,20 +1,22 @@
|
||||
{% load django_ledger %}
|
||||
{% load i18n %}
|
||||
<div id="djl-bill-card-widget" class="">
|
||||
|
||||
{% if not create_bill %}
|
||||
{% if style == 'dashboard' %}
|
||||
<!-- Dashboard Style Card -->
|
||||
<div class="">
|
||||
<div class="card-body ">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h6 class="text-uppercase text-secondary mb-0">
|
||||
<div class="card-body">
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-3 text-primary">
|
||||
<h6 class="text-uppercase text-primary mb-0">
|
||||
<i class="fas fa-file-invoice me-2"></i>{% trans 'Bill' %}
|
||||
</h6>
|
||||
<span class="badge bg-{{ bill.get_status_badge_color }}">{{ bill.get_bill_status_display }}</span>
|
||||
<span class="badge bg-{{ bill.get_status_badge_color }}">{{ bill.get_bill_status_display }}</span>
|
||||
</div>
|
||||
<h4 class="card-title">{{ bill.vendor.vendor_name }}</h4>
|
||||
<p class="text-sm text-muted mb-4">{{ bill.vendor.address_1 }}</p>
|
||||
|
||||
|
||||
{% if not bill.is_past_due %}
|
||||
<p class="text-info mb-2">
|
||||
<i class="fas fa-clock me-2"></i>{% trans 'Due in' %}: {{ bill.date_due | timeuntil }}
|
||||
@ -91,12 +93,20 @@
|
||||
<!-- Detail Style Card -->
|
||||
<div class="">
|
||||
<div class="card-header p-2 bg-{{ bill.get_status_badge_color }}">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="fas fa-file-invoice me-3 text-white"></i>
|
||||
<h2 class="mb-0 text-white">
|
||||
<div class="d-flex align-items-center justify-content-center mb-2 text-primary">
|
||||
<i class="fas fa-file-invoice me-3 "></i>
|
||||
<h4 class="mb-0 text-primary me-2">
|
||||
{% trans 'Bill' %} {{ bill.bill_number }}
|
||||
</h2>
|
||||
</h4>
|
||||
|
||||
</div>
|
||||
<a href="{% url 'bill_list' request.dealer.slug %}"
|
||||
class="btn btn-phoenix-primary mb-2">
|
||||
<i class="fas fa-long-arrow-alt-left me-2"></i>{% trans 'Back to Bill List' %}
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<div class="card-body p-2 text-center">
|
||||
{% if bill.is_draft %}
|
||||
@ -221,12 +231,15 @@
|
||||
<div class="d-flex flex-wrap gap-2 mt-2">
|
||||
<!-- Update Button -->
|
||||
{% if perms.django_ledger.change_billmodel%}
|
||||
<a href="{% url 'bill-update' dealer_slug=dealer_slug entity_slug=entity_slug bill_pk=bill.uuid %}" class="btn btn-phoenix-primary">
|
||||
<i class="fas fa-edit me-2"></i>{% trans 'Update' %}
|
||||
</a>
|
||||
<button class="btn btn-phoenix-primary" {% if not request.is_accountant %} disabled {% endif %}>
|
||||
<a href="{% url 'bill-update' dealer_slug=dealer_slug entity_slug=entity_slug bill_pk=bill.uuid %}">
|
||||
<i class="fas fa-edit me-2"></i>{% trans 'Update' %}
|
||||
</a>
|
||||
</button>
|
||||
<!-- Mark as Draft -->
|
||||
{% if bill.can_draft %}
|
||||
<button class="btn btn-phoenix-success"
|
||||
{% if not request.is_accountant %} disabled {% endif %}
|
||||
onclick="showPOModal('Mark as Draft', '{% url 'bill-action-mark-as-draft' dealer_slug=request.dealer.slug entity_slug=entity_slug bill_pk=bill.pk %}', 'Mark as Draft')">
|
||||
<i class="fas fa-check-circle me-2"></i>{% trans 'Mark as Draft' %}
|
||||
</button>
|
||||
@ -234,6 +247,7 @@
|
||||
<!-- Mark as Review -->
|
||||
{% if bill.can_review %}
|
||||
<button class="btn btn-phoenix-warning"
|
||||
{% if not request.is_accountant %} disabled {% endif %}
|
||||
onclick="showPOModal('Mark as Review', '{% url 'bill-action-mark-as-review' dealer_slug=request.dealer.slug entity_slug=entity_slug bill_pk=bill.pk %}', 'Mark as Review')">
|
||||
<i class="fas fa-check-circle me-2"></i>{% trans 'Mark as Review' %}
|
||||
</button>
|
||||
@ -245,6 +259,11 @@
|
||||
<i class="fas fa-check-circle me-2"></i>{% trans 'Mark as Approved' %}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if bill.can_approve and not request.is_manager %}
|
||||
<button class="btn btn-phoenix-warning" disabled>
|
||||
<i class="fas fa-hourglass-start me-2"></i><span class="text-warning">{% trans 'Waiting for Manager Approval' %}</span>
|
||||
</button>
|
||||
{% endif %}
|
||||
<!-- Mark as Paid -->
|
||||
{% if bill.can_pay %}
|
||||
<button class="btn btn-phoenix-success"
|
||||
@ -262,6 +281,7 @@
|
||||
<!-- Cancel Button -->
|
||||
{% if bill.can_cancel %}
|
||||
<button class="btn btn-phoenix-danger"
|
||||
{% if not request.is_accountant %} disabled {% endif %}
|
||||
onclick="showPOModal('Mark as Canceled', '{% url 'bill-action-mark-as-canceled' dealer_slug=request.dealer.slug entity_slug=entity_slug bill_pk=bill.pk %}', 'Mark as Canceled')">
|
||||
<i class="fas fa-check-circle me-2"></i>{% trans 'Mark as Canceled' %}
|
||||
</button>
|
||||
@ -277,6 +297,7 @@
|
||||
<!-- Create Bill Card -->
|
||||
{% if perms.django_ledger.add_billmodel%}
|
||||
<div class=" bg-light">
|
||||
|
||||
<div class="card-body text-center p-5">
|
||||
<a href="{% url 'django_ledger:bill-create' entity_slug=entity_slug %}"
|
||||
class="text-primary">
|
||||
|
||||
@ -3,15 +3,7 @@
|
||||
|
||||
{% if style == 'card_1' %}
|
||||
<div class="card h-100" style="height: 25rem;">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title fs-3 fw-light mb-0">
|
||||
{% if title %}
|
||||
{{ title }}
|
||||
{% else %}
|
||||
{% trans 'Notes' %}
|
||||
{% endif %}
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
<div class="card-body overflow-auto">
|
||||
{% if notes_html %}
|
||||
{{ notes_html|safe }}
|
||||
|
||||
@ -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 %}
|
||||
|
||||
@ -137,6 +137,29 @@
|
||||
<div class="d-flex align-items-center mb-1"><span class="me-2 uil uil-file-check-alt"></span>
|
||||
<h5 class="text-body-highlight fw-bold mb-0">{{ _("Lead Source")}}</h5>
|
||||
</div>
|
||||
{% if lead.source == 'REFERRALS' %}
|
||||
<span class="ms-2 fa fa-users"></span>
|
||||
{% elif lead.source == 'WHATSAPP' %}
|
||||
<span class="ms-2 fa fa-whatsapp"></span>
|
||||
{% elif lead.source == 'SHOWROOM' %}
|
||||
<span class="ms-2 fa fa-building"></span>
|
||||
{% elif lead.source == 'TIKTOK' %}
|
||||
<span class="ms-2 fa fa-tiktok"></span>
|
||||
{% elif lead.source == 'INSTAGRAM' %}
|
||||
<span class="ms-2 fa fa-instagram"></span>
|
||||
{% elif lead.source == 'X' %}
|
||||
<span class="ms-2 fa fa-times-circle"></span>
|
||||
{% elif lead.source == 'FACEBOOK' %}
|
||||
<span class="ms-2 fa fa-facebook-f"></span>
|
||||
{% elif lead.source == 'MOTORY' %}
|
||||
<span class="ms-2 fa fa-car-side"></span>
|
||||
{% elif lead.source == 'INFLUENCERS' %}
|
||||
<span class="ms-2 fa fa-user-check"></span>
|
||||
{% elif lead.source == 'YOUTUBE' %}
|
||||
<span class="ms-2 fa fa-youtube"></span>
|
||||
{% elif lead.source == 'CAMPAIGN' %}
|
||||
<span class="ms-2 fa fa-bullhorn"></span>
|
||||
{% endif %}
|
||||
<span class="text-body-secondary">{{ lead.source|upper }}</span>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
@ -490,6 +513,7 @@
|
||||
<th class="sort align-middle pe-3 text-uppercase" scope="col" data-sort="sent" style="width:15%; min-width:130px">Assigned to</th>
|
||||
<th class="sort align-middle text-start text-uppercase" scope="col" data-sort="date" style="min-width:165px">Due Date</th>
|
||||
<th class="sort align-middle text-start text-uppercase" scope="col" data-sort="date" style="min-width:165px">Completed</th>
|
||||
<th class="sort align-middle text-start text-uppercase" scope="col" data-sort="date" style="min-width:165px"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="list" id="all-tasks-table-body">
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n static crispy_forms_filters %}
|
||||
|
||||
{% block title %}
|
||||
{# Check if an 'object' exists in the context #}
|
||||
{% if object %}
|
||||
{% trans 'Update Lead'%}
|
||||
{% trans 'Update Lead' %}
|
||||
{% else %}
|
||||
{% trans 'Add New Lead'%}
|
||||
{% trans 'Add New Lead' %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block customcss %}
|
||||
<style>
|
||||
.htmx-indicator{
|
||||
@ -17,49 +18,68 @@
|
||||
.htmx-request .htmx-indicator{
|
||||
opacity:1;
|
||||
}
|
||||
.htmx-request.htmx-indicator{
|
||||
.htmx-request.htmx-indicator{ /* For elements with htmx-indicator itself becoming the target */
|
||||
opacity:1;
|
||||
}
|
||||
/* Style for the inline spinner if needed */
|
||||
.inline-spinner {
|
||||
width: 1.5rem; /* Adjust size */
|
||||
height: 1.5rem; /* Adjust size */
|
||||
vertical-align: middle;
|
||||
margin-left: 0.5rem; /* Space from the field */
|
||||
}
|
||||
</style>
|
||||
{% endblock customcss %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<h1>{% if object %}{{ _("Update Lead") }}{% else %}{{ _("Create New Lead") }}{% endif %}</h1>
|
||||
<div class="row mb-3">
|
||||
<div class="col-sm-6 col-md-8">
|
||||
<div class="row justify-content-center mt-5 mb-3">
|
||||
<div class="col-lg-8 col-md-10">
|
||||
<div class="card shadow-sm border-0 rounded-3">
|
||||
<div class="card-header bg-gray-300 py-3 border-0 rounded-top-3">
|
||||
<h3 class="mb-0 fs-4 text-center">
|
||||
{% if object %}
|
||||
{{ _("Update Lead") }}
|
||||
{% else %}
|
||||
{{ _("Create New Lead") }}
|
||||
{% endif %}
|
||||
</h3>
|
||||
|
||||
</div>
|
||||
<div class="card-body bg-light-subtle">
|
||||
<form class="form" method="post">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
|
||||
<form class="form" method="post">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<div class="d-flex justify-content-start">
|
||||
<button class="btn btn-sm btn-phoenix-success me-2" type="submit"><i class="fa-solid fa-floppy-disk me-1"></i>
|
||||
<!--<i class="bi bi-save"></i> -->
|
||||
{{ _("Save") }}
|
||||
</button>
|
||||
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-sm btn-phoenix-danger"><i class="fa-solid fa-ban me-1"></i>{% trans "Cancel" %}</a>
|
||||
</div>
|
||||
</form>
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="d-grid gap-2 d-md-flex justify-content-md-center mt-3">
|
||||
|
||||
<button class="btn btn-phoenix-success btn-lg me-md-2" type="submit">
|
||||
<i class="fa-solid fa-floppy-disk me-1"></i>
|
||||
{{ _("Save") }}
|
||||
</button>
|
||||
<a href="{{ request.META.HTTP_REFERER }}" class="btn btn-phoenix-danger btn-lg">
|
||||
<i class="fa-solid fa-ban me-1"></i>
|
||||
{% trans "Cancel" %}
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// First, create the spinner div (or use the existing one)
|
||||
const spinner = document.createElement('div');
|
||||
spinner.id = 'spinner';
|
||||
spinner.className = 'htmx-indicator spinner-border inline-spinner';
|
||||
spinner.setAttribute('role', 'status');
|
||||
spinner.className = 'htmx-indicator spinner-border text-primary inline-spinner';
|
||||
spinner.innerHTML = '<span class="visually-hidden">Loading...</span>';
|
||||
const targetFieldDiv = document.getElementById('div_id_id_car_model');
|
||||
|
||||
// Find the form field you want to place it next to
|
||||
// Replace 'id_your_field_name' with the actual ID of your form field
|
||||
const targetField = document.getElementById('div_id_id_car_model');
|
||||
|
||||
if (targetField) {
|
||||
// Insert the spinner right after the target field
|
||||
targetField.parentNode.insertBefore(spinner, targetField.nextSibling);
|
||||
if (targetFieldDiv) {
|
||||
targetFieldDiv.parentNode.insertBefore(spinner, targetFieldDiv.nextSibling);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="row g-3 mt-4 mb-4">
|
||||
<h2 class="mb-2">{{ _("Leads")|capfirst }}</h2>
|
||||
<h2 class="mb-2">{{ _("Leads")|capfirst }}<li class="fas fa-bullhorn text-primary ms-2"></li></h2>
|
||||
<!-- Action Tracking Modal -->
|
||||
{% include "crm/leads/partials/update_action.html" %}
|
||||
|
||||
|
||||
@ -68,7 +68,7 @@
|
||||
<div class="row justify-content-center">
|
||||
<div class="col">
|
||||
<div class="d-flex justify-content-between mb-3">
|
||||
<h3>{{ _("Lead Tracking")}}</h3>
|
||||
<h3>{{ _("Lead Tracking")}}<li class="fas fa-bullhorn text-primary ms-2"></li></h3>
|
||||
</div>
|
||||
|
||||
<div class="row g-3">
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
{% block content %}
|
||||
<div class="row g-3 mt-4">
|
||||
<div class="col-12">
|
||||
<h2 class="mb-3">{{ _("Opportunities") }}</h2>
|
||||
<h2 class="mb-3">{{ _("Opportunities") }} <li class="fas fas fa-rocket text-primary ms-2"></li></h2>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="d-flex flex-column flex-md-row justify-content-between align-items-md-center gap-3 mb-4">
|
||||
|
||||
@ -9,38 +9,38 @@
|
||||
{% trans 'Add New Customer'%}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
<link rel="stylesheet" href="{% static 'flags/sprite.css' %}">
|
||||
<div class="row">
|
||||
<div class="row mb-3">
|
||||
<div class="col-sm-6 col-md-8">
|
||||
<div class="d-sm-flex justify-content-between">
|
||||
<h3 class="mb-3">
|
||||
<div class="row justify-content-center mt-5 mb-3">
|
||||
|
||||
<div class="col-lg-8 col-md-10">
|
||||
<div class="card shadow-sm border-o rounded-3">
|
||||
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
|
||||
<h3 class="mb-0 fs-4 text-center">
|
||||
{% if customer.created %}
|
||||
<i class="fa-solid fa-user"></i> {{ _("Edit Customer") }}
|
||||
{% else %}
|
||||
<i class="fa-solid fa-user"></i> {{ _("Add Customer") }}
|
||||
{% endif %}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-sm-6 col-md-8">
|
||||
<div class="card-body bg-light-subtle">
|
||||
<form method="post" class="form row g-3 needs-validation" enctype="multipart/form-data" novalidate>
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<div class="col-12">
|
||||
<button class="btn btn-sm btn-phoenix-success me-2" type="submit"><i class="fa-solid fa-floppy-disk me-1"></i>
|
||||
<!--<i class="bi bi-save"></i> -->
|
||||
{{ _("Save") }}
|
||||
</button>
|
||||
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-sm btn-phoenix-danger"><i class="fa-solid fa-ban me-1"></i>{% trans "Cancel" %}</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="d-grid gap-2 d-md-flex justify-content-md-center mt-3">
|
||||
<button class="btn btn-phoenix-success btn-lg me-md-2" type="submit"><i class="fa-solid fa-floppy-disk me-1"></i>{{ _("Save") }}</button>
|
||||
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-lg btn-phoenix-danger"><i class="fa-solid fa-ban me-1"></i>{% trans "Cancel" %}</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{%endblock %}
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="row g-3 mt-4">
|
||||
<h2 class="mb-2">{{ _("Customers")|capfirst }}</h2>
|
||||
<h2 class="mb-2">{{ _("Customers")|capfirst }} <li class="fas fa-people-group text-primary ms-2"></li></h2>
|
||||
<div class="row g-3 justify-content-between mb-4">
|
||||
<div class="col-auto">
|
||||
<div class="d-md-flex justify-content-between">
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n static custom_filters%}
|
||||
{% load i18n static custom_filters crispy_forms_filters %}
|
||||
{%block title%}{%trans 'Profile'%} {%endblock%}
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
@ -180,6 +180,26 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6">
|
||||
<div class="card h-100">
|
||||
<div class="bg-holder" style="background-image:url({% static 'images/bg/bg-left-20.png' %});background-position:left bottom;background-size:auto;"></div>
|
||||
<div class="card-body d-flex flex-column justify-content-center position-relative">
|
||||
<h4 class="mb-3">{{ _("VAT") }}</h4>
|
||||
<div class="d-flex justify-content-center ">
|
||||
<div class="text-center me-3">
|
||||
<div class="row">
|
||||
<form action="{% url 'dealer_vat_rate_update' request.dealer.slug %}" method="post">
|
||||
{% csrf_token %}
|
||||
{{vatform|crispy}}
|
||||
<button class="btn btn-sm btn-phoenix-primary" type="submit">{% trans 'Update' %}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -28,16 +28,27 @@
|
||||
aria-label="Close"></button>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="container-fluid m-0 {% if not vendor_exists %}disabled{% endif %}">
|
||||
<form method="post" id="carForm" class="form needs-validation" novalidate>
|
||||
{% csrf_token %}
|
||||
{% include 'partials/form_errors.html' %}
|
||||
|
||||
<!---->
|
||||
<div class="row justify-content-center mt-5 mb-3 {% if not vendor_exists %}disabled{% endif %}">
|
||||
|
||||
<div class="col-lg-8 col-md-10">
|
||||
<div class="card shadow-sm border-0 rounded-3">
|
||||
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
|
||||
<h3 class="mb-0 fs-4 text-center text-white">
|
||||
<h3 class="mb-3">{% trans 'Add Car' %} <li class="fas fa-car-on text-primary ms-2"></li></h3>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body bg-light-subtle">
|
||||
<form method="post" id="carForm" class="form needs-validation" novalidate>
|
||||
{% csrf_token %}
|
||||
{% include 'partials/form_errors.html' %}
|
||||
<div class="d-flex flex-column">
|
||||
<div class="d-flex flex-column flex-sm-grow-1 p-0">
|
||||
<div class="row g-4">
|
||||
<h3 class="mb-3">{% trans 'Add Car' %}</h3>
|
||||
|
||||
<!-- VIN Section -->
|
||||
<div class="col-lg-12 col-xl-6">
|
||||
<div class="col-lg-12 ">
|
||||
<div class="card bg-body mb-3 shadow-sm">
|
||||
<div class="card-body">
|
||||
<!-- Improved VIN input with integrated buttons -->
|
||||
@ -144,7 +155,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-3">
|
||||
<div class="col-xl-6">
|
||||
<div class="col-lg-12">
|
||||
<div class="row">
|
||||
<!--Vendor Field-->
|
||||
<div class="col-lg-4 col-xl-4">
|
||||
@ -202,28 +213,39 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--Save Buttons-->
|
||||
<div class="btn-group mt-3">
|
||||
<button type="submit"
|
||||
|
||||
|
||||
<hr class="my-2">
|
||||
<!--Save Buttons-->
|
||||
<div class="d-grid gap-2 d-md-flex justify-content-md-center mt-3">
|
||||
|
||||
<button type="submit"
|
||||
name="add_another"
|
||||
value="true"
|
||||
class="btn btn-phoenix-success me-1">
|
||||
class="btn btn-lg btn-phoenix-success md-me-2">
|
||||
{% trans "Save and Add Another" %}
|
||||
</button>
|
||||
<button type="submit"
|
||||
name="go_to_stats"
|
||||
value="true"
|
||||
class="btn btn-phoenix-primary">
|
||||
class="btn btn-lg btn-phoenix-primary">
|
||||
{% trans "Save and Go to Inventory" %}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
<!-- Modal sections remain largely unchanged -->
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal sections remain largely unchanged -->
|
||||
<!--Specification Modal-->
|
||||
<div class="modal fade"
|
||||
id="specificationsModal"
|
||||
@ -312,7 +334,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!---->
|
||||
<script>
|
||||
function getCookie(name) {
|
||||
let cookieValue = null;
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
<div class="container-fluid" id="projectSummary">
|
||||
<div class="row g-3 justify-content-between align-items-end mb-4">
|
||||
<div class="col-12 col-sm-auto">
|
||||
<h2 class="text-body-emphasis fw-bold mb-0">{{ _("Inventory") }}</h2>
|
||||
<h2 class="text-body-emphasis fw-bold mb-0">{{ _("Inventory") }}<li class="fas fa-store text-primary ms-2"></li></h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-3 justify-content-between align-items-end mb-2">
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
{% load i18n %}
|
||||
{% block title %}{{ _("Add New Expense") }}{% endblock title %}
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
{% comment %} <div class="row">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
@ -13,9 +13,7 @@
|
||||
<form method="post" action="">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<!--<button type="submit" class="btn btn-primary">{% trans 'Save' %}</button> -->
|
||||
{% comment %} <button class="btn btn-sm btn-success me-1" type="submit"><i class="fa-solid fa-floppy-disk"></i>{{ _("Save") }}</button> {% endcomment %}
|
||||
|
||||
|
||||
<div class="d-flex justify-content-start">
|
||||
<button class="btn btn-sm btn-phoenix-success me-2" type="submit"><i class="fa-solid fa-floppy-disk me-1"></i>{{ _("Save") }}</button>
|
||||
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-sm btn-phoenix-danger"><i class="fa-solid fa-ban me-1"></i>{% trans "Cancel" %}</a>
|
||||
@ -26,6 +24,43 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> {% endcomment %}
|
||||
|
||||
|
||||
|
||||
<!---->
|
||||
<div class="row justify-content-center mt-5 mb-3">
|
||||
|
||||
<div class="col-lg-8 col-md-10">
|
||||
<div class="card shadow-sm border-0 rounded-3">
|
||||
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
|
||||
<h3 class="mb-0 fs-4 text-center text-white">
|
||||
{{ _("Add Expense") }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body bg-light-subtle">
|
||||
|
||||
<form method="post" action="">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
|
||||
<hr class="my-2">
|
||||
<div class="d-grid gap-2 d-md-flex justify-content-md-center mt-3">
|
||||
<button class="btn btn-lg btn-phoenix-success md-me-2" type="submit"><i class="fa-solid fa-floppy-disk me-1"></i>{{ _("Save") }}</button>
|
||||
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-lg btn-phoenix-danger"><i class="fa-solid fa-ban me-1"></i>{% trans "Cancel" %}</a>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!---->
|
||||
{% endblock content %}
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
{% load i18n %}
|
||||
{% block title %}{{ _("Update Expense") }}{% endblock title %}
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
{% comment %} <div class="row">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
@ -19,6 +19,40 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> {% endcomment %}
|
||||
|
||||
|
||||
<!---->
|
||||
<div class="row justify-content-center mt-5 mb-3">
|
||||
|
||||
<div class="col-lg-8 col-md-10">
|
||||
<div class="card shadow-sm border-0 rounded-3">
|
||||
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
|
||||
<h3 class="mb-0 fs-4 text-center text-white">
|
||||
{{ _("Update Expense") }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body bg-light-subtle">
|
||||
|
||||
<form method="post" action="">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<hr class="my-2">
|
||||
<div class="d-grid gap-2 d-md-flex justify-content-md-center mt-3">
|
||||
<button class="btn btn-lg btn-phoenix-success md-me-2" type="submit"><i class="fa-solid fa-floppy-disk me-1"></i>{{ _("Save") }}</button>
|
||||
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-lg btn-phoenix-danger"><i class="fa-solid fa-ban me-1"></i>{% trans "Cancel" %}</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<!---->
|
||||
{% endblock content %}
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
{% comment %} <div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card bg-body">
|
||||
@ -38,6 +38,45 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> {% endcomment %}
|
||||
|
||||
|
||||
<!---->
|
||||
<div class="row justify-content-center mt-5 mb-3">
|
||||
|
||||
<div class="col-lg-8 col-md-10">
|
||||
<div class="card shadow-sm border-0 rounded-3">
|
||||
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
|
||||
<h3 class="mb-0 fs-4 text-center text-white">
|
||||
{% if service.pk %}
|
||||
{{ _("Update Service") }}
|
||||
{% else %}
|
||||
{{ _("Add Service") }}
|
||||
{% endif %}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body bg-light-subtle">
|
||||
|
||||
<form method="post" action="">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
|
||||
<hr class="my-2">
|
||||
<div class="d-grid gap-2 d-md-flex justify-content-md-center mt-3">
|
||||
<button class="btn btn-lg btn-phoenix-success md-me-2" type="submit"><i class="fa-solid fa-floppy-disk me-1"></i>{{ _("Save") }}</button>
|
||||
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-lg btn-phoenix-danger"><i class="fa-solid fa-ban me-1"></i>{% trans "Cancel" %}</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!---->
|
||||
{% endblock content %}
|
||||
|
||||
|
||||
@ -12,7 +12,8 @@
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
{% comment %} <div class="container">
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-8">
|
||||
<div class="card shadow rounded bg-body">
|
||||
@ -46,5 +47,48 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> {% endcomment %}
|
||||
|
||||
|
||||
|
||||
<!---->
|
||||
<div class="row justify-content-center mt-5 mb-3">
|
||||
|
||||
<div class="col-lg-8 col-md-10">
|
||||
<div class="card shadow-sm border-0 rounded-3">
|
||||
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
|
||||
<h3 class="mb-0 fs-4 text-center text-white">
|
||||
{% if customer.created %}
|
||||
{{ _("Edit Bank Account") }}
|
||||
{% else %}
|
||||
{{ _("Add Bank Account") }}
|
||||
{% endif %}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body bg-light-subtle">
|
||||
|
||||
<form method="post" class="form" novalidate>
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
{% for error in form.errors %}
|
||||
<div class="text-danger">{{ error }}</div>
|
||||
{% endfor %}
|
||||
<hr class="my-2">
|
||||
<div class="d-grid gap-2 d-md-flex justify-content-md-center mt-3">
|
||||
<button class="btn btn-lg btn-phoenix-success md-me-2" type="submit"><i class="fa-solid fa-floppy-disk me-1"></i>{{ _("Save") }}</button>
|
||||
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-lg btn-phoenix-danger"><i class="fa-solid fa-ban me-1"></i>{% trans "Cancel" %}</a>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!---->
|
||||
{% endblock %}
|
||||
@ -12,6 +12,7 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% comment %}
|
||||
<div class="row my-5">
|
||||
<!-- Display Form Errors -->
|
||||
<div class="card shadow rounded bg-body">
|
||||
@ -46,5 +47,46 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> {% endcomment %}
|
||||
|
||||
<!---->
|
||||
<div class="row justify-content-center mt-5 mb-3">
|
||||
|
||||
<div class="col-lg-8 col-md-10">
|
||||
<div class="card shadow-sm border-0 rounded-3">
|
||||
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
|
||||
<h3 class="mb-0 fs-4 text-center text-white">
|
||||
{% if account.created %}
|
||||
|
||||
<i class="fa-solid fa-book"></i> {{ _("Edit Account") }}
|
||||
{% else %}
|
||||
|
||||
<i class="fa-solid fa-book"></i> {{ _("Add Account") }}
|
||||
{% endif %}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body bg-light-subtle">
|
||||
|
||||
<form method="post" class="form" novalidate>
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
{% for error in form.errors %}
|
||||
<div class="text-danger">{{ error }}</div>
|
||||
{% endfor %}
|
||||
<hr class="my-2">
|
||||
<div class="d-grid gap-2 d-md-flex justify-content-md-center mt-3">
|
||||
<button class="btn btn-lg btn-phoenix-success md-me-2" type="submit"><i class="fa-solid fa-floppy-disk me-1"></i>{{ _("Save") }}</button>
|
||||
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-lg btn-phoenix-danger"><i class="fa-solid fa-ban me-1"></i>{% trans "Cancel" %}</a>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!---->
|
||||
{% endblock %}
|
||||
@ -5,7 +5,7 @@
|
||||
{% block title %}{{ _("Create Journal Entry") }}{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mt-4">
|
||||
{% comment %} <div class="row mt-4">
|
||||
<h3 class="text-center">{% trans "Create Journal Entry" %}</h3>
|
||||
<form id="mainForm" method="post" class="needs-validation">
|
||||
{% csrf_token %}
|
||||
@ -17,5 +17,40 @@
|
||||
<a href="{% url 'journalentry_list' request.dealer.slug ledger.pk %}" class="btn btn-phoenix-secondary"><i class="fa-solid fa-ban me-1"></i> {% trans "Cancel" %}</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div> {% endcomment %}
|
||||
|
||||
<!---->
|
||||
|
||||
<div class="row justify-content-center mt-5 mb-3">
|
||||
|
||||
<div class="col-lg-8 col-md-10">
|
||||
<div class="card shadow-sm border-0 rounded-3">
|
||||
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
|
||||
<h3 class="mb-0 fs-4 text-center text-white">
|
||||
{% trans "Create Journal Entry" %}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body bg-light-subtle">
|
||||
|
||||
<form id="mainForm" method="post" class="needs-validation">
|
||||
{% csrf_token %}
|
||||
<div class="row g-3">
|
||||
{{ form|crispy }}
|
||||
</div>
|
||||
<hr class="my-2">
|
||||
<div class="d-grid gap-2 d-md-flex justify-content-md-center mt-3">
|
||||
<button class="btn btn-lg btn-phoenix-success md-me-2" type="submit"><i class="fa-solid fa-floppy-disk me-1"></i>{{ _("Save") }}</button>
|
||||
<a href="{% url 'journalentry_list' request.dealer.slug ledger.pk %}" class="btn btn-lg btn-phoenix-secondary"><i class="fa-solid fa-ban me-1"></i> {% trans "Cancel" %}</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!---->
|
||||
{% endblock content %}
|
||||
@ -5,17 +5,13 @@
|
||||
{% block title %}{{ _("Create Ledger") }}{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mt-4">
|
||||
{% comment %} <div class="row mt-4">
|
||||
<h3 class="text-center">{% trans "Create Ledger" %}</h3>
|
||||
<form id="mainForm" method="post" class="needs-validation">
|
||||
{% csrf_token %}
|
||||
<div class="row g-3">
|
||||
{{ form|crispy }}
|
||||
</div>
|
||||
{% comment %} <div class="mt-5 text-center">
|
||||
<button type="submit" class="btn btn-success me-2"><i class="fa-solid fa-floppy-disk"></i>{% trans "Save" %}</button>
|
||||
<a href="{% url 'ledger_list' %}" class="btn btn-danger"><i class="fa-solid fa-ban"></i> {% trans "Cancel" %}</a>
|
||||
</div> {% endcomment %}
|
||||
|
||||
<div class="d-flex justify-content-center">
|
||||
<button class="btn btn-sm btn-phoenix-success me-2" type="submit"><i class="fa-solid fa-floppy-disk me-1"></i>{{ _("Save") }}</button>
|
||||
@ -24,5 +20,44 @@
|
||||
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div> {% endcomment %}
|
||||
|
||||
|
||||
|
||||
<!---->
|
||||
<div class="row justify-content-center mt-5 mb-3">
|
||||
|
||||
<div class="col-lg-8 col-md-10">
|
||||
<div class="card shadow-sm border-0 rounded-3">
|
||||
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
|
||||
<h3 class="mb-0 fs-4 text-center text-white">
|
||||
{% trans "Create Ledger" %}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body bg-light-subtle">
|
||||
|
||||
<form id="mainForm" method="post" class="needs-validation">
|
||||
{% csrf_token %}
|
||||
<div class="row g-3">
|
||||
{{ form|crispy }}
|
||||
</div>
|
||||
|
||||
<hr class="my-2">
|
||||
<div class="d-grid gap-2 d-md-flex justify-content-md-center mt-3">
|
||||
<button class="btn btn-lg btn-phoenix-success md-me-2" type="submit"><i class="fa-solid fa-floppy-disk me-1"></i>{{ _("Save") }}</button>
|
||||
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-lg btn-phoenix-danger"><i class="fa-solid fa-ban me-1"></i>{% trans "Cancel" %}</a>
|
||||
</div>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!---->
|
||||
{% endblock content %}
|
||||
@ -87,7 +87,7 @@
|
||||
<span class="fas fa-ellipsis-h fs-10"></span>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-end py-2">
|
||||
{% if perms.django_ledger.change_ledgermodel%}
|
||||
{% if perms.django_ledger.change_ledgermodel %}
|
||||
{% if ledger.can_lock %}
|
||||
<a href="{% url 'ledger-action-lock' dealer_slug=request.dealer.slug entity_slug=entity_slug ledger_pk=ledger.uuid %}"
|
||||
class="dropdown-item has-text-info has-text-weight-bold">{% trans 'Lock' %}</a>
|
||||
|
||||
@ -1,3 +1,19 @@
|
||||
<style>
|
||||
.fade-out {
|
||||
animation: fadeOut 1s ease-out forwards;
|
||||
}
|
||||
|
||||
@keyframes fadeOut {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
<li class="nav-item dropdown">
|
||||
<!-- Notification counter -->
|
||||
<div class="notification-count">
|
||||
@ -236,6 +252,10 @@
|
||||
notificationCard.classList.remove('unread');
|
||||
notificationCard.classList.add('read');
|
||||
updateCounter('decrement');
|
||||
notificationCard.closest('.notification-card').classList.add('fade-out');
|
||||
setTimeout(() => {
|
||||
notificationCard.closest('.notification-card').remove();
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -9,29 +9,45 @@
|
||||
{% trans 'Add New Organization'%}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="container-fluid mt-4">
|
||||
<!--Heading-->
|
||||
<h3 class="mb-3">
|
||||
{% if object %}
|
||||
{% trans 'Update Organization'%}
|
||||
{% else %}
|
||||
{% trans 'Add New Organization'%}
|
||||
{% endif %}
|
||||
</h3>
|
||||
<!--form body-->
|
||||
<div class="row justify-content-center mt-5 mb-3">
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-sm-6 col-md-8">
|
||||
<form class="form" method="post" enctype="multipart/form-data">
|
||||
<div class="col-lg-8 col-md-10">
|
||||
<div class="card shadow-sm border-0 rounded-3">
|
||||
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
|
||||
<h3 class="mb-0 fs-4 text-center text-white">
|
||||
{% if object %}
|
||||
{% trans 'Update Organization'%}
|
||||
{% else %}
|
||||
{% trans 'Add New Organization'%}
|
||||
{% endif %}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body bg-light-subtle">
|
||||
<form class="form" method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
{{ redirect_field }}
|
||||
{{ form|crispy }}
|
||||
<button class="btn btn-sm btn-phoenix-success me-2" type="submit"><i class="fa-solid fa-floppy-disk me-1"></i>{{ _("Save") }}</button>
|
||||
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-sm btn-phoenix-danger"><i class="fa-solid fa-ban me-1"></i>{% trans "Cancel" %}</a>
|
||||
|
||||
<hr class="my-2">
|
||||
<div class="d-grid gap-2 d-md-flex justify-content-md-center mt-3">
|
||||
<button class="btn btn-lg btn-phoenix-success md-me-2" type="submit"><i class="fa-solid fa-floppy-disk me-1"></i>{{ _("Save") }}</button>
|
||||
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-lg btn-phoenix-danger"><i class="fa-solid fa-ban me-1"></i>{% trans "Cancel" %}</a>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{% endblock%}
|
||||
@ -11,7 +11,7 @@
|
||||
{% block content %}
|
||||
<section class="pt-5 pb-9 ">
|
||||
<div class="row overflow-x-auto whitespace-nowrap -mx-2 sm:mx-0">
|
||||
<h2 class="mb-4">{% trans 'Organizations' %}</h2>
|
||||
<h2 class="mb-4">{% trans 'Organizations' %} <li class="fas fa-city text-primary ms-2"></li></h2>
|
||||
|
||||
<div class="row g-3 justify-content-between mb-4">
|
||||
<div class="col-auto">
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<div class="search-box me-2">
|
||||
<form class="position-relative show" id="search-form">
|
||||
<input name="q" id="search-input" class="form-control form-control-sm search-input search" type="search"
|
||||
aria-label="Search" placeholder="{{ _('Search') }}" value="{{ request.GET.q }}" />
|
||||
aria-label="Search" placeholder="{{ _('Search...') }}" value="{{ request.GET.q }}" />
|
||||
<span class="fa fa-magnifying-glass search-box-icon"></span>
|
||||
{% if request.GET.q %}
|
||||
<button type="button" class="btn-close position-absolute end-0 top-50 translate-middle cursor-pointer shadow-none"
|
||||
|
||||
@ -12,13 +12,13 @@
|
||||
}
|
||||
|
||||
.pricing-card .card.selected {
|
||||
border-color: #0d6efd;
|
||||
border-color:rgb(20, 108, 241);
|
||||
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.4);
|
||||
}
|
||||
|
||||
.btn-check:checked + .btn .card {
|
||||
/* fallback if JS fails */
|
||||
border-color: #0d6efd;
|
||||
border-color:rgb(13, 91, 207);
|
||||
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
|
||||
}
|
||||
|
||||
@ -50,29 +50,37 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="container py-5">
|
||||
<h1 class="text-center mb-5">{{ _("Choose Your Plan")}}</h1>
|
||||
<h1 class="text-center mb-5 text-primary">{{ _("Choose Your Plan")}}</h1>
|
||||
<form method="POST" action="{% url 'submit_plan' request.dealer.slug %}" id="wizardForm">
|
||||
{% csrf_token %}
|
||||
<!-- Step 1: Plan Selection -->
|
||||
<div class="step" id="step1">
|
||||
<h4 class="mb-4">1. {{ _("Select a Plan")}}</h4>
|
||||
<div class="row g-4">
|
||||
{% for pp in plan_list %}
|
||||
<div class="col-md-6 col-lg-3">
|
||||
|
||||
<!--step1: Choose Plans-->
|
||||
<div class="step row justify-content-center mt-5 mb-3" id="step1">
|
||||
|
||||
<div class="col-lg-8 col-md-10">
|
||||
<div class="card shadow-sm border-0 rounded-3">
|
||||
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
|
||||
<h4 class="mb-0 fs-4 text-center text-white">
|
||||
1. {{ _("Select a Plan")}}
|
||||
</h4>
|
||||
</div>
|
||||
<div class="card-body bg-light-subtle">
|
||||
{% for pp in plan_list %}
|
||||
<div class="col-md-12 mb-3">
|
||||
<input type="radio" class="btn-check" name="selected_plan" id="plan_{{ forloop.counter }}" value="{{ pp.id }}"
|
||||
data-name="{{ pp.plan.name }}" data-price="{{ pp.price }}" autocomplete="off" {% if forloop.first %}checked{% endif %}>
|
||||
<label class="btn w-100 p-0 pricing-card" for="plan_{{ forloop.counter }}">
|
||||
<div class="card h-100 border border-2 rounded-4">
|
||||
<div class="card-body p-4">
|
||||
<h4 class="mb-3">{{ pp.plan.name }}</h4>
|
||||
<h4 class="mb-3">{{ pp.plan.name|capfirst }}</h4>
|
||||
<h5 class="mb-4">{{ pp.price }} <span class="icon-saudi_riyal"></span><span class="fs-6 fw-normal">/ {{ pp.pricing.period }}</span> {% trans "days" %}</h5>
|
||||
<h6>{{ _("Included") }}</h6>
|
||||
<h5>{{_("Include Haikal's")}}</h5>
|
||||
<ul class="fa-ul ps-3">
|
||||
{% if pp.plan.description %}
|
||||
{% for line in pp.plan.description|splitlines %}
|
||||
<li class="mb-2">
|
||||
<span class="fa-li"><i class="fas fa-check text-primary"></i></span>
|
||||
{{ line }}
|
||||
{{ line|capfirst}}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
@ -82,124 +90,156 @@
|
||||
</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 2: User Info -->
|
||||
<div class="step d-none" id="step2">
|
||||
<h4 class="mb-4">2. {{ _("Enter Your Information")}}</h4>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label" for="first_name">{{ _("First Name")}}</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-user"></i></span>
|
||||
<input type="text" name="first_name" id="first_name" class="form-control form-control-sm" required placeholder="{{ _("First Name")}}" value="{{ request.user.first_name }}">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!--step2: Information-->
|
||||
|
||||
<div class="step d-none row justify-content-center mt-5 mb-3" id="step2">
|
||||
|
||||
<div class="col-lg-8 col-md-10">
|
||||
<div class="card shadow-sm border-0 rounded-3">
|
||||
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
|
||||
<h3 class="mb-0 fs-4 text-center text-white">
|
||||
2. {{ _("Enter Your Information")}}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label" for="last_name">{{ _("Last Name")}}</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-user"></i></span>
|
||||
<input type="text" name="last_name" id="last_name" class="form-control form-control-sm" required placeholder="{{ _("Last Name")}}" value="{{ request.user.last_name }}">
|
||||
<div class="card-body bg-light-subtle">
|
||||
|
||||
<label class="form-label" for="first_name">{{ _("First Name")}}</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-user"></i></span>
|
||||
<input type="text" name="first_name" id="first_name" class="form-control form-control-sm" required placeholder="{{ _("First Name")}}" value="{{ request.user.first_name }}">
|
||||
</div>
|
||||
|
||||
<label class="form-label" for="last_name">{{ _("Last Name")}}</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-user"></i></span>
|
||||
<input type="text" name="last_name" id="last_name" class="form-control form-control-sm" required placeholder="{{ _("Last Name")}}" value="{{ request.user.last_name }}">
|
||||
</div>
|
||||
|
||||
<label class="form-label" for="email">{{ _("Email Address")}}</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-envelope"></i></span>
|
||||
<input type="email" name="email" id="email" class="form-control form-control-sm" required placeholder="email@example.com" value="{{ request.user.email }}">
|
||||
</div>
|
||||
|
||||
<label class="form-label" for="phone">{{ _("Phone Number")}}</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-phone"></i></span>
|
||||
<input type="text" name="phone" id="phone" class="form-control form-control-sm" dir="ltr" placeholder="{{ _("Phone Number")}}" value="{{ request.dealer.phone_number.raw_input }}" required>
|
||||
</div>
|
||||
|
||||
<label class="form-label" for="company">{{ _("Company") }}</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-building"></i></span>
|
||||
<input type="text" name="company" id="company" class="form-control form-control-sm" placeholder="{{ _("Company") }}" value="{{ request.dealer.get_local_name }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label" for="email">{{ _("Email Address")}}</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-envelope"></i></span>
|
||||
<input type="email" name="email" id="email" class="form-control form-control-sm" required placeholder="email@example.com" value="{{ request.user.email }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label" for="phone">{{ _("Phone Number")}}</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-phone"></i></span>
|
||||
<input type="text" name="phone" id="phone" class="form-control form-control-sm" dir="ltr" placeholder="{{ _("Phone Number")}}" value="{{ request.dealer.phone_number.raw_input }}" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label" for="company">{{ _("Company") }}</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-building"></i></span>
|
||||
<input type="text" name="company" id="company" class="form-control form-control-sm" placeholder="{{ _("Company") }}" value="{{ request.dealer.get_local_name }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 3: Payment -->
|
||||
<div class="step d-none" id="step3">
|
||||
<h4 class="mb-4">3. {{ _("Payment Information")}}</h4>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label" for="card_name">{{ _("Cardholder Name")}}</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-user"></i></span>
|
||||
<input type="text" name="card_name" id="card_name" class="form-control form-control-sm" placeholder="{{ _("Cardholder Name")}}" required>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!--step3: Payment-->
|
||||
<div class="step d-none row justify-content-center mt-5 mb-3" id="step3">
|
||||
|
||||
<div class="col-lg-8 col-md-10">
|
||||
<div class="card shadow-sm border-0 rounded-3">
|
||||
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
|
||||
<h3 class="mb-0 fs-4 text-center text-white">
|
||||
3. {{ _("Payment Information")}}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body bg-light-subtle">
|
||||
<label class="form-label" for="card_name">{{ _("Cardholder Name")}}</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-user"></i></span>
|
||||
<input type="text" name="card_name" id="card_name" class="form-control form-control-sm" placeholder="{{ _("Cardholder Name")}}" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label" for="card_number">{{ _("Card Number")}}</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-credit-card"></i></span>
|
||||
<input type="text" name="card_number" id="card_number" class="form-control form-control-sm" placeholder="{{ _("Card Number")}}"
|
||||
maxlength="19" pattern="^\d{4}\s\d{4}\s\d{4}\s\d{4}$"
|
||||
inputmode="numeric" required title="Enter a 16-digit card number">
|
||||
</div>
|
||||
</div>
|
||||
<label class="form-label" for="card_number">{{ _("Card Number")}}</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-credit-card"></i></span>
|
||||
<input type="text" name="card_number" id="card_number" class="form-control form-control-sm" placeholder="{{ _("Card Number")}}"
|
||||
maxlength="19" pattern="^\d{4}\s\d{4}\s\d{4}\s\d{4}$"
|
||||
inputmode="numeric" required title="Enter a 16-digit card number">
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<label class="form-label" for="card_expiry">{{ _("Expiry Date")}} (MM/YY)</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="far fa-calendar-alt"></i></span>
|
||||
<input type="text" name="card_expiry" id="card_expiry" class="form-control form-control-sm" placeholder="{{ _("Expiry Date")}}"
|
||||
|
||||
<label class="form-label" for="card_expiry">{{ _("Expiry Date")}} (MM/YY)</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="far fa-calendar-alt"></i></span>
|
||||
<input type="text" name="card_expiry" id="card_expiry" class="form-control form-control-sm" placeholder="{{ _("Expiry Date")}}"
|
||||
maxlength="5" pattern="^(0[1-9]|1[0-2])\/\d{2}$"
|
||||
inputmode="numeric" required title="Enter expiry in MM/YY format">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2">
|
||||
<label class="form-label" for="card_cvv">{{ _("CVV") }}</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-lock"></i></span>
|
||||
<input type="text" name="card_cvv" id="card_cvv" class="form-control form-control-sm" placeholder="{{ _("CVV") }}"
|
||||
maxlength="3" pattern="^\d{3}$"
|
||||
inputmode="numeric" required title="Enter 3-digit CVV">
|
||||
<label class="form-label" for="card_cvv">{{ _("CVV") }}</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-lock"></i></span>
|
||||
<input type="text" name="card_cvv" id="card_cvv" class="form-control form-control-sm" placeholder="{{ _("CVV") }}"
|
||||
maxlength="3" pattern="^\d{3}$"
|
||||
inputmode="numeric" required title="Enter 3-digit CVV">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 4: Confirmation -->
|
||||
<div class="step d-none" id="step4">
|
||||
<h4 class="mb-4">4. {{ _("Confirm Your Information")}}</h4>
|
||||
<div class="summary-box">
|
||||
<h5><i class="fas fa-file-invoice-dollar me-2"></i>{{ _("Order Summary")}}</h5>
|
||||
<div class="summary-item"><i class="fas fa-box"></i><strong>{{ _("Plan") }}:</strong> <span id="summary_plan"></span></div>
|
||||
<div class="summary-item"><i class="fas fa-tag"></i><strong>{{ _("Price") }}:</strong> <span id="summary_price"></span></div>
|
||||
<div class="summary-item"><i class="fas fa-receipt"></i><strong>{{ _("VAT") }} (15%):</strong> <span id="summary-tax">0.00</span> <span class="icon-saudi_riyal"></span></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!--Step4 confirmation-->
|
||||
|
||||
<div class="step d-none row justify-content-center mt-5 mb-3" id="step4">
|
||||
|
||||
<div class="col-lg-8 col-md-10">
|
||||
<div class="card shadow-sm border-0 rounded-3">
|
||||
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
|
||||
4. {{ _("Confirm Your Information")}}
|
||||
</div>
|
||||
<div class="card-body bg-light-subtle me-2">
|
||||
<h5 class="text-center"><i class="fas fa-file-invoice-dollar me-2"></i>{{ _("Order Summary")}}</h5>
|
||||
<div class="summary-item"><i class="fas fa-box me-2"></i><strong>{{ _("Plan") }}:</strong> <span id="summary_plan"></span></div>
|
||||
<div class="summary-item"><i class="fas fa-tag me-2"></i><strong>{{ _("Price") }}:</strong> <span id="summary_price"></span></div>
|
||||
<div class="summary-item"><i class="fas fa-receipt me-2"></i><strong>{{ _("VAT") }} (15%):</strong> <span id="summary-tax">0.00</span> <span class="icon-saudi_riyal"></span></div>
|
||||
<div class="summary-item"><i class="fas fa-hand-holding-usd me-2"></i><strong>{{ _("Total") }}:</strong> <span id="summary-total">0.00</span> <span class="icon-saudi_riyal"></span></div>
|
||||
<hr>
|
||||
<div class="summary-item"><i class="fas fa-hand-holding-usd"></i><strong>{{ _("Total") }}:</strong> <span id="summary-total">0.00</span> <span class="icon-saudi_riyal"></span></div>
|
||||
|
||||
|
||||
<h5 class="mt-4"><i class="fas fa-user me-2"></i>{{ _("User Information")}}</h5>
|
||||
<div class="summary-item"><i class="fas fa-signature"></i><strong>{{ _("Name") }}:</strong> <span id="summary_name"></span></div>
|
||||
<div class="summary-item"><i class="fas fa-envelope"></i><strong>{{ _("Email") }}:</strong> <span id="summary_email"></span></div>
|
||||
<div class="summary-item"><i class="fas fa-building"></i><strong>{{ _("Company") }}:</strong> <span id="summary_company"></span></div>
|
||||
<div class="summary-item"><i class="fas fa-phone"></i><strong>{{ _("Phone") }}:</strong> <span id="summary_phone"></span></div>
|
||||
<h5 class="mt-4 text-center"><i class="fas fa-user me-2"></i>{{ _("User Information")}}</h5>
|
||||
<div class="summary-item"><i class="fas fa-signature me-2"></i><strong>{{ _("Name") }}:</strong> <span id="summary_name"></span></div>
|
||||
<div class="summary-item"><i class="fas fa-envelope me-2"></i><strong>{{ _("Email") }}:</strong> <span id="summary_email"></span></div>
|
||||
<div class="summary-item"><i class="fas fa-building me-2"></i><strong>{{ _("Company") }}:</strong> <span id="summary_company"></span></div>
|
||||
<div class="summary-item"><i class="fas fa-phone me-2"></i><strong>{{ _("Phone") }}:</strong> <span id="summary_phone"></span></div>
|
||||
<hr>
|
||||
|
||||
<h5 class="mt-4"><i class="fas fa-credit-card me-2"></i>{{ _("Payment") }}</h5>
|
||||
<div class="summary-item"><i class="fas fa-user"></i><strong>{{ _("Cardholder") }}:</strong> <span id="summary_card_name"></span></div>
|
||||
<div class="summary-item"><i class="fas fa-credit-card"></i><strong>{{ _("Card Number")}}:</strong> <span id="summary_card_number"></span></div>
|
||||
<div class="summary-item"><i class="far fa-calendar-alt"></i><strong>{{ _("Expiry") }}:</strong> <span id="summary_card_expiry"></span></div>
|
||||
<h5 class="mt-4 text-center"><i class="fas fa-credit-card me-2"></i>{{ _("Payment") }}</h5>
|
||||
<div class="summary-item"><i class="fas fa-user me-2"></i><strong>{{ _("Cardholder") }}:</strong> <span id="summary_card_name"></span></div>
|
||||
<div class="summary-item"><i class="fas fa-credit-card me-2"></i><strong>{{ _("Card Number")}}:</strong> <span id="summary_card_number"></span></div>
|
||||
<div class="summary-item"><i class="far fa-calendar-alt me-2"></i><strong>{{ _("Expiry") }}:</strong> <span id="summary_card_expiry"></span></div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<!---->
|
||||
|
||||
<!-- Navigation -->
|
||||
<div class="d-flex justify-content-between mt-4">
|
||||
<button type="button" class="btn btn-phoenix-secondary" id="prevBtn" disabled>{{ _("Previous") }}</button>
|
||||
<button type="button" class="btn btn-phoenix-primary" id="nextBtn">{{ _("Next") }}</button>
|
||||
<button type="submit" class="btn btn-phoenix-success d-none" id="submitBtn">{{ _("Confirm") }}</button>
|
||||
<button type="button" class="btn btn-lg btn-phoenix-secondary" id="prevBtn" disabled>{{ _("Previous") }}</button>
|
||||
<button type="button" class="btn btn-lg btn-phoenix-primary" id="nextBtn">{{ _("Next") }}</button>
|
||||
<button type="submit" class="btn btn-lg btn-phoenix-primary d-none" id="submitBtn">{{ _("Confirm") }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@ -32,15 +32,26 @@
|
||||
{% if not create_po %}
|
||||
{% if style == 'po-detail' %}
|
||||
<div class="card shadow-sm border-0 mb-2">
|
||||
<div class="card-header bg-light">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="card-header bg-light ">
|
||||
<div class="d-flex align-items-center mb-2 ">
|
||||
<span class="me-3 text-primary">
|
||||
{% icon 'uil:bill' 36 %}
|
||||
</span>
|
||||
<h2 class="h3 mb-0">
|
||||
<h2 class="h3 mb-0 text-primary me-4">
|
||||
{{ po_model.po_number }}
|
||||
</h2>
|
||||
|
||||
|
||||
</div>
|
||||
<p>
|
||||
<a class="btn btn-phoenix-primary"
|
||||
href="{% url 'purchase_order_list' request.dealer.slug request.dealer.entity.slug %}"
|
||||
title="Click to view the complete list of Purchase Orders"
|
||||
role="button">
|
||||
<i class="fas fa-list me-2"></i>{% trans 'PO List' %}
|
||||
</a>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
@ -52,6 +63,7 @@
|
||||
{{ po_model.get_po_status_display }}
|
||||
</span>
|
||||
</h3>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@ -99,14 +111,7 @@
|
||||
</div>
|
||||
|
||||
|
||||
<div class='col-12 col-md-12 col-lg-2 d-grid align-content-end'>
|
||||
<a class="btn btn-phoenix-primary py-2"
|
||||
href="{% url 'purchase_order_list' request.dealer.slug request.dealer.entity.slug %}"
|
||||
title="Click to view the complete list of Purchase Orders"
|
||||
role="button">
|
||||
<i class="fas fa-list me-2"></i>{% trans 'PO List' %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@ -10,44 +10,42 @@
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="row mt-4">
|
||||
<div class="row">
|
||||
<div class="col-xl-9">
|
||||
<div class="d-sm-flex justify-content-between">
|
||||
|
||||
<!---->
|
||||
<div class="row justify-content-center mt-5 mb-3">
|
||||
|
||||
<h3 class="mb-3">
|
||||
{% if vendor.created %}
|
||||
<!--<i class="bi bi-pencil-square"></i>-->
|
||||
{{ _("Edit Purchase Order") }}
|
||||
{% else %}
|
||||
<!--<i class="bi bi-person-plus"></i> -->
|
||||
{{ _("Add New Purchase Order") }}
|
||||
{% endif %}
|
||||
</h3>
|
||||
<div class="col-lg-8 col-md-10 ">
|
||||
<div class="card shadow-sm border-0 rounded-3">
|
||||
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
|
||||
<h3 class="mb-0 fs-4 text-center text-white">
|
||||
{{ _("Add New Purchase Order") }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body bg-light-subtle">
|
||||
<form class="row g-3 mb-9" method="post" class="form" enctype="multipart/form-data" novalidate >
|
||||
{% csrf_token %}
|
||||
{{ redirect_field }}
|
||||
{{ form|crispy }}
|
||||
{% for error in form.errors %}
|
||||
<div class="text-danger">{{ error }}</div>
|
||||
{% endfor %}
|
||||
|
||||
<hr class="my-2">
|
||||
<div class="d-grid gap-2 d-md-flex justify-content-md-center mt-3">
|
||||
<button class="btn btn-lg btn-phoenix-success md-me-2" type="submit"><i class="fa-solid fa-floppy-disk me-1"></i>{{ _("Save") }}</button>
|
||||
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-lg btn-phoenix-danger"><i class="fa-solid fa-ban me-1"></i>{% trans "Cancel" %}</a>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xl-9">
|
||||
</div>
|
||||
|
||||
<form class="row g-3 mb-9" method="post" class="form" enctype="multipart/form-data" novalidate >
|
||||
{% csrf_token %}
|
||||
{{ redirect_field }}
|
||||
{{ form|crispy }}
|
||||
{% for error in form.errors %}
|
||||
<div class="text-danger">{{ error }}</div>
|
||||
{% endfor %}
|
||||
<div class="d-flex justify-content-start">
|
||||
<button class="btn btn-sm btn-phoenix-success me-2" type="submit"><i class="fa-solid fa-floppy-disk me-1"></i>
|
||||
<!--<i class="bi bi-save"></i> -->
|
||||
{{ _("Save") }}
|
||||
</button>
|
||||
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-sm btn-phoenix-danger"><i class="fa-solid fa-ban me-1"></i>{% trans "Cancel" %}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
{% endblock %}
|
||||
@ -13,21 +13,26 @@
|
||||
<div class="alert alert-success">{{ message }}</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<!-- Add New PO Button -->
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<h3 class="">
|
||||
{{ _("Purchase Orders") |capfirst }}
|
||||
|
||||
<h2 class="">
|
||||
{{ _("Purchase Orders") |capfirst }} <li class="fas fa-file-invoice text-primary ms-2"></li>
|
||||
</h2>
|
||||
{% if perms.django_ledger.add_purchaseordermodel%}
|
||||
<div>
|
||||
{% if perms.django_ledger.add_purchaseordermodel %}
|
||||
|
||||
<div class="row g-3 justify-content-between mb-4">
|
||||
<div class="col-auto">
|
||||
<div class="d-md-flex justify-content-between">
|
||||
{% if perms.django_ledger.add_purchaseordermodel %}
|
||||
<a href="{% url 'purchase_order_create' request.dealer.slug request.dealer.entity.slug %}"
|
||||
class="btn btn-md btn-phoenix-primary"><i class="fa fa-plus me-2"></i>{{ _("Create New PO") }}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% include "partials/search_box.html" %}
|
||||
<div class="col-auto">
|
||||
<div class="d-flex">
|
||||
{% include 'partials/search_box.html' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive px-1 scrollbar mt-3">
|
||||
<table class= "table align-items-center table-flush table-hover">
|
||||
@ -74,7 +79,7 @@
|
||||
{% endif %}
|
||||
{% if po.po_status == 'fulfilled' %}
|
||||
{% if perms.inventory.add_car %}
|
||||
<a href="{% url 'view_items_inventory' dealer_slug=request.dealer.slug entity_slug=entity_slug po_pk=po.pk %}" class="dropdown-item text-success-dark">{% trans 'Add Inventory Items' %}</a>
|
||||
<a href="{% url 'view_items_inventory' dealer_slug=request.dealer.slug entity_slug=entity_slug po_pk=po.pk %}" class="dropdown-item text-success-dark">{% trans 'Inventory Items' %}</a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<button class="dropdown-item text-warning-dark" disabled><span class="fas fa-exclamation-triangle me-1"></span> Fulfill the PO Before Viewing Inventory</button>
|
||||
|
||||
@ -24,13 +24,11 @@
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<button type="submit"
|
||||
class="btn btn-phoenix-success w-100 my-2">{% trans 'Save PO' %}
|
||||
class="btn btn-phoenix-success my-2 me-2">{% trans 'Save PO' %}
|
||||
</button>
|
||||
<a href="{% url 'purchase_order_detail' request.dealer.slug request.dealer.entity.slug po_model.uuid %}"
|
||||
class="btn btn-phoenix-secondary w-100 my-2">{% trans 'Back to PO Detail' %}</a>
|
||||
<a href="{% url 'purchase_order_list' request.dealer.slug request.dealer.entity.slug %}"
|
||||
class="btn btn-phoenix-info
|
||||
info w-100 my-2">{% trans 'PO List' %}</a>
|
||||
class="btn btn-phoenix-secondary my-2">{% trans 'Back to PO Detail' %}</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n custom_filters%}
|
||||
{% load crispy_forms_filters %}
|
||||
|
||||
{% block title %}{{ _("View Quotation") }}{% endblock title %}
|
||||
|
||||
@ -83,7 +84,12 @@
|
||||
{% if perms.django_ledger.can_approve_estimatemodel %}
|
||||
<button id="accept_estimate" onclick="setFormAction('approved')" class="btn btn-phoenix-secondary" data-bs-toggle="modal" data-bs-target="#confirmModal"><span class="d-none d-sm-inline-block"><i class="fa-solid fa-check-double"></i> {% trans 'Mark As Approved' %}</span></button>
|
||||
{% endif %}
|
||||
{% elif estimate.status == 'approved' %}
|
||||
{% if estimate.can_approve and not request.is_manager %}
|
||||
<button class="btn btn-phoenix-warning" disabled>
|
||||
<i class="fas fa-hourglass-start me-2"></i><span class="text-warning">{% trans 'Waiting for Manager Approval' %}</span>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% elif estimate.status == 'approved' %}
|
||||
{% if perms.django_ledger.change_estimatemodel %}
|
||||
<a href="{% url 'send_email' request.dealer.slug estimate.pk %}" class="btn btn-phoenix-primary me-2"><span class="fa-regular fa-paper-plane me-sm-2"></span><span class="d-none d-sm-inline-block">{% trans 'Send Quotation' %}</span></a>
|
||||
{% endif %}
|
||||
@ -186,15 +192,22 @@
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr class="bg-body-secondary total-sum">
|
||||
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="7">{% trans "Vat" %} ({{data.vat|percentage}})</td>
|
||||
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="7">{% trans "Vat" %} ({{data.vat}})</td>
|
||||
<td class="align-middle text-start fw-semibold">
|
||||
<span id="grand-total">+ {{data.total_vat_amount|floatformat}}<span class="icon-saudi_riyal"></span></span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="bg-body-secondary total-sum">
|
||||
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="7">{% trans "Discount Amount" %}</td>
|
||||
<td class="align-middle text-start text-danger fw-semibold ">
|
||||
<span id="grand-total">- {{data.total_discount|floatformat}}<span class="icon-saudi_riyal"></span></span>
|
||||
<td class="align-middle text-start text-danger fw-semibold">
|
||||
<form action="{% url 'update_estimate_discount' request.dealer.slug estimate.pk %}" method="post">
|
||||
{% csrf_token %}
|
||||
<div class="input-group input-group-sm">
|
||||
<input type="number" class="form-control" name="discount_amount" value="{{data.total_discount}}" step="0.01" style="width: 1px;">
|
||||
<span class="input-group-text"><span class="icon-saudi_riyal"></span></span>
|
||||
<button type="submit" class="btn btn-sm btn-phoenix-primary ms-n2">{% trans "Update" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="bg-body-secondary total-sum">
|
||||
@ -203,6 +216,7 @@
|
||||
{% for service in data.additionals %}
|
||||
<small><span class="fw-semibold">+ {{service.name}} - {{service.price_|floatformat}}<span class="icon-saudi_riyal"></span></span></small><br>
|
||||
{% endfor %}
|
||||
<button class="btn btn-phoenix-primary btn-xs ms-auto" type="button" data-bs-toggle="modal" data-bs-target="#additionalModal"><span class="fas fa-plus me-1"></span>{{ _("Add") }}</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="bg-body-secondary total-sum">
|
||||
@ -221,6 +235,27 @@
|
||||
</section>
|
||||
<!-- <section> close ============================-->
|
||||
<!-- ============================================-->
|
||||
<!-- add update Modal -->
|
||||
<div class="modal fade" id="additionalModal" tabindex="-1" aria-labelledby="additionalModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-md">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header justify-content-between align-items-start gap-5 px-4 pt-4 pb-3 border-0">
|
||||
<h4 class="modal-title" id="additionalModalLabel">{% trans 'Additional Services' %}</h4>
|
||||
<button class="btn p-0 text-body-quaternary fs-6" data-bs-dismiss="modal" aria-label="Close">
|
||||
<span class="fas fa-times"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form action="{% url 'update_estimate_additionals' request.dealer.slug estimate.pk %}" method="post">
|
||||
{% csrf_token %}
|
||||
{{additionals_form|crispy}}
|
||||
<button type="submit" class="btn btn-phoenix-primary">{% trans 'Update' %}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block customJS %}
|
||||
|
||||
@ -120,8 +120,89 @@
|
||||
}
|
||||
</style>
|
||||
{% endblock customCSS %}
|
||||
<!---->
|
||||
{% block content%}
|
||||
|
||||
<div class="row justify-content-center mt-5 mb-3">
|
||||
{% if not items %}
|
||||
<div class="alert alert-outline-warning d-flex align-items-center" role="alert">
|
||||
<i class="fa-solid fa-circle-info fs-6"></i>
|
||||
<p class="mb-0 flex-1">{{ _("Please add at least one car before creating a quotation.") }}<a class="ms-3 text-body-primary fs-9" href="{% url 'car_add' request.dealer.slug %}"> {{ _("Add Car") }} </a></p>
|
||||
<button class="btn-close" type="button" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if not customer_count %}
|
||||
<div class="alert alert-outline-warning d-flex align-items-center" role="alert">
|
||||
<i class="fa-solid fa-circle-info fs-6"></i>
|
||||
<p class="mb-0 flex-1"> {{ _("Please add at least one customer before creating a quotation.") }}<a class="ms-3 text-body-primary fs-9" href="{% url 'customer_create' request.dealer.slug %}"> {{ _("Add Customer") }} </a></p>
|
||||
<button class="btn-close" type="button" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="col-lg-8 col-md-10">
|
||||
<div class="card shadow-sm border-0 rounded-3">
|
||||
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
|
||||
<h3 class="mb-0 fs-4 text-center text-white">
|
||||
<i class="fa-regular fa-file-lines"></i> {% trans "Create Quotation" %}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body bg-light-subtle">
|
||||
|
||||
|
||||
<form id="mainForm" method="post" class="needs-validation {% if not items or not customer_count %}disabled{% endif %}">
|
||||
|
||||
{% csrf_token %}
|
||||
<div class="row g-3 col-12">
|
||||
{{ form|crispy }}
|
||||
<div class="custom-select">
|
||||
<!-- Hidden native select for form submission -->
|
||||
<select class="native-select" name="item" required tabindex="-1">
|
||||
<option value="">Select a car</option>
|
||||
{% for item in items %}
|
||||
<option value="{{ item.hash }}"></option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
<!-- Custom select UI -->
|
||||
<div class="select-trigger">
|
||||
<div class="selected-value">
|
||||
<span>Select a car</span>
|
||||
</div>
|
||||
<i class="fas fa-chevron-down dropdown-icon"></i>
|
||||
</div>
|
||||
|
||||
<div class="options-container">
|
||||
{% for item in items %}
|
||||
<div class="option" data-value="{{ item.hash }}" data-image="{{item.logo}}">
|
||||
<img src="{{item.logo}}" alt="{{item.model}}">
|
||||
<span>{{item.make}} {{item.model}} {{item.serie}} {{item.trim}} {{item.color_name}}</span>
|
||||
<div class="color-box" style="background-color: rgb({{ item.exterior_color }});"></div>
|
||||
<div class="color-box" style="background-color: rgb({{ item.interior_color }});"></div>
|
||||
<span style="color:gray;">({{item.hash_count}} in stock)</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<hr class="my-2">
|
||||
<div class="d-grid gap-2 d-md-flex justify-content-md-center mt-3">
|
||||
<button class="btn btn-lg btn-phoenix-success md-me-2" type="submit"><i class="fa-solid fa-floppy-disk me-1"></i>{{ _("Save") }}</button>
|
||||
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-lg btn-phoenix-danger"><i class="fa-solid fa-ban me-1"></i>{% trans "Cancel" %}</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
<!---->
|
||||
|
||||
{% comment %} {% block content %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mt-4">
|
||||
{% if not items %}
|
||||
<div class="alert alert-outline-warning d-flex align-items-center" role="alert">
|
||||
@ -184,7 +265,7 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
{% endblock content %} {% endcomment %}
|
||||
|
||||
{% block customJS %}
|
||||
<script>
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
{% load i18n %}
|
||||
{% block title %}{{ _("Invoice") }}{% endblock title %}
|
||||
{% block content %}
|
||||
<div class="row paid">
|
||||
{% comment %} <div class="row paid">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
@ -19,6 +19,42 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> {% endcomment %}
|
||||
|
||||
<!---->
|
||||
<div class="row paid justify-content-center mt-5 mb-3">
|
||||
|
||||
<div class="col-lg-8 col-md-10">
|
||||
<div class="card shadow-sm border-0 rounded-3">
|
||||
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
|
||||
<h3 class="mb-0 fs-4 text-center text-white">
|
||||
<i class="fa-solid fa-receipt"></i> {{ _("Add Invoice") }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body bg-light-subtle">
|
||||
|
||||
|
||||
<form method="post" action="">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
|
||||
<hr class="my-2">
|
||||
<div class="d-grid gap-2 d-md-flex justify-content-md-center mt-3">
|
||||
<button class="btn btn-lg btn-phoenix-success md-me-2" type="submit"><i class="fa-solid fa-floppy-disk me-1"></i>{{ _("Save") }}</button>
|
||||
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-lg btn-phoenix-danger"><i class="fa-solid fa-ban me-1"></i>{% trans "Cancel" %}</a>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!---->
|
||||
{% endblock content %}
|
||||
|
||||
|
||||
@ -82,7 +82,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
{% if perms.django_ledger.add_payment%}
|
||||
{% if perms.inventory.add_payment%}
|
||||
{% if invoice.invoice_status == 'in_review' %}
|
||||
<button id="accept_invoice" class="btn btn-phoenix-secondary" data-bs-toggle="modal" data-bs-target="#confirmModal"><span class="d-none d-sm-inline-block"><i class="fa-solid fa-check-double"></i> {% trans 'Accept' %}</span></button>
|
||||
{% endif %}
|
||||
|
||||
@ -39,6 +39,8 @@
|
||||
{{ form.arabic_name|as_crispy_field }}
|
||||
{{ form.email|as_crispy_field }}
|
||||
{{ form.phone_number|as_crispy_field }}
|
||||
{{ form.address|as_crispy_field }}
|
||||
{{ form.image|as_crispy_field }}
|
||||
{{ form.group|as_crispy_field }}
|
||||
{% for error in form.errors %}
|
||||
<div class="text-danger">{{ error }}</div>
|
||||
|
||||
41
templates/vendors/vendor_form.html
vendored
41
templates/vendors/vendor_form.html
vendored
@ -13,7 +13,7 @@
|
||||
|
||||
{% block content %}
|
||||
|
||||
|
||||
{% comment %}
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col-xl-9">
|
||||
@ -54,4 +54,43 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endcomment %}
|
||||
|
||||
<!---->
|
||||
<div class="row justify-content-center mt-5 mb-3">
|
||||
|
||||
<div class="col-lg-8 col-md-10">
|
||||
<div class="card shadow-sm border-0 rounded-3">
|
||||
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
|
||||
<h3 class="mb-0 fs-4 text-center text-white">
|
||||
{% if vendor.created %}
|
||||
{{ _("Edit Vendor") }}
|
||||
{% else %}
|
||||
{{ _("Add Vendor") }}
|
||||
{% endif %}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body bg-light-subtle">
|
||||
<form class="row g-3 mb-9" method="post" class="form" enctype="multipart/form-data" novalidate >
|
||||
{% csrf_token %}
|
||||
{{ redirect_field }}
|
||||
{{ form|crispy }}
|
||||
{% for error in form.errors %}
|
||||
<div class="text-danger">{{ error }}</div>
|
||||
{% endfor %}
|
||||
|
||||
<hr class="my-2">
|
||||
<div class="d-grid gap-2 d-md-flex justify-content-md-center mt-3">
|
||||
<button class="btn btn-lg btn-phoenix-success md-me-2" type="submit"><i class="fa-solid fa-floppy-disk me-1"></i>{{ _("Save") }}</button>
|
||||
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-lg btn-phoenix-danger"><i class="fa-solid fa-ban me-1"></i>{% trans "Cancel" %}</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!---->
|
||||
{% endblock %}
|
||||
@ -28,21 +28,21 @@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@300;400;600;700;800;900&display=swap" rel="stylesheet">
|
||||
<link href="{% static 'vendors/simplebar/simplebar.min.css' %}" rel="stylesheet">
|
||||
<link href="{% static 'css/sweetalert2.min.css' %}" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://unicons.iconscout.com/release/v4.0.8/css/line.css">
|
||||
{% comment %} <link href="{% static 'vendors/simplebar/simplebar.min.css' %}" rel="stylesheet"> {% endcomment %}
|
||||
{% comment %} <link href="{% static 'css/sweetalert2.min.css' %}" rel="stylesheet"> {% endcomment %}
|
||||
{% comment %} <link rel="stylesheet" href="https://unicons.iconscout.com/release/v4.0.8/css/line.css"> {% endcomment %}
|
||||
|
||||
{% if LANGUAGE_CODE == 'en' %}
|
||||
<link href="{% static 'css/theme.min.css' %}" type="text/css" rel="stylesheet" id="style-default">
|
||||
<link href="{% static 'css/user.min.css' %}" type="text/css" rel="stylesheet" id="user-style-default">
|
||||
{% else %}
|
||||
<link href="{% static 'css/theme-rtl.min.css' %}" type="text/css" rel="stylesheet" id="style-rtl">
|
||||
<link href="{% static 'css/user-rtl.min.css' %}" type="text/css" rel="stylesheet" id="user-style-rtl">
|
||||
{% endif %}
|
||||
{% if LANGUAGE_CODE == 'ar' %}
|
||||
<link href="{% static 'css/theme-rtl.min.css' %}" type="text/css" rel="stylesheet" id="style-rtl">
|
||||
<link href="{% static 'css/user-rtl.min.css' %}" type="text/css" rel="stylesheet" id="user-style-rtl">
|
||||
{% else %}
|
||||
<link href="{% static 'css/theme.min.css' %}" type="text/css" rel="stylesheet" id="style-default">
|
||||
<link href="{% static 'css/user.min.css' %}" type="text/css" rel="stylesheet" id="user-style-default">
|
||||
{% endif %}
|
||||
|
||||
<script src="{% static 'vendors/simplebar/simplebar.min.js' %}"></script>
|
||||
<script src="{% static 'js/config.js' %}"></script>
|
||||
<script src="{% static 'js/sweetalert2.all.min.js' %}"></script>
|
||||
{% comment %} <script src="{% static 'js/sweetalert2.all.min.js' %}"></script> {% endcomment %}
|
||||
<link href="{% static 'css/custom.css' %}" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
@ -52,9 +52,27 @@
|
||||
{% block content %}
|
||||
{% endblock content %}
|
||||
|
||||
{% comment %}
|
||||
<script src="{% static 'vendors/anchorjs/anchor.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/is/is.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/list.js/list.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/dayjs/dayjs.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/echarts/echarts.min.js' %}"></script>
|
||||
<script src="{% static 'js/travel-agency-dashboard.js' %}"></script>
|
||||
<script src="{% static 'vendors/mapbox-gl/mapbox-gl.js' %}"></script>
|
||||
<script src="https://unpkg.com/@turf/turf@6/turf.min.js"></script>
|
||||
<script src="{% static 'vendors/swiper/swiper-bundle.min.js' %}"></script> {% endcomment %}
|
||||
|
||||
<script src="{% static 'vendors/popper/popper.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/bootstrap/bootstrap.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/fontawesome/all.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/lodash/lodash.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/feather-icons/feather.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/typed.js/typed.umd.js' %}"></script>
|
||||
<script src="{% static 'js/phoenix.js' %}"></script>
|
||||
|
||||
{% comment %} <script src="{% static 'vendors/popper/popper.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/bootstrap/bootstrap.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/anchorjs/anchor.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/is/is.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/fontawesome/all.min.js' %}"></script>
|
||||
@ -68,7 +86,7 @@
|
||||
<script src="{% static 'vendors/mapbox-gl/mapbox-gl.js' %}"></script>
|
||||
<script src="https://unpkg.com/@turf/turf@6/turf.min.js"></script>
|
||||
<script src="{% static 'vendors/swiper/swiper-bundle.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/typed.js/typed.umd.js' %}"></script>
|
||||
<script src="{% static 'vendors/typed.js/typed.umd.js' %}"></script> {% endcomment %}
|
||||
|
||||
{% block customJS %}
|
||||
{% endblock customJS %}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user