Compare commits
7 Commits
76ecf45a88
...
e9d6a5da27
| Author | SHA1 | Date | |
|---|---|---|---|
| e9d6a5da27 | |||
| fe66ce1ca1 | |||
|
|
41feca462d | ||
|
|
5c4ea15fdf | ||
|
|
faf4eba38a | ||
|
|
6621c1814c | ||
| 296c8a1404 |
18
inventory/migrations/0002_vendor_active.py
Normal file
18
inventory/migrations/0002_vendor_active.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.1.7 on 2025-05-20 12:00
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='vendor',
|
||||
name='active',
|
||||
field=models.BooleanField(default=True, verbose_name='Active'),
|
||||
),
|
||||
]
|
||||
19
inventory/migrations/0003_alter_opportunity_customer.py
Normal file
19
inventory/migrations/0003_alter_opportunity_customer.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Generated by Django 5.1.7 on 2025-05-20 12:45
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0002_vendor_active'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='opportunity',
|
||||
name='customer',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='opportunities', to='inventory.customer'),
|
||||
),
|
||||
]
|
||||
18
inventory/migrations/0004_opportunity_slug.py
Normal file
18
inventory/migrations/0004_opportunity_slug.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.1.7 on 2025-05-20 12:49
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0003_alter_opportunity_customer'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='opportunity',
|
||||
name='slug',
|
||||
field=models.SlugField(blank=True, help_text='Unique slug for the opportunity.', null=True, unique=True, verbose_name='Slug'),
|
||||
),
|
||||
]
|
||||
20
inventory/migrations/0005_notes_dealer.py
Normal file
20
inventory/migrations/0005_notes_dealer.py
Normal file
@ -0,0 +1,20 @@
|
||||
# Generated by Django 5.1.7 on 2025-05-20 13:45
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0004_opportunity_slug'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='notes',
|
||||
name='dealer',
|
||||
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='notes', to='inventory.dealer'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
@ -188,7 +188,13 @@ class CarMake(models.Model, LocalizedNameMixin):
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.slug:
|
||||
self.slug = slugify(self.name)
|
||||
base_slug = slugify(self.name)
|
||||
self.slug = base_slug
|
||||
counter = 1
|
||||
|
||||
while self.__class__.objects.filter(slug=self.slug).exclude(pk=self.pk).exists():
|
||||
self.slug = f"{base_slug}-{counter}"
|
||||
counter += 1
|
||||
super().save(*args, **kwargs)
|
||||
def __str__(self):
|
||||
return self.name
|
||||
@ -204,7 +210,13 @@ class CarModel(models.Model, LocalizedNameMixin):
|
||||
slug = models.SlugField(max_length=255, unique=True, blank=True, null=True)
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.slug:
|
||||
self.slug = slugify(self.name)
|
||||
base_slug = slugify(self.name)
|
||||
self.slug = base_slug
|
||||
counter = 1
|
||||
|
||||
while self.__class__.objects.filter(slug=self.slug).exclude(pk=self.pk).exists():
|
||||
self.slug = f"{base_slug}-{counter}"
|
||||
counter += 1
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
@ -227,7 +239,13 @@ class CarSerie(models.Model, LocalizedNameMixin):
|
||||
slug = models.SlugField(max_length=255, unique=True, blank=True, null=True)
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.slug:
|
||||
self.slug = slugify(self.name)
|
||||
base_slug = slugify(self.name)
|
||||
self.slug = base_slug
|
||||
counter = 1
|
||||
|
||||
while self.__class__.objects.filter(slug=self.slug).exclude(pk=self.pk).exists():
|
||||
self.slug = f"{base_slug}-{counter}"
|
||||
counter += 1
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
@ -249,7 +267,13 @@ class CarTrim(models.Model, LocalizedNameMixin):
|
||||
slug = models.SlugField(max_length=255, unique=True, blank=True, null=True)
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.slug:
|
||||
self.slug = slugify(self.name)
|
||||
base_slug = slugify(self.name)
|
||||
self.slug = base_slug
|
||||
counter = 1
|
||||
|
||||
while self.__class__.objects.filter(slug=self.slug).exclude(pk=self.pk).exists():
|
||||
self.slug = f"{base_slug}-{counter}"
|
||||
counter += 1
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
@ -288,7 +312,13 @@ class CarSpecification(models.Model, LocalizedNameMixin):
|
||||
slug = models.SlugField(max_length=255, unique=True, blank=True, null=True)
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.slug:
|
||||
self.slug = slugify(self.name)
|
||||
base_slug = slugify(self.name)
|
||||
self.slug = base_slug
|
||||
counter = 1
|
||||
|
||||
while self.__class__.objects.filter(slug=self.slug).exclude(pk=self.pk).exists():
|
||||
self.slug = f"{base_slug}-{counter}"
|
||||
counter += 1
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
@ -324,7 +354,13 @@ class CarOption(models.Model, LocalizedNameMixin):
|
||||
slug = models.SlugField(max_length=255, unique=True, blank=True, null=True)
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.slug:
|
||||
self.slug = slugify(self.name)
|
||||
base_slug = slugify(self.name)
|
||||
self.slug = base_slug
|
||||
counter = 1
|
||||
|
||||
while self.__class__.objects.filter(slug=self.slug).exclude(pk=self.pk).exists():
|
||||
self.slug = f"{base_slug}-{counter}"
|
||||
counter += 1
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
@ -1139,7 +1175,13 @@ class Customer(models.Model):
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.slug:
|
||||
self.slug = slugify(f"{self.first_name} {self.last_name}")
|
||||
base_slug = slugify(f"{self.last_name} {self.first_name}")
|
||||
self.slug = base_slug
|
||||
counter = 1
|
||||
|
||||
while self.__class__.objects.filter(slug=self.slug).exclude(pk=self.pk).exists():
|
||||
self.slug = f"{base_slug}-{counter}"
|
||||
counter += 1
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
class Meta:
|
||||
@ -1447,7 +1489,8 @@ class Lead(models.Model):
|
||||
def full_name(self):
|
||||
return f"{self.first_name} {self.last_name}"
|
||||
def convert_to_customer(self):
|
||||
self.status = Status.QUALIFIED
|
||||
self.status = Status.NEGOTIATION
|
||||
self.is_converted = True
|
||||
self.save()
|
||||
return self.get_customer_model()
|
||||
def get_status(self):
|
||||
@ -1507,8 +1550,14 @@ class Lead(models.Model):
|
||||
def save(self, *args, **kwargs):
|
||||
self.status = self.get_status()
|
||||
if not self.slug:
|
||||
self.slug = slugify(f"{self.first_name} {self.last_name}")
|
||||
super(Lead, self).save(*args, **kwargs)
|
||||
base_slug = slugify(f"{self.last_name} {self.first_name}")
|
||||
self.slug = base_slug
|
||||
counter = 1
|
||||
|
||||
while self.__class__.objects.filter(slug=self.slug).exclude(pk=self.pk).exists():
|
||||
self.slug = f"{base_slug}-{counter}"
|
||||
counter += 1
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
class Schedule(models.Model):
|
||||
PURPOSE_CHOICES = [
|
||||
@ -1587,7 +1636,7 @@ class Opportunity(models.Model):
|
||||
Dealer, on_delete=models.CASCADE, related_name="opportunities"
|
||||
)
|
||||
customer = models.ForeignKey(
|
||||
CustomerModel, on_delete=models.CASCADE, related_name="opportunities",null=True,blank=True
|
||||
Customer, on_delete=models.CASCADE, related_name="opportunities",null=True,blank=True
|
||||
)
|
||||
car = models.ForeignKey(
|
||||
Car, on_delete=models.SET_NULL, null=True, blank=True, verbose_name=_("Car")
|
||||
@ -1611,15 +1660,22 @@ class Opportunity(models.Model):
|
||||
created = models.DateTimeField(auto_now_add=True, verbose_name=_("Created"))
|
||||
updated = models.DateTimeField(auto_now=True, verbose_name=_("Updated"))
|
||||
estimate = models.OneToOneField(EstimateModel, related_name="opportunity",on_delete=models.SET_NULL,null=True,blank=True)
|
||||
slug = models.SlugField(null=True, blank=True, unique=True,verbose_name=_("Slug"),
|
||||
help_text=_("Unique slug for the opportunity."))
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.slug:
|
||||
self.slug = slugify(f"opportunity {self.customer.first_name} {self.customer.last_name}")
|
||||
super(Opportunity, self).save(*args, **kwargs)
|
||||
class Meta:
|
||||
verbose_name = _("Opportunity")
|
||||
verbose_name_plural = _("Opportunities")
|
||||
|
||||
def __str__(self):
|
||||
return f"Opportunity for {self.customer.customer_name}"
|
||||
return f"Opportunity for {self.customer.first_name} {self.customer.last_name}"
|
||||
|
||||
|
||||
class Notes(models.Model):
|
||||
dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name="notes")
|
||||
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
|
||||
object_id = models.UUIDField()
|
||||
content_object = GenericForeignKey("content_type", "object_id")
|
||||
@ -1755,12 +1811,19 @@ class Vendor(models.Model, LocalizedNameMixin):
|
||||
logo = models.ImageField(
|
||||
upload_to="logos/vendors", blank=True, null=True, verbose_name=_("Logo")
|
||||
)
|
||||
active = models.BooleanField(default=True, verbose_name=_("Active"))
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At"))
|
||||
slug = models.SlugField(max_length=255, unique=True, verbose_name=_("Slug"), null=True,blank=True)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.slug:
|
||||
self.slug = slugify(self.name)
|
||||
base_slug = slugify(self.name)
|
||||
self.slug = base_slug
|
||||
counter = 1
|
||||
|
||||
while self.__class__.objects.filter(slug=self.slug).exclude(pk=self.pk).exists():
|
||||
self.slug = f"{base_slug}-{counter}"
|
||||
counter += 1
|
||||
super().save(*args, **kwargs)
|
||||
class Meta:
|
||||
verbose_name = _("Vendor")
|
||||
|
||||
@ -83,6 +83,11 @@ urlpatterns = [
|
||||
views.CustomerDetailView.as_view(),
|
||||
name="customer_detail",
|
||||
),
|
||||
path(
|
||||
"customers/<slug:slug>/add-note/",
|
||||
views.add_note_to_customer,
|
||||
name="add_note_to_customer",
|
||||
),
|
||||
path(
|
||||
"customers/<slug:slug>/update/",
|
||||
views.CustomerUpdateView.as_view(),
|
||||
@ -94,11 +99,6 @@ urlpatterns = [
|
||||
views.OpportunityCreateView.as_view(),
|
||||
name="create_opportunity",
|
||||
),
|
||||
path(
|
||||
"customers/<slug:slug>/add-note/",
|
||||
views.add_note_to_customer,
|
||||
name="add_note_to_customer",
|
||||
),
|
||||
path("crm/leads/create/", views.lead_create, name="lead_create"),
|
||||
path(
|
||||
"crm/leads/<slug:slug>/view/", views.LeadDetailView.as_view(), name="lead_detail"
|
||||
@ -109,13 +109,21 @@ urlpatterns = [
|
||||
|
||||
path("crm/leads/", views.LeadListView.as_view(), name="lead_list"),
|
||||
path(
|
||||
"crm/leads/<int:pk>/update/", views.LeadUpdateView.as_view(), name="lead_update"
|
||||
"crm/leads/<slug:slug>/update/", views.LeadUpdateView.as_view(), name="lead_update"
|
||||
),
|
||||
path("crm/leads/<slug:slug>/delete/", views.LeadDeleteView, name="lead_delete"),
|
||||
path("crm/leads/<int:pk>/lead-convert/", views.lead_convert, name="lead_convert"),
|
||||
path("crm/leads/<int:pk>/add-note/", views.add_note_to_lead, name="add_note_to_lead"),
|
||||
path('crm/leads/<int:pk>/update-note/', views.update_note, name='update_note_to_lead'),
|
||||
path("crm/leads/<slug:slug>/lead-convert/", views.lead_convert, name="lead_convert"),
|
||||
path("crm/leads/<int:pk>/delete-note/", views.delete_note, name="delete_note_to_lead"),
|
||||
path(
|
||||
"crm/<int:pk>/update-note/",
|
||||
views.update_note,
|
||||
name="update_note",
|
||||
),
|
||||
path(
|
||||
"crm/<str:content_type>/<slug:slug>/add-note/",
|
||||
views.add_note,
|
||||
name="add_note",
|
||||
),
|
||||
path(
|
||||
"crm/<int:pk>/update-task/",
|
||||
views.update_task,
|
||||
@ -152,13 +160,12 @@ urlpatterns = [
|
||||
name="schedule_cancel",
|
||||
),
|
||||
path(
|
||||
"crm/leads/<int:pk>/transfer/",
|
||||
"crm/leads/<slug:slug>/transfer/",
|
||||
views.lead_transfer,
|
||||
name="lead_transfer",
|
||||
),
|
||||
|
||||
path(
|
||||
"crm/opportunities/<int:pk>/add_note/",
|
||||
"crm/opportunities/<slug:slug>/add_note/",
|
||||
views.add_note_to_opportunity,
|
||||
name="add_note_to_opportunity",
|
||||
),
|
||||
@ -168,17 +175,22 @@ urlpatterns = [
|
||||
name="opportunity_create",
|
||||
),
|
||||
path(
|
||||
"crm/opportunities/<int:pk>/create/",
|
||||
"crm/opportunities/<slug:slug>/create/",
|
||||
views.OpportunityCreateView.as_view(),
|
||||
name="lead_opportunity_create",
|
||||
),
|
||||
path(
|
||||
"crm/opportunities/<slug:slug>/create/",
|
||||
views.OpportunityCreateView.as_view(),
|
||||
name="opportunity_create",
|
||||
),
|
||||
path(
|
||||
"crm/opportunities/<int:pk>/",
|
||||
"crm/opportunities/<slug:slug>/",
|
||||
views.OpportunityDetailView.as_view(),
|
||||
name="opportunity_detail",
|
||||
),
|
||||
path(
|
||||
"crm/opportunities/<int:pk>/edit/",
|
||||
"crm/opportunities/<slug:slug>/edit/",
|
||||
views.OpportunityUpdateView.as_view(),
|
||||
name="update_opportunity",
|
||||
),
|
||||
@ -193,7 +205,7 @@ urlpatterns = [
|
||||
name="delete_opportunity",
|
||||
),
|
||||
path(
|
||||
"crm/opportunities/<int:pk>/opportunity_update_status/",
|
||||
"crm/opportunities/<slug:slug>/opportunity_update_status/",
|
||||
views.opportunity_update_status,
|
||||
name="opportunity_update_status",
|
||||
),
|
||||
|
||||
@ -22,7 +22,7 @@ from django.http.response import StreamingHttpResponse
|
||||
# Django
|
||||
from django.db.models import Q
|
||||
from django.conf import settings
|
||||
from django.db import transaction
|
||||
from django.db import IntegrityError, transaction
|
||||
from django.db.models import Func
|
||||
from django.contrib import messages
|
||||
from django.http import Http404, JsonResponse, HttpResponseForbidden
|
||||
@ -1953,7 +1953,7 @@ class CustomerDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView
|
||||
|
||||
|
||||
@login_required
|
||||
def add_note_to_customer(request, pk):
|
||||
def add_note_to_customer(request, slug):
|
||||
"""
|
||||
This function allows authenticated users to add a note to a specific customer. The
|
||||
note creation is handled by a form, which is validated after submission. If the form
|
||||
@ -1970,7 +1970,7 @@ def add_note_to_customer(request, pk):
|
||||
POST request, it renders the note form template with context including
|
||||
the form and customer.
|
||||
"""
|
||||
customer = get_object_or_404(models.Customer, pk=pk)
|
||||
customer = get_object_or_404(models.Customer, slug=slug)
|
||||
if request.method == "POST":
|
||||
form = forms.NoteForm(request.POST)
|
||||
if form.is_valid():
|
||||
@ -1979,7 +1979,7 @@ def add_note_to_customer(request, pk):
|
||||
|
||||
note.created_by = request.user
|
||||
note.save()
|
||||
return redirect("customer_detail", pk=customer.pk)
|
||||
return redirect("customer_detail", slug=customer.slug)
|
||||
else:
|
||||
form = forms.NoteForm()
|
||||
return render(
|
||||
@ -2144,13 +2144,14 @@ class VendorListView(LoginRequiredMixin, ListView):
|
||||
context_object_name = "vendors"
|
||||
paginate_by = 10
|
||||
template_name = "vendors/vendors_list.html"
|
||||
# ordering = ["-created"]
|
||||
|
||||
# def get_queryset(self):
|
||||
# query = self.request.GET.get("q")
|
||||
# dealer = get_user_type(self.request)
|
||||
# vendors = dealer.entity.get_vendors().filter(active=True)
|
||||
# return apply_search_filters(vendors, query)
|
||||
def get_queryset(self):
|
||||
query = self.request.GET.get("q")
|
||||
dealer = get_user_type(self.request)
|
||||
vendors = super().get_queryset().filter(dealer=dealer,active=True)
|
||||
if query:
|
||||
return apply_search_filters(vendors, query)
|
||||
return vendors
|
||||
|
||||
|
||||
@login_required
|
||||
@ -2205,9 +2206,16 @@ class VendorCreateView(
|
||||
success_message = _("Vendor created successfully")
|
||||
|
||||
def form_valid(self, form):
|
||||
if vendor:= models.Vendor.objects.filter(email=form.instance.email).first():
|
||||
if not vendor.active:
|
||||
messages.error(self.request, _("Vendor Account with this email is Deactivated,Please Contact Admin"))
|
||||
else:
|
||||
messages.error(self.request, _("Vendor with this email already exists"))
|
||||
return redirect("vendor_create")
|
||||
dealer = get_user_type(self.request)
|
||||
form.instance.dealer = dealer
|
||||
form.instance.save()
|
||||
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
@ -4596,6 +4604,7 @@ class LeadDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
|
||||
context["transfer_form"] = forms.LeadTransferForm()
|
||||
context["activity_form"] = forms.ActivityForm()
|
||||
context["staff_task_form"] = forms.StaffTaskForm()
|
||||
context["note_form"] = forms.NoteForm()
|
||||
return context
|
||||
|
||||
|
||||
@ -4797,7 +4806,7 @@ def LeadDeleteView(request, slug):
|
||||
|
||||
|
||||
@login_required
|
||||
def add_note_to_lead(request, pk):
|
||||
def add_note_to_lead(request, slug):
|
||||
"""
|
||||
Adds a note to a specific lead. This view is accessible only to authenticated
|
||||
users. The function handles the POST request to create a new note associated
|
||||
@ -4813,7 +4822,7 @@ def add_note_to_lead(request, pk):
|
||||
note creation or renders the note form template for GET or invalid POST requests.
|
||||
:rtype: HttpResponse
|
||||
"""
|
||||
lead = get_object_or_404(models.Lead, pk=pk)
|
||||
lead = get_object_or_404(models.Lead, slug=slug)
|
||||
if request.method == "POST":
|
||||
form = forms.NoteForm(request.POST)
|
||||
if form.is_valid():
|
||||
@ -4829,7 +4838,7 @@ def add_note_to_lead(request, pk):
|
||||
|
||||
|
||||
@login_required
|
||||
def add_note_to_opportunity(request, pk):
|
||||
def add_note_to_opportunity(request, slug):
|
||||
"""
|
||||
Add a note to a specific opportunity identified by its primary key.
|
||||
|
||||
@ -4843,7 +4852,7 @@ def add_note_to_opportunity(request, pk):
|
||||
:type pk: int
|
||||
:return: A redirect response to the detailed view of the opportunity.
|
||||
"""
|
||||
opportunity = get_object_or_404(models.Opportunity, pk=pk)
|
||||
opportunity = get_object_or_404(models.Opportunity, slug=slug)
|
||||
if request.method == "POST":
|
||||
notes = request.POST.get("notes")
|
||||
if not notes:
|
||||
@ -4851,7 +4860,7 @@ def add_note_to_opportunity(request, pk):
|
||||
else:
|
||||
models.Notes.objects.create(content_object=opportunity, created_by=request.user,note=notes)
|
||||
messages.success(request, _("Note added successfully"))
|
||||
return redirect("opportunity_detail", pk=opportunity.pk)
|
||||
return redirect("opportunity_detail", slug=opportunity.slug)
|
||||
|
||||
|
||||
@login_required
|
||||
@ -4909,14 +4918,15 @@ def delete_note(request, pk):
|
||||
"""
|
||||
note = get_object_or_404(models.Notes, pk=pk, created_by=request.user)
|
||||
lead_pk = note.content_object.pk
|
||||
lead = models.Lead.objects.get(pk=lead_pk)
|
||||
note.delete()
|
||||
messages.success(request, _("Note deleted successfully."))
|
||||
return redirect("lead_detail", pk=lead_pk)
|
||||
return redirect("lead_detail", slug=lead.slug)
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required("inventory.change_lead", raise_exception=True)
|
||||
def lead_convert(request, pk):
|
||||
def lead_convert(request, slug):
|
||||
"""
|
||||
Converts a lead into a customer and creates a corresponding opportunity.
|
||||
|
||||
@ -4933,20 +4943,20 @@ def lead_convert(request, pk):
|
||||
:return: An HTTP response redirecting to the lead list view.
|
||||
:rtype: HttpResponse
|
||||
"""
|
||||
lead = get_object_or_404(models.Lead, pk=pk)
|
||||
lead = get_object_or_404(models.Lead, slug=slug)
|
||||
dealer = get_user_type(request)
|
||||
if hasattr(lead, "opportunity"):
|
||||
messages.error(request, _("Lead is already converted to customer"))
|
||||
else:
|
||||
customer = lead.convert_to_customer()
|
||||
models.Opportunity.objects.create(dealer=dealer,customer=customer,lead=lead,probability=50,stage=models.Stage.PROSPECT,staff=lead.staff,status=models.Status.QUALIFIED)
|
||||
models.Opportunity.objects.create(dealer=dealer,customer=customer,lead=lead,probability=50,stage=models.Stage.NEGOTIATION,staff=lead.staff)
|
||||
messages.success(request, _("Lead converted to customer successfully"))
|
||||
return redirect("lead_list")
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required("inventory.add_lead", raise_exception=True)
|
||||
def schedule_lead(request, pk):
|
||||
def schedule_lead(request, slug):
|
||||
"""
|
||||
Handles the scheduling of a lead for an appointment.
|
||||
|
||||
@ -4969,7 +4979,7 @@ def schedule_lead(request, pk):
|
||||
messages.error(request, _("You do not have permission to schedule lead"))
|
||||
return redirect("lead_list")
|
||||
dealer = get_user_type(request)
|
||||
lead = get_object_or_404(models.Lead, pk=pk, dealer=dealer)
|
||||
lead = get_object_or_404(models.Lead, slug=slug, dealer=dealer)
|
||||
if request.method == "POST":
|
||||
form = forms.ScheduleForm(request.POST)
|
||||
if form.is_valid():
|
||||
@ -4997,7 +5007,7 @@ def schedule_lead(request, pk):
|
||||
)
|
||||
except ValidationError as e:
|
||||
messages.error(request, str(e))
|
||||
return redirect("schedule_lead", pk=lead.pk)
|
||||
return redirect("schedule_lead", slug=lead.slug)
|
||||
|
||||
client = get_object_or_404(User, email=lead.email)
|
||||
# Create Appointment
|
||||
@ -5021,7 +5031,7 @@ def schedule_lead(request, pk):
|
||||
|
||||
@login_required
|
||||
@permission_required("inventory.change_lead", raise_exception=True)
|
||||
def lead_transfer(request, pk):
|
||||
def lead_transfer(request, slug):
|
||||
"""
|
||||
Handles the transfer of a lead to a different staff member. This view is accessible
|
||||
only to authenticated users with the 'inventory.change_lead' permission. If the
|
||||
@ -5033,7 +5043,7 @@ def lead_transfer(request, pk):
|
||||
:param pk: The primary key of the lead to be transferred.
|
||||
:return: An HTTP redirect response to the lead list view.
|
||||
"""
|
||||
lead = get_object_or_404(models.Lead, pk=pk)
|
||||
lead = get_object_or_404(models.Lead, slug=slug)
|
||||
if request.method == "POST":
|
||||
form = forms.LeadTransferForm(request.POST)
|
||||
if form.is_valid():
|
||||
@ -5193,24 +5203,14 @@ class OpportunityCreateView(CreateView,SuccessMessageMixin, LoginRequiredMixin):
|
||||
template_name = "crm/opportunities/opportunity_form.html"
|
||||
success_message = "Opportunity created successfully."
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
def get_initial(self):
|
||||
initial = super().get_initial()
|
||||
dealer = get_user_type(self.request)
|
||||
return context
|
||||
|
||||
# def get_form_kwargs(self):
|
||||
# kwargs = super().get_form_kwargs()
|
||||
# dealer = get_user_type(self.request)
|
||||
# kwargs["car"].queryset = models.Car.objects.filter(dealer=dealer,)
|
||||
# return kwargs
|
||||
|
||||
# def get_initial(self):
|
||||
# initial = super().get_initial()
|
||||
# if self.kwargs.get("pk", None):
|
||||
# lead = models.Lead.objects.get(pk=self.kwargs.get("pk"))
|
||||
|
||||
# initial["customer"] = lead.customer
|
||||
# return initial
|
||||
if self.kwargs.get("slug", None):
|
||||
lead = models.Lead.objects.get(slug=self.kwargs.get("slug"),dealer=dealer)
|
||||
initial["lead"] = lead
|
||||
initial['stage'] = models.Stage.PROPOSAL
|
||||
return initial
|
||||
|
||||
def form_valid(self, form):
|
||||
dealer = get_user_type(self.request)
|
||||
@ -5251,7 +5251,7 @@ class OpportunityUpdateView(LoginRequiredMixin,SuccessMessageMixin, UpdateView):
|
||||
success_message = "Opportunity updated successfully."
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy("opportunity_detail", kwargs={"pk": self.object.pk})
|
||||
return reverse_lazy("opportunity_detail", kwargs={"slug": self.object.slug})
|
||||
|
||||
|
||||
class OpportunityDetailView(LoginRequiredMixin, DetailView):
|
||||
@ -5359,7 +5359,7 @@ def delete_opportunity(request, pk):
|
||||
|
||||
|
||||
@login_required
|
||||
def opportunity_update_status(request, pk):
|
||||
def opportunity_update_status(request, slug):
|
||||
"""
|
||||
Update the status and/or stage of a specific Opportunity instance. This is a
|
||||
view function, which is generally tied to a URL endpoint in a Django application.
|
||||
@ -5384,7 +5384,7 @@ def opportunity_update_status(request, pk):
|
||||
frontend behavior.
|
||||
:rtype: HttpResponse
|
||||
"""
|
||||
opportunity = get_object_or_404(models.Opportunity, pk=pk)
|
||||
opportunity = get_object_or_404(models.Opportunity, slug=slug)
|
||||
status = request.GET.get("status")
|
||||
stage = request.GET.get("stage")
|
||||
if status:
|
||||
@ -5393,7 +5393,7 @@ def opportunity_update_status(request, pk):
|
||||
opportunity.stage = stage
|
||||
opportunity.save()
|
||||
messages.success(request,_("Opportunity status updated successfully"))
|
||||
response = HttpResponse(redirect("opportunity_detail",pk=opportunity.pk))
|
||||
response = HttpResponse(redirect("opportunity_detail",slug=opportunity.slug))
|
||||
response['HX-Refresh'] = 'true'
|
||||
return response
|
||||
|
||||
@ -7783,6 +7783,7 @@ def add_activity(request,content_type,slug):
|
||||
else:
|
||||
messages.error(request, _("Activity form is not valid"))
|
||||
return redirect(f"{content_type}_detail", slug=slug)
|
||||
|
||||
def add_task(request,content_type,slug):
|
||||
try:
|
||||
model = apps.get_model(f'inventory.{content_type}')
|
||||
@ -7823,4 +7824,43 @@ def update_task(request,pk):
|
||||
tasks = models.Tasks.objects.filter(
|
||||
content_type__model="lead", object_id=lead.id
|
||||
)
|
||||
return render(request,'crm/leads/lead_detail.html',{'lead':lead,'tasks':tasks})
|
||||
return render(request,'crm/leads/lead_detail.html',{'lead':lead,'tasks':tasks})
|
||||
|
||||
def add_note(request,content_type,slug):
|
||||
try:
|
||||
model = apps.get_model(f'inventory.{content_type}')
|
||||
except LookupError:
|
||||
raise Http404("Model not found")
|
||||
|
||||
obj = get_object_or_404(model, slug=slug)
|
||||
dealer = get_user_type(request)
|
||||
if request.method == "POST":
|
||||
form = forms.NoteForm(request.POST)
|
||||
if form.is_valid():
|
||||
note = form.save(commit=False)
|
||||
note.dealer = dealer
|
||||
note.content_object = obj
|
||||
note.created_by = request.user
|
||||
|
||||
note.save()
|
||||
messages.success(request, _("Note added successfully"))
|
||||
else:
|
||||
print(form.errors)
|
||||
messages.error(request, _("Note form is not valid"))
|
||||
return redirect(f"{content_type}_detail", slug=slug)
|
||||
|
||||
def update_note(request,pk):
|
||||
note = get_object_or_404(models.Notes, pk=pk)
|
||||
lead = get_object_or_404(models.Lead, pk=note.content_object.id)
|
||||
dealer = get_user_type(request)
|
||||
if request.method == "POST":
|
||||
note.note = request.POST.get('note')
|
||||
note.save()
|
||||
messages.success(request, _("Note updated successfully"))
|
||||
return redirect(f"lead_detail", slug=lead.slug)
|
||||
else:
|
||||
messages.error(request, _("Note form is not valid"))
|
||||
notes = models.Notes.objects.filter(
|
||||
content_type__model="lead", object_id=lead.id,dealer=dealer
|
||||
)
|
||||
return render(request,'crm/leads/lead_detail.html',{'lead':lead,'notes':notes})
|
||||
@ -230,7 +230,7 @@
|
||||
<div class="modal fade" id="exampleModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form class="modal-content" action="{% url 'lead_transfer' lead.pk %}" method="post">
|
||||
<form class="modal-content" action="{% url 'lead_transfer' lead.slug %}" method="post">
|
||||
{% csrf_token %}
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="exampleModalLabel">Reassign Lead To Another Employee</h5>
|
||||
@ -301,10 +301,7 @@
|
||||
<div class="tab-pane fade" id="tab-notes" role="tabpanel" aria-labelledby="notes-tab">
|
||||
<div class="mb-1 d-flex align-items-center justify-content-between">
|
||||
<h3 class="mb-4" id="scrollspyNotes">{{ _("Notes") }}</h3>
|
||||
<a id="addBtn" href="#" class="btn btn-sm btn-phoenix-primary mb-3 btn-sm" data-url="{% url 'add_note_to_lead' lead.pk %}" data-bs-toggle="modal" data-bs-target="#noteModal" data-note-title="{{ _("Add") }}<i class='fa fa-plus-circle text-success ms-2'></i>">
|
||||
<span class="fas fa-plus me-1"></span>
|
||||
{% trans 'Add Note' %}
|
||||
</a>
|
||||
<button class="btn btn-phoenix-primary btn-sm" type="button" onclick="reset_form()" data-bs-toggle="modal" data-bs-target="#noteModal"><span class="fas fa-plus me-1"></span>{{ _("Add Note") }}</button>
|
||||
</div>
|
||||
|
||||
<div class="border-top border-bottom border-translucent" id="leadDetailsTable">
|
||||
@ -331,9 +328,18 @@
|
||||
<td class="align-middle text-body-tertiary text-start white-space-nowrap">{{ note.created|naturalday|capfirst }}</td>
|
||||
<td class="align-middle text-end white-space-nowrap pe-0 action py-2">
|
||||
{% if note.created_by == request.user %}
|
||||
<a id="updateBtn" href="#" class="btn btn-sm btn-phoenix-primary me-2" data-url="{% url 'update_note_to_lead' note.pk %}" data-bs-toggle="modal" data-bs-target="#noteModal" data-note-title="{{ _("Update") }}<i class='fas fa-pen-square text-primary ms-2'></i>">
|
||||
<i class="fas fa-pen"></i>
|
||||
</a>
|
||||
<a id="updateBtn"
|
||||
href="#"
|
||||
onclick="updateNote(this)"
|
||||
class="btn btn-sm btn-phoenix-primary me-2"
|
||||
data-pk="{{ note.pk }}"
|
||||
data-note="{{ note.note|escapejs }}"
|
||||
data-url="{% url 'update_note' note.pk %}"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#noteModal"
|
||||
data-note-title="{{ _('Update') }}<i class='fas fa-pen-square text-primary ms-2'></i>">
|
||||
{{ _("Update") }}
|
||||
</a>
|
||||
<button class="btn btn-phoenix-danger btn-sm delete-btn"
|
||||
data-url="{% url 'delete_note_to_lead' note.pk %}"
|
||||
data-message="Are you sure you want to delete this note?"
|
||||
@ -535,7 +541,7 @@
|
||||
</div>
|
||||
{% include 'modal/delete_modal.html' %}
|
||||
<!-- add update Modal -->
|
||||
<div class="modal fade" id="noteModal" tabindex="-1" aria-labelledby="noteModalLabel" aria-hidden="true">
|
||||
{% comment %} <div class="modal fade" id="noteModal" tabindex="-1" aria-labelledby="noteModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-md">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header justify-content-between align-items-start gap-5 px-4 pt-4 pb-3 border-0">
|
||||
@ -549,7 +555,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> {% endcomment %}
|
||||
|
||||
<!-- activity Modal -->
|
||||
{% include "components/activity_modal.html" with content_type="lead" slug=lead.slug %}
|
||||
@ -559,7 +565,7 @@
|
||||
<div class="modal-dialog modal-md">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header justify-content-between align-items-start gap-5 px-4 pt-4 pb-3 border-0">
|
||||
<h4 class="modal-title" id="noteModalLabel">{% trans 'Task' %}</h4>
|
||||
<h4 class="modal-title" id="taskModalLabel">{% trans 'Task' %}</h4>
|
||||
<button class="btn p-0 text-body-quaternary fs-6" data-bs-dismiss="modal" aria-label="Close">
|
||||
<span class="fas fa-times"></span>
|
||||
</button>
|
||||
@ -574,34 +580,44 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- note Modal -->
|
||||
<div class="modal fade" id="noteModal" tabindex="-1" aria-labelledby="noteModalLabel" aria-hidden="true">
|
||||
|
||||
<div class="modal-dialog modal-md">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header justify-content-between align-items-start gap-5 px-4 pt-4 pb-3 border-0">
|
||||
<h4 class="modal-title" id="noteModalLabel">{% trans 'Note' %}</h4>
|
||||
<button class="btn p-0 text-body-quaternary fs-6" data-bs-dismiss="modal" aria-label="Close">
|
||||
<span class="fas fa-times"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form action="{% url 'add_note' 'lead' lead.slug %}" method="post" class="add_note_form">
|
||||
{% csrf_token %}
|
||||
{{ note_form|crispy }}
|
||||
<button type="submit" class="btn btn-success w-100">{% trans 'Save' %}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
||||
{% block customJS %}
|
||||
<script>
|
||||
function updateNote(e) {
|
||||
let url = e.getAttribute('data-url')
|
||||
let note = e.getAttribute('data-note')
|
||||
document.querySelector('#id_note').value = note
|
||||
let form = document.querySelector('.add_note_form')
|
||||
form.action = url
|
||||
}
|
||||
function reset_form() {
|
||||
document.querySelector('#id_note').value = ""
|
||||
let form = document.querySelector('.add_note_form')
|
||||
form.action = "{% url 'add_note' 'lead' lead.slug %}"
|
||||
}
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const noteModal = document.getElementById("noteModal");
|
||||
const modalTitle = document.getElementById("noteModalLabel");
|
||||
|
||||
const modalBody = noteModal.querySelector(".modal-body");
|
||||
|
||||
noteModal.addEventListener("show.bs.modal", function (event) {
|
||||
const button = event.relatedTarget;
|
||||
const url = button.getAttribute("data-url");
|
||||
const title = button.getAttribute("data-note-title");
|
||||
|
||||
fetch(url)
|
||||
.then((response) => response.text())
|
||||
.then((html) => {
|
||||
modalBody.innerHTML = html;
|
||||
modalTitle.innerHTML = title;
|
||||
})
|
||||
.catch((error) => {
|
||||
modalBody.innerHTML = '<p class="text-danger">{% trans 'Error loading form. Please try again later' %}.</p>';
|
||||
console.error("Error loading form:", error);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
let Toast = Swal.mixin({
|
||||
toast: true,
|
||||
position: "top-end",
|
||||
@ -723,5 +739,6 @@
|
||||
titleText: msg
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock customJS %}
|
||||
|
||||
@ -240,7 +240,7 @@
|
||||
</td> {% endcomment %}
|
||||
<td class="align-middle white-space-nowrap text-body-tertiary text-opacity-85 fw-semibold text-body-highlight">
|
||||
{% if lead.opportunity %}
|
||||
<a href="{% url 'opportunity_detail' lead.opportunity.id %}">
|
||||
<a href="{% url 'opportunity_detail' lead.opportunity.slug %}">
|
||||
<span class="badge badge-phoenix badge-phoenix-success">Opportunity {{ lead.opportunity.lead}} <i class="fa-solid fa-arrow-up-right-from-square"></i></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
@ -260,15 +260,15 @@
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-end py-2">
|
||||
{% if perms.inventory.change_lead %}
|
||||
<a href="{% url 'lead_update' lead.id %}" class="dropdown-item text-success-dark">{% trans "Edit" %}</a>
|
||||
<a href="{% url 'lead_update' lead.slug %}" class="dropdown-item text-success-dark">{% trans "Edit" %}</a>
|
||||
{% endif %}
|
||||
<button class="dropdown-item text-primary" onclick="openActionModal('{{ lead.id }}', '{{ lead.action }}', '{{ lead.next_action }}', '{{ lead.next_action_date|date:"Y-m-d\TH:i" }}')">
|
||||
<button class="dropdown-item text-primary" onclick="openActionModal('{{ lead.pk }}', '{{ lead.action }}', '{{ lead.next_action }}', '{{ lead.next_action_date|date:"Y-m-d\TH:i" }}')">
|
||||
{% trans "Update Actions" %}
|
||||
</button>
|
||||
<a href="{% url 'send_lead_email' lead.id %}" class="dropdown-item text-success-dark">{% trans "Send Email" %}</a>
|
||||
<a href="{% url 'schedule_lead' lead.id %}" class="dropdown-item text-success-dark">{% trans "Schedule Event" %}</a>
|
||||
<a href="{% url 'send_lead_email' lead.slug %}" class="dropdown-item text-success-dark">{% trans "Send Email" %}</a>
|
||||
<a href="{% url 'schedule_lead' lead.slug %}" class="dropdown-item text-success-dark">{% trans "Schedule Event" %}</a>
|
||||
{% if not lead.opportunity %}
|
||||
<a href="{% url 'lead_convert' lead.id %}" class="dropdown-item text-success-dark">{% trans "Convert" %}</a>
|
||||
<a href="{% url 'lead_opportunity_create' lead.slug %}" class="dropdown-item text-success-dark">{% trans "Convert to Opportunity" %}</a>
|
||||
{% endif %}
|
||||
<div class="dropdown-divider"></div>
|
||||
{% if perms.inventory.delete_lead %}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
{% if form.instance.pk %}
|
||||
<form method="post" action="{% url 'update_note_to_lead' note.pk %}" enctype="multipart/form-data">
|
||||
{% else %}
|
||||
<form method="post" action="{% url 'add_note_to_lead' lead.pk %}" enctype="multipart/form-data">
|
||||
<form method="post" action="{% url 'add_note_to_lead' lead.slug %}" enctype="multipart/form-data">
|
||||
{% endif %}
|
||||
|
||||
{% csrf_token %}
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
<a class="dropdown-item" href="{% url 'estimate_create_from_opportunity' opportunity.pk %}">{{ _("Create Quotation")}}</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
<li><a class="dropdown-item" href="{% url 'update_opportunity' opportunity.pk %}">Update Opportunity</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'update_opportunity' opportunity.slug %}">Update Opportunity</a></li>
|
||||
<li><a class="dropdown-item text-danger" href="">Delete Opportunity</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@ -79,7 +79,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
{% comment %} <div class="card">
|
||||
<div class="card-body">
|
||||
<h4 class="mb-5">{{ _("Other Information")}}</h4>
|
||||
<div class="row g-3">
|
||||
@ -101,7 +101,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> {% endcomment %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-7 col-xxl-8">
|
||||
@ -330,7 +330,7 @@
|
||||
</div>
|
||||
<div class="tab-pane fade" id="tab-notes" role="tabpanel" aria-labelledby="notes-tab">
|
||||
<h2 class="mb-4">Notes</h2>
|
||||
<form action="{% url 'add_note_to_opportunity' opportunity.pk %}" method="post">
|
||||
<form action="{% url 'add_note_to_opportunity' opportunity.slug %}" method="post">
|
||||
{% csrf_token %}
|
||||
<textarea class="form-control mb-3" id="notes" rows="4" name="notes" required> </textarea>
|
||||
<button type="submit" class="btn btn-primary mb-3">Add Note</button>
|
||||
@ -366,7 +366,7 @@
|
||||
<button class="btn btn-link p-0 ms-3 fs-9 text-primary fw-bold text-decoration-none"><span class="fas fa-sort me-1 fw-extra-bold fs-10"></span>Sorting</button>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<a href="{% url 'schedule_lead' opportunity.lead.id %}" class="btn btn-primary"><span class="fa-solid fa-plus me-2"></span>Add Meeting </a>
|
||||
<a href="{% url 'schedule_lead' opportunity.lead.slug %}" class="btn btn-primary"><span class="fa-solid fa-plus me-2"></span>Add Meeting </a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-3">
|
||||
@ -394,7 +394,7 @@
|
||||
</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<a href="{% url 'schedule_lead' opportunity.lead.id %}" class="btn btn-primary"><span class="fa-solid fa-plus me-2"></span>Add Call</a>
|
||||
<a href="{% url 'schedule_lead' opportunity.lead.slug %}" class="btn btn-primary"><span class="fa-solid fa-plus me-2"></span>Add Call</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border-top border-bottom border-translucent" id="leadDetailsTable" data-list='{"valueNames":["name","description","create_date","create_by","last_activity"],"page":5,"pagination":true}'>
|
||||
@ -553,5 +553,5 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% include "components/activity_modal.html" with content_type="opportunity" pk=opportunity.pk %}
|
||||
{% include "components/activity_modal.html" with content_type="opportunity" slug=opportunity.slug %}
|
||||
{% endblock %}
|
||||
@ -96,10 +96,10 @@
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<a class="btn btn-sm btn-phoenix-primary" href="{% url 'opportunity_detail' opportunity.pk %}">
|
||||
<a class="btn btn-sm btn-phoenix-primary" href="{% url 'opportunity_detail' opportunity.slug %}">
|
||||
{{ _("View Details") }} <i class="fa-solid fa-eye ms-2"></i>
|
||||
</a>
|
||||
<a class="btn btn-sm btn-phoenix-success" href="{% url 'update_opportunity' opportunity.pk %}">
|
||||
<a class="btn btn-sm btn-phoenix-success" href="{% url 'update_opportunity' opportunity.slug %}">
|
||||
{{ _("Update") }} <i class="fa-solid fa-pen ms-2"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{% load i18n static crispy_forms_filters %}
|
||||
|
||||
<form method="post" action="{% url 'add_note_to_customer' customer.pk %}" enctype="multipart/form-data">
|
||||
<form method="post" action="{% url 'add_note_to_customer' customer.slug %}" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<button type="submit" class="btn btn-sm btn-success w-100">{{ _("Add") }}</button>
|
||||
|
||||
@ -4,48 +4,101 @@
|
||||
{% block title %}{{ _("Expenses") }}{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mt-4 mx-4">
|
||||
<div class="d-flex justify-content-between mb-2 p-6">
|
||||
<span></span>
|
||||
<h3 class="text-center">{% trans "Expenses" %}</h3>
|
||||
|
||||
<div class="row mt-4">
|
||||
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<h3 class="">{% trans "Expenses" %}</h3>
|
||||
<a href="{% url 'item_expense_create' %}" class="btn btn-md btn-phoenix-primary"><i class="fa fa-plus me-2"></i>{% trans "Add Expense" %}</a>
|
||||
|
||||
</div>
|
||||
<div class="mx-n4 px-4 mx-lg-n6 px-lg-6 pt-7 border-y">
|
||||
|
||||
<div class="table-responsive mx-n1 px-1 scrollbar">
|
||||
<table class="table fs-9 mb-0 border-top border-translucent">
|
||||
<thead>
|
||||
<tr>
|
||||
<div class="col-12">
|
||||
<form method="get" class=" mb-4">
|
||||
<div class="input-group input-group-sm">
|
||||
<button class="btn btn-sm btn-secondary rounded-start" type="submit">
|
||||
{% trans "search" %}
|
||||
</button>
|
||||
<input type="text"
|
||||
name="q"
|
||||
class="form-control form-control-sm rounded-end"
|
||||
value="{{ request.GET.q }}"
|
||||
placeholder="{% trans 'Search bank accounts...' %}" />
|
||||
{% if request.GET.q %}
|
||||
<a href="{% url request.resolver_match.view_name %}"
|
||||
class="btn btn-sm btn-outline-danger ms-1 rounded">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
{% if page_obj.object_list %}
|
||||
<div class="table-responsive px-1 scrollbar mt-3">
|
||||
<table class="table align-items-center table-flush">
|
||||
<thead>
|
||||
<tr class="bg-body-highlight">
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Item Number" %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Name" %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Unit of Measure" %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Action" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="list">
|
||||
{% for expense in expenses %}
|
||||
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
|
||||
<td class="align-middle product white-space-nowrap py-0">{{ expense.item_number }}</td>
|
||||
<td class="align-middle product white-space-nowrap">{{ expense.name }}</td>
|
||||
<td class="align-middle product white-space-nowrap">{{ expense.uom }}</td>
|
||||
<td class="">
|
||||
<a href="{% url 'item_expense_update' expense.pk %}"
|
||||
|
||||
<tbody class="list">
|
||||
|
||||
{% for expense in expenses %}
|
||||
|
||||
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{{ expense.item_number }}
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{{ expense.name }}
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{{ expense.uom }}
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
<a href="{% url 'item_expense_update' expense.pk %}"
|
||||
class="btn btn-sm btn-phoenix-success">
|
||||
{% trans "Update" %}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
|
||||
</td>
|
||||
<td class="align-middle white-space-nowrap text-start">
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="6" class="text-center">{% trans "No Invoice Found" %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="d-flex justify-content-center">
|
||||
<tr>
|
||||
<td colspan="6" class="text-center text-muted">{% trans "No Accounts Found" %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between mt-3"><span class="d-none d-sm-inline-block" data-list-info="data-list-info">{{ page_obj.start_index }} {{ _("to") }} {{ page_obj.end_index }}<span class="text-body-tertiary"> {{ _("Items of")}} </span>{{ page_obj.paginator.count }}</span>
|
||||
<div class="d-flex">
|
||||
|
||||
{% if is_paginated %}
|
||||
{% include 'partials/pagination.html' %}
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@ -1,22 +1,44 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n static %}
|
||||
|
||||
{% block title %}{{ _("Expenses") }}{% endblock title %}
|
||||
{% block title %}{{ _("Services") }}{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="d-flex justify-content-between">
|
||||
|
||||
<div class="row mt-4">
|
||||
|
||||
<h3 class="mb-2">{% trans "Services" %}</h3>
|
||||
<a href="{% url 'item_service_create' %}" class="btn btn-md btn-phoenix-primary"><i class="fa fa-plus me-2"></i>{% trans "Add Service" %}</a>
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<h3 class="">{% trans "Services" %}</h3>
|
||||
<a href="{% url 'item_service_create' %}" class="btn btn-md btn-phoenix-primary"><i class="fa fa-plus me-2"></i>{% trans "Add Service" %}</a>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<form method="get" class=" mb-4">
|
||||
<div class="input-group input-group-sm">
|
||||
<button class="btn btn-sm btn-secondary rounded-start" type="submit">
|
||||
{% trans "search" %}
|
||||
</button>
|
||||
<input type="text"
|
||||
name="q"
|
||||
class="form-control form-control-sm rounded-end"
|
||||
value="{{ request.GET.q }}"
|
||||
placeholder="{% trans 'Search bank accounts...' %}" />
|
||||
{% if request.GET.q %}
|
||||
<a href="{% url request.resolver_match.view_name %}"
|
||||
class="btn btn-sm btn-outline-danger ms-1 rounded">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="table-responsive scrollbar transition">
|
||||
<table class="table table-sm fs-9 mb-0 border-translucent">
|
||||
{% if page_obj.object_list %}
|
||||
<div class="table-responsive px-1 scrollbar mt-3">
|
||||
<table class="table align-items-center table-flush">
|
||||
<thead>
|
||||
<tr>
|
||||
<tr class="bg-body-highlight">
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Item Number" %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Name" %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Unit of Measure" %}</th>
|
||||
@ -25,36 +47,61 @@
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Action" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody class="list">
|
||||
|
||||
{% for service in services %}
|
||||
|
||||
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
|
||||
<td class="align-middle product white-space-nowrap py-0">{{ service.pk }}</td>
|
||||
<td class="align-middle product white-space-nowrap">{{ service.get_local_name }}</td>
|
||||
<td class="align-middle product white-space-nowrap">{{ service.uom }}</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{% if service.taxable %}
|
||||
Yes
|
||||
{% else %}
|
||||
No
|
||||
{% endif %}
|
||||
{{ service.pk }}
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">{{ service.item.co }}</td>
|
||||
<td class="text-center">
|
||||
<a href="{% url 'item_service_update' service.pk %}"
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{{ service.get_local_name }}
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{{ service.uom }}
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{% if service.taxable %}
|
||||
Yes
|
||||
{% else %}
|
||||
No
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{{ service.item.co }}
|
||||
</td>
|
||||
<td class="align-middle white-space-nowrap text-start">
|
||||
<a href="{% url 'item_service_update' service.pk %}"
|
||||
class="btn btn-sm btn-phoenix-success">
|
||||
{% trans "Update" %}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
|
||||
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="6" class="text-center">{% trans "No Invoice Found" %}</td>
|
||||
<td colspan="6" class="text-center text-muted">{% trans "No Accounts Found" %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="d-flex justify-content-center">
|
||||
|
||||
<div class="d-flex justify-content-between mt-3"><span class="d-none d-sm-inline-block" data-list-info="data-list-info">{{ page_obj.start_index }} {{ _("to") }} {{ page_obj.end_index }}<span class="text-body-tertiary"> {{ _("Items of")}} </span>{{ page_obj.paginator.count }}</span>
|
||||
<div class="d-flex">
|
||||
|
||||
{% if is_paginated %}
|
||||
{% include 'partials/pagination.html' %}
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@ -5,52 +5,89 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="row mt-4">
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
|
||||
<h3 class="">{% trans "Bank Accounts" %}</h3>
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<h3 class="">{% trans "Bank Accounts" %}</h3>
|
||||
<a href="{% url 'bank_account_create' %}" class="btn btn-md btn-phoenix-primary"><i class="fa fa-plus me-2"></i>{% trans "Add Bank Account" %}</a>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<form method="get" class=" mb-4">
|
||||
<div class="input-group input-group-sm">
|
||||
<button class="btn btn-sm btn-secondary rounded-start" type="submit">
|
||||
{% trans "search" %}
|
||||
</button>
|
||||
<input type="text"
|
||||
name="q"
|
||||
class="form-control form-control-sm rounded-end"
|
||||
value="{{ request.GET.q }}"
|
||||
placeholder="{% trans 'Search bank accounts...' %}" />
|
||||
{% if request.GET.q %}
|
||||
<a href="{% url request.resolver_match.view_name %}"
|
||||
class="btn btn-sm btn-outline-danger ms-1 rounded">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{% if page_obj.object_list %}
|
||||
<div class="table-responsive px-1 scrollbar mt-3">
|
||||
<table class="table align-items-center table-flush">
|
||||
<thead>
|
||||
|
||||
<tr class="bg-body-highlight">
|
||||
<th class="border-top border-translucent ps-3">{% trans "Name" %}</th>
|
||||
<th class="border-top border-translucent ps-3">{% trans "Account Number" %}</th>
|
||||
<th class="border-top border-translucent text-end pe-3">{% trans "Type" %}</th>
|
||||
<th class="border-top border-translucent text-end pe-3" scope="col">{% trans "Action" %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Name" %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Account Number" %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Type" %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Action" %}</th>
|
||||
</tr>
|
||||
|
||||
</thead>
|
||||
<tbody class="list">
|
||||
{% for bank in bank_accounts %}
|
||||
{% for bank in bank_accounts %}
|
||||
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
|
||||
<td class="align-middle ps-3">{{ bank.name }}</td>
|
||||
<td class="align-middle ps-3">{{ bank.account_number }}</td>
|
||||
<td class="align-middle product text-end pe-3 ">{{ bank.account_type|capfirst }}</td>
|
||||
<td class="align-middle product text-end pe-3 ">
|
||||
<a href="{% url 'bank_account_update' bank.pk %}"
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{{ bank.name }}
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{{ bank.account_number }}
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{{ bank.account_type|capfirst }}
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
<a href="{% url 'bank_account_update' bank.pk %}"
|
||||
class="btn btn-sm btn-phoenix-success">
|
||||
{% trans "Update" %}
|
||||
</a>
|
||||
</td>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="6" class="text-center">{% trans "No Bank Accounts Found" %}</td>
|
||||
<td colspan="6" class="text-center text-muted">{% trans "No Accounts Found" %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="d-flex justify-content-center">
|
||||
<div class="d-flex justify-content-between mt-3"><span class="d-none d-sm-inline-block" data-list-info="data-list-info">{{ page_obj.start_index }} {{ _("to") }} {{ page_obj.end_index }}<span class="text-body-tertiary"> {{ _("Items of")}} </span>{{ page_obj.paginator.count }}</span>
|
||||
<div class="d-flex">
|
||||
|
||||
{% if is_paginated %}
|
||||
{% include 'partials/pagination.html' %}
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{% endblock %}
|
||||
@ -10,129 +10,102 @@
|
||||
</a>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="d-flex flex-column min-vh-100">
|
||||
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
|
||||
<a href="{% url 'bill_create' %}" class="btn btn-md btn-phoenix-primary">
|
||||
<i class="fa fa-plus me-2"></i>
|
||||
{% trans 'New Bill' %}</a>
|
||||
</div>
|
||||
<div class="d-flex flex-column flex-sm-grow-1 p-4">
|
||||
<main class="d-grid gap-4 p-1">
|
||||
<!-- Search Bar -->
|
||||
<div class="row g-4">
|
||||
<div class="col-12">
|
||||
<div class="row-fluid p-2">
|
||||
<form method="get">
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<h3 class="">{% trans "Bills" %}</h3>
|
||||
<a href="{% url 'bill_create' %}" class="btn btn-md btn-phoenix-primary"><i class="fa fa-plus me-2"></i>{% trans 'New Bill' %}</a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="col-12">
|
||||
<form method="get" class=" mb-4">
|
||||
<div class="input-group input-group-sm">
|
||||
<button class="btn btn-sm btn-secondary rounded-start" type="submit">{% trans 'search' %}</button>
|
||||
<input type="text" name="q" class="form-control form-control-sm rounded-end" value="{{ request.GET.q }}" placeholder="{% trans 'Search bills...' %}" />
|
||||
{% if request.GET.q %}
|
||||
<a href="{% url request.resolver_match.view_name %}" class="btn btn-sm btn-outline-danger ms-1 rounded"><i class="bi bi-x-lg"></i></a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Customer Table -->
|
||||
<div id="accountsTable">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm fs-9 mb-0">
|
||||
<thead>
|
||||
<tr class="bg-body-highlight">
|
||||
<th class="border-top border-translucent ps-3">
|
||||
{% trans 'Bill Number' %}
|
||||
</th>
|
||||
<th class="border-top border-translucent">
|
||||
{% trans 'Bill Status' %}
|
||||
</th>
|
||||
<th class="border-top border-translucent text-end pe-3">
|
||||
{% trans 'Vendor' %}
|
||||
</th>
|
||||
<th class="border-top border-translucent text-end pe-3" scope="col">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="list">
|
||||
{% for bill in bills %}
|
||||
<tr>
|
||||
<td class="align-middle ps-3">{{ bill.bill_number }}</td>
|
||||
<td class="align-middle">
|
||||
{% if bill.is_draft %}
|
||||
<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 }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="align-middle text-end py-3 pe-3">
|
||||
{{bill.vendor.vendor_name}}
|
||||
</td>
|
||||
<td class="align-middle text-end py-3 pe-3">
|
||||
<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>
|
||||
<div class="dropdown-menu dropdown-menu-end py-2">
|
||||
<a href="{% url 'bill_detail' bill.pk %}" class="dropdown-item text-success-dark">{% trans 'View' %}</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="5" class="text-center text-muted">
|
||||
{% trans 'No bill found.' %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between mt-3">
|
||||
<span class="d-none d-sm-inline-block" data-list-info="data-list-info">{{ page_obj.start_index }} {{ _('to') }} {{ page_obj.end_index }}<span class="text-body-tertiary">{{ _('Items of') }}</span>{{ page_obj.paginator.count }}</span>
|
||||
<div class="d-flex">
|
||||
<nav aria-label="Page navigation">
|
||||
<ul class="pagination mb-0">
|
||||
{% if page_obj.has_previous %}
|
||||
<li class="page-item py-0">
|
||||
<a class="page-link" href="?page={{ page_obj.previous_page_number }}" aria-label="Previous"><span aria-hidden="true"><span class="fas fa-chevron-left"></span></span></a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="Previous"><span aria-hidden="true"><span class="fas fa-chevron-left"></span></span></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% for num in page_obj.paginator.page_range %}
|
||||
{% if page_obj.number == num %}
|
||||
<li class="page-item active">
|
||||
<a class="page-link" href="?page={{ num }}">{{ num }}</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ num }}">{{ num }}</a>
|
||||
</li>
|
||||
<button class="btn btn-sm btn-secondary rounded-start" type="submit">
|
||||
{% trans "search" %}
|
||||
</button>
|
||||
<input type="text"
|
||||
name="q"
|
||||
class="form-control form-control-sm rounded-end"
|
||||
value="{{ request.GET.q }}"
|
||||
placeholder="{% trans 'Search bills...' %}" />
|
||||
{% if request.GET.q %}
|
||||
<a href="{% url request.resolver_match.view_name %}"
|
||||
class="btn btn-sm btn-outline-danger ms-1 rounded">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if page_obj.has_next %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ page_obj.next_page_number }}" aria-label="Next"><span aria-hidden="true"><span class="fas fa-chevron-right"></span></span></a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="Next"><span aria-hidden="true"><span class="fas fa-chevron-right"></span></span></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="table-responsive px-1 scrollbar mt-3">
|
||||
<table class="table align-items-center table-flush">
|
||||
<thead>
|
||||
<tr class="bg-body-highlight">
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans 'Bill Number' %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans 'Bill Status' %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans 'Vendor' %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans 'Action'%}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody class="list">
|
||||
{% for bill in bills %}
|
||||
|
||||
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{{ bill.bill_number }}
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{% if bill.is_draft %}
|
||||
<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 }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{{bill.vendor.vendor_name}}
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
<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>
|
||||
<div class="dropdown-menu dropdown-menu-end py-2">
|
||||
<a href="{% url 'bill_detail' bill.pk %}" class="dropdown-item text-success-dark">{% trans 'View' %}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="5" class="text-center text-muted">
|
||||
{% trans 'No bill found.' %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mt-3"><span class="d-none d-sm-inline-block" data-list-info="data-list-info">{{ page_obj.start_index }} {{ _("to") }} {{ page_obj.end_index }}<span class="text-body-tertiary"> {{ _("Items of")}} </span>{{ page_obj.paginator.count }}</span>
|
||||
<div class="d-flex">
|
||||
|
||||
{% if is_paginated %}
|
||||
{% include 'partials/pagination.html' %}
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Delete Modal -->
|
||||
|
||||
<div class="modal fade" id="deleteModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<div class="modal-content rounded">
|
||||
@ -56,58 +56,82 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm fs-9 mb-0">
|
||||
<tr>
|
||||
<th class="has-text-centered">{{ _('JE Number') }}</th>
|
||||
<th class="has-text-centered">{{ _('Date') }}</th>
|
||||
<th class="has-text-centered">{{ _('Debit') }}</th>
|
||||
<th class="has-text-centered">{{ _('Credit') }}</th>
|
||||
<th class="has-text-centered">{{ _('Description') }}</th>
|
||||
|
||||
<div class="row mt-4">
|
||||
|
||||
<div class="table-responsive px-1 scrollbar mt-3">
|
||||
<table class="table align-items-center table-flush">
|
||||
<thead>
|
||||
<tr class="bg-body-highlight">
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{{ _('JE Number') }}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{{ _('Date') }}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{{ _('Debit') }}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{{ _('Credit') }}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{{ _('Description') }}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{{ _('Actions') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="list">
|
||||
{% for tx in account.transactionmodel_set.all %}
|
||||
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{{ tx.journal_entry.je_number }}
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{{ tx.journal_entry.timestamp }}
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{% if tx.tx_type == 'debit' %}
|
||||
<i class="fa-solid fa-circle-up"></i> {{ tx.amount }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{% if tx.tx_type == 'credit' %}
|
||||
<i class="fa-solid fa-circle-down"></i> {{ tx.amount }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{{ tx.description }}
|
||||
</td>
|
||||
<td class="align-middle white-space-nowrap text-start">
|
||||
<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>
|
||||
<div class="dropdown-menu dropdown-menu-end py-2">
|
||||
<a class="dropdown-item" href="{% url 'payment_details' tx.journal_entry.pk %}">{% trans 'view'|capfirst %}</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
<th class="has-text-centered">{{ _('Actions') }}</th>
|
||||
</tr>
|
||||
|
||||
{% for tx in account.transactionmodel_set.all %}
|
||||
<tr class="has-text-centered">
|
||||
<td>{{ tx.journal_entry.je_number }}</td>
|
||||
<td>{{ tx.journal_entry.timestamp }}</td>
|
||||
<td class="text-success">
|
||||
{% if tx.tx_type == 'debit' %}
|
||||
<i class="fa-solid fa-circle-up"></i> {{ tx.amount }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-danger">
|
||||
{% if tx.tx_type == 'credit' %}
|
||||
<i class="fa-solid fa-circle-down"></i> {{ tx.amount }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ tx.description }}</td>
|
||||
|
||||
<td>
|
||||
<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>
|
||||
<div class="dropdown-menu dropdown-menu-end py-2">
|
||||
<a class="dropdown-item" href="{% url 'payment_details' tx.journal_entry.pk %}">{% trans 'view'|capfirst %}</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr class="has-text-weight-bold">
|
||||
<td></td>
|
||||
<td class="has-text-right"><span class="fw-bold fs-8">{{ _("Total") }}</span></td>
|
||||
<td class="has-text-centered"><span class="fw-bold fs-8 text-success">{{ total_debits }} <span class="currency">{{ CURRENCY }}</span></span></td>
|
||||
<td class="has-text-centered"><span class="fw-bold fs-8 text-danger">{{ total_credits }} <span class="currency">{{ CURRENCY }}</span></span></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
<span class="fw-bold fs-8">{{ _("Total") }}</span>
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
<span class="fw-bold fs-8 text-success">{{ total_debits }} <span class="currency">{{ CURRENCY }}</span></span>
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
<span class="fw-bold fs-8 text-danger">{{ total_credits }} <span class="currency">{{ CURRENCY }}</span></span>
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
|
||||
</td>
|
||||
<td class="align-middle white-space-nowrap text-start">
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="mt-3 d-flex">
|
||||
<a class="btn btn-sm btn-phoenix-primary me-1" href="{% url 'account_update' account.pk %}">
|
||||
<!-- <i class="bi bi-pencil-square"></i> -->
|
||||
@ -122,6 +146,8 @@
|
||||
<i class="fa-regular fa-circle-left"></i> {% trans 'Back to List' %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!--test-->
|
||||
{% endblock %}
|
||||
|
||||
@ -8,16 +8,12 @@
|
||||
</a>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<!-- Search Bar -->
|
||||
<!--test-->
|
||||
<div class="row mt-4">
|
||||
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
|
||||
<a href="{% url 'account_create' %}" class="btn btn-md btn-phoenix-primary">
|
||||
<i class="fa fa-plus me-2"></i>
|
||||
{% trans 'New Account' %}</a>
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<h3 class=""><i class="fa-solid fa-book"></i> {% trans "Accounts" %}</h3>
|
||||
<a href="{% url 'account_create' %}" class="btn btn-md btn-phoenix-primary"><i class="fa fa-plus me-2"></i>{% trans 'New Account' %}</a>
|
||||
</div>
|
||||
<h3 class="mb-3">
|
||||
<i class="fa-solid fa-book"></i> {% trans "Accounts" %}</h3>
|
||||
<div class="col-12">
|
||||
<form method="get" class=" mb-4">
|
||||
<div class="input-group input-group-sm">
|
||||
@ -38,25 +34,24 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% if page_obj.object_list %}
|
||||
|
||||
|
||||
|
||||
{% if page_obj.object_list %}
|
||||
<div class="table-responsive px-1 scrollbar mt-3">
|
||||
<table class="table align-items-center table-flush">
|
||||
<thead>
|
||||
|
||||
<tr class="bg-body-highlight">
|
||||
<th class="border-top border-translucent">{% trans "Type" %}</th>
|
||||
<th class="border-top border-translucent ps-3">{% trans "Account Name" %}</th>
|
||||
<th class="border-top border-translucent">{% trans "Code" %}</th>
|
||||
<th class="border-top border-translucent text-end pe-3">{% trans "Balance Type" %}</th>
|
||||
<th class="border-top border-translucent text-end pe-3">{% trans "Active" %}</th>
|
||||
<th class="border-top border-translucent text-end align-middle pe-0 ps-4" scope="col"></th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Type" %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Account Name" %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Code" %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Balance Type" %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Active" %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="list">
|
||||
{% for account in accounts %}
|
||||
|
||||
|
||||
{% for account in accounts %}
|
||||
|
||||
<div class="modal fade" id="deleteModal"
|
||||
data-bs-backdrop="static"
|
||||
@ -91,26 +86,33 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<tr>
|
||||
<td class="align-middle ps-3 fw-lighter">{{ account.role_bs|upper }}</td>
|
||||
<td class="align-middle ps-3">{{ account.name }}</td>
|
||||
<td class="align-middle">{{ account.code }}</td>
|
||||
<td class="align-middle text-end py-3 pe-3">
|
||||
|
||||
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{{ account.role_bs|upper }}
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{{ account.name }}
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{{ account.code }}
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
|
||||
{% if account.balance_type == 'debit' %}
|
||||
<div class="badge badge-phoenix fs-10 badge-phoenix-success"><span class="fw-bold"><i class="fa-solid fa-circle-up"></i> {{ _("Debit") }}</span></div>
|
||||
{% else %}
|
||||
<div class="badge badge-phoenix fs-10 badge-phoenix-danger"><span class="fw-bold"><i class="fa-solid fa-circle-down"></i> {{ _("Credit") }}</span></div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="align-middle text-end py-3 pe-3">
|
||||
{% if account.active %}
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{% if account.active %}
|
||||
<span class="fw-bold text-success fas fa-check-circle"></span>
|
||||
{% else %}
|
||||
<span class="fw-bold text-danger far fa-times-circle"></span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<td class="align-middle white-space-nowrap text-start">
|
||||
<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>
|
||||
<div class="dropdown-menu dropdown-menu-end py-2"><a href="{% url 'account_detail' account.uuid %}" class="dropdown-item text-success-dark">
|
||||
@ -118,21 +120,17 @@
|
||||
</a>
|
||||
<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>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="5" class="text-center text-muted">
|
||||
{% trans "No account found." %}
|
||||
</td>
|
||||
<td colspan="6" class="text-center text-muted">{% trans "No Accounts Found" %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between mt-3"><span class="d-none d-sm-inline-block" data-list-info="data-list-info">{{ page_obj.start_index }} {{ _("to") }} {{ page_obj.end_index }}<span class="text-body-tertiary"> {{ _("Items of")}} </span>{{ page_obj.paginator.count }}</span>
|
||||
<div class="d-flex">
|
||||
|
||||
@ -142,7 +140,11 @@
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
</div>
|
||||
<!--test-->
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
||||
@ -31,33 +31,20 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--test-->
|
||||
<div class="row mt-4">
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<h3 class="">{% trans "Journal Entries" %}</h3>
|
||||
<div class="d-flex gap-2">
|
||||
{% comment %} {% if ledger.is_posted %}
|
||||
{% if ledger.is_locked %}
|
||||
<a class="btn btn-sm btn-phoenix-danger" href="{% url 'unlock_all_journals' ledger.entity_slug ledger.pk %} ">{% trans 'UnLock All' %}</a>
|
||||
{% else %}
|
||||
<a class="btn btn-sm btn-phoenix-danger" href="{% url 'lock_all_journals' ledger.entity_slug ledger.pk %} ">{% trans 'Lock All' %}</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if not ledger.is_locked %}
|
||||
{% if ledger.is_posted %}
|
||||
<a class="btn btn-sm btn-phoenix-success" href="{% url 'unpost_all_journals' ledger.entity_slug ledger.pk %} ">{% trans 'UnPost All' %}</a>
|
||||
{% else %}
|
||||
<a class="btn btn-sm btn-phoenix-success" href="{% url 'post_all_journals' ledger.entity_slug ledger.pk %} ">{% trans 'Post All' %}</a>
|
||||
{% endif %}
|
||||
{% endif %} {% endcomment %}
|
||||
<a href="{% url 'journalentry_create' ledger.pk %}" class="btn btn-sm btn-phoenix-primary">{% trans "Add Journal Entry" %}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive px-1 scrollbar">
|
||||
<table class="table fs-9 mb-0 border-top border-translucent">
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<h3 class="">{% trans "Journal Entries" %}</h3>
|
||||
<a href="{% url 'journalentry_create' ledger.pk %}" class="btn btn-md btn-phoenix-primary"><i class="fa fa-plus me-2"></i>{% trans "Add Journal Entry" %}</a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="table-responsive px-1 scrollbar">
|
||||
<table class="table align-items-center table-flush">
|
||||
<thead>
|
||||
<tr>
|
||||
<tr class="bg-body-highlight">
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Document Number" %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Timestamp" %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Activity" %}</th>
|
||||
@ -126,8 +113,10 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="d-flex justify-content-center">
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<!--test-->
|
||||
|
||||
|
||||
{% endblock %}
|
||||
@ -11,9 +11,9 @@
|
||||
<h3 class="mb-3 mt-5"><i class="fa-solid fa-right-left"></i> {% trans "Transactions" %}</h3>
|
||||
|
||||
<div class="table-responsive px-1 scrollbar">
|
||||
<table class="table fs-9 mb-0 border-top border-translucent">
|
||||
<table class="table align-items-center table-flush">
|
||||
<thead>
|
||||
<tr>
|
||||
<tr class="bg-body-highlight">
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "#" %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Timestamp" %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Account Name" %}</th>
|
||||
|
||||
@ -4,18 +4,41 @@
|
||||
{% block title %}{{ _("Ledger") }}{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
|
||||
<div class="row mt-4">
|
||||
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<h3 class="">{% trans "Ledger" %}</h3>
|
||||
<h3 class="">{% trans "Ledger" %}</h3>
|
||||
<a href="{% url 'ledger_create' %}" class="btn btn-md btn-phoenix-primary"><i class="fa fa-plus me-2"></i>{% trans 'Create Ledger' %}</a>
|
||||
</div>
|
||||
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
|
||||
<a href="{% url 'ledger_create' %}" class="btn btn-md btn-phoenix-primary">
|
||||
<i class="fa fa-plus me-2"></i>
|
||||
{% trans 'Create Ledger' %}</a>
|
||||
|
||||
<div class="col-12">
|
||||
<form method="get" class=" mb-4">
|
||||
<div class="input-group input-group-sm">
|
||||
<button class="btn btn-sm btn-secondary rounded-start" type="submit">
|
||||
{% trans "search" %}
|
||||
</button>
|
||||
<input type="text"
|
||||
name="q"
|
||||
class="form-control form-control-sm rounded-end"
|
||||
value="{{ request.GET.q }}"
|
||||
placeholder="{% trans 'Search ledgers...' %}" />
|
||||
{% if request.GET.q %}
|
||||
<a href="{% url request.resolver_match.view_name %}"
|
||||
class="btn btn-sm btn-outline-danger ms-1 rounded">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="table-responsive px-1 scrollbar mt-3">
|
||||
<table class="table fs-9 mb-0 border-top border-translucent">
|
||||
<table class="table align-items-center table-flush">
|
||||
<thead>
|
||||
|
||||
<tr class="bg-body-highlight">
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Ledger Name" %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Journal Entries" %}</th>
|
||||
@ -25,11 +48,13 @@
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Action" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody class="list">
|
||||
{% for ledger in ledgers %}
|
||||
|
||||
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{% if ledger.invoicemodel %}
|
||||
{% if ledger.invoicemodel %}
|
||||
<a href="{% url 'invoice_detail' ledger.invoicemodel.pk %}">{{ ledger.get_wrapped_model_instance }}</a>
|
||||
{% elif ledger.billmodel %}
|
||||
<a href="{% url 'bill_detail' ledger.billmodel.pk %}">{{ ledger.get_wrapped_model_instance }}</a>
|
||||
@ -38,7 +63,7 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
<a class="btn btn-sm btn-primary"
|
||||
<a class="btn btn-sm btn-primary"
|
||||
href="{% url 'journalentry_list' ledger.pk %}">
|
||||
<i class="fa-solid fa-right-left"></i>
|
||||
<span>
|
||||
@ -48,21 +73,21 @@
|
||||
</a>
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{{ ledger.created |date }}
|
||||
{{ ledger.created |date }}
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{% if ledger.is_posted %}
|
||||
{% if ledger.is_posted %}
|
||||
<i class="fa-solid fa-square-check text-success"></i>
|
||||
{% else %}
|
||||
<i class="fa-solid fa-circle-xmark text-danger"></i>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{% if ledger.is_locked %}
|
||||
{% if ledger.is_locked %}
|
||||
<i class="fa-solid fa-lock text-success"></i>
|
||||
{% else %}
|
||||
<i class="fa-solid fa-unlock text-danger"></i>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="align-middle white-space-nowrap text-start">
|
||||
<div class="btn-reveal-trigger position-static">
|
||||
@ -108,19 +133,30 @@
|
||||
class="dropdown-item has-text-danger has-text-weight-bold">{% trans 'Delete' %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="6" class="text-center">{% trans "No Bank Accounts Found" %}</td>
|
||||
<td colspan="6" class="text-center">{% trans "No Entries found" %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="d-flex justify-content-center">
|
||||
|
||||
<div class="d-flex justify-content-between mt-3"><span class="d-none d-sm-inline-block" data-list-info="data-list-info">{{ page_obj.start_index }} {{ _("to") }} {{ page_obj.end_index }}<span class="text-body-tertiary"> {{ _("Items of")}} </span>{{ page_obj.paginator.count }}</span>
|
||||
<div class="d-flex">
|
||||
|
||||
{% if is_paginated %}
|
||||
{% include 'partials/pagination.html' %}
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
243
templates/vendors/vendors_list.html
vendored
243
templates/vendors/vendors_list.html
vendored
@ -5,128 +5,143 @@
|
||||
{% block vendors %}<a class="nav-link active">{{ _("Vendors")|capfirst }}</a>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="pt-5 pb-9">
|
||||
|
||||
<div class="row">
|
||||
|
||||
<h2 class="mb-4">{{ _("Vendors")|capfirst }}</h2>
|
||||
|
||||
<div class="row g-3 justify-content-between mb-4">
|
||||
<div class="col-auto">
|
||||
<div class="d-md-flex justify-content-between">
|
||||
<div>
|
||||
<a href="{% url 'vendor_create' %}" class="btn btn-md btn-phoenix-primary"><span class="fas fa-plus me-2"></span>{{ _("Add Vendor") }}</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<h3 class="">{{ _("Vendors")|capfirst }}</h2>
|
||||
<a href="{% url 'vendor_create' %}" class="btn btn-md btn-phoenix-primary"><i class="fa fa-plus me-2"></i>{{ _("Add Vendor") }}</a>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="d-flex">
|
||||
<div class="search-box me-2">
|
||||
<form method="get" class="d-inline-block position-relative">
|
||||
<input name="q" class="form-control search-input search" type="search" placeholder="{{ _('Enter vendor name') }}" aria-label="Search" value="{{ request.GET.q }}"/>
|
||||
<span class="fas fa-search search-box-icon"></span>
|
||||
{% if request.GET.q %}
|
||||
<a href="{% url request.resolver_match.view_name %}" class="btn btn-outline-danger ms-1">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<form method="get" class=" mb-4">
|
||||
<div class="input-group input-group-sm">
|
||||
<button class="btn btn-sm btn-secondary rounded-start" type="submit">
|
||||
{% trans "search" %}
|
||||
</button>
|
||||
<input type="text"
|
||||
name="q"
|
||||
class="form-control form-control-sm rounded-end"
|
||||
value="{{ request.GET.q }}"
|
||||
placeholder="{{ _('Enter vendor name') }}" />
|
||||
{% if request.GET.q %}
|
||||
<a href="{% url request.resolver_match.view_name %}"
|
||||
class="btn btn-sm btn-outline-danger ms-1 rounded">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% if page_obj.object_list %}
|
||||
<div class="table-responsive scrollbar mx-n1 px-1">
|
||||
|
||||
<table class="table table-hover fs-9 mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sort white-space-nowrap align-middle text-uppercase ps-0" scope="col" data-sort="name" style="width:25%;">{{ _("Name")|capfirst }}</th>
|
||||
<th class="sort align-middle ps-4 pe-5 text-uppercase border-end border-translucent" scope="col" data-sort="email" style="width:15%;">
|
||||
<div class="d-inline-flex flex-center">
|
||||
<div class="d-flex align-items-center px-1 py-1 bg-success-subtle rounded me-2"><span class="text-success-dark" data-feather="mail"></span></div><span>{{ _("email")|capfirst }}</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="sort align-middle ps-4 pe-5 text-uppercase border-end border-translucent" scope="col" data-sort="phone" style="width:15%; min-width: 180px;">
|
||||
<div class="d-inline-flex flex-center">
|
||||
<div class="d-flex align-items-center px-1 py-1 bg-primary-subtle rounded me-2"><span class="text-primary-dark" data-feather="phone"></span></div><span>{{ _("Phone") }}</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="sort align-middle ps-4 pe-5 text-uppercase border-end border-translucent" scope="col" data-sort="contact" style="width:15%;">
|
||||
<div class="d-inline-flex flex-center">
|
||||
<div class="d-flex align-items-center px-1 py-1 bg-info-subtle rounded me-2"><span class="text-info-dark" data-feather="user"></span></div><span>{{ _("Contact name")|capfirst }}</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="sort align-middle ps-4 pe-5 text-uppercase border-end border-translucent" scope="col" data-sort="company" style="width:15%;">
|
||||
<div class="d-inline-flex flex-center">
|
||||
<div class="d-flex align-items-center px-1 py-1 bg-warning-subtle rounded me-2"><span class="text-warning-dark" data-feather="grid"></span></div><span>{{ _("Address")|capfirst }}</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="sort align-middle ps-4 pe-5 text-uppercase" scope="col" data-sort="date" style="width:15%;">
|
||||
{{ _("Create date") }}</th>
|
||||
<th class="sort text-end align-middle pe-0 ps-4" scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="list" id="leal-tables-body">
|
||||
|
||||
|
||||
{% for vendor in vendors %}
|
||||
<!-- Delete Modal -->
|
||||
|
||||
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
|
||||
|
||||
<td class="name align-middle white-space-nowrap ps-0">
|
||||
<div class="d-flex align-items-center">
|
||||
{% if vendor.logo %}
|
||||
<div class="avatar avatar-xl me-3"><img class="rounded-circle" src="{{ vendor.logo.url }}" alt="" />
|
||||
{% else %}
|
||||
<div class="avatar avatar-xl me-3"><img class="rounded-circle" src="{% static 'images/icons/picture.svg' %}" alt="" />
|
||||
{% endif %}
|
||||
{% if page_obj.object_list %}
|
||||
<div class="table-responsive px-1 scrollbar mt-3">
|
||||
<table class= "table align-items-center table-flush table-hover">
|
||||
<thead>
|
||||
|
||||
<tr class="bg-body-highlight">
|
||||
<th class="sort white-space-nowrap align-middle" scope="col" data-sort="name" style="width:25%;">{{ _("Name")|capfirst }}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col" data-sort="email" style="width:15%;">
|
||||
<div class="d-inline-flex flex-center">
|
||||
<div class="d-flex align-items-center px-1 py-1 bg-success-subtle rounded me-2"><span class="text-success-dark" data-feather="mail"></span></div><span>{{ _("email")|capfirst }}</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col" data-sort="phone" style="width:15%; min-width: 180px;">
|
||||
<div class="d-inline-flex flex-center">
|
||||
<div class="d-flex align-items-center px-1 py-1 bg-primary-subtle rounded me-2"><span class="text-primary-dark" data-feather="phone"></span></div><span>{{ _("Phone") }}</span>
|
||||
</div>
|
||||
<div><a class="fs-8 fw-bold" href="{% url 'vendor_detail' vendor.slug%}">{{ vendor.arabic_name }}</a>
|
||||
<div class="d-flex align-items-center">
|
||||
<p class="mb-0 text-body-highlight fw-semibold fs-9 me-2">{{ vendor.name}}</p><!--<span class="badge badge-phoenix badge-phoenix-primary">{{ vendor.vendor_model.uuid }}</span>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</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 }}">{{ vendor.phone_number }}</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="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>
|
||||
<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="align-middle white-space-nowrap text-end pe-0 ps-4">
|
||||
<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>
|
||||
<div class="dropdown-menu dropdown-menu-end py-2">
|
||||
<a href="{% url 'vendor_update' vendor.slug %}" class="dropdown-item text-success-dark">
|
||||
{% trans "Edit" %}
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<button class="delete-btn dropdown-item text-danger"
|
||||
data-url="{% url 'vendor_delete' vendor.slug %}"
|
||||
data-message="{{ _("Are you sure you want to delete this vendor")}}?"
|
||||
data-bs-toggle="modal" data-bs-target="#deleteModal">
|
||||
{{ _("Delete") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col" data-sort="contact" style="width:15%;">
|
||||
<div class="d-inline-flex flex-center">
|
||||
<div class="d-flex align-items-center px-1 py-1 bg-info-subtle rounded me-2"><span class="text-info-dark" data-feather="user"></span></div><span>{{ _("Contact name")|capfirst }}</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col" data-sort="company" style="width:15%;">
|
||||
<div class="d-inline-flex flex-center">
|
||||
<div class="d-flex align-items-center px-1 py-1 bg-warning-subtle rounded me-2"><span class="text-warning-dark" data-feather="grid"></span></div><span>{{ _("Address")|capfirst }}</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col" data-sort="date" style="width:15%;">{{ _("Create date") }}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col"></th>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
{% endif %}
|
||||
</table>
|
||||
</div>
|
||||
<div class="row align-items-center justify-content-end py-4 pe-0 fs-9">
|
||||
<!-- Optional: Pagination -->
|
||||
{% if is_paginated %}
|
||||
{% include 'partials/pagination.html' %}
|
||||
</thead>
|
||||
<tbody class="list">
|
||||
{% for vendor in vendors %}
|
||||
|
||||
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
<div class="d-flex align-items-center">
|
||||
{% if vendor.logo %}
|
||||
<div class="avatar avatar-xl me-3"><img class="rounded-circle" src="{{ vendor.logo.url }}" alt="" />
|
||||
{% else %}
|
||||
<div class="avatar avatar-xl me-3"><img class="rounded-circle" src="{% static 'images/icons/picture.svg' %}" alt="" />
|
||||
{% endif %}
|
||||
</div>
|
||||
<div><a class="fs-8 fw-bold" href="{% url 'vendor_detail' vendor.slug %}">{{ vendor.arabic_name }}</a>
|
||||
<div class="d-flex align-items-center">
|
||||
<p class="mb-0 text-body-highlight fw-semibold fs-9 me-2">{{ vendor.name}}</p><!--<span class="badge badge-phoenix badge-phoenix-primary">{{ vendor.vendor_model.uuid }}</span>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
<a class="text-body-highlight" href="">{{ vendor.email }}</a>
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
<a class="text-body-highlight" href="tel:{{ vendor.phone }}">{{ vendor.phone_number }}</a>
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{{ vendor.contact_person }}
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{{ vendor.address }}
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{{ vendor.created_at|date }}
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
<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>
|
||||
<div class="dropdown-menu dropdown-menu-end py-2">
|
||||
<a href="{% url 'vendor_update' vendor.slug %}" class="dropdown-item text-success-dark">
|
||||
{% trans "Edit" %}
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<button class="delete-btn dropdown-item text-danger"
|
||||
data-url="{% url 'vendor_delete' vendor.slug %}"
|
||||
data-message="{{ _("Are you sure you want to delete this vendor")}}?"
|
||||
data-bs-toggle="modal" data-bs-target="#deleteModal">
|
||||
{{ _("Delete") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="6" class="text-center text-muted">{% trans "No Accounts Found" %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between mt-3"><span class="d-none d-sm-inline-block" data-list-info="data-list-info">{{ page_obj.start_index }} {{ _("to") }} {{ page_obj.end_index }}<span class="text-body-tertiary"> {{ _("Items of")}} </span>{{ page_obj.paginator.count }}</span>
|
||||
<div class="d-flex">
|
||||
|
||||
{% if is_paginated %}
|
||||
{% include 'partials/pagination.html' %}
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
{% include 'modal/delete_modal.html' %}
|
||||
{% endblock %}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user