update
This commit is contained in:
parent
d53ce70f03
commit
9a40e70ce3
@ -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}),
|
||||||
|
}
|
||||||
25
inventory/migrations/0004_purchaseorder.py
Normal file
25
inventory/migrations/0004_purchaseorder.py
Normal 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')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
28
inventory/migrations/0005_saleorder_delete_purchaseorder.py
Normal file
28
inventory/migrations/0005_saleorder_delete_purchaseorder.py
Normal 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',
|
||||||
|
),
|
||||||
|
]
|
||||||
20
inventory/migrations/0006_alter_car_vendor.py
Normal file
20
inventory/migrations/0006_alter_car_vendor.py
Normal 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'),
|
||||||
|
),
|
||||||
|
]
|
||||||
18
inventory/migrations/0007_alter_cartransfer_status.py
Normal file
18
inventory/migrations/0007_alter_cartransfer_status.py
Normal 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')]),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -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
|
||||||
@ -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])
|
||||||
|
|||||||
@ -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(
|
||||||
|
|||||||
@ -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
|
||||||
@ -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()
|
||||||
|
|||||||
BIN
static/images/images/IT-Consulting.jpg
Normal file
BIN
static/images/images/IT-Consulting.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 124 KiB |
BIN
static/images/images/cit.jpg
Normal file
BIN
static/images/images/cit.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
@ -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 %}
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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}}
|
||||||
|
|||||||
@ -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 %}
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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 %}
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
359
templates/sales/estimates/sale_order.html
Normal file
359
templates/sales/estimates/sale_order.html
Normal 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 can’t 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 – we’ll 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>
|
||||||
@ -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">
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
18
templates/vendors/vendors_list.html
vendored
18
templates/vendors/vendors_list.html
vendored
@ -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>
|
||||||
|
|||||||
2
templates/vendors/view_vendor.html
vendored
2
templates/vendors/view_vendor.html
vendored
@ -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>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user