Merge branch 'main' of http://10.10.1.136:3000/ismail/haikal into frontend

This commit is contained in:
Faheedkhan 2025-07-27 16:44:24 +03:00
commit ac9727dde4
21 changed files with 379 additions and 94 deletions

View File

@ -1,15 +1,15 @@
from inventory import views
from django.conf import settings
from django.contrib import admin from django.contrib import admin
from django.urls import path, include from django.urls import path, include
from django.conf.urls.static import static
from django.conf import settings
from django.conf.urls.i18n import i18n_patterns
from inventory import views
# from debug_toolbar.toolbar import debug_toolbar_urls
from inventory.notifications.sse import NotificationSSEApp
# import debug_toolbar
from schema_graph.views import Schema from schema_graph.views import Schema
from django.conf.urls.static import static
from django.conf.urls.i18n import i18n_patterns
from inventory.notifications.sse import NotificationSSEApp
# import debug_toolbar
# from two_factor.urls import urlpatterns as tf_urls # from two_factor.urls import urlpatterns as tf_urls
# from debug_toolbar.toolbar import debug_toolbar_urls
urlpatterns = [ urlpatterns = [
# path('__debug__/', include(debug_toolbar.urls)), # path('__debug__/', include(debug_toolbar.urls)),

View File

@ -1603,7 +1603,7 @@ class PermissionForm(forms.ModelForm):
"django_ledger.invoicemodel", "django_ledger.invoicemodel",
"django_ledger.vendormodel", "django_ledger.vendormodel",
"django_ledger.journalentrymodel" "django_ledger.journalentrymodel"
"django_ledger.purchaseordermodel", # TODO add purchase order "django_ledger.purchaseordermodel",
] ]
permissions = cache.get( permissions = cache.get(

View File

@ -0,0 +1,28 @@
from django.core.management.base import BaseCommand
from django.contrib.auth import get_user_model
import datetime
from inventory.models import Dealer
from plans.models import Plan, Order,PlanPricing
User = get_user_model()
class Command(BaseCommand):
help = ""
def handle(self, *args, **options):
dealer = Dealer.objects.get(user__email="dealer6@example.com")
user = dealer.user
user.userplan.expire = datetime.datetime.now().date()
user.userplan.save()
pp = PlanPricing.objects.get(plan__name="Basic")
order = Order.objects.create(
user=user,
plan=pp.plan,
pricing=pp.pricing,
amount=pp.price,
currency="SA",
tax=15,
status=1,
)
order.complete_order()
print(user.userplan)

View File

@ -0,0 +1,72 @@
from django.core.management.base import BaseCommand
from django.utils import timezone
from django.conf import settings
from django.template.loader import render_to_string
from plans.models import UserPlan, Order
from datetime import timedelta
from django.utils.translation import activate, get_language
from django_q.tasks import async_task
import logging
from inventory.tasks import send_bilingual_reminder, handle_email_result
logger = logging.getLogger(__name__)
class Command(BaseCommand):
help = "Handles subscription plan maintenance tasks"
def handle(self, *args, **options):
self.stdout.write("Starting plans maintenance...")
# 1. Send expiration reminders
self.send_expiration_reminders()
# 2. Deactivate expired plans
self.deactivate_expired_plans()
# 3. Clean up old incomplete orders
self.cleanup_old_orders()
self.stdout.write("Maintenance completed!")
def send_expiration_reminders(self):
"""Queue email reminders for expiring plans"""
reminder_days = getattr(settings, 'PLANS_EXPIRATION_REMIND', [3, 7, 14])
today = timezone.now().date()
for days in reminder_days:
target_date = today + timedelta(days=days)
expiring_plans = UserPlan.objects.filter(
active=True,
expire=target_date
).select_related('user', 'plan')
self.stdout.write(f"Queuing {days}-day reminders for {expiring_plans.count()} plans")
for user_plan in expiring_plans:
# Queue email task
async_task(
send_bilingual_reminder,
user_plan.user_id,
user_plan.plan_id,
user_plan.expire,
days,
hook=handle_email_result
)
def deactivate_expired_plans(self):
"""Deactivate plans that have expired (synchronous)"""
expired_plans = UserPlan.objects.filter(
active=True,
expire__lt=timezone.now().date()
)
count = expired_plans.update(active=False)
self.stdout.write(f"Deactivated {count} expired plans")
def cleanup_old_orders(self):
"""Delete incomplete orders older than 30 days"""
cutoff = timezone.now() - timedelta(days=30)
count, _ = Order.objects.filter(
created__lt=cutoff,
status=Order.STATUS.NEW
).delete()
self.stdout.write(f"Cleaned up {count} old incomplete orders")

View File

@ -321,7 +321,6 @@ class BasePurchaseOrderActionActionView(
f"User {user_username} attempting to call action '{self.action_name}' " f"User {user_username} attempting to call action '{self.action_name}' "
f"on Purchase Order ID: {po_model.pk} (Entity: {entity_slug})." f"on Purchase Order ID: {po_model.pk} (Entity: {entity_slug})."
) )
print(self.action_name)
if self.action_name == "mark_as_fulfilled": if self.action_name == "mark_as_fulfilled":
try: try:
if po_model.can_fulfill(): if po_model.can_fulfill():

View File

@ -24,6 +24,7 @@ from . import models
from django.utils.timezone import now from django.utils.timezone import now
from django.db import transaction from django.db import transaction
from django_q.tasks import async_task from django_q.tasks import async_task
from plans.signals import order_completed, activate_user_plan
# logging # logging
import logging import logging
@ -1153,3 +1154,10 @@ def bill_model_after_approve_notification(sender, instance, created, **kwargs):
please complete the bill payment. please complete the bill payment.
""", """,
) )
def handle_upgrade(sender, order, **kwargs):
logger.info(f"User {order.user} upgraded to {order.plan}")
order_completed.connect(handle_upgrade)

View File

@ -1,11 +1,18 @@
import logging import logging
from plans.models import Plan
from django.conf import settings
from django.db import transaction from django.db import transaction
from django_ledger.io import roles from django_ledger.io import roles
from django_q.tasks import async_task from django_q.tasks import async_task
from django.core.mail import send_mail from django.core.mail import send_mail
from appointment.models import StaffMember from appointment.models import StaffMember
from django.utils.translation import activate
from django.contrib.auth import get_user_model
from allauth.account.models import EmailAddress from allauth.account.models import EmailAddress
from django.core.mail import EmailMultiAlternatives
from inventory.models import DealerSettings, Dealer from inventory.models import DealerSettings, Dealer
from django.template.loader import render_to_string
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import User, Group, Permission from django.contrib.auth.models import User, Group, Permission
@ -1151,14 +1158,6 @@ def create_user_dealer(email, password, name, arabic_name, phone, crn, vrn, addr
user.set_password(password) user.set_password(password)
user.save() user.save()
#TODO remove this later
EmailAddress.objects.create(
user=user,
email=user.email,
verified=True,
primary=True
)
group = Group.objects.create(name=f"{user.pk}-Admin") group = Group.objects.create(name=f"{user.pk}-Admin")
user.groups.add(group) user.groups.add(group)
for perm in Permission.objects.filter( for perm in Permission.objects.filter(
@ -1195,3 +1194,64 @@ def create_user_dealer(email, password, name, arabic_name, phone, crn, vrn, addr
# instance.user.groups.add(group) # instance.user.groups.add(group)
# transaction.on_commit(run) # transaction.on_commit(run)
def send_bilingual_reminder(user_id, plan_id, expiration_date, days_until_expire):
"""Send bilingual email reminder using Django-Q"""
try:
user = User.objects.get(id=user_id)
plan = Plan.objects.get(id=plan_id)
# Determine user language preference
user_language = getattr(user, 'language', settings.LANGUAGE_CODE)
activate(user_language)
# Context data
context = {
'user': user,
'plan': plan,
'expiration_date': expiration_date,
'days_until_expire': days_until_expire,
'SITE_NAME': settings.SITE_NAME,
'RENEWAL_URL': "url" ,#settings.RENEWAL_URL,
'direction': 'rtl' if user_language.startswith('ar') else 'ltr'
}
# Subject with translation
subject_en = f"Your {plan.name} subscription expires in {days_until_expire} days"
subject_ar = f"اشتراكك في {plan.name} ينتهي خلال {days_until_expire} أيام"
# Render templates
text_content = render_to_string([
f'emails/expiration_reminder_{user_language}.txt',
'emails/expiration_reminder.txt'
], context)
html_content = render_to_string([
f'emails/expiration_reminder_{user_language}.html',
'emails/expiration_reminder.html'
], context)
# Create email
email = EmailMultiAlternatives(
subject=subject_ar if user_language.startswith('ar') else subject_en,
body=text_content,
from_email=settings.DEFAULT_FROM_EMAIL,
to=[user.email]
)
email.attach_alternative(html_content, "text/html")
email.send()
return f"Sent to {user.email} in {user_language}"
except Exception as e:
logger.error(f"Email failed: {str(e)}")
raise
def handle_email_result(task):
"""Callback for email results"""
if task.success:
logger.info(f"Email task succeeded: {task.result}")
else:
logger.error(f"Email task failed: {task.result}")

View File

@ -701,7 +701,7 @@ urlpatterns = [
), ),
path( path(
"<slug:dealer_slug>/ledgers/<slug:entity_slug>/delete/<uuid:ledger_pk>/", "<slug:dealer_slug>/ledgers/<slug:entity_slug>/delete/<uuid:ledger_pk>/",
views.LedgerModelDeleteView.as_view(), views.LedgerModelDeleteView,
name="ledger-delete", name="ledger-delete",
), ),
path( path(

View File

@ -4024,7 +4024,7 @@ class BankAccountDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailV
def get_queryset(self): def get_queryset(self):
dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"]) dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"])
query = self.request.GET.get("q") query = self.request.GET.get("q")
qs = self.model.objects.filter(entity=dealer.entity) qs = self.model.objects.filter(entity_model=dealer.entity)
if query: if query:
qs = apply_search_filters(qs, query) qs = apply_search_filters(qs, query)
return qs return qs
@ -4067,8 +4067,8 @@ class BankAccountUpdateView(
dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"]) dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"])
entity = dealer.entity entity = dealer.entity
kwargs = super().get_form_kwargs() kwargs = super().get_form_kwargs()
kwargs["entity_slug"] = entity.slug # Get entity_slug from URL kwargs["entity_slug"] = entity.slug
kwargs["user_model"] = entity.admin # Get user_model from the request kwargs["user_model"] = entity.admin
return kwargs return kwargs
def get_form(self, form_class=None): def get_form(self, form_class=None):
@ -4082,7 +4082,6 @@ class BankAccountUpdateView(
] ]
) )
form.fields["account_model"].queryset = account_qs form.fields["account_model"].queryset = account_qs
return form return form
def get_success_url(self): def get_success_url(self):
@ -8938,7 +8937,7 @@ class LedgerModelDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailV
permission_required = "django_ledger.view_ledgermodel" permission_required = "django_ledger.view_ledgermodel"
class LedgerModelCreateView(LedgerModelCreateViewBase): class LedgerModelCreateView(LedgerModelCreateViewBase,SuccessMessageMixin):
""" """
Handles the creation of LedgerModel entities. Handles the creation of LedgerModel entities.
@ -8954,6 +8953,7 @@ class LedgerModelCreateView(LedgerModelCreateViewBase):
template_name = "ledger/ledger/ledger_form.html" template_name = "ledger/ledger/ledger_form.html"
permission_required = ["django_ledger.add_ledgermodel"] permission_required = ["django_ledger.add_ledgermodel"]
success_message = _("Ledger created successfully")
def get_form(self, form_class=None): def get_form(self, form_class=None):
return LedgerModelCreateForm( return LedgerModelCreateForm(
@ -8963,10 +8963,11 @@ class LedgerModelCreateView(LedgerModelCreateViewBase):
) )
def form_valid(self, form): def form_valid(self, form):
form.field["entity"] = self.request.dealer.entity form.fields["entity"] = self.request.dealer.entity
return super().form_valid(form) return super().form_valid(form)
def get_success_url(self): def get_success_url(self):
messages.success(self.request, self.success_message)
return reverse( return reverse(
"ledger_list", "ledger_list",
kwargs={ kwargs={
@ -9000,34 +9001,47 @@ class LedgerModelModelActionView(LedgerModelModelActionViewBase):
) )
class LedgerModelDeleteView(LedgerModelDeleteViewBase, SuccessMessageMixin):
"""
Handles the deletion of a Ledger model instance.
Provides functionality for rendering a confirmation template and deleting a @login_required
ledger instance from the system. Extends functionality for managing success @permission_required("django_ledger.delete_ledgermodel", raise_exception=True)
messages and redirections upon successful deletion. def LedgerModelDeleteView(request, dealer_slug,entity_slug,ledger_pk):
ledger = LedgerModel.objects.filter(pk=ledger_pk).first()
if request.method == "POST":
ledger.delete()
messages.success(request, _("Ledger deleted successfully"))
return redirect("ledger_list", dealer_slug=dealer_slug, entity_slug=entity_slug)
return render(request,"ledger/ledger/ledger_delete.html",{"ledger_model":ledger})
# class LedgerModelDeleteView(DeleteView, SuccessMessageMixin):
# """
# Handles the deletion of a Ledger model instance.
:ivar template_name: Path to the template used for rendering the delete # Provides functionality for rendering a confirmation template and deleting a
confirmation view. # ledger instance from the system. Extends functionality for managing success
:type template_name: str # messages and redirections upon successful deletion.
:ivar success_message: Success message displayed upon successful deletion
of the ledger instance.
:type success_message: str
"""
template_name = "ledger/ledger/ledger_delete.html" # :ivar template_name: Path to the template used for rendering the delete
success_message = _("Ledger deleted successfully") # confirmation view.
permission_required = ["django_ledger.delete_ledgermodel"] # :type template_name: str
# :ivar success_message: Success message displayed upon successful deletion
# of the ledger instance.
# :type success_message: str
# """
def get_success_url(self): # template_name = "ledger/ledger/ledger_delete.html"
return reverse( # pk_url_kwarg = 'ledger_pk'
"ledger_list", # context_object_name = 'ledger_model'
kwargs={
"dealer_slug": self.kwargs["dealer_slug"], # success_message = _("Ledger deleted successfully")
"entity_slug": self.kwargs["entity_slug"], # permission_required = ["django_ledger.delete_ledgermodel"]
},
) # def get_success_url(self):
# return reverse(
# "ledger_list",
# kwargs={
# "dealer_slug": self.kwargs["dealer_slug"],
# "entity_slug": self.kwargs["entity_slug"],
# },
# )
class JournalEntryListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): class JournalEntryListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
@ -9121,7 +9135,7 @@ class JournalEntryCreateView(
@login_required @login_required
@permission_required("django_ledger.delete_journalentrymodel", raise_exception=True) @permission_required("django_ledger.delete_journalentrymodel", raise_exception=True)
def JournalEntryDeleteView(request, pk): def JournalEntryDeleteView(request,dealer_slug, pk):
""" """
Handles the deletion of a specific journal entry. This view facilitates Handles the deletion of a specific journal entry. This view facilitates
the deletion of a journal entry identified by its primary key (pk). If the the deletion of a journal entry identified by its primary key (pk). If the
@ -9142,10 +9156,10 @@ def JournalEntryDeleteView(request, pk):
ledger = journal_entry.ledger ledger = journal_entry.ledger
if not journal_entry.can_delete(): if not journal_entry.can_delete():
messages.error(request, _("Journal Entry cannot be deleted")) messages.error(request, _("Journal Entry cannot be deleted"))
return redirect("journalentry_list", pk=ledger.pk) return redirect("journalentry_list",dealer_slug=dealer_slug, pk=ledger.pk)
journal_entry.delete() journal_entry.delete()
messages.success(request, "Journal Entry deleted") messages.success(request, "Journal Entry deleted")
return redirect("journalentry_list", pk=ledger.pk) return redirect("journalentry_list",dealer_slug=dealer_slug, pk=ledger.pk)
return render( return render(
request, request,
"ledger/journal_entry/journal_entry_delete.html", "ledger/journal_entry/journal_entry_delete.html",
@ -9375,39 +9389,91 @@ def payment_callback(request, dealer_slug):
payment_id = request.GET.get("id") payment_id = request.GET.get("id")
history = models.PaymentHistory.objects.filter(transaction_id=payment_id).first() history = models.PaymentHistory.objects.filter(transaction_id=payment_id).first()
payment_status = request.GET.get("status") payment_status = request.GET.get("status")
order = Order.objects.filter(user=dealer.user, status=1).first() order = Order.objects.filter(user=dealer.user, status=1).first() # Status 1 = NEW
if payment_status == "paid": if payment_status == "paid":
# Get or create billing info (optional step)
billing_info, created = BillingInfo.objects.get_or_create( billing_info, created = BillingInfo.objects.get_or_create(
user=dealer.user, user=dealer.user,
tax_number=dealer.vrn, defaults={
name=dealer.arabic_name, 'tax_number': dealer.vrn,
street=dealer.address, 'name': dealer.arabic_name,
zipcode=dealer.entity.zip_code if dealer.entity.zip_code else " ", 'street': dealer.address,
city=dealer.entity.city if dealer.entity.city else " ", 'zipcode': dealer.entity.zip_code or " ",
country=dealer.entity.country if dealer.entity.country else " ", 'city': dealer.entity.city or " ",
'country': dealer.entity.country or " ",
}
) )
if created:
userplan = UserPlan.objects.create(
user=request.user,
plan=order.plan,
active=True,
)
userplan.initialize()
order.complete_order() try:
history.status = "paid" # COMPLETE THE ORDER - This handles plan activation/upgrade
history.save() order.complete_order() # Critical step: activates the plan
invoice = order.get_invoices().first()
return render( # Update payment history
request, "payment_success.html", {"order": order, "invoice": invoice} history.status = "paid"
) history.save()
# Retrieve invoice
invoice = order.get_invoices().first()
return render(
request,
"payment_success.html",
{"order": order, "invoice": invoice}
)
except Exception as e:
# Handle activation errors (log, notify admin, etc.)
logger.error(f"Plan activation failed: {str(e)}")
history.status = "failed"
history.save()
return render(request, "payment_failed.html", {"message": "Plan activation error"})
elif payment_status == "failed": elif payment_status == "failed":
history.status = "failed" history.status = "failed"
history.save() history.save()
return render(request, "payment_failed.html", {"message": message})
return render(request, "payment_failed.html", {"message": message}) # Handle unexpected status
return render(request, "payment_failed.html", {"message": "Unknown payment status"})
# def payment_callback(request, dealer_slug):
# message = request.GET.get("message")
# dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
# payment_id = request.GET.get("id")
# history = models.PaymentHistory.objects.filter(transaction_id=payment_id).first()
# payment_status = request.GET.get("status")
# order = Order.objects.filter(user=dealer.user, status=1).first()
# if payment_status == "paid":
# billing_info, created = BillingInfo.objects.get_or_create(
# user=dealer.user,
# tax_number=dealer.vrn,
# name=dealer.arabic_name,
# street=dealer.address,
# zipcode=dealer.entity.zip_code if dealer.entity.zip_code else " ",
# city=dealer.entity.city if dealer.entity.city else " ",
# country=dealer.entity.country if dealer.entity.country else " ",
# )
# if created:
# userplan = UserPlan.objects.create(
# user=request.user,
# plan=order.plan,
# active=True,
# )
# userplan.initialize()
# order.complete_order()
# history.status = "paid"
# history.save()
# invoice = order.get_invoices().first()
# return render(
# request, "payment_success.html", {"order": order, "invoice": invoice}
# )
# elif payment_status == "failed":
# history.status = "failed"
# history.save()
# return render(request, "payment_failed.html", {"message": message})
@login_required @login_required

View File

@ -1,9 +1,17 @@
{% extends "account/email/base_message.txt" %} {% extends "account/email/base_message.txt" %}
{% load account %} {% load account i18n %}
{% load i18n %}
{% block content %}{% autoescape off %}{% user_display user as user_display %}{% blocktranslate with site_name=current_site.name site_domain=current_site.domain %}You're receiving this email because user {{ user_display }} has given your email address to register an account on {{ site_domain }}.{% endblocktranslate %} {% block content %}{% autoescape off %}{% user_display user as user_display %}
{% blocktranslate with site_domain=current_site.domain %}تتلقى هذا البريد لأن {{ user_display }} استخدم بريدك للتسجيل في {{ site_domain }}.{% endblocktranslate %}
{% if code %}{% blocktranslate %}Your email verification code is listed below. Please enter it in your open browser window.{% endblocktranslate %} {% if code %}
{% blocktranslate %}أدخل رمز التحقق في المتصفح:{% endblocktranslate %}
{{ code }}{% else %}{% blocktranslate %}To confirm this is correct, go to {{ activate_url }}{% endblocktranslate %}{% endif %}{% endautoescape %}{% endblock content %} {{ code }}
{% blocktranslate %}ينتهي صلاحية هذا الرمز خلال {{ code_expiration }} دقيقة.{% endblocktranslate %}
{% else %}
{% blocktranslate %}أكد هذا التسجيل بالزيارة:{% endblocktranslate %}
{{ activate_url }}
{% endif %}{% endautoescape %}{% endblock content %}

View File

@ -27,6 +27,7 @@
<h3 class="mb-4">{% trans "Change Password" %}</h3> <h3 class="mb-4">{% trans "Change Password" %}</h3>
</div> </div>
<form method="post" <form method="post"
hx-boost="false"
action="{% url 'account_change_password' %}" action="{% url 'account_change_password' %}"
class="form needs-validation" class="form needs-validation"
novalidate> novalidate>

View File

@ -182,9 +182,9 @@ document.addEventListener('htmx:afterRequest', function(evt) {
return alert("Error: Could Not Find Resource"); return alert("Error: Could Not Find Resource");
} }
if (evt.detail.successful != true) { if (evt.detail.successful != true) {
console.log(evt) console.log(evt.detail.xhr.statusText)
/* Notify of an unexpected error, & print error to console */ /* Notify of an unexpected error, & print error to console */
notify("error", "Unexpected Error"); notify("error", `Unexpected Error ,${evt.detail.xhr.statusText}`);
} }
}); });
document.body.addEventListener('htmx:beforeSwap', function(evt) { document.body.addEventListener('htmx:beforeSwap', function(evt) {

View File

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<body style="font-family: 'Segoe UI', Tahoma, sans-serif; direction: rtl;">
<p>مرحباً {{ user.get_full_name }}،</p>
<p>
اشتراكك في <strong>{{ plan.name }}</strong> سينتهي خلال
{{ days_until_expire }} يوم في {{ expiration_date|date:"j F Y" }}.
</p>
<p>
<a href="{{ RENEWAL_URL }}">جدد اشتراكك الآن</a> لمواصلة الخدمة.
</p>
<p>مع أطيب التحيات،<br>
فريق {{ SITE_NAME }}</p>
</body>
</html>

View File

@ -0,0 +1,8 @@
مرحباً {{ user.get_full_name }}،
اشتراكك في {{ plan.name }} سينتهي خلال {{ days_until_expire }} يوم في {{ expiration_date|date:"j F Y" }}.
جدد اشتراكك الآن: {{ RENEWAL_URL }}
مع أطيب التحيات،
فريق {{ SITE_NAME }}

View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<body style="font-family: Arial, sans-serif; direction: {{ direction }};">
<p>Hello {{ user.get_full_name }},</p>
<p>Your <strong>{{ plan.name }}</strong> subscription will expire
in {{ days_until_expire }} days on {{ expiration_date|date:"F j, Y" }}.</p>
<p><a href="{{ RENEWAL_URL }}">Renew now</a> to continue service.</p>
<p>Best regards,<br>
The {{ SITE_NAME }} Team</p>
</body>
</html>

View File

@ -0,0 +1,8 @@
Hello {{ user.get_full_name }},
Your {{ plan.name }} subscription will expire in {{ days_until_expire }} days on {{ expiration_date|date:"F j, Y" }}.
Renew now: {{ RENEWAL_URL }}
Best regards,
{{ SITE_NAME }} Team

View File

@ -6,7 +6,7 @@
{{ _("Add New Expense") }} {{ _("Add New Expense") }}
{% endblock title %} {% endblock title %}
{% block content %} {% block content %}
<!----> <!---->
<div class="row justify-content-center mt-5 mb-3"> <div class="row justify-content-center mt-5 mb-3">
@ -19,7 +19,6 @@
</h3> </h3>
</div> </div>
<div class="card-body bg-light-subtle"> <div class="card-body bg-light-subtle">
<form method="post" action=""> <form method="post" action="">
{% csrf_token %} {% csrf_token %}
{{ form|crispy }} {{ form|crispy }}
@ -29,11 +28,7 @@
<button class="btn btn-lg btn-phoenix-primary md-me-2" type="submit"><i class="fa-solid fa-floppy-disk me-1"></i>{{ _("Save") }}</button> <button class="btn btn-lg btn-phoenix-primary md-me-2" type="submit"><i class="fa-solid fa-floppy-disk me-1"></i>{{ _("Save") }}</button>
<a href="{% url 'item_expense_list' request.dealer.slug %}" class="btn btn-lg btn-phoenix-secondary"><i class="fa-solid fa-ban me-1"></i>{% trans "Cancel" %}</a> <a href="{% url 'item_expense_list' request.dealer.slug %}" class="btn btn-lg btn-phoenix-secondary"><i class="fa-solid fa-ban me-1"></i>{% trans "Cancel" %}</a>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
</div> </div>

View File

@ -5,7 +5,7 @@
{{ _("Create Bill") }} {{ _("Create Bill") }}
{% endblock title %} {% endblock title %}
{% block content %} {% block content %}
<div class="row mt-4"> <div class="row mt-4" hx-boost="true">
<h3 class="text-center">{% trans "Create Bill" %}<span class="fas fa-money-bills ms-2 text-primary"></span></h3> <h3 class="text-center">{% trans "Create Bill" %}<span class="fas fa-money-bills ms-2 text-primary"></span></h3>
<form id="mainForm" method="post" class="needs-validation"> <form id="mainForm" method="post" class="needs-validation">
{% csrf_token %} {% csrf_token %}
@ -15,7 +15,7 @@
<button class="btn btn-sm btn-phoenix-success me-2" type="submit"> <button class="btn btn-sm btn-phoenix-success me-2" type="submit">
<i class="fa-solid fa-floppy-disk me-1"></i>{{ _("Save") }} <i class="fa-solid fa-floppy-disk me-1"></i>{{ _("Save") }}
</button> </button>
<a href="{{ request.META.HTTP_REFERER }}" <a href="{% url 'bill_list' request.dealer.slug %}"
class="btn btn-sm btn-phoenix-danger"><i class="fa-solid fa-ban me-1"></i>{% trans "Cancel" %}</a> class="btn btn-sm btn-phoenix-danger"><i class="fa-solid fa-ban me-1"></i>{% trans "Cancel" %}</a>
</div> </div>
</form> </form>

View File

@ -5,7 +5,7 @@
{% block content %} {% block content %}
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6"> <div class="col-md-6">
<form action="{% url 'journalentry_delete' journal_entry.pk %}" <form action="{% url 'journalentry_delete' request.dealer.slug journal_entry.pk %}"
method="post"> method="post">
{% csrf_token %} {% csrf_token %}
<div class="card"> <div class="card">
@ -13,7 +13,7 @@
<h5 class="card-title fw-light">Are you sure you want to delete?</h5> <h5 class="card-title fw-light">Are you sure you want to delete?</h5>
</div> </div>
<div class="card-body text-center"> <div class="card-body text-center">
<a href="{% url 'journalentry_list' journal_entry.ledger.pk %}" <a href="{% url 'journalentry_list' request.dealer.slug journal_entry.ledger.pk %}"
class="btn btn-phoenix-primary me-2">{% trans 'Go Back' %}</a> class="btn btn-phoenix-primary me-2">{% trans 'Go Back' %}</a>
<button type="submit" class="btn btn-phoenix-danger">{% trans 'Delete' %}</button> <button type="submit" class="btn btn-phoenix-danger">{% trans 'Delete' %}</button>
</div> </div>

View File

@ -27,7 +27,7 @@
{% for transaction in transactions %} {% for transaction in transactions %}
<tr class="hover-actions-trigger btn-reveal-trigger position-static"> <tr class="hover-actions-trigger btn-reveal-trigger position-static">
<td>{{ forloop.counter }}</td> <td>{{ forloop.counter }}</td>
<td class="align-middle product white-space-nowrap py-0">{{ transaction.created|date }}</td> <td class="align-middle product white-space-nowrap py-0">{{ transaction.created|date }}</td>
<td class="align-middle product white-space-nowrap">{{ transaction.account.name }}</td> <td class="align-middle product white-space-nowrap">{{ transaction.account.name }}</td>
<td class="align-middle product white-space-nowrap">{{ transaction.account.code }}</td> <td class="align-middle product white-space-nowrap">{{ transaction.account.code }}</td>
<td class="align-middle product white-space-nowrap text-success"> <td class="align-middle product white-space-nowrap text-success">

View File

@ -5,7 +5,7 @@
{% block content %} {% block content %}
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6"> <div class="col-md-6">
<form action="{% url 'ledger-delete' entity_slug=view.kwargs.entity_slug ledger_pk=ledger_model.uuid %}" <form action="{% url 'ledger-delete' dealer_slug=request.dealer.slug entity_slug=request.dealer.entity.slug ledger_pk=ledger_model.uuid %}"
method="post"> method="post">
{% csrf_token %} {% csrf_token %}
<div class="card"> <div class="card">
@ -13,7 +13,7 @@
<h5 class="card-title fw-light">{{ ledger_model.get_delete_message }}</h5> <h5 class="card-title fw-light">{{ ledger_model.get_delete_message }}</h5>
</div> </div>
<div class="card-body text-center"> <div class="card-body text-center">
<a href="{% url 'ledger_list' %}" class="btn btn-phoenix-primary me-2">{% trans 'Go Back' %}</a> <a href="{% url 'ledger_list' request.dealer.slug request.dealer.entity.slug %}" class="btn btn-phoenix-primary me-2">{% trans 'Go Back' %}</a>
<button type="submit" class="btn btn-phoenix-danger">{% trans 'Delete' %}</button> <button type="submit" class="btn btn-phoenix-danger">{% trans 'Delete' %}</button>
</div> </div>
</div> </div>