This commit is contained in:
gitea 2025-01-26 15:11:15 +00:00
parent d53ce70f03
commit 9a40e70ce3
30 changed files with 1156 additions and 471 deletions

View File

@ -10,6 +10,7 @@ from .mixins import AddClassMixin
from django.forms.models import inlineformset_factory from django.forms.models import inlineformset_factory
from django_ledger.forms.invoice import InvoiceModelCreateForm as InvoiceModelCreateFormBase from django_ledger.forms.invoice import InvoiceModelCreateForm as InvoiceModelCreateFormBase
from django_ledger.forms.bill import BillModelCreateForm as BillModelCreateFormBase from django_ledger.forms.bill import BillModelCreateForm as BillModelCreateFormBase
from django_ledger.forms.vendor import VendorModelForm
from .models import ( from .models import (
Dealer, Dealer,
# Branch, # Branch,
@ -31,9 +32,10 @@ from .models import (
SaleQuotationCar, SaleQuotationCar,
AdditionalServices, AdditionalServices,
Staff, Staff,
Opportunity, Priority, Sources, Lead, Activity, Notes, CarModel Opportunity, Priority, Sources, Lead, Activity, Notes, CarModel,
SaleOrder
) )
from django_ledger.models import ItemModel, InvoiceModel, BillModel from django_ledger.models import ItemModel, InvoiceModel, BillModel,VendorModel
from django.forms import ModelMultipleChoiceField, ValidationError, DateInput from django.forms import ModelMultipleChoiceField, ValidationError, DateInput
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
import django_tables2 as tables import django_tables2 as tables
@ -98,27 +100,53 @@ class DealerForm(forms.ModelForm):
] ]
# Customer Form class CustomerForm(forms.Form):
class CustomerForm(forms.ModelForm, AddClassMixin): first_name = forms.CharField()
class Meta: middle_name = forms.CharField()
model = Customer last_name = forms.CharField()
fields = [ national_id = forms.CharField(max_length=10)
"title", email = forms.EmailField()
"first_name", phone_number = PhoneNumberField(
"middle_name", min_length=10,
"last_name", max_length=10,
"gender", region="SA",
"dob", )
"email", address = forms.CharField()
"national_id",
class OrganizationForm(forms.Form):
name = forms.CharField()
arabic_name = forms.CharField()
email = forms.EmailField()
phone_number = PhoneNumberField(
min_length=10,
max_length=10,
region="SA",
)
crn = forms.CharField()
vrn = forms.CharField()
address = forms.CharField()
contact_person = forms.CharField(required=False)
logo = forms.ImageField(required=False)
# class CustomerForm(forms.ModelForm, AddClassMixin):
# class Meta:
# model = Customer
# fields = [
# "title",
# "first_name",
# "middle_name",
# "last_name",
# "gender",
# "dob",
# "email",
# "national_id",
"phone_number", # "phone_number",
"address", # "address",
] # ]
widgets = { # widgets = {
"phone_number": forms.TextInput(attrs={"class": "phone"}), # "phone_number": forms.TextInput(attrs={"class": "phone"}),
"dob": forms.DateInput(attrs={"type": "date"}), # "dob": forms.DateInput(attrs={"type": "date"}),
} # }
class CarForm( class CarForm(
@ -160,10 +188,11 @@ class CarForm(
(obj.id_car_model, obj.get_local_name()) for obj in queryset (obj.id_car_model, obj.get_local_name()) for obj in queryset
] ]
if "vendor" in self.fields: if "vendor" in self.fields:
queryset = self.fields["vendor"].queryset self.fields["vendor"].queryset = VendorModel.objects.filter(active=True)
self.fields["vendor"].choices = [ # queryset = self.fields["vendor"].queryset
(obj.pk, obj.get_local_name()) for obj in queryset # self.fields["vendor"].choices = [
] # (obj.pk, obj.get_local_name()) for obj in queryset
# ]
class CarUpdateForm(forms.ModelForm, AddClassMixin): class CarUpdateForm(forms.ModelForm, AddClassMixin):
@ -193,12 +222,12 @@ class CarUpdateForm(forms.ModelForm, AddClassMixin):
# (branch.id, branch.get_local_name()) for branch in self.fields['branch'].queryset # (branch.id, branch.get_local_name()) for branch in self.fields['branch'].queryset
# ] # ]
if "vendor" in self.fields: # if "vendor" in self.fields:
queryset = self.fields["vendor"].queryset # queryset = self.fields["vendor"].queryset
if queryset: # if queryset:
self.fields["vendor"].choices = [ # self.fields["vendor"].choices = [
(vendor.id, vendor.get_local_name()) for vendor in queryset # (vendor.id, vendor.get_local_name()) for vendor in queryset
] # ]
class CarFinanceForm(AddClassMixin, forms.ModelForm): class CarFinanceForm(AddClassMixin, forms.ModelForm):
@ -269,20 +298,22 @@ class CarRegistrationForm(forms.ModelForm):
fields = ["car", "plate_number", "text1", "text2", "text3", "registration_date"] fields = ["car", "plate_number", "text1", "text2", "text3", "registration_date"]
class VendorForm(forms.ModelForm): class VendorForm(VendorModelForm):
class Meta: pass
model = Vendor # class VendorForm(forms.ModelForm):
fields = [ # class Meta:
"name", # model = Vendor
"arabic_name", # fields = [
"crn", # "name",
"vrn", # "arabic_name",
"email", # "crn",
"phone_number", # "vrn",
"contact_person", # "email",
"address", # "phone_number",
"logo", # "contact_person",
] # "address",
# "logo",
# ]
class CarColorsForm(forms.ModelForm): class CarColorsForm(forms.ModelForm):
@ -343,22 +374,22 @@ class QuotationForm(forms.ModelForm):
).distinct() ).distinct()
class OrganizationForm(forms.ModelForm): # class OrganizationForm(forms.ModelForm):
class Meta: # class Meta:
model = Organization # model = Organization
fields = [ # fields = [
"name", # "name",
"arabic_name", # "arabic_name",
"crn", # "crn",
"vrn", # "vrn",
"phone_number", # "phone_number",
"address", # "address",
"logo", # "logo",
] # ]
def __init__(self, *args, **kwargs): # def __init__(self, *args, **kwargs):
dealer = kwargs.pop("dealer", None) # dealer = kwargs.pop("dealer", None)
super().__init__(*args, **kwargs) # super().__init__(*args, **kwargs)
class RepresentativeForm(forms.ModelForm): class RepresentativeForm(forms.ModelForm):
@ -654,3 +685,12 @@ class BillModelCreateForm(BillModelCreateFormBase):
self.fields['prepaid_account'].widget = forms.HiddenInput() self.fields['prepaid_account'].widget = forms.HiddenInput()
self.fields['unearned_account'].widget = forms.HiddenInput() self.fields['unearned_account'].widget = forms.HiddenInput()
self.fields['date_draft'] = forms.DateField(widget=DateInput(attrs={'type': 'date'})) self.fields['date_draft'] = forms.DateField(widget=DateInput(attrs={'type': 'date'}))
class SaleOrderForm(forms.ModelForm):
class Meta:
model = SaleOrder
fields = '__all__'
widgets = {
'address': forms.Textarea(attrs={'rows': 3}),
'comments': forms.Textarea(attrs={'rows': 3}),
}

View File

@ -0,0 +1,25 @@
# Generated by Django 4.2.17 on 2025-01-23 08:51
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('inventory', '0003_cartransfer'),
]
operations = [
migrations.CreateModel(
name='PurchaseOrder',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('payment_method', models.CharField(choices=[('cash', 'Cash'), ('finance', 'Finance'), ('lease', 'Lease')], max_length=20)),
('trade_in', models.CharField(blank=True, max_length=100, null=True)),
('comments', models.TextField(blank=True, null=True)),
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='purchase_orders', to='inventory.car', verbose_name='Car')),
('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='purchase_orders', to='inventory.customer', verbose_name='Customer')),
],
),
]

View File

@ -0,0 +1,28 @@
# Generated by Django 4.2.17 on 2025-01-23 11:06
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('inventory', '0004_purchaseorder'),
]
operations = [
migrations.CreateModel(
name='SaleOrder',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('payment_method', models.CharField(choices=[('cash', 'Cash'), ('finance', 'Finance'), ('lease', 'Lease')], max_length=20)),
('trade_in', models.CharField(blank=True, max_length=100, null=True)),
('comments', models.TextField(blank=True, null=True)),
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sale_orders', to='inventory.car', verbose_name='Car')),
('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sale_orders', to='inventory.customer', verbose_name='Customer')),
],
),
migrations.DeleteModel(
name='PurchaseOrder',
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 4.2.17 on 2025-01-24 15:32
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('django_ledger', '0017_alter_accountmodel_unique_together_and_more'),
('inventory', '0005_saleorder_delete_purchaseorder'),
]
operations = [
migrations.AlterField(
model_name='car',
name='vendor',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='cars', to='django_ledger.vendormodel', verbose_name='Vendor'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.17 on 2025-01-26 07:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0006_alter_car_vendor'),
]
operations = [
migrations.AlterField(
model_name='cartransfer',
name='status',
field=models.CharField(default='draft', max_length=10, verbose_name=[('draft', 'Draft'), ('approved', 'Approved'), ('pending', 'Pending'), ('accepted', 'Accepted'), ('success', 'Success'), ('reject', 'Reject'), ('cancelled', 'Cancelled')]),
),
]

View File

@ -278,6 +278,7 @@ class CarTransferStatusChoices(models.TextChoices):
accepted = "accepted", _("Accepted") accepted = "accepted", _("Accepted")
success = "success", _("Success") success = "success", _("Success")
reject = "reject", _("Reject") reject = "reject", _("Reject")
cancelled = "cancelled", _("Cancelled")
class CarStatusChoices(models.TextChoices): class CarStatusChoices(models.TextChoices):
@ -332,7 +333,7 @@ class Car(models.Model):
) )
vendor = models.ForeignKey( vendor = models.ForeignKey(
"Vendor", VendorModel,
models.DO_NOTHING, models.DO_NOTHING,
null=True, null=True,
blank=True, blank=True,
@ -408,6 +409,21 @@ class Car(models.Model):
def get_car_group(self): def get_car_group(self):
return f"{self.id_car_make.get_local_name} {self.id_car_model.get_local_name}" return f"{self.id_car_make.get_local_name} {self.id_car_model.get_local_name}"
def to_dict(self):
return {
"vin": self.vin,
"make": self.id_car_make.name if self.id_car_make else "Unknown Make",
"model": self.id_car_model.name if self.id_car_model else "Unknown Model",
"trim": self.id_car_trim.name if self.id_car_trim else "Unknown Trim",
"year": self.year,
"display_name": self.get_car_group,
"status": self.status,
"stock_type": self.stock_type,
"remarks": self.remarks,
"mileage": self.mileage,
"receiving_date": self.receiving_date.strftime('%Y-%m-%d %H:%M:%S'),
"id": self.id,
}
class CarTransfer(models.Model): class CarTransfer(models.Model):
car = models.ForeignKey( car = models.ForeignKey(
@ -445,7 +461,7 @@ class CarTransfer(models.Model):
@property @property
def total_price(self): def total_price(self):
return self.quantity * self.car.finances.cost_price return self.quantity * self.car.finances.total_vat
class Meta: class Meta:
verbose_name = _("Car Transfer Log") verbose_name = _("Car Transfer Log")
verbose_name_plural = _("Car Transfer Logs") verbose_name_plural = _("Car Transfer Logs")
@ -529,6 +545,16 @@ class CarFinance(models.Model):
def revenue(self): def revenue(self):
return self.selling_price - self.cost_price return self.selling_price - self.cost_price
def to_dict(self):
return {
"cost_price": str(self.cost_price),
"selling_price": str(self.selling_price),
"discount_amount": str(self.discount_amount),
"total": str(self.total),
"total_discount": str(self.total_discount),
"total_vat": str(self.total_vat),
"vat_amount": str(self.vat_amount),
}
def __str__(self): def __str__(self):
return f"Car: {self.car}, Selling Price: {self.selling_price}" return f"Car: {self.car}, Selling Price: {self.selling_price}"
@ -1513,3 +1539,35 @@ class UserActivityLog(models.Model):
def __str__(self): def __str__(self):
return f"{self.user.email} - {self.action} - {self.timestamp}" return f"{self.user.email} - {self.action} - {self.timestamp}"
class SaleOrder(models.Model):
customer = models.ForeignKey(
Customer,
on_delete=models.CASCADE,
related_name="sale_orders",
verbose_name=_("Customer"),
)
car = models.ForeignKey(
Car,
on_delete=models.CASCADE,
related_name="sale_orders",
verbose_name=_("Car"),
)
payment_method = models.CharField(max_length=20, choices=[
('cash', 'Cash'),
('finance', 'Finance'),
('lease', 'Lease'),
])
trade_in = models.CharField(max_length=100, blank=True, null=True)
comments = models.TextField(blank=True, null=True)
def __str__(self):
return f"Sale Order for {self.full_name} - {self.make} {self.model}"
@property
def full_name(self):
return f"{self.customer.first_name} {self.customer.last_name}"
@property
def price(self):
return self.car.finances.selling_price

View File

@ -1,4 +1,6 @@
from decimal import Decimal
from django.db.models.signals import post_save, post_delete, pre_delete, pre_save from django.db.models.signals import post_save, post_delete, pre_delete, pre_save
from .utils import to_dict
from django.dispatch import receiver from django.dispatch import receiver
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
@ -10,6 +12,7 @@ from django_ledger.models import (
ItemModelAbstract, ItemModelAbstract,
UnitOfMeasureModel, UnitOfMeasureModel,
VendorModel, VendorModel,
EstimateModel
) )
from . import models from . import models
from django.utils.timezone import now from django.utils.timezone import now
@ -674,35 +677,36 @@ def create_customer(sender, instance, created, **kwargs):
# Create Item # Create Item
@receiver(post_save, sender=models.Car) @receiver(post_save, sender=models.Car)
def create_item_model(sender, instance, created, **kwargs): def create_item_model(sender, instance, created, **kwargs):
name = instance.dealer.name entity = instance.dealer.entity
entity = EntityModel.objects.filter(name=name).first()
if created: if created:
coa = entity.get_default_coa() coa = entity.get_default_coa()
uom = entity.get_uom_all().get(name="Unit") uom = entity.get_uom_all().get(name="Unit")
if not entity.get_items_all().filter(name=instance.vin).first(): if not entity.get_items_all().filter(name=instance.vin).exists():
entity.create_item_product( product = entity.create_item_product(
name=f"{instance.vin}", name=instance.vin,
item_type=ItemModel.ITEM_TYPE_MATERIAL, item_type=ItemModel.ITEM_TYPE_MATERIAL,
uom_model=uom, uom_model=uom,
coa_model=coa, coa_model=coa,
additional_info={}
) )
entity.create_item_inventory(
name=f"{instance.vin}", product = entity.get_items_all().filter(name=instance.vin).first()
item_type=ItemModel.ITEM_TYPE_MATERIAL, product.additional_info.update({'car_info': instance.to_dict()})
uom_model=uom, product.save()
coa_model=coa,
)
# # update price - CarFinance # # update price - CarFinance
@receiver(post_save, sender=models.CarFinance) @receiver(post_save, sender=models.CarFinance)
def update_item_model_cost(sender, instance, created, **kwargs): def update_item_model_cost(sender, instance, created, **kwargs):
ItemModel.objects.filter(item_id=instance.car.vin).update( entity = instance.car.dealer.entity
inventory_received_value=instance.cost_price,
default_amount=instance.cost_price, product = entity.get_items_all().filter(name=instance.car.vin).first()
) product.default_amount = instance.selling_price
product.additional_info = {}
product.additional_info.update({"car_finance":instance.to_dict()})
product.additional_info.update({"additional_services": [to_dict(service) for service in instance.additional_services.all()]})
product.save()
print(f"Inventory item updated with CarFinance data for Car: {instance.car}") print(f"Inventory item updated with CarFinance data for Car: {instance.car}")
@ -852,4 +856,10 @@ def update_car_status_on_reservation_update(sender, instance, **kwargs):
else: else:
if not car.reservations.filter(reserved_until__gt=now()).exists(): if not car.reservations.filter(reserved_until__gt=now()).exists():
car.status = models.CarStatusChoices.AVAILABLE car.status = models.CarStatusChoices.AVAILABLE
car.save() car.save()
# @receiver(post_save, sender=EstimateModel)
# def update_estimate_status(sender, instance,created, **kwargs):
# items = instance.get_itemtxs_data()[0].all()
# total = sum([Decimal(item.item_model.additional_info['car_finance']["selling_price"]) * Decimal(item.ce_quantity) for item in items])

View File

@ -64,26 +64,26 @@ urlpatterns = [
# CRM URLs # CRM URLs
path("customers/", views.CustomerListView.as_view(), name="customer_list"), path("customers/", views.CustomerListView.as_view(), name="customer_list"),
path( path(
"customers/<int:pk>/", "customers/<uuid:pk>/",
views.CustomerDetailView.as_view(), views.CustomerDetailView.as_view(),
name="customer_detail", name="customer_detail",
), ),
path( path(
"customers/create/", views.CustomerCreateView.as_view(), name="customer_create" "customers/create/", views.CustomerCreateView, name="customer_create"
), ),
path( path(
"customers/<int:pk>/update/", "customers/<str:pk>/update/",
views.CustomerUpdateView.as_view(), views.CustomerUpdateView,
name="customer_update", name="customer_update",
), ),
path("customers/<int:pk>/delete/", views.delete_customer, name="customer_delete"), path("customers/<str:pk>/delete/", views.delete_customer, name="customer_delete"),
path( path(
"customers/<int:customer_id>/opportunities/create/", "customers/<str:customer_id>/opportunities/create/",
views.OpportunityCreateView.as_view(), views.OpportunityCreateView.as_view(),
name="create_opportunity", name="create_opportunity",
), ),
path( path(
"customers/<int:pk>/add-note/", "customers/<str:pk>/add-note/",
views.add_note_to_customer, views.add_note_to_customer,
name="add_note_to_customer", name="add_note_to_customer",
), ),
@ -147,16 +147,16 @@ urlpatterns = [
), ),
# Vendor URLs # Vendor URLs
path("vendors", views.VendorListView.as_view(), name="vendor_list"), path("vendors", views.VendorListView.as_view(), name="vendor_list"),
path("vendors/<int:pk>/", views.VendorDetailView.as_view(), name="vendor_detail"), path("vendors/<uuid:pk>/", views.VendorDetailView.as_view(), name="vendor_detail"),
path("vendors/create/", views.VendorCreateView.as_view(), name="vendor_create"), path("vendors/create/", views.VendorCreateView.as_view(), name="vendor_create"),
path( path(
"vendors/<int:pk>/update/", "vendors/<uuid:pk>/update/",
views.VendorUpdateView.as_view(), views.VendorUpdateView.as_view(),
name="vendor_update", name="vendor_update",
), ),
path( path(
"vendors/<int:pk>/delete/", "vendors/<uuid:pk>/delete/",
views.VendorDetailView.as_view(), views.delete_vendor,
name="vendor_delete", name="vendor_delete",
), ),
# Car URLs # Car URLs
@ -263,9 +263,8 @@ urlpatterns = [
name="mark_quotation", name="mark_quotation",
), ),
path( path(
"sales/quotations/<int:pk>/post_quotation/", "sales/quotations/<int:pk>/post_quotation/",views.post_quotation,
views.post_quotation, name="post_quotation"
name="post_quotation",
), ),
path( path(
"sales/quotations/<int:pk>/invoice_detail/", "sales/quotations/<int:pk>/invoice_detail/",
@ -291,22 +290,22 @@ urlpatterns = [
"organizations/", views.OrganizationListView.as_view(), name="organization_list" "organizations/", views.OrganizationListView.as_view(), name="organization_list"
), ),
path( path(
"organizations/<int:pk>/", "organizations/<uuid:pk>/",
views.OrganizationDetailView.as_view(), views.OrganizationDetailView.as_view(),
name="organization_detail", name="organization_detail",
), ),
path( path(
"organizations/create/", "organizations/create/",
views.OrganizationCreateView.as_view(), views.OrganizationCreateView,
name="organization_create", name="organization_create",
), ),
path( path(
"organizations/<int:pk>/update/", "organizations/<uuid:pk>/update/",
views.OrganizationUpdateView.as_view(), views.OrganizationUpdateView,
name="organization_update", name="organization_update",
), ),
path( path(
"organizations/<int:pk>/delete/", "organizations/<uuid:pk>/delete/",
views.OrganizationDeleteView.as_view(), views.OrganizationDeleteView.as_view(),
name="organization_delete", name="organization_delete",
), ),
@ -403,6 +402,8 @@ urlpatterns = [
path( path(
"sales/estimates/<uuid:pk>/send_email", views.send_email_view, name="send_email" "sales/estimates/<uuid:pk>/send_email", views.send_email_view, name="send_email"
), ),
path('sales/estimates/<uuid:pk>/sale_order/', views.create_sale_order, name='create_sale_order'),
# Invoice # Invoice
path("sales/invoices/", views.InvoiceListView.as_view(), name="invoice_list"), path("sales/invoices/", views.InvoiceListView.as_view(), name="invoice_list"),
path( path(

View File

@ -15,7 +15,7 @@ from django.core.mail import send_mail
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from inventory.utilities.financials import get_financial_value from inventory.utilities.financials import get_financial_value
from django_ledger.models.items import ItemModel from django_ledger.models.items import ItemModel
from django_ledger.models import InvoiceModel, EstimateModel,BillModel from django_ledger.models import InvoiceModel, EstimateModel,BillModel,VendorModel,CustomerModel
from decimal import Decimal from decimal import Decimal
@ -136,51 +136,61 @@ def get_financial_values(model):
"car_and_item_info": [], "car_and_item_info": [],
"additional_services": [], "additional_services": [],
} }
data = model.get_itemtxs_data()[0].all()
data = model.get_itemtxs_data()[0].all() for item in data:
if not item.item_model.additional_info.get("car_finance"):
return {
"vat_amount": 0,
"total": 0,
"grand_total": 0,
"discount_amount": 0,
"vat": 0,
"car_and_item_info": [],
"additional_services": [],
}
if isinstance(model, InvoiceModel): if isinstance(model, InvoiceModel):
data = model.ce_model.get_itemtxs_data()[0].all() if model.ce_model:
data = model.ce_model.get_itemtxs_data()[0].all()
car_and_item_info = [ else:
{ data = model.get_itemtxs_data()[0].all()
"car": models.Car.objects.get(vin=x.item_model.name), total = sum([Decimal(item.item_model.additional_info["car_finance"]["selling_price"]) * Decimal(item.ce_quantity or item.quantity) for item in data])
"total": models.Car.objects.get(
vin=x.item_model.name
).finances.selling_price
* Decimal(x.ce_quantity),
"itemmodel": x,
}
for x in data
]
total = sum(
Decimal(models.Car.objects.get(vin=x.item_model.name).finances.total)
* Decimal(x.ce_quantity)
for x in data
)
discount_amount = sum( discount_amount = sum(
models.CarFinance.objects.get(car__vin=i.item_model.name).discount_amount Decimal(i.item_model.additional_info['car_finance']["discount_amount"])
for i in data for i in data
) )
additional_services = [] additional_services = []
for i in data: for i in data:
cf = models.CarFinance.objects.get(car__vin=i.item_model.name) if i.item_model.additional_info['additional_services']:
if cf.additional_services.exists():
additional_services.extend( additional_services.extend(
[ [
{"name": x.name, "price": x.price} {"name": x.name, "price": x.price}
for x in cf.additional_services.all() for x in i.item_model.additional_info['additional_services']
] ]
) )
grand_total = Decimal(total) - Decimal(discount_amount) grand_total = Decimal(total) - Decimal(discount_amount)
vat_amount = round(Decimal(grand_total) * Decimal(vat.rate), 2) vat_amount = round(Decimal(grand_total) * Decimal(vat.rate), 2)
car_and_item_info = [
{
"info": x.item_model.additional_info['car_info'],
"finances": x.item_model.additional_info['car_finance'],
"quantity": x.ce_quantity or x.quantity,
"total": Decimal(x.item_model.additional_info['car_finance']['selling_price'])
* Decimal(x.ce_quantity or x.quantity),
}
for x in data
]
return { return {
"car_and_item_info": car_and_item_info,
"total": total, "total": total,
"discount_amount": discount_amount, "discount_amount": discount_amount,
"car_and_item_info": car_and_item_info,
"additional_services": additional_services, "additional_services": additional_services,
"grand_total": grand_total + vat_amount, "grand_total": grand_total + vat_amount,
"vat_amount": vat_amount, "vat_amount": vat_amount,
@ -194,12 +204,13 @@ def set_invoice_payment(dealer, entity, invoice, amount, payment_method):
if invoice.terms == "on_receipt": if invoice.terms == "on_receipt":
for x in invoice.get_itemtxs_data()[0].all(): for x in invoice.get_itemtxs_data()[0].all():
vat_amount += models.Car.objects.get( # vat_amount += models.Car.objects.get(
vin=x.item_model.name # vin=x.item_model.name
).finances.vat_amount * Decimal(x.quantity) # ).finances.vat_amount * Decimal(x.quantity)
total_amount += Decimal(x.unit_cost) * Decimal(x.quantity) total_amount += Decimal(x.unit_cost) * Decimal(x.quantity)
grand_total = total_amount - Decimal(vat_amount) # grand_total = total_amount - Decimal(vat_amount)
total_amount
ledger = LedgerModel.objects.filter( ledger = LedgerModel.objects.filter(
name__icontains=str(invoice.pk), entity=entity name__icontains=str(invoice.pk), entity=entity
@ -234,11 +245,13 @@ def set_invoice_payment(dealer, entity, invoice, amount, payment_method):
tx_type="debit", tx_type="debit",
description="Payment Received", description="Payment Received",
) )
# if total_amount + invoice.
TransactionModel.objects.create( TransactionModel.objects.create(
journal_entry=journal, journal_entry=journal,
account=credit_account, # Credit Accounts Receivable account=credit_account, # Credit Accounts Receivable
amount=grand_total, # Payment amount amount=total_amount, # Payment amount
tx_type="credit", tx_type="credit",
description="Payment Received", description="Payment Received",
) )
@ -329,26 +342,19 @@ def transfer_car(car,transfer):
from_dealer = transfer.from_dealer from_dealer = transfer.from_dealer
to_dealer = transfer.to_dealer to_dealer = transfer.to_dealer
# add transfer.to_dealer as customer in transfer.from_dealer entity # add transfer.to_dealer as customer in transfer.from_dealer entity
instance = models.Customer.objects.filter(
dealer=from_dealer,
email=to_dealer.user.email,
).first()
if not instance:
instance = models.Customer.objects.create(
dealer=from_dealer,
title=models.Title.MR,
email=to_dealer.user.email,
first_name=to_dealer.user.first_name,
last_name=to_dealer.user.last_name,
phone_number=f"05685412{random.randint(10, 99)}",
address=to_dealer.address,
national_id=f"{random.randint(100, 9999)}",
dob="1990-01-01",
)
# create invoice from transfer.from_dealer to transfer.to_dealer customer = from_dealer.entity.get_customers().filter(
name = f"{instance.first_name} {instance.middle_name} {instance.last_name}" email=to_dealer.user.email).first()
customer = from_dealer.entity.get_customers().filter(customer_name=name).first() if not customer:
customer = from_dealer.entity.create_customer(
customer_model_kwargs={
"customer_name": to_dealer.name,
"email": to_dealer.user.email,
"address_1": to_dealer.address
}
)
customer.additional_info.update({"type":"organization"})
customer.save()
invoice = from_dealer.entity.create_invoice( invoice = from_dealer.entity.create_invoice(
customer_model=customer, customer_model=customer,
@ -369,7 +375,7 @@ def transfer_car(car,transfer):
invoice_itemtxs = { invoice_itemtxs = {
item.item_number: { item.item_number: {
"unit_cost": car.finances.cost_price, "unit_cost": transfer.total_price,
"quantity": transfer.quantity, "quantity": transfer.quantity,
"total_amount": transfer.total_price, "total_amount": transfer.total_price,
} }
@ -381,46 +387,33 @@ def transfer_car(car,transfer):
operation=InvoiceModel.ITEMIZE_APPEND, operation=InvoiceModel.ITEMIZE_APPEND,
) )
invoice.save()
invoice.mark_as_review() invoice.mark_as_review()
invoice.mark_as_approved(from_dealer.entity.slug, from_dealer.entity.admin) invoice.mark_as_approved(from_dealer.entity.slug, from_dealer.entity.admin)
invoice.mark_as_paid(from_dealer.entity.slug, from_dealer.entity.admin) # invoice.mark_as_paid(from_dealer.entity.slug, from_dealer.entity.admin)
invoice.save() invoice.save()
#create car item product in to_dealer entity #create car item product in to_dealer entity
uom = to_dealer.entity.get_uom_all().filter(name=item.uom.name).first() uom = to_dealer.entity.get_uom_all().filter(name=item.uom.name).first()
#create item product in the reciever ledger
product = to_dealer.entity.create_item_product( product = to_dealer.entity.create_item_product(
name=item.name, name=item.name,
uom_model=uom, uom_model=uom,
item_type=item.item_type, item_type=item.item_type,
coa_model=to_dealer.entity.get_default_coa(), coa_model=to_dealer.entity.get_default_coa(),
) )
car_dict = vars(car).copy() product.additional_info.update({'car_info': car.to_dict()})
del car_dict["_state"]
for key, value in car_dict.items():
if isinstance(value, datetime.datetime):
car_dict[key] = value.strftime('%Y-%m-%d %H:%M:%S')
product.additional_info = json.dumps({"car_info": car_dict})
product.save() product.save()
#add the sender as vendor and create a bill for it #add the sender as vendor and create a bill for it
vendor_instance, created = models.Vendor.objects.get_or_create( vendor = None
dealer=to_dealer, vendor = to_dealer.entity.get_vendors().filter(vendor_name=from_dealer.name).first()
crn=from_dealer.crn, if not vendor:
vrn=from_dealer.vrn, vendor = VendorModel.objects.create(entity_model=to_dealer.entity, vendor_name=from_dealer.name,additional_info={"info":to_dict(from_dealer)})
name=from_dealer.name,
email=from_dealer.user.email,
arabic_name=from_dealer.arabic_name,
address=from_dealer.address,
phone_number=from_dealer.phone_number,
contact_person='',
)
#transfer the car to to_dealer and create items record #transfer the car to to_dealer and create items record
vendor = to_dealer.entity.get_vendors().filter(vendor_name=vendor_instance.name).first()
bill = to_dealer.entity.create_bill( bill = to_dealer.entity.create_bill(
vendor_model=vendor, vendor_model=vendor,
terms=BillModel.TERMS_NET_30, terms=BillModel.TERMS_NET_30,
@ -428,10 +421,10 @@ def transfer_car(car,transfer):
prepaid_account=to_dealer.entity.get_default_coa_accounts().get(name="Prepaid Expenses", active=True), prepaid_account=to_dealer.entity.get_default_coa_accounts().get(name="Prepaid Expenses", active=True),
coa_model=to_dealer.entity.get_default_coa(), coa_model=to_dealer.entity.get_default_coa(),
) )
bill.additional_info = {}
bill_itemtxs = { bill_itemtxs = {
item.item_number: { product.item_number: {
"unit_cost": car.finances.cost_price, "unit_cost": transfer.total_price,
"quantity": transfer.quantity, "quantity": transfer.quantity,
"total_amount": transfer.total_price, "total_amount": transfer.total_price,
} }
@ -439,16 +432,23 @@ def transfer_car(car,transfer):
bill_itemtxs = bill.migrate_itemtxs(itemtxs=bill_itemtxs, bill_itemtxs = bill.migrate_itemtxs(itemtxs=bill_itemtxs,
commit=True, commit=True,
operation=BillModel.ITEMIZE_REPLACE) operation=BillModel.ITEMIZE_APPEND)
bill.additional_info.update({'car_info': car.to_dict()})
bill.additional_info.update({'car_finance': car.finances.to_dict()})
bill.mark_as_review()
bill.mark_as_approved(to_dealer.entity.slug, to_dealer.entity.admin)
bill.save()
car.dealer = to_dealer car.dealer = to_dealer
car.vendor = vendor_instance car.vendor = vendor
car.receiving_date = datetime.datetime.now() car.receiving_date = datetime.datetime.now()
car.finances.additional_services.clear() car.finances.additional_services.clear()
if hasattr(car, "custom_cards"): if hasattr(car, "custom_cards"):
car.custom_cards.delete() car.custom_cards.delete()
# car.finances.cost_price = 0
car.finances.cost_price = transfer.total_price
car.finances.selling_price = 0 car.finances.selling_price = 0
car.finances.discount_amount = 0 car.finances.discount_amount = 0
car.finances.save() car.finances.save()
@ -456,8 +456,6 @@ def transfer_car(car,transfer):
car.location.showroom = to_dealer car.location.showroom = to_dealer
car.location.description = "" car.location.description = ""
car.location.save() car.location.save()
# car.reservations.all().delete()
car.status = models.CarStatusChoices.AVAILABLE car.status = models.CarStatusChoices.AVAILABLE
transfer.status = models.CarTransferStatusChoices.success transfer.status = models.CarTransferStatusChoices.success
transfer.active = False transfer.active = False
@ -467,6 +465,20 @@ def transfer_car(car,transfer):
return True return True
#pay the pill #pay the pill
# set_bill_payment(to_dealer,to_dealer.entity,bill,transfer.total_price,"credit") # set_bill_payment(to_dealer,to_dealer.entity,bill,transfer.total_price,"credit")
def to_dict(obj):
obj_dict = vars(obj).copy()
if '_state' in vars(obj):
del obj_dict["_state"]
for key, value in obj_dict.items():
if isinstance(value, datetime.datetime):
obj_dict[key] = value.strftime('%Y-%m-%d %H:%M:%S')
elif hasattr(value,'pk') or hasattr(value,'id'):
try:
obj_dict[key] = value.name
except AttributeError:
obj_dict[key] = str(value)
else:
obj_dict[key] = str(value)
return obj_dict

View File

@ -1,7 +1,10 @@
from decimal import Decimal from decimal import Decimal
from django.core.paginator import Paginator from django.core.paginator import Paginator
from django.forms import DateField, DateInput, HiddenInput, TextInput from django.forms import DateField, DateInput, HiddenInput, TextInput
from django_ledger.forms.bill import ApprovedBillModelUpdateForm, InReviewBillModelUpdateForm from django_ledger.forms.bill import (
ApprovedBillModelUpdateForm,
InReviewBillModelUpdateForm,
)
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django_ledger.models import ( from django_ledger.models import (
@ -15,12 +18,15 @@ from django_ledger.models import (
CustomerModel, CustomerModel,
LedgerModel, LedgerModel,
ItemModel, ItemModel,
BillModel BillModel,
VendorModel,
) )
from django_ledger.forms.bank_account import ( from django_ledger.forms.bank_account import (
BankAccountCreateForm, BankAccountCreateForm,
BankAccountUpdateForm, BankAccountUpdateForm,
) )
from django_ledger.forms.customer import CustomerModelForm
from django_ledger.forms.bill import BillModelCreateForm from django_ledger.forms.bill import BillModelCreateForm
from django_ledger.forms.invoice import ( from django_ledger.forms.invoice import (
DraftInvoiceModelUpdateForm, DraftInvoiceModelUpdateForm,
@ -84,6 +90,7 @@ from .utils import (
get_user_type, get_user_type,
set_bill_payment, set_bill_payment,
set_invoice_payment, set_invoice_payment,
to_dict,
transfer_car, transfer_car,
) )
from django.contrib.auth.models import User from django.contrib.auth.models import User
@ -93,6 +100,7 @@ from django.contrib.auth import authenticate
import cv2 import cv2
import numpy as np import numpy as np
from pyzbar.pyzbar import decode from pyzbar.pyzbar import decode
from django.core.files.storage import default_storage
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -289,6 +297,12 @@ class CarCreateView(LoginRequiredMixin, CreateView):
template_name = "inventory/car_form.html" template_name = "inventory/car_form.html"
# success_url = reverse_lazy('inventory_stats') # success_url = reverse_lazy('inventory_stats')
def get_form(self, form_class=None):
form = super().get_form(form_class)
dealer = get_user_type(self.request)
form.fields["vendor"].queryset = dealer.entity.get_vendors().filter(active=True)
return form
def get_success_url(self): def get_success_url(self):
"""Determine the redirect URL based on user choice.""" """Determine the redirect URL based on user choice."""
if self.request.POST.get("add_another"): if self.request.POST.get("add_another"):
@ -779,52 +793,76 @@ class CarLocationUpdateView(UpdateView):
def get_success_url(self): def get_success_url(self):
return reverse_lazy("car_detail", kwargs={"pk": self.object.car.pk}) return reverse_lazy("car_detail", kwargs={"pk": self.object.car.pk})
class CarTransferCreateView(CreateView): class CarTransferCreateView(CreateView):
model = models.CarTransfer model = models.CarTransfer
form_class = forms.CarTransferForm form_class = forms.CarTransferForm
template_name = "inventory/car_location_form.html" template_name = "inventory/car_location_form.html"
def get_form(self, form_class = None): def get_form(self, form_class=None):
form = super().get_form(form_class) form = super().get_form(form_class)
form.fields['to_dealer'].queryset = models.Dealer.objects.exclude(pk=get_user_type(self.request).pk).all() form.fields["to_dealer"].queryset = models.Dealer.objects.exclude(
form.fields['car'].queryset = models.Car.objects.filter(pk=self.kwargs["pk"]) pk=get_user_type(self.request).pk
).all()
form.fields["car"].queryset = models.Car.objects.filter(pk=self.kwargs["pk"])
return form return form
def get_initial(self): def get_initial(self):
initial = super().get_initial() initial = super().get_initial()
initial["car"] = get_object_or_404(models.Car, pk=self.kwargs["pk"]) initial["car"] = get_object_or_404(models.Car, pk=self.kwargs["pk"])
return initial return initial
def form_valid(self, form): def form_valid(self, form):
form.instance.from_dealer = get_user_type(self.request) form.instance.from_dealer = get_user_type(self.request)
form.instance.car.status = "transfer" form.instance.car.status = "transfer"
form.instance.car.save() form.instance.car.save()
return super().form_valid(form) return super().form_valid(form)
def get_success_url(self): def get_success_url(self):
return reverse_lazy("car_detail", kwargs={"pk": self.object.car.pk}) return reverse_lazy("car_detail", kwargs={"pk": self.object.car.pk})
def CarTransferDetailView(request, pk): def CarTransferDetailView(request, pk):
transfer = get_object_or_404(models.CarTransfer, pk=pk) transfer = get_object_or_404(models.CarTransfer, pk=pk)
context = {"transfer":transfer} context = {"transfer": transfer}
return render(request,'inventory/transfer_details.html',context) return render(request, "inventory/transfer_details.html", context)
def car_transfer_approve(request, car_pk,transfer_pk):
def car_transfer_approve(request, car_pk, transfer_pk):
car = get_object_or_404(models.Car, pk=car_pk) car = get_object_or_404(models.Car, pk=car_pk)
transfer = get_object_or_404(models.CarTransfer, pk=transfer_pk) transfer = get_object_or_404(models.CarTransfer, pk=transfer_pk)
action = request.GET.get("action")
if action == "cancel":
transfer.status = "cancel"
transfer.active = False
transfer.save()
transfer.car.status = "available"
transfer.car.save()
messages.success(request, _("Car transfer canceled successfully."))
models.Notification.objects.create(
user=transfer.from_dealer.user,
message=f"Car transfer request from {transfer.to_dealer} is canceled.",
)
return redirect("car_detail", pk=car.pk)
transfer.status = "approved" transfer.status = "approved"
transfer.save() transfer.save()
url = request.build_absolute_uri(reverse('transfer_preview',kwargs={'car_pk':car.pk,"transfer_pk":transfer.pk})) url = request.build_absolute_uri(
reverse(
"transfer_preview", kwargs={"car_pk": car.pk, "transfer_pk": transfer.pk}
)
)
models.Notification.objects.create( models.Notification.objects.create(
user=transfer.to_dealer.user, user=transfer.to_dealer.user,
message=f"Car transfer request from {transfer.from_dealer} is waiting for your acceptance. <a href='{url}'> Accept</a>", message=f"Car transfer request from {transfer.from_dealer} is waiting for your acceptance. <a href='{url}'> Accept</a>",
) )
messages.success(request, _("Car transfer approved successfully.")) messages.success(request, _("Car transfer approved successfully."))
return redirect("car_detail", pk=car.pk) return redirect("car_detail", pk=car.pk)
def car_transfer_accept_reject(request, car_pk,transfer_pk):
def car_transfer_accept_reject(request, car_pk, transfer_pk):
car = get_object_or_404(models.Car, pk=car_pk) car = get_object_or_404(models.Car, pk=car_pk)
transfer = get_object_or_404(models.CarTransfer, pk=transfer_pk) transfer = get_object_or_404(models.CarTransfer, pk=transfer_pk)
status = request.GET.get("status") status = request.GET.get("status")
if status == "rejected": if status == "rejected":
transfer.status = "reject" transfer.status = "reject"
transfer.active = False transfer.active = False
@ -837,7 +875,7 @@ def car_transfer_accept_reject(request, car_pk,transfer_pk):
elif status == "accepted": elif status == "accepted":
transfer.status = "accept" transfer.status = "accept"
transfer.save() transfer.save()
success = transfer_car(car,transfer) success = transfer_car(car, transfer)
if success: if success:
messages.success(request, _("Car Transfer Completed successfully.")) messages.success(request, _("Car Transfer Completed successfully."))
models.Notification.objects.create( models.Notification.objects.create(
@ -845,12 +883,13 @@ def car_transfer_accept_reject(request, car_pk,transfer_pk):
message=f"Car transfer request from {transfer.to_dealer} is completed.", message=f"Car transfer request from {transfer.to_dealer} is completed.",
) )
return redirect("inventory_stats") return redirect("inventory_stats")
def CarTransferPreviewView(request, car_pk,transfer_pk):
def CarTransferPreviewView(request, car_pk, transfer_pk):
transfer = get_object_or_404(models.CarTransfer, pk=transfer_pk) transfer = get_object_or_404(models.CarTransfer, pk=transfer_pk)
if transfer.to_dealer != get_user_type(request): if transfer.to_dealer != get_user_type(request):
return redirect("car_detail", pk=car_pk) return redirect("car_detail", pk=car_pk)
return render(request,'inventory/transfer_preview.html',{"transfer":transfer}) return render(request, "inventory/transfer_preview.html", {"transfer": transfer})
# def get_context_data(self, **kwargs): # def get_context_data(self, **kwargs):
# estimate = kwargs.get("object") # estimate = kwargs.get("object")
# if estimate.get_itemtxs_data(): # if estimate.get_itemtxs_data():
@ -865,11 +904,9 @@ def CarTransferPreviewView(request, car_pk,transfer_pk):
# return super().get_context_data(**kwargs) # return super().get_context_data(**kwargs)
# class CarTransferView(View): # class CarTransferView(View):
# template_name = "inventory/car_location_form.html" # template_name = "inventory/car_location_form.html"
# def get(self, request, *args, **kwargs): # def get(self, request, *args, **kwargs):
# form = forms.CarTransferForm() # form = forms.CarTransferForm()
# car = models.Car.objects.filter(pk=self.kwargs["pk"]) # car = models.Car.objects.filter(pk=self.kwargs["pk"])
@ -878,7 +915,7 @@ def CarTransferPreviewView(request, car_pk,transfer_pk):
# form.initial['car'] = car.first() # form.initial['car'] = car.first()
# context = {"form": form} # context = {"form": form}
# return render(request, self.template_name,context) # return render(request, self.template_name,context)
# def post(self, request, *args, **kwargs): # def post(self, request, *args, **kwargs):
# form = forms.CarTransferForm(request.POST) # form = forms.CarTransferForm(request.POST)
# if form.is_valid(): # if form.is_valid():
@ -894,6 +931,7 @@ def CarTransferPreviewView(request, car_pk,transfer_pk):
# # messages.success(request, "Car transfered successfully.") # # messages.success(request, "Car transfered successfully.")
# return redirect("car_detail", pk=self.kwargs["pk"]) # return redirect("car_detail", pk=self.kwargs["pk"])
class CustomCardCreateView(LoginRequiredMixin, CreateView): class CustomCardCreateView(LoginRequiredMixin, CreateView):
model = models.CustomCard model = models.CustomCard
form_class = forms.CustomCardForm form_class = forms.CustomCardForm
@ -997,7 +1035,7 @@ class DealerUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
class CustomerListView(LoginRequiredMixin, ListView): class CustomerListView(LoginRequiredMixin, ListView):
model = models.Customer model = CustomerModel
home_label = _("customers") home_label = _("customers")
context_object_name = "customers" context_object_name = "customers"
paginate_by = 10 paginate_by = 10
@ -1008,13 +1046,13 @@ class CustomerListView(LoginRequiredMixin, ListView):
query = self.request.GET.get("q") query = self.request.GET.get("q")
dealer = get_user_type(self.request) dealer = get_user_type(self.request)
customers = models.Customer.objects.filter(dealer=dealer) customers = dealer.entity.get_customers().filter(active=True,additional_info__type="customer")
if query: if query:
customers = customers.filter( customers = customers.filter(
Q(national_id__icontains=query) Q(first_name__icontains=query)
| Q(first_name__icontains=query)
| Q(last_name__icontains=query) | Q(last_name__icontains=query)
| Q(additional_info__info__icontains=query)
) )
return customers return customers
@ -1025,7 +1063,7 @@ class CustomerListView(LoginRequiredMixin, ListView):
class CustomerDetailView(LoginRequiredMixin, DetailView): class CustomerDetailView(LoginRequiredMixin, DetailView):
model = models.Customer model = CustomerModel
template_name = "customers/view_customer.html" template_name = "customers/view_customer.html"
context_object_name = "customer" context_object_name = "customer"
@ -1033,21 +1071,22 @@ class CustomerDetailView(LoginRequiredMixin, DetailView):
dealer = get_user_type(self.request) dealer = get_user_type(self.request)
entity = dealer.entity entity = dealer.entity
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
name = f"{context['customer'].first_name} {context['customer'].middle_name} {context['customer'].last_name}" # customer = f"{context['customer'].first_name} {context['customer'].middle_name} {context['customer'].last_name}"
context["estimates"] = entity.get_estimates().filter( # context["estimates"] = entity.get_estimates().filter(
customer__customer_name=name # customer__customer_name=name
) # )
context["notes"] = models.Notes.objects.filter( context["estimates"] = context["customer"].estimatemodel_set.all()
content_type__model="customer", object_id=self.object.id # context["notes"] = models.Notes.objects.filter(
) # content_type__model="customer", object_id=self.object.id
context["activities"] = models.Activity.objects.filter( # )
content_type__model="customer", object_id=self.object.id # context["activities"] = models.Activity.objects.filter(
) # content_type__model="customer", object_id=self.object.id
# )
return context return context
def add_note_to_customer(request, pk): def add_note_to_customer(request, pk):
customer = get_object_or_404(models.Customer, pk=pk) customer = get_object_or_404(CustomerModel, pk=pk)
if request.method == "POST": if request.method == "POST":
form = forms.NoteForm(request.POST) form = forms.NoteForm(request.POST)
if form.is_valid(): if form.is_valid():
@ -1063,7 +1102,7 @@ def add_note_to_customer(request, pk):
def add_activity_to_customer(request, pk): def add_activity_to_customer(request, pk):
customer = get_object_or_404(models.Customer, pk=pk) customer = get_object_or_404(CustomerModel, pk=pk)
if request.method == "POST": if request.method == "POST":
form = forms.ActivityForm(request.POST) form = forms.ActivityForm(request.POST)
if form.is_valid(): if form.is_valid():
@ -1079,32 +1118,69 @@ def add_activity_to_customer(request, pk):
) )
class CustomerCreateView( def CustomerCreateView(request):
LoginRequiredMixin, if request.method == "POST":
SuccessMessageMixin, customer_dict = {
CreateView, x: request.POST[x] for x in request.POST if x != "csrfmiddlewaretoken"
): }
model = models.Customer dealer = get_user_type(request)
form_class = forms.CustomerForm customer_name = (
template_name = "customers/customer_form.html" customer_dict["first_name"]
success_url = reverse_lazy("customer_list") + " "
success_message = _("Customer created successfully.") + customer_dict["middle_name"]
+ " "
+ customer_dict["last_name"]
)
def form_valid(self, form): instance = dealer.entity.create_customer(
form.instance.dealer = get_user_type(self.request) customer_model_kwargs={
return super().form_valid(form) "customer_name": customer_name,
"address_1": customer_dict["address"],
"phone": customer_dict["phone_number"],
"email": customer_dict["email"],
}
)
customer_dict["pk"] = str(instance.pk)
instance.additional_info["customer_info"] = customer_dict
instance.additional_info["type"] = "customer"
instance.save()
messages.success(request, _("Customer created successfully."))
return redirect("customer_list")
form = forms.CustomerForm()
return render(request, "customers/customer_form.html", {"form": form})
class CustomerUpdateView( def CustomerUpdateView(request, pk):
LoginRequiredMixin, customer = get_object_or_404(CustomerModel, pk=pk)
SuccessMessageMixin, if request.method == "POST":
UpdateView, # form = forms.CustomerForm(request.POST, instance=customer)
): customer_dict = {
model = models.Customer x: request.POST[x] for x in request.POST if x != "csrfmiddlewaretoken"
form_class = forms.CustomerForm }
template_name = "customers/customer_form.html" dealer = get_user_type(request)
success_url = reverse_lazy("customer_list") customer_name = (
success_message = _("Customer updated successfully.") customer_dict["first_name"]
+ " "
+ customer_dict["middle_name"]
+ " "
+ customer_dict["last_name"]
)
instance = dealer.entity.get_customers().get(pk=pk)
instance.customer_name = customer_name
instance.address_1 = customer_dict["address"]
instance.phone = customer_dict["phone_number"]
instance.email = customer_dict["email"]
customer_dict["pk"] = str(instance.pk)
instance.additional_info["customer_info"] = customer_dict
instance.save()
messages.success(request, _("Customer updated successfully."))
return redirect("customer_list")
else:
form = forms.CustomerForm(initial=customer.additional_info["customer_info"] if "customer_info" in customer.additional_info else {})
return render(request, "customers/customer_form.html", {"form": form})
@login_required @login_required
@ -1116,7 +1192,7 @@ def delete_customer(request, pk):
class VendorListView(LoginRequiredMixin, ListView): class VendorListView(LoginRequiredMixin, ListView):
model = models.Vendor model = VendorModel
context_object_name = "vendors" context_object_name = "vendors"
paginate_by = 10 paginate_by = 10
template_name = "vendors/vendors_list.html" template_name = "vendors/vendors_list.html"
@ -1124,11 +1200,12 @@ class VendorListView(LoginRequiredMixin, ListView):
def get_queryset(self): def get_queryset(self):
dealer = get_user_type(self.request) dealer = get_user_type(self.request)
return models.Vendor.objects.filter(dealer=dealer)
return dealer.entity.get_vendors().filter(active=True)
class VendorDetailView(LoginRequiredMixin, DetailView): class VendorDetailView(LoginRequiredMixin, DetailView):
model = models.Vendor model = VendorModel
template_name = "vendors/view_vendor.html" template_name = "vendors/view_vendor.html"
@ -1137,14 +1214,17 @@ class VendorCreateView(
SuccessMessageMixin, SuccessMessageMixin,
CreateView, CreateView,
): ):
model = models.Vendor model = VendorModel
form_class = forms.VendorForm form_class = forms.VendorForm
template_name = "vendors/vendor_form.html" template_name = "vendors/vendor_form.html"
success_url = reverse_lazy("vendor_list") success_url = reverse_lazy("vendor_list")
success_message = _("Vendor created successfully.") success_message = _("Vendor created successfully.")
def form_valid(self, form): def form_valid(self, form):
form.instance.dealer = get_user_type(self.request) dealer = get_user_type(self.request)
instance = form.save(commit=False)
instance.entity_model = dealer.entity
instance.save()
return super().form_valid(form) return super().form_valid(form)
@ -1153,7 +1233,7 @@ class VendorUpdateView(
SuccessMessageMixin, SuccessMessageMixin,
UpdateView, UpdateView,
): ):
model = models.Vendor model = VendorModel
form_class = forms.VendorForm form_class = forms.VendorForm
template_name = "vendors/vendor_form.html" template_name = "vendors/vendor_form.html"
success_url = reverse_lazy("vendor_list") success_url = reverse_lazy("vendor_list")
@ -1162,8 +1242,9 @@ class VendorUpdateView(
@login_required @login_required
def delete_vendor(request, pk): def delete_vendor(request, pk):
vendor = get_object_or_404(models.Vendor, pk=pk) vendor = get_object_or_404(VendorModel, pk=pk)
vendor.delete() vendor.active = False
vendor.save()
messages.success(request, _("Vendor deleted successfully.")) messages.success(request, _("Vendor deleted successfully."))
return redirect("vendor_list") return redirect("vendor_list")
@ -1630,14 +1711,14 @@ def custom_bad_request_view(request, exception=None):
class OrganizationListView(LoginRequiredMixin, ListView): class OrganizationListView(LoginRequiredMixin, ListView):
model = models.Organization model = CustomerModel
template_name = "organizations/organization_list.html" template_name = "organizations/organization_list.html"
context_object_name = "organizations" context_object_name = "organizations"
paginate_by = 10 paginate_by = 10
def get_queryset(self): def get_queryset(self):
dealer = get_user_type(self.request) dealer = get_user_type(self.request)
return models.Organization.objects.filter(dealer=dealer).all() return dealer.entity.get_customers().filter(additional_info__type="organization",active=True).all()
class OrganizationDetailView(DetailView): class OrganizationDetailView(DetailView):
@ -1646,29 +1727,69 @@ class OrganizationDetailView(DetailView):
context_object_name = "organization" context_object_name = "organization"
class OrganizationCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): def OrganizationCreateView(request):
model = models.Organization if request.method == "POST":
form_class = forms.OrganizationForm form = forms.OrganizationForm(request.POST)
template_name = "organizations/organization_form.html"
success_url = reverse_lazy("organization_list") #upload logo
success_message = "Organization created successfully." image = request.FILES.get('logo')
file_name = default_storage.save('images/{}'.format(image.name), image)
file_url = default_storage.url(file_name)
organization_dict = {
x: request.POST[x] for x in request.POST if x != "csrfmiddlewaretoken"
}
dealer = get_user_type(request)
def form_valid(self, form): instance = dealer.entity.create_customer(
if form.is_valid(): customer_model_kwargs={
form.instance.dealer = self.request.user.dealer "customer_name": organization_dict["name"],
form.save() "address_1": organization_dict["address"],
return super().form_valid(form) "phone": organization_dict["phone_number"],
else: "email": organization_dict["email"],
return form.errors }
)
organization_dict["logo"] = file_url
organization_dict["pk"] = str(instance.pk)
instance.additional_info["organization_info"] = organization_dict
instance.additional_info["type"] = "organization"
instance.save()
messages.success(request, _("Organization created successfully."))
return redirect("organization_list")
else:
form = forms.OrganizationForm()
return render(request, "organizations/organization_form.html", {"form": form})
class OrganizationUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): def OrganizationUpdateView(request,pk):
model = models.Organization organization = get_object_or_404(CustomerModel, pk=pk)
form_class = forms.OrganizationForm if request.method == "POST":
template_name = "organizations/organization_form.html" form = forms.OrganizationForm(request.POST)
success_url = reverse_lazy("organization_list")
success_message = "Organization updated successfully." organization_dict = {
x: request.POST[x] for x in request.POST if x != "csrfmiddlewaretoken"
}
dealer = get_user_type(request)
instance = dealer.entity.get_customers().get(pk=organization.additional_info['organization_info']['pk'])
instance.customer_name = organization_dict["name"]
instance.address_1 = organization_dict["address"]
instance.phone = organization_dict["phone_number"]
instance.email = organization_dict["email"]
organization_dict["logo"] = organization.additional_info['organization_info']['logo']
organization_dict["pk"] = str(instance.pk)
instance.additional_info["organization_info"] = organization_dict
instance.additional_info["type"] = "organization"
instance.save()
messages.success(request, _("Organization created successfully."))
return redirect("organization_list")
else:
form = forms.OrganizationForm(initial=organization.additional_info["organization_info"] or {})
form.fields.pop('logo', None)
return render(request, "organizations/organization_form.html", {"form": form})
class OrganizationDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView): class OrganizationDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
model = models.Organization model = models.Organization
@ -2228,6 +2349,7 @@ def create_estimate(request):
) )
form = EstimateModelCreateForm(entity_slug=entity.slug, user_model=entity.admin) form = EstimateModelCreateForm(entity_slug=entity.slug, user_model=entity.admin)
form.fields["customer"].queryset = entity.get_customers().filter(active=True)
car_list = models.Car.objects.filter( car_list = models.Car.objects.filter(
dealer=dealer, finances__selling_price__gt=0 dealer=dealer, finances__selling_price__gt=0
).exclude(status="reserved") ).exclude(status="reserved")
@ -2243,7 +2365,7 @@ def create_estimate(request):
for x in car_list for x in car_list
], ],
} }
print(context)
return render(request, "sales/estimates/estimate_form.html", context) return render(request, "sales/estimates/estimate_form.html", context)
@ -2269,6 +2391,24 @@ class EstimateDetailView(LoginRequiredMixin, DetailView):
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
def create_sale_order(request, pk):
estimate = get_object_or_404(EstimateModel, pk=pk)
items = estimate.get_itemtxs_data()[0].all()
if request.method == "POST":
form = forms.SaleOrderForm(request.POST)
if form.is_valid():
form.save()
return redirect("success")
else:
form = forms.SaleOrderForm()
return render(
request,
"sales/estimates/sale_order.html",
{"form": form, "estimate": estimate, "items": items},
)
class PaymentRequest(LoginRequiredMixin, DetailView): class PaymentRequest(LoginRequiredMixin, DetailView):
model = EstimateModel model = EstimateModel
template_name = "sales/estimates/payment_request_detail.html" template_name = "sales/estimates/payment_request_detail.html"
@ -2297,7 +2437,7 @@ class EstimatePreviewView(LoginRequiredMixin, DetailView):
kwargs["total"] = data["grand_total"] kwargs["total"] = data["grand_total"]
kwargs["discount_amount"] = data["discount_amount"] kwargs["discount_amount"] = data["discount_amount"]
kwargs["vat"] = data["vat"] kwargs["vat"] = data["vat"]
kwargs["car_and_item_info"] = data["car_and_item_info"] # kwargs["car_and_item_info"] = data["car_and_item_info"]
kwargs["additional_services"] = data["additional_services"] kwargs["additional_services"] = data["additional_services"]
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
@ -2558,7 +2698,6 @@ class InvoicePreviewView(LoginRequiredMixin, DetailView):
def PaymentCreateView(request, pk): def PaymentCreateView(request, pk):
print(pk)
invoice = InvoiceModel.objects.filter(pk=pk).first() invoice = InvoiceModel.objects.filter(pk=pk).first()
bill = BillModel.objects.filter(pk=pk).first() bill = BillModel.objects.filter(pk=pk).first()
model = invoice if invoice else bill model = invoice if invoice else bill
@ -2572,36 +2711,37 @@ def PaymentCreateView(request, pk):
invoice = form.cleaned_data.get("invoice") invoice = form.cleaned_data.get("invoice")
bill = form.cleaned_data.get("bill") bill = form.cleaned_data.get("bill")
payment_method = form.cleaned_data.get("payment_method") payment_method = form.cleaned_data.get("payment_method")
redirect_url = 'invoice_detail' if invoice else 'bill_detail' redirect_url = "invoice_detail" if invoice else "bill_detail"
model = invoice if invoice else bill model = invoice if invoice else bill
if not model.is_approved(): if not model.is_approved():
model.mark_as_approved(user_model=entity.admin) model.mark_as_approved(user_model=entity.admin)
try: try:
if invoice: if invoice:
set_invoice_payment(dealer,entity,invoice,amount,payment_method) set_invoice_payment(dealer, entity, invoice, amount, payment_method)
elif bill: elif bill:
set_bill_payment(dealer,entity,bill,amount,payment_method) set_bill_payment(dealer, entity, bill, amount, payment_method)
messages.success(request, "Payment created successfully!") messages.success(request, "Payment created successfully!")
return redirect(redirect_url, pk=model.pk) return redirect(redirect_url, pk=model.pk)
except Exception as e: except Exception as e:
messages.error(request, f"Error creating payment: {str(e)}") messages.error(request, f"Error creating payment: {str(e)}")
else: else:
messages.error(request, f"Invalid form data: {str(form.errors)}") messages.error(request, f"Invalid form data: {str(form.errors)}")
# return redirect(redirect_url, pk=model.pk) # return redirect(redirect_url, pk=model.pk)
form = forms.PaymentForm() form = forms.PaymentForm()
if model: if model:
form.initial["amount"] = model.amount_due - model.amount_paid form.initial["amount"] = model.amount_due - model.amount_paid
if isinstance(model, InvoiceModel): if isinstance(model, InvoiceModel):
form.initial["invoice"] = model form.initial["invoice"] = model
form.fields['bill'].widget = HiddenInput() form.fields["bill"].widget = HiddenInput()
elif isinstance(model, BillModel): elif isinstance(model, BillModel):
form.initial["bill"] = model form.initial["bill"] = model
form.fields['invoice'].widget = HiddenInput() form.fields["invoice"].widget = HiddenInput()
return render( return render(
request, "sales/payments/payment_form.html", {"model": model, "form": form} request, "sales/payments/payment_form.html", {"model": model, "form": form}
) )
def PaymentListView(request): def PaymentListView(request):
dealer = get_user_type(request) dealer = get_user_type(request)
@ -2874,7 +3014,7 @@ def fetch_notifications(request):
# for notification in notifications # for notification in notifications
# ] # ]
# return JsonResponse({"notifications": notifications_data}) # return JsonResponse({"notifications": notifications_data})
return render(request,'notifications.html',{'notifications_':notifications}) return render(request, "notifications.html", {"notifications_": notifications})
class ItemServiceCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): class ItemServiceCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
@ -2973,19 +3113,23 @@ class BillListView(ListView):
model = BillModel model = BillModel
template_name = "ledger/bills/bill_list.html" template_name = "ledger/bills/bill_list.html"
context_object_name = "bills" context_object_name = "bills"
def get_queryset(self): def get_queryset(self):
dealer = get_user_type(self.request) dealer = get_user_type(self.request)
qs = dealer.entity.get_bills() qs = dealer.entity.get_bills()
return qs return qs
class BillDetailView(LoginRequiredMixin, DetailView): class BillDetailView(LoginRequiredMixin, DetailView):
model = BillModel model = BillModel
template_name = "ledger/bills/bill_detail.html" template_name = "ledger/bills/bill_detail.html"
context_object_name = "bill" context_object_name = "bill"
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
bill = kwargs.get("object") bill = kwargs.get("object")
if bill.get_itemtxs_data(): if bill.get_itemtxs_data():
txs = bill.get_itemtxs_data()[0] txs = bill.get_itemtxs_data()[0]
car_and_item_info = [ car_and_item_info = [
{ {
"car": models.Car.objects.get(vin=x.item_model.name), "car": models.Car.objects.get(vin=x.item_model.name),
@ -3004,49 +3148,58 @@ class BillDetailView(LoginRequiredMixin, DetailView):
* Decimal(x.quantity) * Decimal(x.quantity)
for x in txs for x in txs
) )
vat = models.VatRate.objects.filter(is_active=True).first()
if vat:
grand_total += round(Decimal(grand_total) * Decimal(vat.rate), 2)
kwargs["car_and_item_info"] = car_and_item_info kwargs["car_and_item_info"] = car_and_item_info
kwargs["grand_total"] = grand_total kwargs["grand_total"] = grand_total
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
class InReviewBillView(LoginRequiredMixin, UpdateView): class InReviewBillView(LoginRequiredMixin, UpdateView):
model = BillModel model = BillModel
form_class = InReviewBillModelUpdateForm form_class = InReviewBillModelUpdateForm
template_name = "ledger/bills/bill_update_form.html" template_name = "ledger/bills/bill_update_form.html"
success_url = reverse_lazy("bill_list") success_url = reverse_lazy("bill_list")
success_message = _("Bill updated successfully.") success_message = _("Bill updated successfully.")
context_object_name = "bill" context_object_name = "bill"
def get_form_kwargs(self): def get_form_kwargs(self):
kwargs = super().get_form_kwargs() kwargs = super().get_form_kwargs()
dealer = get_user_type(self.request) dealer = get_user_type(self.request)
kwargs["entity_model"] = dealer.entity kwargs["entity_model"] = dealer.entity
kwargs["user_model"] = dealer.entity.admin kwargs["user_model"] = dealer.entity.admin
return kwargs return kwargs
def get_success_url(self): def get_success_url(self):
return reverse_lazy("bill_detail", kwargs={"pk": self.kwargs["pk"]}) return reverse_lazy("bill_detail", kwargs={"pk": self.kwargs["pk"]})
def form_valid(self, form): def form_valid(self, form):
dealer = get_user_type(self.request) dealer = get_user_type(self.request)
form.instance.entity = dealer.entity form.instance.entity = dealer.entity
self.object.mark_as_review() self.object.mark_as_review()
return super().form_valid(form) return super().form_valid(form)
class ApprovedBillModelView(LoginRequiredMixin, UpdateView): class ApprovedBillModelView(LoginRequiredMixin, UpdateView):
model = BillModel model = BillModel
form_class = ApprovedBillModelUpdateForm form_class = ApprovedBillModelUpdateForm
template_name = "ledger/bills/bill_update_form.html" template_name = "ledger/bills/bill_update_form.html"
success_url = reverse_lazy("bill_list") success_url = reverse_lazy("bill_list")
success_message = _("Bill updated successfully.") success_message = _("Bill updated successfully.")
context_object_name = "bill" context_object_name = "bill"
def get_form_kwargs(self): def get_form_kwargs(self):
kwargs = super().get_form_kwargs() kwargs = super().get_form_kwargs()
dealer = get_user_type(self.request) dealer = get_user_type(self.request)
kwargs["entity_model"] = dealer.entity kwargs["entity_model"] = dealer.entity
kwargs["user_model"] = dealer.entity.admin kwargs["user_model"] = dealer.entity.admin
return kwargs return kwargs
def get_success_url(self): def get_success_url(self):
return reverse_lazy("bill_detail", kwargs={"pk": self.kwargs["pk"]}) return reverse_lazy("bill_detail", kwargs={"pk": self.kwargs["pk"]})
def form_valid(self, form): def form_valid(self, form):
dealer = get_user_type(self.request) dealer = get_user_type(self.request)
form.instance.entity = dealer.entity form.instance.entity = dealer.entity
@ -3054,25 +3207,27 @@ class ApprovedBillModelView(LoginRequiredMixin, UpdateView):
self.object.mark_as_approved(user_model=dealer.entity.admin) self.object.mark_as_approved(user_model=dealer.entity.admin)
return super().form_valid(form) return super().form_valid(form)
def bill_mark_as_approved(request,pk):
bill = get_object_or_404(BillModel,pk=pk) def bill_mark_as_approved(request, pk):
if request.method == "POST": bill = get_object_or_404(BillModel, pk=pk)
dealer = get_user_type(request) if request.method == "POST":
dealer = get_user_type(request)
if bill.is_approved(): if bill.is_approved():
messages.error(request, _("Bill is already approved.")) messages.error(request, _("Bill is already approved."))
return redirect("bill_detail",pk=bill.pk) return redirect("bill_detail", pk=bill.pk)
bill.mark_as_approved(user_model=dealer.entity.admin) bill.mark_as_approved(user_model=dealer.entity.admin)
bill.save() bill.save()
messages.success(request, _("Bill marked as approved successfully.")) messages.success(request, _("Bill marked as approved successfully."))
return redirect("bill_detail",pk=bill.pk) return redirect("bill_detail", pk=bill.pk)
def bill_mark_as_paid(request,pk):
bill = get_object_or_404(BillModel,pk=pk) def bill_mark_as_paid(request, pk):
if request.method == "POST": bill = get_object_or_404(BillModel, pk=pk)
dealer = get_user_type(request) if request.method == "POST":
dealer = get_user_type(request)
if bill.is_paid(): if bill.is_paid():
messages.error(request, _("Bill is already paid.")) messages.error(request, _("Bill is already paid."))
return redirect("bill_detail",pk=bill.pk) return redirect("bill_detail", pk=bill.pk)
if bill.amount_due == bill.amount_paid: if bill.amount_due == bill.amount_paid:
bill.mark_as_paid(user_model=dealer.entity.admin) bill.mark_as_paid(user_model=dealer.entity.admin)
bill.save() bill.save()
@ -3082,16 +3237,18 @@ def bill_mark_as_paid(request,pk):
bill.ledger.save() bill.ledger.save()
messages.success(request, _("Bill marked as paid successfully.")) messages.success(request, _("Bill marked as paid successfully."))
else: else:
messages.error(request, _("Amount paid is not equal to amount due.")) messages.error(request, _("Amount paid is not equal to amount due."))
return redirect("bill_detail",pk=bill.pk) return redirect("bill_detail", pk=bill.pk)
# def get_context_data(self, **kwargs): # def get_context_data(self, **kwargs):
# dealer = get_user_type(self.request) # dealer = get_user_type(self.request)
# context = super().get_context_data(**kwargs) # context = super().get_context_data(**kwargs)
# context['entity_model'] = dealer.entity # context['entity_model'] = dealer.entity
# context['user_model'] = dealer.entity.admin # context['user_model'] = dealer.entity.admin
# return context # return context
# class BillCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): # class BillCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
# model = BillModel # model = BillModel
# form_class = BillModelCreateForm # form_class = BillModelCreateForm
@ -3104,6 +3261,7 @@ def bill_mark_as_paid(request,pk):
# kwargs["entity_model"] = dealer.entity # kwargs["entity_model"] = dealer.entity
# return kwargs # return kwargs
# def form_valid(self, form): # def form_valid(self, form):
# dealer = get_user_type(self.request) # dealer = get_user_type(self.request)
# form.instance.entity = dealer.entity # form.instance.entity = dealer.entity
@ -3117,7 +3275,7 @@ def bill_create(request):
dealer = get_user_type(request) dealer = get_user_type(request)
entity = dealer.entity entity = dealer.entity
if request.method == "POST": if request.method == "POST":
data = json.loads(request.body) data = json.loads(request.body)
vendor_id = data.get("vendor") vendor_id = data.get("vendor")
terms = data.get("terms") terms = data.get("terms")
@ -3217,14 +3375,16 @@ def bill_create(request):
), ),
} }
) )
car_list = models.Car.objects.filter(dealer=dealer) car_list = models.Car.objects.filter(
dealer=dealer, finances__selling_price__gt=0,status="available"
)
context = { context = {
"form": form, "form": form,
"items": [ "items": [
{ {
"car": x, "car": x,
"product": entity.get_items_all() "product": entity.get_items_products()
.filter(item_role=ItemModel.ITEM_ROLE_PRODUCT, name=x.vin) .filter(name=x.vin)
.first(), .first(),
} }
for x in car_list for x in car_list
@ -3233,6 +3393,7 @@ def bill_create(request):
return render(request, "ledger/bills/bill_form.html", context) return render(request, "ledger/bills/bill_form.html", context)
def BillDeleteView(request, pk): def BillDeleteView(request, pk):
bill = get_object_or_404(BillModel, pk=pk) bill = get_object_or_404(BillModel, pk=pk)
bill.delete() bill.delete()

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -13,7 +13,7 @@
<div class="me-3 flex-1 mt-2"> <div class="me-3 flex-1 mt-2">
<h4 class="fs-9 text-body-emphasis">{{ _("System")}}:</h4> <h4 class="fs-9 text-body-emphasis">{{ _("System")}}:</h4>
{% if not notification.is_read %} {% if not notification.is_read %}
<p class="fs-9 text-body-highlight"><span class="far fa-envelope text-success-dark fs-8 me-1"></span><span class="me-1">{{ notification.message }}</span> <span class="ms-2 text-body-tertiary text-opacity-85 fw-bold fs-10 text-end">{{ notification.created|timesince }}</span></p> <p class="fs-9 text-body-highlight"><span class="far fa-envelope text-success-dark fs-8 me-1"></span><span class="me-1">{{ notification.message|safe }}</span> <span class="ms-2 text-body-tertiary text-opacity-85 fw-bold fs-10 text-end">{{ notification.created|timesince }}</span></p>
{% else %} {% else %}
<p class="fs-9 text-body-highlight"><span class="far fa-envelope-open text-danger-dark fs-8 me-1"></span><span>{{ notification.message|safe }}</span> <span class="ms-2 text-body-tertiary text-opacity-85 fw-bold fs-10 text-end">{{ notification.created|timesince }}</span></p> <p class="fs-9 text-body-highlight"><span class="far fa-envelope-open text-danger-dark fs-8 me-1"></span><span>{{ notification.message|safe }}</span> <span class="ms-2 text-body-tertiary text-opacity-85 fw-bold fs-10 text-end">{{ notification.created|timesince }}</span></p>
{% endif %} {% endif %}

View File

@ -24,93 +24,9 @@
<div class="col-sm-6 col-md-8"> <div class="col-sm-6 col-md-8">
<form method="post" class="form row g-3 needs-validation" novalidate> <form method="post" class="form row g-3 needs-validation" novalidate>
{% csrf_token %} {% csrf_token %}
<div class="col-sm-6 col-md-4"> {{ form|crispy }}
<div class="form-floating"> <div class="col-12">
<input type="text" class="form-control" id="{{ form.first_name.id_for_label }}" name="{{ form.first_name.name }}" value="" placeholder="{{ _("First Name")}}" required> <button class="btn btn-primary" type="submit">{% trans "Save" %}</button>
<label for="{{ form.first_name.id_for_label }}">{{ _("First Name")}}</label>
<div class="invalid-feedback mt-0">{{ form.first_name.errors|striptags }}</div>
</div>
</div>
<div class="col-sm-6 col-md-4">
<div class="form-floating">
<input type="text" class="form-control" id="{{ form.middle_name.id_for_label }}" name="{{ form.middle_name.name }}" value="" placeholder="{{ _("Middle Name")}}" required>
<label for="{{ form.middle_name.id_for_label }}">{{ _("Middle Name")}}</label>
<div class="invalid-feedback mt-0">{{ form.middle_name.errors }}</div>
</div>
</div>
<div class="col-sm-6 col-md-4">
<div class="form-floating">
<input type="text" class="form-control" id="{{ form.last_name.id_for_label }}" name="{{ form.last_name.name }}" value="" placeholder="{{ _("Last Name")}}" required>
<label for="{{ form.last_name.id_for_label }}">{{ _("Last Name")}}</label>
<div class="invalid-feedback mt-0">{{ form.last_name.errors }}</div>
</div>
</div>
<div class="col-sm-6 col-md-2">
<div class="form-floating">
<select class="form-control" id="{{ form.title.id_for_label }}" name="{{ form.title.name }}">
{% for value, label in form.title.field.choices %}
<option value="{{ value }}" {% if form.title.value == value %}selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
<label for="{{ form.title.id_for_label }}">Title</label>
{{ form.title.errors }}
</div>
</div>
<div class="col-sm-6 col-md-2">
<div class="form-floating">
<select class="form-control" id="{{ form.gender.id_for_label }}" name="{{ form.gender.name }}">
{% for value, label in form.gender.field.choices %}
<option value="{{ value }}" {% if form.gender.value == value %}selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
<label for="{{ form.gender.id_for_label }}">{{ _("Gender") }}</label>
{{ form.gender.errors }}
</div>
</div>
<div class="col-sm-6 col-md-4">
<div class="form-floating">
<input type="date" class="form-control" id="{{ form.dob.id_for_label }}" name="{{ form.dob.name }}" value="{{ form.dob.value|date:'Y-m-d' }}">
<label for="{{ form.dob.id_for_label }}">{{ _("Date of Birth")}}</label>
{{ form.dob.errors }}
</div>
</div>
<div class="col-sm-6 col-md-4">
<div class="form-floating">
<input type="email" class="form-control" id="{{ form.email.id_for_label }}" name="{{ form.email.name }}" value="" placeholder="{{ _("Email") }}" required>
<label for="{{ form.email.id_for_label }}">{{ _("Email") }}</label>
{{ form.email.errors }}
</div>
</div>
<div class="col-sm-6 col-md-4">
<div class="form-floating">
<input type="text" class="form-control" id="{{ form.national_id.id_for_label }}" name="{{ form.national_id.name }}" value="" placeholder="{{ _("Enter national ID")}}" required>
<label for="{{ form.national_id.id_for_label }}">{{ _("National ID")}}</label>
{{ form.national_id.errors }}
</div>
</div>
<div class="col-sm-6 col-md-4">
<div class="form-floating">
<input type="tel" class="form-control" id="{{ form.phone_number.id_for_label }}" name="{{ form.phone_number.name }}" value="{{ form.phone_number.value|default:'' }}" placeholder="phone">
<i class="{{ country.flag_css }}"></i>
<label for="{{ form.phone_number.id_for_label }}">{{ _("Phone Number")}}</label>
{{ form.phone_number.errors }}
</div>
</div>
<div class="col-sm-6 col-md-4">
<div class="form-floating">
<textarea class="form-control" id="{{ form.address.id_for_label }}" name="{{ form.address.name }}" placeholder="Enter address">{{ form.address.value|default:'' }}</textarea>
<label for="{{ form.address.id_for_label }}">{{ _("Address") }}</label>
{{ form.address.errors }}
</div>
</div>
<div class="col-sm-6 col-md-4">
<div class="d-flex mb-3">
<button class="btn btn-primary me-2 px-6" type="submit">
{{ _("Save") }}
</button>
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-phoenix-primary">{% trans "cancel"|capfirst %}</a>
</div>
</div> </div>
</form> </form>
</div> </div>

View File

@ -92,9 +92,9 @@
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal"> <button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">
{% trans "No" %} {% trans "No" %}
</button> </button>
<a type="button" class="btn btn-danger btn-sm" href="{% url 'customer_delete' customer.id %}"> {% comment %} <a type="button" class="btn btn-danger btn-sm" href="{% url 'customer_delete' customer.id %}">
{% trans "Yes" %} {% trans "Yes" %}
</a> </a> {% endcomment %}
</div> </div>
</div> </div>
@ -106,23 +106,23 @@
</td> </td>
<td class="name align-middle white-space-nowrap ps-0"> <td class="name align-middle white-space-nowrap ps-0">
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<div><a class="fs-8 fw-bold" href="{% url 'customer_detail' customer.id %}">{{ customer.first_name }} {{ customer.middle_name }} {{ customer.last_name }}</a> <div><a class="fs-8 fw-bold" href="{% url 'customer_detail' customer.pk %}">{{ customer.customer_name }}</a>
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
</div> </div>
</div> </div>
</div> </div>
</td> </td>
<td class="email align-middle white-space-nowrap fw-semibold ps-4 border-end border-translucent"><a class="text-body-highlight" href="">{{ customer.email }}</a></td> <td class="email align-middle white-space-nowrap fw-semibold ps-4 border-end border-translucent"><a class="text-body-highlight" href="">{{ customer.email }}</a></td>
<td class="phone align-middle white-space-nowrap fw-semibold ps-4 border-end border-translucent"><a class="text-body-highlight" href="tel:{{ customer.phone_number }}">{{ customer.phone_number }}</a></td> <td class="phone align-middle white-space-nowrap fw-semibold ps-4 border-end border-translucent"><a class="text-body-highlight" href="tel:{{ customer.phone }}">{{ customer.phone }}</a></td>
<td class="contact align-middle white-space-nowrap ps-4 border-end border-translucent fw-semibold text-body-highlight">{{ customer.national_id }}</td> <td class="contact align-middle white-space-nowrap ps-4 border-end border-translucent fw-semibold text-body-highlight">{{ customer.additional_info.customer_info.national_id }}</td>
<td class="company align-middle white-space-nowrap text-body-tertiary text-opacity-85 ps-4 border-end border-translucent fw-semibold text-body-highlight"> <td class="company align-middle white-space-nowrap text-body-tertiary text-opacity-85 ps-4 border-end border-translucent fw-semibold text-body-highlight">
{{ customer.address }}</td> {{ customer.address_1 }}</td>
<td class="date align-middle white-space-nowrap text-body-tertiary text-opacity-85 ps-4 text-body-tertiary">{{ customer.created|date }}</td> <td class="date align-middle white-space-nowrap text-body-tertiary text-opacity-85 ps-4 text-body-tertiary">{{ customer.created|date }}</td>
<td class="align-middle white-space-nowrap text-end pe-0 ps-4"> <td class="align-middle white-space-nowrap text-end pe-0 ps-4">
<div class="btn-reveal-trigger position-static"> <div class="btn-reveal-trigger position-static">
<button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button> <button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button>
<div class="dropdown-menu dropdown-menu-end py-2"> <div class="dropdown-menu dropdown-menu-end py-2">
<a href="{% url 'customer_update' customer.id %}" class="dropdown-item text-success-dark">{% trans "Edit" %}</a> <a href="{% url 'customer_update' customer.pk %}" class="dropdown-item text-success-dark">{% trans "Edit" %}</a>
<div class="dropdown-divider"></div><button class="dropdown-item text-danger" data-bs-toggle="modal" data-bs-target="#deleteModal">{% trans "Delete" %}</button> <div class="dropdown-divider"></div><button class="dropdown-item text-danger" data-bs-toggle="modal" data-bs-target="#deleteModal">{% trans "Delete" %}</button>
</div> </div>
</div> </div>

View File

@ -27,7 +27,7 @@
</button> </button>
<a type="button" <a type="button"
class="btn btn-sm btn-danger" class="btn btn-sm btn-danger"
href="{% url 'customer_delete' customer.id %}"> href="{% url 'customer_delete' customer.pk %}">
{% trans 'Yes' %} {% trans 'Yes' %}
</a> </a>
</div> </div>

View File

@ -98,7 +98,7 @@
<td> <td>
{% if car.finances and not car.get_transfer %} {% if car.finances and not car.get_transfer %}
{% if car.location %} {% if car.location.is_owner_showroom %} {% trans 'Our Showroom' %} {% else %} {{ car.location.showroom.get_local_name }} {% endif %} {% if car.location %} {% if car.location.is_owner_showroom %} {% trans 'Our Showroom' %} {% else %} {{ car.location.showroom.get_local_name }} {% endif %}
<a href="{% url 'transfer' car.location.pk %}" class="btn btn-phoenix-danger btn-sm"> <a href="{% url 'transfer' car.pk %}" class="btn btn-phoenix-danger btn-sm">
{% trans "transfer"|capfirst %} {% trans "transfer"|capfirst %}
</a> </a>
{% else %} {% trans "No location available." %} {% else %} {% trans "No location available." %}
@ -312,6 +312,11 @@
<a class="btn btn-sm btn-phoenix-success" href="{% url 'transfer_detail' car.get_transfer.pk %}">Approve</a> <a class="btn btn-sm btn-phoenix-success" href="{% url 'transfer_detail' car.get_transfer.pk %}">Approve</a>
{% endif %} {% endif %}
</td> </td>
<td>
{% if car.get_transfer.status == "draft" %}
<a class="btn btn-sm btn-phoenix-success" href="{% url 'transfer_detail' car.get_transfer.pk %}?action=cancel">Cancel</a>
{% endif %}
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@ -8,6 +8,7 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<!-- Approve Modal -->
<div class="modal fade" id="approveCardModal" tabindex="-1" aria-labelledby="approveModalLabel" aria-hidden="true"> <div class="modal fade" id="approveCardModal" tabindex="-1" aria-labelledby="approveModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm"> <div class="modal-dialog modal-sm">
<div class="modal-content"> <div class="modal-content">
@ -28,6 +29,24 @@
</div> </div>
</div> </div>
</div> </div>
<!-- Cancel Modal -->
<div class="modal fade" id="cancelCardModal" tabindex="-1" aria-labelledby="cancelModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header bg-primary">
<h5 class="modal-title text-light" id="cancelModalLabel">{% trans 'Car Transfer Cancel' %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
{% trans 'Are you sure?' %}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-sm btn-phoenix-danger" data-bs-dismiss="modal">{% trans 'No' %}</button>
<a href="{% url 'transfer_confirm' transfer.car.pk transfer.pk %}?action=cancel" type="button" type="submit" class="btn btn-phoenix-success btn-sm">{% trans 'Confirm' %}</a>
</div>
</div>
</div>
</div>
<div class="row p-4"> <div class="row p-4">
<div class="row g-1"> <div class="row g-1">
@ -37,8 +56,11 @@
<div class="btn-group"> <div class="btn-group">
<button type="button" class="btn btn-sm btn-phoenix-success" data-bs-toggle="modal" data-bs-target="#approveCardModal"> <button type="button" class="btn btn-sm btn-phoenix-success" data-bs-toggle="modal" data-bs-target="#approveCardModal">
{% trans 'Approve' %} {% trans 'Approve' %}
</button> </button>
<a href="{{ request.META.HTTP_REFERER }}" class="btn btn-sm btn-danger">{% trans 'Cancel' %}</a> <button type="button" class="btn btn-sm btn-phoenix-warning" data-bs-toggle="modal" data-bs-target="#cancelCardModal">
{% trans 'Cancel Transfer' %}
</button>
<a href="{{ request.META.HTTP_REFERER }}" class="btn btn-sm btn-info">{% trans 'Return' %}</a>
</div> </div>
</div> </div>
</div> </div>

View File

@ -278,8 +278,8 @@
<tr> <tr>
<td>{{ transfer.car }}</td> <td>{{ transfer.car }}</td>
<td class="text-center">{{ transfer.quantity }}</td> <td class="text-center">{{ transfer.quantity }}</td>
<td class="text-center">{{ transfer.car.finances.cost_price }}</td> <td class="text-center">{{ transfer.car.finances.selling_price }}</td>
<td class="text-center">{{ transfer.total_price }}</td> <td class="text-center">{{ transfer.total_price }}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@ -226,10 +226,16 @@
<td class="align-middle text-body-tertiary fw-semibold">{{item.total}}</td> <td class="align-middle text-body-tertiary fw-semibold">{{item.total}}</td>
</tr> </tr>
{% endfor %} {% endfor %}
<tr class="bg-body-secondary total-sum">
<td class="align-middle ps-4 fw-bolder text-body-highlight" colspan="4">{% trans "Vat Amount" %}</td>
<td class="align-middle text-start fw-bolder">
<span id="grand-total">{{bill.additional_info.car_finance.vat_amount}}</span>
</td>
</tr>
<tr class="bg-body-secondary total-sum"> <tr class="bg-body-secondary total-sum">
<td class="align-middle ps-4 fw-bolder text-body-highlight" colspan="4">{% trans "Grand Total" %}</td> <td class="align-middle ps-4 fw-bolder text-body-highlight" colspan="4">{% trans "Grand Total" %}</td>
<td class="align-middle text-start fw-bolder"> <td class="align-middle text-start fw-bolder">
<span id="grand-total">{{grand_total}}</span> <span id="grand-total">{{bill.additional_info.car_finance.total_vat}}</span>
</td> </td>
</tr> </tr>
</tbody> </tbody>

View File

@ -75,12 +75,17 @@
<tr> <tr>
<td class="align-middle ps-3">{{ bill.bill_number }}</td> <td class="align-middle ps-3">{{ bill.bill_number }}</td>
<td class="align-middle"> <td class="align-middle">
{% if bill.bill.status == 'draft' %} {% if bill.is_draft %}
<span class="badge badge-phoenix badge-phoenix-warning"> <span class="badge badge-phoenix badge-phoenix-warning">
{% elif bill.is_review %}
<span class="badge badge-phoenix badge-phoenix-info">
{% elif bill.is_approved %}
<span class="badge badge-phoenix badge-phoenix-success">
{% elif bill.is_paid %}
<span class="badge badge-phoenix badge-phoenix-success">
{% endif %}
{{ bill.bill_status }} {{ bill.bill_status }}
</span> </span>
{% endif %}
</td> </td>
<td class="align-middle text-end py-3 pe-3"> <td class="align-middle text-end py-3 pe-3">
{{bill.vendor.vendor_name}} {{bill.vendor.vendor_name}}

View File

@ -1,5 +1,5 @@
<li class="nav-item dropdown"> <li class="nav-item dropdown">
<div class="notification-count" hx-get="{% url 'fetch_notifications' %}" hx-trigger="every 10s" hx-swap="innerHTML" hx-select=".notification-count"> <div class="notification-count" hx-get="{% url 'fetch_notifications' %}" hx-trigger="every 20s" hx-swap="innerHTML" hx-select=".notification-count">
{% if notifications_ %} {% if notifications_ %}
<span class="badge bg-danger rounded-pill " id="notification-counter" style="position: absolute; top: 8px; right: 3px; font-size: 0.50rem;">{{ notifications_.count }}</span> <span class="badge bg-danger rounded-pill " id="notification-counter" style="position: absolute; top: 8px; right: 3px; font-size: 0.50rem;">{{ notifications_.count }}</span>
{% endif %} {% endif %}

View File

@ -96,7 +96,7 @@
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal"> <button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">
{% trans "No" %} {% trans "No" %}
</button> </button>
<a type="button" class="btn btn-danger btn-sm" href="{% url 'organization_delete' org.id %}"> <a type="button" class="btn btn-danger btn-sm" href="{% url 'organization_delete' org.pk %}">
{% trans "Yes" %} {% trans "Yes" %}
</a> </a>
</div> </div>
@ -108,23 +108,23 @@
<td class="name align-middle white-space-nowrap ps-0"> <td class="name align-middle white-space-nowrap ps-0">
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<div><a class="fs-8 fw-bold" href="{% url 'organization_detail' org.id %}">{{ org.name }}</a> <div><a class="fs-8 fw-bold" href="{% url 'organization_detail' org.pk %}">{{ org.name }}</a>
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<p class="mb-0 text-body-highlight fw-semibold fs-9 me-2">{{ org.arabic_name }}</p><span class="badge badge-phoenix badge-phoenix-primary">{{ org.id}}</span> <p class="mb-0 text-body-highlight fw-semibold fs-9 me-2">{{ org.arabic_name }}</p><span class="badge badge-phoenix badge-phoenix-primary">{{ org.pk}}</span>
</div> </div>
</div> </div>
</div> </div>
</td> </td>
<td class="email align-middle white-space-nowrap fw-semibold ps-4 border-end border-translucent">{{ org.crn }}</td> <td class="email align-middle white-space-nowrap fw-semibold ps-4 border-end border-translucent">{{ org.additional_info.organization_info.crn }}</td>
<td class="phone align-middle white-space-nowrap fw-semibold ps-4 border-end border-translucent">{{ org.vrn }}</td> <td class="phone align-middle white-space-nowrap fw-semibold ps-4 border-end border-translucent">{{ org.additional_info.organization_info.vrn }}</td>
<td class="phone align-middle white-space-nowrap ps-4 border-end border-translucent fw-semibold text-body-highlight"><a class="text-body-highlight" href="tel:{{ org.phone_number }}">{{ org.phone_number }}</a></td> <td class="phone align-middle white-space-nowrap ps-4 border-end border-translucent fw-semibold text-body-highlight"><a class="text-body-highlight" href="tel:{{ org.phone }}">{{ org.phone }}</a></td>
<td class="company align-middle white-space-nowrap text-body-tertiary text-opacity-85 ps-4 border-end border-translucent fw-semibold text-body-highlight"> <td class="company align-middle white-space-nowrap text-body-tertiary text-opacity-85 ps-4 border-end border-translucent fw-semibold text-body-highlight">
{{ org.address }}</td> {{ org.address_1 }}</td>
<td class="date align-middle white-space-nowrap text-body-tertiary text-opacity-85 ps-4 text-body-tertiary">{{ org.created_at|date }}</td> <td class="date align-middle white-space-nowrap text-body-tertiary text-opacity-85 ps-4 text-body-tertiary">{{ org.created|date }}</td>
<td class="align-middle white-space-nowrap text-end pe-0 ps-4"> <td class="align-middle white-space-nowrap text-end pe-0 ps-4">
<div class="btn-reveal-trigger position-static"> <div class="btn-reveal-trigger position-static">
<button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button> <button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button>
<div class="dropdown-menu dropdown-menu-end py-2"><a href="{% url 'organization_update' org.id %}" class="dropdown-item text-success-dark"> <div class="dropdown-menu dropdown-menu-end py-2"><a href="{% url 'organization_update' org.pk %}" class="dropdown-item text-success-dark">
{% trans "Edit" %} {% trans "Edit" %}
</a> </a>
<div class="dropdown-divider"></div><button class="dropdown-item text-danger" data-bs-toggle="modal" data-bs-target="#deleteModal">{% trans "Delete" %}</button> <div class="dropdown-divider"></div><button class="dropdown-item text-danger" data-bs-toggle="modal" data-bs-target="#deleteModal">{% trans "Delete" %}</button>

View File

@ -41,16 +41,13 @@
<div class="d-flex align-items-center gap-2"> <div class="d-flex align-items-center gap-2">
{% if estimate.status == 'draft' %} {% if estimate.status == 'draft' %}
<a href="{% url 'send_email' 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 Estimate' %}</span></a> <a href="{% url 'send_email' 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 Estimate' %}</span></a>
<button id="mark_as_sent_estimate" class="btn btn-phoenix-secondary" onclick="setFormAction('review')" data-bs-toggle="modal" data-bs-target="#confirmModal"><span class="d-none d-sm-inline-block">{% trans 'Mark As Sent' %}</span></button> <button id="mark_as_sent_estimate" class="btn btn-phoenix-secondary" onclick="setFormAction('review')" data-bs-toggle="modal" data-bs-target="#confirmModal"><span class="d-none d-sm-inline-block">{% trans 'Mark As Sent' %}</span></button>
{% endif %} {% elif estimate.status == 'in_review' %}
{% if estimate.status == 'in_review' %} <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">{% trans 'Mark As Accept' %}</span></button>
<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">{% trans 'Mark As Accept' %}</span></button> {% elif estimate.status == 'approved' %}
{% endif %} <a href="{% url 'invoice_create' estimate.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{% trans 'Create Invoice' %}</span></a>
{% if estimate.status == 'approved' %} {% elif estimate.status == 'in_review' %}
<a href="{% url 'invoice_create' estimate.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{% trans 'Create Invoice' %}</span></a> <a href="{% url 'estimate_preview' estimate.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{% trans 'Preview' %}</span></a>
{% endif %}
{% if estimate.status == 'in_review' %}
<a href="{% url 'estimate_preview' estimate.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{% trans 'Preview' %}</span></a>
{% endif %} {% endif %}
</div> </div>
</div> </div>
@ -103,6 +100,8 @@
<span class="badge text-bg-info">{% trans "In Review" %}</span> <span class="badge text-bg-info">{% trans "In Review" %}</span>
{% elif estimate.status == 'approved' %} {% elif estimate.status == 'approved' %}
<span class="badge text-bg-success">{% trans "Approved" %}</span> <span class="badge text-bg-success">{% trans "Approved" %}</span>
{% elif estimate.status == 'completed' %}
<span class="badge text-bg-success">{% trans "completed" %}</span>
{% elif estimate.status == 'canceled' %} {% elif estimate.status == 'canceled' %}
<span class="badge text-bg-danger">{% trans "canceled" %}</span> <span class="badge text-bg-danger">{% trans "canceled" %}</span>
{% endif %} {% endif %}
@ -129,9 +128,9 @@
{% for item in car_and_item_info %} {% for item in car_and_item_info %}
<tr> <tr>
<td class="">{{forloop.counter}}</td> <td class="">{{forloop.counter}}</td>
<td class="">{{item.car.id_car_model}}</td> <td class="">{{item.info.make}}</td>
<td class="align-middle">{{item.itemmodel.ce_quantity}}</td> <td class="align-middle">{{item.quantity}}</td>
<td class="align-middle ps-5">{{item.car.finances.selling_price}}</td> <td class="align-middle ps-5">{{item.finances.selling_price}}</td>
<td class="align-middle text-body-tertiary fw-semibold">{{item.total}}</td> <td class="align-middle text-body-tertiary fw-semibold">{{item.total}}</td>
</tr> </tr>
{% endfor %} {% endfor %}

View File

@ -279,10 +279,10 @@
<tbody> <tbody>
{% for item in car_and_item_info %} {% for item in car_and_item_info %}
<tr> <tr>
<td>{{ item.car.id_car_model }}</td> <td class="">{{item.info.make}}</td>
<td class="text-center">{{ item.itemmodel.ce_quantity }}</td> <td class="align-middle">{{item.quantity}}</td>
<td class="text-center">{{ item.car.finances.selling_price }}</td> <td class="align-middle ps-5">{{item.finances.selling_price}}</td>
<td class="highlight fw-semibold text-center">{{ item.total }}</td> <td class="align-middle text-body-tertiary fw-semibold">{{item.total}}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View File

@ -0,0 +1,359 @@
{% load static i18n %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sale Order</title>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Google Fonts - Roboto -->
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
<!-- Custom CSS -->
<style>
body {
background-color: #f8f9fa;
font-family: 'Roboto', sans-serif;
margin: 0;
padding: 0;
}
.estimate-row {
background: #fff;
border-radius: 12px;
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1);
padding: 3rem;
margin: 3rem auto;
max-width: 1000px;
}
.estimate-header {
border-bottom: 2px solid #dee2e6;
padding-bottom: 1.5rem;
margin-bottom: 2.5rem;
text-align: center;
}
.estimate-header h1 {
font-size: 2.5rem;
font-weight: 700;
color: #333;
margin-bottom: 0.5rem;
}
.estimate-header p {
font-size: 1.1rem;
color: #666;
font-weight: 400;
}
.estimate-details {
margin-bottom: 2.5rem;
}
.estimate-details p {
margin: 0.75rem 0;
color: #555;
font-size: 1rem;
font-weight: 400;
}
.estimate-table {
margin-bottom: 2.5rem;
}
.estimate-table th {
background-color: #f8f9fa;
font-weight: 500;
color: #333;
}
.estimate-table td {
color: #555;
font-weight: 400;
}
.additional-charges {
margin-top: 2rem;
margin-bottom: 1.5rem;
}
.additional-charges p {
margin: 0.5rem 0;
color: #555;
font-size: 1rem;
font-weight: 400;
}
.estimate-total {
text-align: right;
font-size: 1.5rem;
font-weight: 500;
color: #333;
margin-top: 1rem;
}
.footer-note {
text-align: center;
color: #777;
margin-top: 3rem;
font-size: 1rem;
font-weight: 400;
}
.logo {
max-width: 150px;
margin-bottom: 1.5rem;
}
.highlight {
color: #007bff;
font-weight: 500;
}
.btn-primary {
background-color: #007bff;
border-color: #007bff;
font-weight: 500;
padding: 0.75rem 1.5rem;
font-size: 1rem;
}
.btn-primary:hover {
background-color: #0056b3;
border-color: #0056b3;
}
.button-row {
display: flex;
justify-content: center;
gap: 10px;
margin-top: 2rem;
}
</style>
{% 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 %}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
</head>
<body>
{% if estimate.status != "in_review" %}
<main class="main" id="top">
<div class="px-3">
<div class="row min-vh-100 flex-center p-5">
<div class="col-12 col-xl-10 col-xxl-8">
<div class="row justify-content-center align-items-center g-5">
<div class="col-12 col-lg-6 text-center order-lg-1"><img class="img-fluid w-lg-100 d-dark-none" src="{% static 'images/spot-illustrations/404-illustration.png' %}" alt="" width="400" /><img class="img-fluid w-md-50 w-lg-100 d-light-none" src="../../assets/img/spot-illustrations/dark_404-illustration.png" alt="" width="540" /></div>
<div class="col-12 col-lg-6 text-center text-lg-start"><img class="img-fluid mb-6 w-50 w-lg-75 d-dark-none" src="{% static 'images/spot-illustrations/404.png' %}" alt="" /><img class="img-fluid mb-6 w-50 w-lg-75 d-light-none" src="../../assets/img/spot-illustrations/dark_404.png" alt="" />
<h2 class="text-body-secondary fw-bolder mb-3">Page Missing!</h2>
<p class="text-body mb-5">But no worries! Our ostrich is looking everywhere <br class="d-none d-sm-block" />
</div>
</div>
</div>
</div>
</div>
<script>
var navbarTopStyle = window.config.config.phoenixNavbarTopStyle;
var navbarTop = document.querySelector('.navbar-top');
if (navbarTopStyle === 'darker') {
navbarTop.setAttribute('data-navbar-appearance', 'darker');
}
var navbarVerticalStyle = window.config.config.phoenixNavbarVerticalStyle;
var navbarVertical = document.querySelector('.navbar-vertical');
if (navbarVertical && navbarVerticalStyle === 'darker') {
navbarVertical.setAttribute('data-navbar-appearance', 'darker');
}
</script>
<div class="support-chat-row">
<div class="row-fluid support-chat">
<div class="card bg-body-emphasis">
<div class="card-header d-flex flex-between-center px-4 py-3 border-bottom border-translucent">
<h5 class="mb-0 d-flex align-items-center gap-2">Demo widget<span class="fa-solid fa-circle text-success fs-11"></span></h5>
<div class="btn-reveal-trigger">
<button class="btn btn-link p-0 dropdown-toggle dropdown-caret-none transition-none d-flex" type="button" id="support-chat-dropdown" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h text-body"></span></button>
<div class="dropdown-menu dropdown-menu-end py-2" aria-labelledby="support-chat-dropdown"><a class="dropdown-item" href="#!">Request a callback</a><a class="dropdown-item" href="#!">Search in chat</a><a class="dropdown-item" href="#!">Show history</a><a class="dropdown-item" href="#!">Report to Admin</a><a class="dropdown-item btn-support-chat" href="#!">Close Support</a></div>
</div>
</div>
<div class="card-body chat p-0">
<div class="d-flex flex-column-reverse scrollbar h-100 p-3">
<div class="text-end mt-6"><a class="mb-2 d-inline-flex align-items-center text-decoration-none text-body-emphasis bg-body-hover rounded-pill border border-primary py-2 ps-4 pe-3" href="#!">
<p class="mb-0 fw-semibold fs-9">I need help with something</p><span class="fa-solid fa-paper-plane text-primary fs-9 ms-3"></span>
</a><a class="mb-2 d-inline-flex align-items-center text-decoration-none text-body-emphasis bg-body-hover rounded-pill border border-primary py-2 ps-4 pe-3" href="#!">
<p class="mb-0 fw-semibold fs-9">I cant reorder a product I previously ordered</p><span class="fa-solid fa-paper-plane text-primary fs-9 ms-3"></span>
</a><a class="mb-2 d-inline-flex align-items-center text-decoration-none text-body-emphasis bg-body-hover rounded-pill border border-primary py-2 ps-4 pe-3" href="#!">
<p class="mb-0 fw-semibold fs-9">How do I place an order?</p><span class="fa-solid fa-paper-plane text-primary fs-9 ms-3"></span>
</a><a class="false d-inline-flex align-items-center text-decoration-none text-body-emphasis bg-body-hover rounded-pill border border-primary py-2 ps-4 pe-3" href="#!">
<p class="mb-0 fw-semibold fs-9">My payment method not working</p><span class="fa-solid fa-paper-plane text-primary fs-9 ms-3"></span>
</a>
</div>
<div class="text-center mt-auto">
<div class="avatar avatar-3xl status-online"><img class="rounded-circle border border-3 border-light-subtle" src="../../assets/img/team/30.webp" alt="" /></div>
<h5 class="mt-2 mb-3">Eric</h5>
<p class="text-center text-body-emphasis mb-0">Ask us anything well get back to you here or by email within 24 hours.</p>
</div>
</div>
</div>
<div class="card-footer d-flex align-items-center gap-2 border-top border-translucent ps-3 pe-4 py-3">
<div class="d-flex align-items-center flex-1 gap-3 border border-translucent rounded-pill px-4">
<input class="form-control outline-none border-0 flex-1 fs-9 px-0" type="text" placeholder="Write message" />
<label class="btn btn-link d-flex p-0 text-body-quaternary fs-9 border-0" for="supportChatPhotos"><span class="fa-solid fa-image"></span></label>
<input class="d-none" type="file" accept="image/*" id="supportChatPhotos" />
<label class="btn btn-link d-flex p-0 text-body-quaternary fs-9 border-0" for="supportChatAttachment"> <span class="fa-solid fa-paperclip"></span></label>
<input class="d-none" type="file" id="supportChatAttachment" />
</div>
<button class="btn p-0 border-0 send-btn"><span class="fa-solid fa-paper-plane fs-9"></span></button>
</div>
</div>
</div>
<button class="btn btn-support-chat p-0 border border-translucent"><span class="fs-8 btn-text text-primary text-nowrap">Chat demo</span><span class="ping-icon-wrapper mt-n4 ms-n6 mt-sm-0 ms-sm-2 position-absolute position-sm-relative"><span class="ping-icon-bg"></span><span class="fa-solid fa-circle ping-icon"></span></span><span class="fa-solid fa-headset text-primary fs-8 d-sm-none"></span><span class="fa-solid fa-chevron-down text-primary fs-7"></span></button>
</div>
</main>
{% else%}
{% comment %} <div class="button-row">
<button id="download-pdf" class="btn btn-primary">
<i class="fas fa-download"></i> {% trans 'Download Estimate' %}
</button>
<button id="accept" class="btn btn-success" data-bs-toggle="modal" data-bs-target="#acceptModal">
<i class="fas fa-check-circle"></i> {% trans 'Accept Estimate' %}
</button>
<button id="reject" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#rejectModal">
<i class="fas fa-times-circle"></i> {% trans 'Reject Estimate' %}
</button>
</div> {% endcomment %}
<!-- Accept Modal -->
<div class="modal fade" id="acceptModal" tabindex="-1" aria-labelledby="acceptModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="acceptModalLabel">{% trans 'Accept Estimate' %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
{% trans 'Are you sure you want to accept this estimate?' %}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans 'Cancel' %}</button>
<a href="{% url 'estimate_mark_as' estimate.pk %}?mark=accepted" type="button" class="btn btn-success" id="confirmAccept">{% trans 'Accept' %}</a>
</div>
</div>
</div>
</div>
<!-- Reject Modal -->
<div class="modal fade" id="rejectModal" tabindex="-1" aria-labelledby="rejectModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="rejectModalLabel">{% trans 'Reject Estimate' %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
{% trans 'Are you sure you want to reject this estimate?' %}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans 'Cancel' %}</button>
<a href="{% url 'estimate_mark_as' estimate.pk %}?mark=rejected" type="button" class="btn btn-danger" id="confirmReject">{% trans 'Reject' %}</a>
</div>
</div>
</div>
</div>
<div class="estimate-row" id="estimate-content">
<!-- Header -->
<div class="estimate-header">
<svg width="101" height="24" viewBox="0 0 101 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- SVG Paths -->
</svg>
<h1 style="margin-top: 10px;"><b>{% trans "Sale Order" %}</b></h1>
<p>{% trans "Thank you for choosing us. We appreciate your business" %}</p>
</div>
<!-- Details -->
<div class="estimate-details">
{% comment %} <p><strong>{% trans "Estimate Number" %} :</strong> <span class="highlight">#{{estimate.estimate_number}}</span></p> {% endcomment %}
<p><strong>{% trans "Date" %} :</strong> {{estimate.date_in_review}}</p>
<p><strong>{% trans "Customer" %} :</strong> {{estimate.customer.customer_name}}</p>
<p><strong>{% trans "Email" %} :</strong> {{estimate.customer.email}}</p>
<p><strong>{% trans "Terms" %} :</strong> {{estimate.terms|title}}</p>
</div>
<!-- Items Table -->
<div class="estimate-table">
<table class="table table-bordered">
<thead>
<tr>
<th>{% trans "Make" %}</th>
<th>{% trans "Model" %}</th>
<th>{% trans "Year" %}</th>
<th>{% trans "VIN" %}</th>
<th class="text-center">{% trans "Quantity" %}</th>
<th class="text-center">{% trans "Unit Price" %}</th>
<th class="text-center">{% trans "Total" %}</th>
</tr>
</thead>
<tbody>
{% for item in items %}
<tr>
<td>{{ item.item_model.additional_info.car_info.make }}</td>
<td>{{ item.item_model.additional_info.car_info.model }}</td>
<td>{{ item.item_model.additional_info.car_info.year }}</td>
<td>{{ item.item_model.additional_info.car_info.vin }}</td>
<td class="text-center">{{ item.ce_quantity }}</td>
<td class="text-center">{{ item.item_model.additional_info.car_finance.selling_price }}</td>
<td class="highlight fw-semibold text-center">{{ item.total }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Additional Charges (VAT and Services) -->
<div class="additional-charges">
<p><strong>{% trans "VAT" %} ({{vat}}%):</strong> <span class="highlight">${{vat_amount}}</span></p>
<p><strong>{% trans "Additional Services" %}:</strong>
<br>
{% for service in additional_services %}
<span class="highlight">{{service.name}} - ${{service.price}}</span><br>
{% endfor %}
</p>
</div>
<!-- Total -->
<div class="estimate-total">
<p><strong>{%trans "Total Amount" %}:</strong> <span class="highlight">${{total}}</span></p>
</div>
<!-- Footer Note -->
<div class="footer-note">
<p>{% trans "If you have any questions, feel free to contact us at" %} <a href="mailto:support@example.com">support@example.com</a>.</p>
<p>{% trans "Thank you for your business" %}</p>
</div>
</div>
{% endif %}
<!-- Bootstrap JS (Optional) -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<!-- jsPDF Library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>
<script>
document.getElementById('download-pdf').addEventListener('click', function () {
const element = document.getElementById('estimate-content');
// Options for html2pdf.js
const options = {
margin: 0, // No margin
filename: 'estimate.pdf', // Name of the downloaded file
image: { type: 'jpeg', quality: 0.98 }, // Image quality
html2canvas: {
scale: 2, // Increase scale for better quality
scrollX: 0, // Ensure no horizontal scroll offset
scrollY: 0, // Ensure no vertical scroll offset
useCORS: true, // Enable CORS for external resources
},
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' } // PDF settings
};
// Generate and download the PDF
html2pdf().from(element).set(options).save();
});
document.getElementById('confirmAccept').addEventListener('click', function () {
// Handle the accept action here
$('#acceptModal').modal('hide');
});
document.getElementById('confirmReject').addEventListener('click', function () {
// Handle the reject action here
$('#rejectModal').modal('hide');
});
</script>
</body>
</html>

View File

@ -222,10 +222,10 @@
{% for item in car_and_item_info %} {% for item in car_and_item_info %}
<tr> <tr>
<td class="">{{forloop.counter}}</td> <td class="">{{forloop.counter}}</td>
<td class="">{{item.car.id_car_model}}</td> <td class="">{{item.info.make}}</td>
<td class="align-middle">{{item.itemmodel.ce_quantity}}</td> <td class="align-middle">{{item.quantity}}</td>
<td class="align-middle ps-5">{{item.car.finances.selling_price}}</td> <td class="align-middle ps-5">{{item.finances.selling_price}}</td>
<td class="align-middle text-body-tertiary fw-semibold">{{item.total}}</td> <td class="align-middle text-body-tertiary fw-semibold">{{item.total}}</td>
</tr> </tr>
{% endfor %} {% endfor %}
<tr class="bg-body-secondary total-sum"> <tr class="bg-body-secondary total-sum">

View File

@ -198,10 +198,10 @@
<tbody> <tbody>
{% for item in car_and_item_info %} {% for item in car_and_item_info %}
<tr> <tr>
<td>{{ item.car.id_car_model }}</td> <td class="">{{item.info.make}}</td>
<td>{{ item.itemmodel.ce_quantity }}</td> <td class="align-middle">{{item.quantity}}</td>
<td>{{ item.car.finances.selling_price }}</td> <td class="align-middle ps-5">{{item.finances.selling_price}}</td>
<td>{{ item.total }}</td> <td class="align-middle text-body-tertiary fw-semibold">{{item.total}}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View File

@ -96,7 +96,7 @@
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal"> <button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">
{% trans "No" %} {% trans "No" %}
</button> </button>
<a type="button" class="btn btn-danger btn-sm" href="{% url 'vendor_delete' vendor.id %}"> <a type="button" class="btn btn-danger btn-sm" href="{% url 'vendor_delete' vendor.pk %}">
{% trans "Yes" %} {% trans "Yes" %}
</a> </a>
</div> </div>
@ -115,27 +115,27 @@
{% endif %} {% endif %}
</div> </div>
<div><a class="fs-8 fw-bold" href="{% url 'vendor_update' vendor.id %}">{{ vendor.name }}</a> <div><a class="fs-8 fw-bold" href="">{{ vendor.vendor_name }}</a>
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<p class="mb-0 text-body-highlight fw-semibold fs-9 me-2">{{ vendor.arabic_name }}</p><span class="badge badge-phoenix badge-phoenix-primary">{{ vendor.id}}</span> <p class="mb-0 text-body-highlight fw-semibold fs-9 me-2">{{ vendor.vendor_name }}</p><span class="badge badge-phoenix badge-phoenix-primary">{{ vendor.id}}</span>
</div> </div>
</div> </div>
</div> </div>
</td> </td>
<td class="email align-middle white-space-nowrap fw-semibold ps-4 border-end border-translucent"><a class="text-body-highlight" href="">{{ vendor.email }}</a></td> <td class="email align-middle white-space-nowrap fw-semibold ps-4 border-end border-translucent"><a class="text-body-highlight" href="">{{ vendor.email }}</a></td>
<td class="phone align-middle white-space-nowrap fw-semibold ps-4 border-end border-translucent"><a class="text-body-highlight" href="tel:{{ vendor.phone_number }}">{{ vendor.phone_number }}</a></td> <td class="phone align-middle white-space-nowrap fw-semibold ps-4 border-end border-translucent"><a class="text-body-highlight" href="tel:{{ vendor.phone }}">{{ vendor.phone }}</a></td>
<td class="contact align-middle white-space-nowrap ps-4 border-end border-translucent fw-semibold text-body-highlight">{{ vendor.contact_person }}</td> <td class="contact align-middle white-space-nowrap ps-4 border-end border-translucent fw-semibold text-body-highlight">{{ vendor.contact_person }}</td>
<td class="company align-middle white-space-nowrap text-body-tertiary text-opacity-85 ps-4 border-end border-translucent fw-semibold text-body-highlight"> <td class="company align-middle white-space-nowrap text-body-tertiary text-opacity-85 ps-4 border-end border-translucent fw-semibold text-body-highlight">
{{ vendor.address }}</td> {{ vendor.address_1 }}</td>
<td class="date align-middle white-space-nowrap text-body-tertiary text-opacity-85 ps-4 text-body-tertiary">{{ vendor.created_at|date }}</td> <td class="date align-middle white-space-nowrap text-body-tertiary text-opacity-85 ps-4 text-body-tertiary">{{ vendor.created|date }}</td>
<td class="align-middle white-space-nowrap text-end pe-0 ps-4"> <td class="align-middle white-space-nowrap text-end pe-0 ps-4">
<div class="btn-reveal-trigger position-static"> <div class="btn-reveal-trigger position-static">
<button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button> <button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button>
<div class="dropdown-menu dropdown-menu-end py-2"><a href="{% url 'vendor_update' vendor.id %}" class="dropdown-item text-success-dark"> <div class="dropdown-menu dropdown-menu-end py-2"><a href="{% url 'vendor_update' vendor.pk %}" class="dropdown-item text-success-dark">
{% trans "Edit" %} {% trans "Edit" %}
</a> </a>
<div class="dropdown-divider"></div><button class="dropdown-item text-danger" data-bs-toggle="modal" data-bs-target="#deleteModal">{% trans "Delete" %}</button> <div class="dropdown-divider"></div><button class="dropdown-item text-danger" data-bs-toggle="modal" data-bs-target="#deleteModal">{% trans "Delete" %}</button>
</div> </div>
</div> </div>
</td> </td>
</tr> </tr>

View File

@ -28,7 +28,7 @@
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal"> <button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">
{% trans "No" %} {% trans "No" %}
</button> </button>
<a type="button" class="btn btn-danger btn-sm" href="{% url 'vendor_delete' vendor.id %}"> <a type="button" class="btn btn-danger btn-sm" href="{% url 'vendor_delete' vendor.pk %}">
{% trans "Yes" %} {% trans "Yes" %}
</a> </a>
</div> </div>