Merge branch 'main' of http://10.10.1.120:3000/tenhal_admin/haikal
This commit is contained in:
commit
d0c37a9722
Binary file not shown.
Binary file not shown.
@ -23,6 +23,7 @@ urlpatterns += i18n_patterns(
|
||||
path('', include('inventory.urls')),
|
||||
path('ledger/', include('django_ledger.urls', namespace='django_ledger')),
|
||||
path("haikalbot/", include("haikalbot.urls")),
|
||||
path('appointment/', include('appointment.urls')),
|
||||
)
|
||||
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,5 +1,4 @@
|
||||
from django_countries.widgets import CountrySelectWidget
|
||||
from django_ledger.models.bill import BillModel
|
||||
from phonenumber_field.formfields import PhoneNumberField
|
||||
from django.core.validators import MinLengthValidator
|
||||
from django.core.validators import RegexValidator
|
||||
@ -10,7 +9,6 @@ from phonenumber_field.phonenumber import PhoneNumber
|
||||
from .mixins import AddClassMixin
|
||||
from django.forms.models import inlineformset_factory
|
||||
from django_ledger.forms.invoice import InvoiceModelCreateForm as InvoiceModelCreateFormBase
|
||||
from django_ledger.forms.bill import BillModelCreateForm as BillModelCreateFormBase
|
||||
from .models import (
|
||||
Dealer,
|
||||
# Branch,
|
||||
@ -554,10 +552,7 @@ class ItemForm(forms.Form):
|
||||
|
||||
class PaymentForm(forms.Form):
|
||||
invoice = forms.ModelChoiceField(
|
||||
queryset=InvoiceModel.objects.all(), label="Invoice", required=False
|
||||
)
|
||||
bill = forms.ModelChoiceField(
|
||||
queryset=BillModel.objects.all(), label="Bill", required=False
|
||||
queryset=InvoiceModel.objects.all(), label="Invoice", required=True
|
||||
)
|
||||
amount = forms.DecimalField(label="Amount", required=True)
|
||||
payment_method = forms.ChoiceField(
|
||||
@ -575,17 +570,15 @@ class PaymentForm(forms.Form):
|
||||
|
||||
def clean_amount(self):
|
||||
invoice = self.cleaned_data['invoice']
|
||||
bill = self.cleaned_data['bill']
|
||||
model = invoice if invoice else bill
|
||||
amount = self.cleaned_data['amount']
|
||||
if amount + model.amount_paid > model.amount_due:
|
||||
raise forms.ValidationError("Payment amount is greater than amount due")
|
||||
if amount < invoice.amount_due:
|
||||
raise forms.ValidationError("Payment amount is greater than invoice amount due")
|
||||
if amount <= 0:
|
||||
raise forms.ValidationError("Payment amount must be greater than 0")
|
||||
if model.is_paid():
|
||||
if invoice.amount_due == invoice.amount_paid or invoice.invoice_status == "paid":
|
||||
raise forms.ValidationError("Invoice is already paid")
|
||||
if amount > model.amount_due:
|
||||
raise forms.ValidationError("Payment amount is greater than amount due")
|
||||
if amount > invoice.amount_due:
|
||||
raise forms.ValidationError("Payment amount is greater than invoice amount due")
|
||||
return amount
|
||||
|
||||
|
||||
@ -644,14 +637,4 @@ class InvoiceModelCreateForm(InvoiceModelCreateFormBase):
|
||||
self.fields['cash_account'].widget = forms.HiddenInput()
|
||||
self.fields['prepaid_account'].widget = forms.HiddenInput()
|
||||
self.fields['unearned_account'].widget = forms.HiddenInput()
|
||||
self.fields['date_draft'] = forms.DateField(widget=DateInput(attrs={'type': 'date'}))
|
||||
|
||||
class BillModelCreateForm(BillModelCreateFormBase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.fields['cash_account'].widget = forms.HiddenInput()
|
||||
self.fields['prepaid_account'].widget = forms.HiddenInput()
|
||||
self.fields['unearned_account'].widget = forms.HiddenInput()
|
||||
self.fields['date_draft'] = forms.DateField(widget=DateInput(attrs={'type': 'date'}))
|
||||
|
||||
self.fields['date_draft'] = forms.DateField(widget=DateInput(attrs={'type': 'date'}))
|
||||
@ -1,25 +0,0 @@
|
||||
# Generated by Django 4.2.17 on 2025-01-16 09:46
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('django_ledger', '0017_alter_accountmodel_unique_together_and_more'),
|
||||
('inventory', '0003_alter_carmake_car_type'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='InvoiceModelBase',
|
||||
fields=[
|
||||
],
|
||||
options={
|
||||
'proxy': True,
|
||||
'indexes': [],
|
||||
'constraints': [],
|
||||
},
|
||||
bases=('django_ledger.invoicemodel',),
|
||||
),
|
||||
]
|
||||
@ -1,164 +1,72 @@
|
||||
from django.urls import path
|
||||
from . import views
|
||||
from allauth.account import views as allauth_views
|
||||
from django.conf.urls import handler400, handler403, handler404, handler500
|
||||
from django.conf.urls import (
|
||||
handler400, handler403, handler404, handler500
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
# main URLs
|
||||
path("", views.HomeView.as_view(), name="landing_page"),
|
||||
path("welcome/", views.WelcomeView.as_view(), name="welcome"),
|
||||
path('', views.HomeView.as_view(), name='landing_page'),
|
||||
path('welcome/', views.WelcomeView.as_view(), name='welcome'),
|
||||
|
||||
# Accounts URLs
|
||||
path("login/", views.Login.as_view(), name="account_login"),
|
||||
path(
|
||||
"logout/",
|
||||
allauth_views.LogoutView.as_view(template_name="account/logout.html"),
|
||||
name="account_logout",
|
||||
),
|
||||
# path('signup/', allauth_views.SignupView.as_view(template_name='account/signup.html'), name='account_signup'),
|
||||
path("signup/", views.dealer_signup, name="account_signup"),
|
||||
path(
|
||||
"password/change/",
|
||||
allauth_views.PasswordChangeView.as_view(
|
||||
template_name="account/password_change.html"
|
||||
),
|
||||
name="account_change_password",
|
||||
),
|
||||
path(
|
||||
"password/reset/",
|
||||
allauth_views.PasswordResetView.as_view(
|
||||
template_name="account/password_reset.html"
|
||||
),
|
||||
name="account_reset_password",
|
||||
),
|
||||
path(
|
||||
"password/reset/done/",
|
||||
allauth_views.PasswordResetDoneView.as_view(
|
||||
template_name="account/password_reset_done.html"
|
||||
),
|
||||
name="account_password_reset_done",
|
||||
),
|
||||
path(
|
||||
"login/code/",
|
||||
allauth_views.RequestLoginCodeView.as_view(
|
||||
template_name="account/request_login_code.html"
|
||||
),
|
||||
),
|
||||
# Dashboards
|
||||
path(
|
||||
"dashboards/accounting/", views.AccountingDashboard.as_view(), name="accounting"
|
||||
),
|
||||
path("test/", views.TestView.as_view(), name="test"),
|
||||
path('login/', views.Login.as_view(), name='account_login'),
|
||||
path('logout/', allauth_views.LogoutView.as_view(template_name='account/logout.html'), name='account_logout'),
|
||||
# path('signup/', allauth_views.SignupView.as_view(template_name='account/signup.html'), name='account_signup'),
|
||||
path('signup/', views.dealer_signup, name='account_signup'),
|
||||
path('password/change/',
|
||||
allauth_views.PasswordChangeView.as_view(template_name='account/password_change.html'),
|
||||
name='account_change_password'),
|
||||
path('password/reset/',
|
||||
allauth_views.PasswordResetView.as_view(template_name='account/password_reset.html'),
|
||||
name='account_reset_password'),
|
||||
path('password/reset/done/',
|
||||
allauth_views.PasswordResetDoneView.as_view(template_name='account/password_reset_done.html'),
|
||||
name='account_password_reset_done'),
|
||||
path('login/code/', allauth_views.RequestLoginCodeView.as_view(template_name='account/request_login_code.html')),
|
||||
#Dashboards
|
||||
path('dashboards/accounting/', views.AccountingDashboard.as_view(), name='accounting'),
|
||||
path('test/', views.TestView.as_view(), name='test'),
|
||||
# Dealer URLs
|
||||
path("dealers/<int:pk>/", views.DealerDetailView.as_view(), name="dealer_detail"),
|
||||
path(
|
||||
"dealers/<int:pk>/update/",
|
||||
views.DealerUpdateView.as_view(),
|
||||
name="dealer_update",
|
||||
),
|
||||
path(
|
||||
"dealers/activity/",
|
||||
views.UserActivityLogListView.as_view(),
|
||||
name="dealer_activity",
|
||||
),
|
||||
path('dealers/<int:pk>/', views.DealerDetailView.as_view(), name='dealer_detail'),
|
||||
path('dealers/<int:pk>/update/', views.DealerUpdateView.as_view(), name='dealer_update'),
|
||||
path('dealers/activity/', views.UserActivityLogListView.as_view(), name='dealer_activity'),
|
||||
# path('dealers/<int:pk>/delete/', views.DealerDeleteView.as_view(), name='dealer_delete'),
|
||||
|
||||
# CRM URLs
|
||||
path("customers/", views.CustomerListView.as_view(), name="customer_list"),
|
||||
path(
|
||||
"customers/<int:pk>/",
|
||||
views.CustomerDetailView.as_view(),
|
||||
name="customer_detail",
|
||||
),
|
||||
path(
|
||||
"customers/create/", views.CustomerCreateView.as_view(), name="customer_create"
|
||||
),
|
||||
path(
|
||||
"customers/<int:pk>/update/",
|
||||
views.CustomerUpdateView.as_view(),
|
||||
name="customer_update",
|
||||
),
|
||||
path("customers/<int:pk>/delete/", views.delete_customer, name="customer_delete"),
|
||||
path(
|
||||
"customers/<int:customer_id>/opportunities/create/",
|
||||
views.OpportunityCreateView.as_view(),
|
||||
name="create_opportunity",
|
||||
),
|
||||
path(
|
||||
"customers/<int:pk>/add-note/",
|
||||
views.add_note_to_customer,
|
||||
name="add_note_to_customer",
|
||||
),
|
||||
path("crm/leads/", views.LeadListView.as_view(), name="lead_list"),
|
||||
path(
|
||||
"crm/leads/<int:pk>/view/", views.LeadDetailView.as_view(), name="lead_detail"
|
||||
),
|
||||
path("crm/leads/create/", views.LeadCreateView.as_view(), name="lead_create"),
|
||||
path(
|
||||
"crm/leads/<int:pk>/update/", views.LeadUpdateView.as_view(), name="lead_update"
|
||||
),
|
||||
path(
|
||||
"crm/leads/<int:pk>/delete/", views.LeadDeleteView.as_view(), name="lead_delete"
|
||||
),
|
||||
path("crm/leads/<int:pk>/add-note/", views.add_note_to_lead, name="add_note"),
|
||||
path(
|
||||
"crm/leads/<int:pk>/add-activity/",
|
||||
views.add_activity_to_lead,
|
||||
name="add_activity",
|
||||
),
|
||||
path(
|
||||
"crm/opportunities/create/",
|
||||
views.OpportunityCreateView.as_view(),
|
||||
name="opportunity_create",
|
||||
),
|
||||
path(
|
||||
"crm/opportunities/<int:pk>/",
|
||||
views.OpportunityDetailView.as_view(),
|
||||
name="opportunity_detail",
|
||||
),
|
||||
path(
|
||||
"crm/opportunities/<int:pk>/edit/",
|
||||
views.OpportunityUpdateView.as_view(),
|
||||
name="update_opportunity",
|
||||
),
|
||||
path(
|
||||
"crm/opportunities/",
|
||||
views.OpportunityListView.as_view(),
|
||||
name="opportunity_list",
|
||||
),
|
||||
path(
|
||||
"crm/opportunities/<int:pk>/delete/",
|
||||
views.delete_opportunity,
|
||||
name="delete_opportunity",
|
||||
),
|
||||
path('customers/', views.CustomerListView.as_view(), name='customer_list'),
|
||||
path('customers/<int:pk>/', views.CustomerDetailView.as_view(), name='customer_detail'),
|
||||
path('customers/create/', views.CustomerCreateView.as_view(), name='customer_create'),
|
||||
path('customers/<int:pk>/update/', views.CustomerUpdateView.as_view(), name='customer_update'),
|
||||
path('customers/<int:pk>/delete/', views.delete_customer, name='customer_delete'),
|
||||
path('customers/<int:customer_id>/opportunities/create/', views.OpportunityCreateView.as_view(), name='create_opportunity'),
|
||||
path('customers/<int:pk>/add-note/', views.add_note_to_customer, name='add_note_to_customer'),
|
||||
|
||||
path('crm/leads/', views.LeadListView.as_view(), name='lead_list'),
|
||||
path('crm/leads/<int:pk>/view/', views.LeadDetailView.as_view(), name='lead_detail'),
|
||||
path('crm/leads/create/', views.LeadCreateView.as_view(), name='lead_create'),
|
||||
path('crm/leads/<int:pk>/update/', views.LeadUpdateView.as_view(), name='lead_update'),
|
||||
path('crm/leads/<int:pk>/delete/', views.LeadDeleteView.as_view(), name='lead_delete'),
|
||||
path('crm/leads/<int:pk>/add-note/', views.add_note_to_lead, name='add_note'),
|
||||
path('crm/leads/<int:pk>/add-activity/', views.add_activity_to_lead, name='add_activity'),
|
||||
path('crm/opportunities/create/', views.OpportunityCreateView.as_view(), name='opportunity_create'),
|
||||
path('crm/opportunities/<int:pk>/', views.OpportunityDetailView.as_view(), name='opportunity_detail'),
|
||||
path('crm/opportunities/<int:pk>/edit/', views.OpportunityUpdateView.as_view(), name='update_opportunity'),
|
||||
path('crm/opportunities/', views.OpportunityListView.as_view(), name='opportunity_list'),
|
||||
path('crm/opportunities/<int:pk>/delete/', views.delete_opportunity, name='delete_opportunity'),
|
||||
# path('crm/opportunities/<int:pk>/logs/', views.OpportunityLogsView.as_view(), name='opportunity_logs'),
|
||||
path(
|
||||
"crm/notifications/",
|
||||
views.NotificationListView.as_view(),
|
||||
name="notifications_history",
|
||||
),
|
||||
path(
|
||||
"crm/fetch_notifications/",
|
||||
views.fetch_notifications,
|
||||
name="fetch_notifications",
|
||||
),
|
||||
path(
|
||||
"crm/notifications/<int:pk>/mark_as_read/",
|
||||
views.mark_notification_as_read,
|
||||
name="mark_notification_as_read",
|
||||
),
|
||||
# Vendor URLs
|
||||
path("vendors", views.VendorListView.as_view(), name="vendor_list"),
|
||||
path("vendors/<int:pk>/", views.VendorDetailView.as_view(), name="vendor_detail"),
|
||||
path("vendors/create/", views.VendorCreateView.as_view(), name="vendor_create"),
|
||||
path(
|
||||
"vendors/<int:pk>/update/",
|
||||
views.VendorUpdateView.as_view(),
|
||||
name="vendor_update",
|
||||
),
|
||||
path(
|
||||
"vendors/<int:pk>/delete/",
|
||||
views.VendorDetailView.as_view(),
|
||||
name="vendor_delete",
|
||||
),
|
||||
path('crm/notifications/', views.NotificationListView.as_view(), name='notifications_history'),
|
||||
path('crm/fetch_notifications/', views.fetch_notifications, name='fetch_notifications'),
|
||||
path('crm/notifications/<int:pk>/mark_as_read/', views.mark_notification_as_read, name='mark_notification_as_read'),
|
||||
|
||||
#Vendor URLs
|
||||
path('vendors', views.VendorListView.as_view(), name='vendor_list'),
|
||||
path('vendors/<int:pk>/', views.VendorDetailView.as_view(), name='vendor_detail'),
|
||||
path('vendors/create/', views.VendorCreateView.as_view(), name='vendor_create'),
|
||||
path('vendors/<int:pk>/update/', views.VendorUpdateView.as_view(), name='vendor_update'),
|
||||
path('vendors/<int:pk>/delete/', views.VendorDetailView.as_view(), name='vendor_delete'),
|
||||
|
||||
# Car URLs
|
||||
path("cars/inventory/", views.CarInventory.as_view(), name="car_inventory_all"),
|
||||
path(
|
||||
@ -218,302 +126,113 @@ urlpatterns = [
|
||||
),
|
||||
path("cars/inventory/search/", views.SearchCodeView.as_view(), name="car_search"),
|
||||
# path('cars/<int:car_pk>/colors/<int:pk>/update/',views.CarColorUpdateView.as_view(),name='color_update'),
|
||||
path("cars/reserve/<int:car_id>/", views.reserve_car_view, name="reserve_car"),
|
||||
path(
|
||||
"reservations/<int:reservation_id>/",
|
||||
views.manage_reservation,
|
||||
name="reservations",
|
||||
),
|
||||
path(
|
||||
"cars/<int:car_pk>/add-custom-card/",
|
||||
views.CustomCardCreateView.as_view(),
|
||||
name="add_custom_card",
|
||||
),
|
||||
|
||||
path('cars/reserve/<int:car_id>/', views.reserve_car_view, name='reserve_car'),
|
||||
path('reservations/<int:reservation_id>/', views.manage_reservation, name='reservations'),
|
||||
path('cars/<int:car_pk>/add-custom-card/', views.CustomCardCreateView.as_view(), name='add_custom_card'),
|
||||
|
||||
# Sales URLs quotation_create
|
||||
path(
|
||||
"sales/quotations/create/",
|
||||
views.QuotationCreateView.as_view(),
|
||||
name="quotation_create",
|
||||
),
|
||||
path(
|
||||
"sales/quotations/<int:pk>/",
|
||||
views.QuotationDetailView.as_view(),
|
||||
name="quotation_detail",
|
||||
),
|
||||
path("sales/quotations/", views.QuotationListView.as_view(), name="quotation_list"),
|
||||
path(
|
||||
"sales/quotations/<int:pk>/confirm/",
|
||||
views.confirm_quotation,
|
||||
name="confirm_quotation",
|
||||
),
|
||||
path(
|
||||
"sales/orders/detail/<int:order_id>/",
|
||||
views.SalesOrderDetailView.as_view(),
|
||||
name="order_detail",
|
||||
),
|
||||
path(
|
||||
"quotation/<int:quotation_id>/pdf/",
|
||||
views.download_quotation_pdf,
|
||||
name="quotation_pdf",
|
||||
),
|
||||
path("generate_invoice/<int:pk>/", views.generate_invoice, name="generate_invoice"),
|
||||
path(
|
||||
"sales/quotations/<int:pk>/mark_quotation/",
|
||||
views.mark_quotation,
|
||||
name="mark_quotation",
|
||||
),
|
||||
path(
|
||||
"sales/quotations/<int:pk>/post_quotation/",
|
||||
views.post_quotation,
|
||||
name="post_quotation",
|
||||
),
|
||||
path(
|
||||
"sales/quotations/<int:pk>/invoice_detail/",
|
||||
views.InvoiceDetailView.as_view(),
|
||||
name="invoice_detail",
|
||||
),
|
||||
path("subscriptions", views.SubscriptionPlans.as_view(), name="subscriptions"),
|
||||
# Payment URLs
|
||||
# path('sales/quotations/<int:pk>/payment/', views.PaymentCreateView.as_view(), name='payment_create'),
|
||||
path(
|
||||
"sales/quotations/<int:pk>/payment/",
|
||||
views.PaymentCreateView,
|
||||
name="payment_create",
|
||||
),
|
||||
path('sales/quotations/create/', views.QuotationCreateView.as_view(), name='quotation_create'),
|
||||
path('sales/quotations/<int:pk>/', views.QuotationDetailView.as_view(), name='quotation_detail'),
|
||||
path('sales/quotations/', views.QuotationListView.as_view(), name='quotation_list'),
|
||||
path('sales/quotations/<int:pk>/confirm/', views.confirm_quotation, name='confirm_quotation'),
|
||||
path('sales/orders/detail/<int:order_id>/', views.SalesOrderDetailView.as_view(), name='order_detail'),
|
||||
path('quotation/<int:quotation_id>/pdf/', views.download_quotation_pdf, name='quotation_pdf'),
|
||||
path('generate_invoice/<int:pk>/', views.generate_invoice, name='generate_invoice'),
|
||||
path('sales/quotations/<int:pk>/mark_quotation/', views.mark_quotation, name='mark_quotation'),
|
||||
path('sales/quotations/<int:pk>/post_quotation/', views.post_quotation, name='post_quotation'),
|
||||
path('sales/quotations/<int:pk>/invoice_detail/', views.invoice_detail, name='invoice_detail'),
|
||||
path('subscriptions', views.SubscriptionPlans.as_view(), name='subscriptions'),
|
||||
#Payment URLs
|
||||
# path('sales/quotations/<int:pk>/payment/', views.PaymentCreateView.as_view(), name='payment_create'),
|
||||
path('sales/quotations/<int:pk>/payment/', views.payment_create, name='payment_create'),
|
||||
|
||||
# Users URLs
|
||||
path("user/create/", views.UserCreateView.as_view(), name="user_create"),
|
||||
path("user/<int:pk>/update/", views.UserUpdateView.as_view(), name="user_update"),
|
||||
path("user/<int:pk>/", views.UserDetailView.as_view(), name="user_detail"),
|
||||
path("user/", views.UserListView.as_view(), name="user_list"),
|
||||
path("user/<int:pk>/confirm/", views.UserDeleteview, name="user_delete"),
|
||||
path('user/create/', views.UserCreateView.as_view(), name='user_create'),
|
||||
path('user/<int:pk>/update/', views.UserUpdateView.as_view(), name='user_update'),
|
||||
path('user/<int:pk>/', views.UserDetailView.as_view(), name='user_detail'),
|
||||
path('user/', views.UserListView.as_view(), name='user_list'),
|
||||
path('user/<int:pk>/confirm/', views.UserDeleteview, name='user_delete'),
|
||||
# Organization URLs
|
||||
path(
|
||||
"organizations/", views.OrganizationListView.as_view(), name="organization_list"
|
||||
),
|
||||
path(
|
||||
"organizations/<int:pk>/",
|
||||
views.OrganizationDetailView.as_view(),
|
||||
name="organization_detail",
|
||||
),
|
||||
path(
|
||||
"organizations/create/",
|
||||
views.OrganizationCreateView.as_view(),
|
||||
name="organization_create",
|
||||
),
|
||||
path(
|
||||
"organizations/<int:pk>/update/",
|
||||
views.OrganizationUpdateView.as_view(),
|
||||
name="organization_update",
|
||||
),
|
||||
path(
|
||||
"organizations/<int:pk>/delete/",
|
||||
views.OrganizationDeleteView.as_view(),
|
||||
name="organization_delete",
|
||||
),
|
||||
path('organizations/', views.OrganizationListView.as_view(), name='organization_list'),
|
||||
path('organizations/<int:pk>/', views.OrganizationDetailView.as_view(), name='organization_detail'),
|
||||
path('organizations/create/', views.OrganizationCreateView.as_view(), name='organization_create'),
|
||||
path('organizations/<int:pk>/update/', views.OrganizationUpdateView.as_view(), name='organization_update'),
|
||||
path('organizations/<int:pk>/delete/', views.OrganizationDeleteView.as_view(), name='organization_delete'),
|
||||
|
||||
# Representative URLs
|
||||
path(
|
||||
"representatives/",
|
||||
views.RepresentativeListView.as_view(),
|
||||
name="representative_list",
|
||||
),
|
||||
path(
|
||||
"representatives/<int:pk>/",
|
||||
views.RepresentativeDetailView.as_view(),
|
||||
name="representative_detail",
|
||||
),
|
||||
path(
|
||||
"representatives/create/",
|
||||
views.RepresentativeCreateView.as_view(),
|
||||
name="representative_create",
|
||||
),
|
||||
path(
|
||||
"representatives/<int:pk>/update/",
|
||||
views.RepresentativeUpdateView.as_view(),
|
||||
name="representative_update",
|
||||
),
|
||||
path(
|
||||
"representatives/<int:pk>/delete/",
|
||||
views.RepresentativeDeleteView.as_view(),
|
||||
name="representative_delete",
|
||||
),
|
||||
# Ledger URLS
|
||||
# Bank Account
|
||||
path(
|
||||
"bank_accounts/", views.BankAccountListView.as_view(), name="bank_account_list"
|
||||
),
|
||||
path(
|
||||
"bank_accounts/<uuid:pk>/",
|
||||
views.BankAccountDetailView.as_view(),
|
||||
name="bank_account_detail",
|
||||
),
|
||||
path(
|
||||
"bank_accounts/create/",
|
||||
views.BankAccountCreateView.as_view(),
|
||||
name="bank_account_create",
|
||||
),
|
||||
path(
|
||||
"bank_accounts/<uuid:pk>/update/",
|
||||
views.BankAccountUpdateView.as_view(),
|
||||
name="bank_account_update",
|
||||
),
|
||||
path(
|
||||
"bank_accounts/<uuid:pk>/delete/",
|
||||
views.bank_account_delete,
|
||||
name="bank_account_delete",
|
||||
),
|
||||
path('representatives/', views.RepresentativeListView.as_view(), name='representative_list'),
|
||||
path('representatives/<int:pk>/', views.RepresentativeDetailView.as_view(), name='representative_detail'),
|
||||
path('representatives/create/', views.RepresentativeCreateView.as_view(), name='representative_create'),
|
||||
path('representatives/<int:pk>/update/', views.RepresentativeUpdateView.as_view(), name='representative_update'),
|
||||
path('representatives/<int:pk>/delete/', views.RepresentativeDeleteView.as_view(), name='representative_delete'),
|
||||
|
||||
#Ledger URLS
|
||||
#Bank Account
|
||||
path('bank_accounts/', views.BankAccountListView.as_view(), name='bank_account_list'),
|
||||
path('bank_accounts/<uuid:pk>/', views.BankAccountDetailView.as_view(), name='bank_account_detail'),
|
||||
path('bank_accounts/create/', views.BankAccountCreateView.as_view(), name='bank_account_create'),
|
||||
path('bank_accounts/<uuid:pk>/update/', views.BankAccountUpdateView.as_view(), name='bank_account_update'),
|
||||
path('bank_accounts/<uuid:pk>/delete/', views.bank_account_delete, name='bank_account_delete'),
|
||||
# Account
|
||||
path("coa_accounts/", views.AccountListView.as_view(), name="account_list"),
|
||||
path(
|
||||
"coa_accounts/<uuid:pk>/",
|
||||
views.AccountDetailView.as_view(),
|
||||
name="account_detail",
|
||||
),
|
||||
path(
|
||||
"coa_accounts/create/", views.AccountCreateView.as_view(), name="account_create"
|
||||
),
|
||||
path(
|
||||
"coa_accounts/<uuid:pk>/update/",
|
||||
views.AccountUpdateView.as_view(),
|
||||
name="account_update",
|
||||
),
|
||||
path("coa_accounts/<uuid:pk>/delete/", views.account_delete, name="account_delete"),
|
||||
path('coa_accounts/', views.AccountListView.as_view(), name='account_list'),
|
||||
path('coa_accounts/<uuid:pk>/', views.AccountDetailView.as_view(), name='account_detail'),
|
||||
path('coa_accounts/create/', views.AccountCreateView.as_view(), name='account_create'),
|
||||
path('coa_accounts/<uuid:pk>/update/', views.AccountUpdateView.as_view(), name='account_update'),
|
||||
path('coa_accounts/<uuid:pk>/delete/', views.account_delete, name='account_delete'),
|
||||
# Estimate
|
||||
path("sales/estimates/", views.EstimateListView.as_view(), name="estimate_list"),
|
||||
path(
|
||||
"sales/estimates/<uuid:pk>/",
|
||||
views.EstimateDetailView.as_view(),
|
||||
name="estimate_detail",
|
||||
),
|
||||
path("sales/estimates/create/", views.estimate_create, name="estimate_create"),
|
||||
path(
|
||||
"sales/estimates/<uuid:pk>/estimate_mark_as/",
|
||||
views.estimate_mark_as,
|
||||
name="estimate_mark_as",
|
||||
),
|
||||
path(
|
||||
"sales/estimates/<uuid:pk>/preview/",
|
||||
views.EstimatePreviewView.as_view(),
|
||||
name="estimate_preview",
|
||||
),
|
||||
path(
|
||||
"sales/estimates/<uuid:pk>/payment_request/",
|
||||
views.PaymentRequest.as_view(),
|
||||
name="payment_request",
|
||||
),
|
||||
path(
|
||||
"sales/estimates/<uuid:pk>/send_email", views.send_email_view, name="send_email"
|
||||
),
|
||||
path('sales/estimates/', views.EstimateListView.as_view(), name='estimate_list'),
|
||||
path('sales/estimates/<uuid:pk>/', views.EstimateDetailView.as_view(), name='estimate_detail'),
|
||||
path('sales/estimates/create/', views.create_estimate, name='estimate_create'),
|
||||
path('sales/estimates/<uuid:pk>/estimate_mark_as/', views.estimate_mark_as, name='estimate_mark_as'),
|
||||
path('sales/estimates/<uuid:pk>/preview/', views.EstimatePreviewView.as_view(), name='estimate_preview'),
|
||||
path('sales/estimates/<uuid:pk>/payment_request/', views.PaymentRequest.as_view(), name='payment_request'),
|
||||
path('sales/estimates/<uuid:pk>/send_email', views.send_email_view, name='send_email'),
|
||||
# Invoice
|
||||
path("sales/invoices/", views.InvoiceListView.as_view(), name="invoice_list"),
|
||||
path(
|
||||
"sales/invoices/<uuid:pk>/create/", views.invoice_create, name="invoice_create"
|
||||
),
|
||||
path(
|
||||
"sales/invoices/<uuid:pk>/",
|
||||
views.InvoiceDetailView.as_view(),
|
||||
name="invoice_detail",
|
||||
),
|
||||
path(
|
||||
"sales/invoices/<uuid:pk>/preview/",
|
||||
views.InvoicePreviewView.as_view(),
|
||||
name="invoice_preview",
|
||||
),
|
||||
path(
|
||||
"sales/invoices/<uuid:pk>/invoice_mark_as/",
|
||||
views.invoice_mark_as,
|
||||
name="invoice_mark_as",
|
||||
),
|
||||
path(
|
||||
"sales/invoices/<uuid:pk>/draft_invoice_update/",
|
||||
views.DraftInvoiceModelUpdateFormView.as_view(),
|
||||
name="draft_invoice_update",
|
||||
),
|
||||
path(
|
||||
"sales/invoices/<uuid:pk>/approved_invoice_update/",
|
||||
views.ApprovedInvoiceModelUpdateFormView.as_view(),
|
||||
name="approved_invoice_update",
|
||||
),
|
||||
path(
|
||||
"sales/invoices/<uuid:pk>/paid_invoice_update/",
|
||||
views.PaidInvoiceModelUpdateFormView.as_view(),
|
||||
name="paid_invoice_update",
|
||||
),
|
||||
# path('sales/estimates/<uuid:pk>/preview/', views.EstimatePreviewView.as_view(), name='estimate_preview'),
|
||||
# path('send_email/<uuid:pk>', views.send_email, name='send_email'),
|
||||
# Payment
|
||||
path("sales/payments/", views.PaymentListView, name="payment_list"),
|
||||
path(
|
||||
"sales/payments/<uuid:pk>/create/",
|
||||
views.PaymentCreateView,
|
||||
name="payment_create",
|
||||
),
|
||||
path("sales/payments/create/", views.PaymentCreateView, name="payment_create"),
|
||||
path(
|
||||
"sales/payments/<uuid:pk>/payment_details/",
|
||||
views.PaymentDetailView,
|
||||
name="payment_details",
|
||||
),
|
||||
path(
|
||||
"sales/payments/<uuid:pk>/payment_mark_as_paid/",
|
||||
views.payment_mark_as_paid,
|
||||
name="payment_mark_as_paid",
|
||||
),
|
||||
# path('sales/payments/<uuid:pk>/update/', views.JournalEntryUpdateView.as_view(), name='payment_update'),
|
||||
# path('sales/payments/<uuid:pk>/delete/', views.JournalEntryDeleteView.as_view(), name='payment_delete'),
|
||||
# path('sales/payments/<uuid:pk>/preview/', views.JournalEntryPreviewView.as_view(), name='payment_preview'),
|
||||
# # Journal
|
||||
# path('sales/journal/<uuid:pk>/create/', views.JournalEntryCreateView.as_view(), name='journal_create'),
|
||||
# Items
|
||||
path(
|
||||
"items/services/", views.ItemServiceListView.as_view(), name="item_service_list"
|
||||
),
|
||||
path(
|
||||
"items/services/create/",
|
||||
views.ItemServiceCreateView.as_view(),
|
||||
name="item_service_create",
|
||||
),
|
||||
path(
|
||||
"items/services/<int:pk>/update/",
|
||||
views.ItemServiceUpdateView.as_view(),
|
||||
name="item_service_update",
|
||||
),
|
||||
# Expanese
|
||||
path(
|
||||
"items/expeneses/",
|
||||
views.ItemExpenseListView.as_view(),
|
||||
name="item_expense_list",
|
||||
),
|
||||
path(
|
||||
"items/expeneses/create/",
|
||||
views.ItemExpenseCreateView.as_view(),
|
||||
name="item_expense_create",
|
||||
),
|
||||
path(
|
||||
"items/expeneses/<uuid:pk>/update/",
|
||||
views.ItemExpenseUpdateView.as_view(),
|
||||
name="item_expense_update",
|
||||
),
|
||||
# Bills
|
||||
path("items/bills/", views.BillListView.as_view(), name="bill_list"),
|
||||
path("items/bills/create/", views.bill_create, name="bill_create"),
|
||||
path(
|
||||
"items/bills/<uuid:pk>/bill_detail/",
|
||||
views.BillDetailView.as_view(),
|
||||
name="bill_detail",
|
||||
),
|
||||
path("items/bills/<uuid:pk>/delete/", views.BillDeleteView, name="bill_delete"),
|
||||
path(
|
||||
"items/bills/<uuid:pk>/in_review/",views.InReviewBillView.as_view(), name="in_review_bill"
|
||||
),
|
||||
path(
|
||||
"items/bills/<uuid:pk>/in_approve/",views.ApprovedBillModelView.as_view(), name="in_approve_bill"
|
||||
),
|
||||
path('items/bills/<uuid:pk>/mark_as_approved/', views.bill_mark_as_approved, name='bill_mark_as_approved'),
|
||||
path('items/bills/<uuid:pk>/mark_as_paid/', views.bill_mark_as_paid, name='bill_mark_as_paid'),
|
||||
path('sales/invoices/', views.InvoiceListView.as_view(), name='invoice_list'),
|
||||
path('sales/invoices/<uuid:pk>/create/', views.invoice_create, name='invoice_create'),
|
||||
path('sales/invoices/<uuid:pk>/', views.InvoiceDetailView.as_view(), name='invoice_detail'),
|
||||
path('sales/invoices/<uuid:pk>/preview/', views.InvoicePreviewView.as_view(), name='invoice_preview'),
|
||||
path('sales/invoices/<uuid:pk>/invoice_mark_as/', views.invoice_mark_as, name='invoice_mark_as'),
|
||||
path('sales/invoices/<uuid:pk>/draft_invoice_update/', views.DraftInvoiceModelUpdateFormView.as_view(), name='draft_invoice_update'),
|
||||
path('sales/invoices/<uuid:pk>/approved_invoice_update/', views.ApprovedInvoiceModelUpdateFormView.as_view(), name='approved_invoice_update'),
|
||||
path('sales/invoices/<uuid:pk>/paid_invoice_update/', views.PaidInvoiceModelUpdateFormView.as_view(), name='paid_invoice_update'),
|
||||
|
||||
# path('sales/estimates/<uuid:pk>/preview/', views.EstimatePreviewView.as_view(), name='estimate_preview'),
|
||||
# path('send_email/<uuid:pk>', views.send_email, name='send_email'),
|
||||
|
||||
#Payment
|
||||
path('sales/payments/', views.PaymentListView, name='payment_list'),
|
||||
path('sales/payments/<uuid:pk>/create/', views.PaymentCreateView, name='payment_create'),
|
||||
path('sales/payments/create/', views.PaymentCreateView, name='payment_create'),
|
||||
path('sales/payments/<uuid:pk>/payment_details/', views.PaymentDetailView, name='payment_details'),
|
||||
path('sales/payments/<uuid:pk>/payment_mark_as_paid/', views.payment_mark_as_paid, name='payment_mark_as_paid'),
|
||||
# path('sales/payments/<uuid:pk>/update/', views.JournalEntryUpdateView.as_view(), name='payment_update'),
|
||||
# path('sales/payments/<uuid:pk>/delete/', views.JournalEntryDeleteView.as_view(), name='payment_delete'),
|
||||
# path('sales/payments/<uuid:pk>/preview/', views.JournalEntryPreviewView.as_view(), name='payment_preview'),
|
||||
# # Journal
|
||||
# path('sales/journal/<uuid:pk>/create/', views.JournalEntryCreateView.as_view(), name='journal_create'),
|
||||
|
||||
# Items
|
||||
path('items/services/', views.ItemServiceListView.as_view(), name='item_service_list'),
|
||||
path('items/services/create/', views.ItemServiceCreateView.as_view(), name='item_service_create'),
|
||||
path('items/services/<int:pk>/update/', views.ItemServiceUpdateView.as_view(), name='item_service_update'),
|
||||
# Expanese
|
||||
path('items/expeneses/', views.ItemExpenseListView.as_view(), name='item_expense_list'),
|
||||
path('items/expeneses/create/', views.ItemExpenseCreateView.as_view(), name='item_expense_create'),
|
||||
path('items/expeneses/<uuid:pk>/update/', views.ItemExpenseUpdateView.as_view(), name='item_expense_update'),
|
||||
# Bills
|
||||
path('items/bills/', views.BillListView.as_view(), name='bill_list'),
|
||||
path('items/bills/create/', views.BillCreateView.as_view(), name='bill_create'),
|
||||
# path('items/bills/<uuid:pk>/update/', views.ItemExpenseUpdateView.as_view(), name='item_expense_update'),
|
||||
]
|
||||
|
||||
|
||||
handler404 = "inventory.views.custom_page_not_found_view"
|
||||
handler500 = "inventory.views.custom_error_view"
|
||||
handler403 = "inventory.views.custom_permission_denied_view"
|
||||
handler400 = "inventory.views.custom_bad_request_view"
|
||||
handler404 = 'inventory.views.custom_page_not_found_view'
|
||||
handler500 = 'inventory.views.custom_error_view'
|
||||
handler403 = 'inventory.views.custom_permission_denied_view'
|
||||
handler400 = 'inventory.views.custom_bad_request_view'
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,57 +0,0 @@
|
||||
from django_ledger.models.invoice import InvoiceModel
|
||||
|
||||
class InvoiceModelBase(InvoiceModel):
|
||||
"""
|
||||
Custom Invoice Model with Net 15 payment terms.
|
||||
"""
|
||||
|
||||
TERMS_ON_RECEIPT = 'on_receipt'
|
||||
TERMS_NET_15 = 'net_15'
|
||||
TERMS_NET_30 = 'net_30'
|
||||
TERMS_NET_60 = 'net_60'
|
||||
TERMS_NET_90 = 'net_90'
|
||||
TERMS_NET_90_PLUS = 'net_90+'
|
||||
|
||||
TERM_CHOICES = [
|
||||
(TERMS_ON_RECEIPT, 'Due On Receipt'),
|
||||
(TERMS_NET_15, 'Net 15 Days'),
|
||||
(TERMS_NET_30, 'Net 30 Days'),
|
||||
(TERMS_NET_60, 'Net 60 Days'),
|
||||
(TERMS_NET_90, 'Net 90 Days'),
|
||||
]
|
||||
TERM_CHOICES_VALID = tuple(i[0] for i in TERM_CHOICES)
|
||||
|
||||
TERM_DAYS_MAPPING = {
|
||||
TERMS_ON_RECEIPT: 0,
|
||||
TERMS_NET_15: 15,
|
||||
TERMS_NET_30: 30,
|
||||
TERMS_NET_60: 60,
|
||||
TERMS_NET_90: 90,
|
||||
TERMS_NET_90_PLUS: 120
|
||||
}
|
||||
|
||||
|
||||
def net_due_group(self):
|
||||
"""
|
||||
Determines the group where the financial instrument falls based on the number of days until the due date.
|
||||
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
The terms group as a string.
|
||||
"""
|
||||
due_in = self.due_in_days()
|
||||
if due_in == 0:
|
||||
return self.TERMS_ON_RECEIPT
|
||||
elif due_in <= 15:
|
||||
return self.TERMS_NET_15
|
||||
elif due_in <= 30:
|
||||
return self.TERMS_NET_30
|
||||
elif due_in <= 60:
|
||||
return self.TERMS_NET_60
|
||||
elif due_in <= 90:
|
||||
return self.TERMS_NET_90
|
||||
return self.TERMS_NET_90_PLUS
|
||||
|
||||
class Meta:
|
||||
proxy = True
|
||||
@ -3,7 +3,6 @@ from django.core.paginator import Paginator
|
||||
from django.forms import DateField, DateInput, HiddenInput, TextInput
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django_ledger.forms.bill import ApprovedBillModelUpdateForm, InReviewBillModelUpdateForm
|
||||
from django_ledger.models import (
|
||||
EntityModel,
|
||||
InvoiceModel,
|
||||
@ -15,13 +14,13 @@ from django_ledger.models import (
|
||||
CustomerModel,
|
||||
LedgerModel,
|
||||
ItemModel,
|
||||
BillModel,
|
||||
BillModel
|
||||
)
|
||||
from django_ledger.forms.bank_account import (
|
||||
BankAccountCreateForm,
|
||||
BankAccountUpdateForm,
|
||||
)
|
||||
|
||||
from django_ledger.forms.bill import BillModelCreateForm
|
||||
from django_ledger.forms.invoice import (
|
||||
DraftInvoiceModelUpdateForm,
|
||||
ApprovedInvoiceModelUpdateForm,
|
||||
@ -65,6 +64,7 @@ from django.urls import reverse, reverse_lazy
|
||||
from django.contrib import messages
|
||||
from django.db.models import Sum, F, Count
|
||||
from django.db import transaction
|
||||
|
||||
from .services import (
|
||||
decodevin,
|
||||
get_make,
|
||||
@ -1603,6 +1603,23 @@ def UserDeleteview(request, pk):
|
||||
return redirect("user_list")
|
||||
|
||||
|
||||
# errors
|
||||
def custom_page_not_found_view(request, exception):
|
||||
return render(request, "errors/404.html", {})
|
||||
|
||||
|
||||
def custom_error_view(request, exception=None):
|
||||
return render(request, "errors/500.html", {})
|
||||
|
||||
|
||||
def custom_permission_denied_view(request, exception=None):
|
||||
return render(request, "errors/403.html", {})
|
||||
|
||||
|
||||
def custom_bad_request_view(request, exception=None):
|
||||
return render(request, "errors/400.html", {})
|
||||
|
||||
|
||||
class OrganizationListView(LoginRequiredMixin, ListView):
|
||||
model = models.Organization
|
||||
template_name = "organizations/organization_list.html"
|
||||
@ -1748,40 +1765,40 @@ def download_quotation_pdf(request, quotation_id):
|
||||
return HttpResponse("Quotation not found", status=404)
|
||||
|
||||
|
||||
# @login_required
|
||||
# def invoice_detail(request, pk):
|
||||
# quotation = get_object_or_404(models.SaleQuotation, pk=pk)
|
||||
# dealer = request.user.dealer
|
||||
# entity = dealer.entity
|
||||
# customer = (
|
||||
# entity.get_customers()
|
||||
# .filter(customer_name=quotation.customer.get_full_name)
|
||||
# .first()
|
||||
# )
|
||||
# invoice_model = entity.get_invoices()
|
||||
@login_required
|
||||
def invoice_detail(request, pk):
|
||||
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
|
||||
dealer = request.user.dealer
|
||||
entity = dealer.entity
|
||||
customer = (
|
||||
entity.get_customers()
|
||||
.filter(customer_name=quotation.customer.get_full_name)
|
||||
.first()
|
||||
)
|
||||
invoice_model = entity.get_invoices()
|
||||
|
||||
# invoice = invoice_model.filter(
|
||||
# customer=customer, date_draft=quotation.date_draft
|
||||
# ).first()
|
||||
# return redirect("quotation_detail", pk=pk)
|
||||
invoice = invoice_model.filter(
|
||||
customer=customer, date_draft=quotation.date_draft
|
||||
).first()
|
||||
return redirect("quotation_detail", pk=pk)
|
||||
|
||||
|
||||
# @login_required
|
||||
# def payment_invoice(request, pk):
|
||||
# quotation = get_object_or_404(models.SaleQuotation, pk=pk)
|
||||
# dealer = request.user.dealer
|
||||
# entity = dealer.entity
|
||||
# customer = (
|
||||
# entity.get_customers()
|
||||
# .filter(customer_name=quotation.customer.get_full_name)
|
||||
# .first()
|
||||
# )
|
||||
# invoice_model = entity.get_invoices()
|
||||
# invoice = invoice_model.filter(
|
||||
# customer=customer, date_draft=quotation.date_draft
|
||||
# ).first()
|
||||
@login_required
|
||||
def payment_invoice(request, pk):
|
||||
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
|
||||
dealer = request.user.dealer
|
||||
entity = dealer.entity
|
||||
customer = (
|
||||
entity.get_customers()
|
||||
.filter(customer_name=quotation.customer.get_full_name)
|
||||
.first()
|
||||
)
|
||||
invoice_model = entity.get_invoices()
|
||||
invoice = invoice_model.filter(
|
||||
customer=customer, date_draft=quotation.date_draft
|
||||
).first()
|
||||
|
||||
# return redirect("quotation_detail", pk=pk)
|
||||
return redirect("quotation_detail", pk=pk)
|
||||
|
||||
|
||||
# class PaymentCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
||||
@ -1802,75 +1819,75 @@ def download_quotation_pdf(request, quotation_id):
|
||||
# return context
|
||||
|
||||
|
||||
# def payment_create(request, pk):
|
||||
# quotation = get_object_or_404(models.SaleQuotation, pk=pk)
|
||||
# dealer = get_user_type(request)
|
||||
# if request.method == "POST":
|
||||
# form = forms.PaymentForm(request.POST)
|
||||
# if form.is_valid():
|
||||
# form.instance.quotation = quotation
|
||||
# insatnce = form.save()
|
||||
def payment_create(request, pk):
|
||||
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
|
||||
dealer = get_user_type(request)
|
||||
if request.method == "POST":
|
||||
form = forms.PaymentForm(request.POST)
|
||||
if form.is_valid():
|
||||
form.instance.quotation = quotation
|
||||
insatnce = form.save()
|
||||
|
||||
# dealer = dealer
|
||||
# entity = dealer.entity
|
||||
# customer = (
|
||||
# entity.get_customers()
|
||||
# .filter(customer_name=quotation.customer.get_full_name)
|
||||
# .first()
|
||||
# )
|
||||
# coa_qs, coa_map = entity.get_all_coa_accounts()
|
||||
# cash_account = coa_qs.first().get_coa_accounts().filter(name="Cash")
|
||||
# recivable_account = (
|
||||
# coa_qs.first().get_coa_accounts().filter(name="Accounts Receivable")
|
||||
# )
|
||||
# journal_entry = JournalEntryModel.objects.filter(
|
||||
# pk=quotation.payment_id
|
||||
# ).first()
|
||||
# TransactionModel.objects.create(
|
||||
# journal_entry=journal_entry,
|
||||
# account=cash_account.first(), # Debit Cash
|
||||
# amount=insatnce.amount, # Payment amount
|
||||
# tx_type="debit",
|
||||
# description="Payment Received",
|
||||
# )
|
||||
dealer = dealer
|
||||
entity = dealer.entity
|
||||
customer = (
|
||||
entity.get_customers()
|
||||
.filter(customer_name=quotation.customer.get_full_name)
|
||||
.first()
|
||||
)
|
||||
coa_qs, coa_map = entity.get_all_coa_accounts()
|
||||
cash_account = coa_qs.first().get_coa_accounts().filter(name="Cash")
|
||||
recivable_account = (
|
||||
coa_qs.first().get_coa_accounts().filter(name="Accounts Receivable")
|
||||
)
|
||||
journal_entry = JournalEntryModel.objects.filter(
|
||||
pk=quotation.payment_id
|
||||
).first()
|
||||
TransactionModel.objects.create(
|
||||
journal_entry=journal_entry,
|
||||
account=cash_account.first(), # Debit Cash
|
||||
amount=insatnce.amount, # Payment amount
|
||||
tx_type="debit",
|
||||
description="Payment Received",
|
||||
)
|
||||
|
||||
# TransactionModel.objects.create(
|
||||
# journal_entry=journal_entry,
|
||||
# account=recivable_account.first(), # Credit Accounts Receivable
|
||||
# amount=insatnce.amount, # Payment amount
|
||||
# tx_type="credit",
|
||||
# description="Payment Received",
|
||||
# )
|
||||
# journal_entry.posted = True
|
||||
# quotation.posted = True
|
||||
# quotation.save()
|
||||
# journal_entry.save()
|
||||
TransactionModel.objects.create(
|
||||
journal_entry=journal_entry,
|
||||
account=recivable_account.first(), # Credit Accounts Receivable
|
||||
amount=insatnce.amount, # Payment amount
|
||||
tx_type="credit",
|
||||
description="Payment Received",
|
||||
)
|
||||
journal_entry.posted = True
|
||||
quotation.posted = True
|
||||
quotation.save()
|
||||
journal_entry.save()
|
||||
|
||||
# invoice_model = (
|
||||
# entity.get_invoices()
|
||||
# .filter(date_approved=quotation.date_approved)
|
||||
# .first()
|
||||
# )
|
||||
invoice_model = (
|
||||
entity.get_invoices()
|
||||
.filter(date_approved=quotation.date_approved)
|
||||
.first()
|
||||
)
|
||||
|
||||
# invoice_model.mark_as_paid(
|
||||
# entity_slug=entity.slug, user_model=request.user.dealer
|
||||
# )
|
||||
# date = timezone.now()
|
||||
# invoice_model.date_paid = date
|
||||
# quotation.date_paid = date
|
||||
# invoice_model.save()
|
||||
# quotation.status = "Paid"
|
||||
# quotation.save()
|
||||
invoice_model.mark_as_paid(
|
||||
entity_slug=entity.slug, user_model=request.user.dealer
|
||||
)
|
||||
date = timezone.now()
|
||||
invoice_model.date_paid = date
|
||||
quotation.date_paid = date
|
||||
invoice_model.save()
|
||||
quotation.status = "Paid"
|
||||
quotation.save()
|
||||
|
||||
# messages.success(request, "Payment created successfully.")
|
||||
# return redirect("quotation_detail", pk=pk)
|
||||
# else:
|
||||
# form = forms.PaymentForm()
|
||||
# return render(
|
||||
# request,
|
||||
# "sales/payments/payment_create.html",
|
||||
# {"quotation": quotation, "form": form},
|
||||
# )
|
||||
messages.success(request, "Payment created successfully.")
|
||||
return redirect("quotation_detail", pk=pk)
|
||||
else:
|
||||
form = forms.PaymentForm()
|
||||
return render(
|
||||
request,
|
||||
"sales/payments/payment_create.html",
|
||||
{"quotation": quotation, "form": form},
|
||||
)
|
||||
|
||||
|
||||
# Ledger
|
||||
@ -2091,7 +2108,7 @@ class EstimateListView(LoginRequiredMixin, ListView):
|
||||
|
||||
# @csrf_exempt
|
||||
@login_required
|
||||
def estimate_create(request):
|
||||
def create_estimate(request):
|
||||
dealer = get_user_type(request)
|
||||
entity = dealer.entity
|
||||
|
||||
@ -2217,7 +2234,7 @@ def estimate_create(request):
|
||||
for x in car_list
|
||||
],
|
||||
}
|
||||
|
||||
print(context)
|
||||
return render(request, "sales/estimates/estimate_form.html", context)
|
||||
|
||||
|
||||
@ -2485,8 +2502,6 @@ def invoice_create(request, pk):
|
||||
invoice.save()
|
||||
messages.success(request, "Invoice created successfully!")
|
||||
return redirect("invoice_detail", pk=invoice.pk)
|
||||
else:
|
||||
print(form.errors)
|
||||
form = forms.InvoiceModelCreateForm(
|
||||
entity_slug=entity.slug, user_model=entity.admin
|
||||
)
|
||||
@ -2535,9 +2550,6 @@ class InvoicePreviewView(LoginRequiredMixin, DetailView):
|
||||
|
||||
def PaymentCreateView(request, pk=None):
|
||||
invoice = InvoiceModel.objects.filter(pk=pk).first()
|
||||
bill = BillModel.objects.filter(pk=pk).first()
|
||||
model = invoice if invoice else bill
|
||||
redirect_url = 'invoice_detail' if invoice else 'bill_detail'
|
||||
dealer = get_user_type(request)
|
||||
entity = dealer.entity
|
||||
form = forms.PaymentForm()
|
||||
@ -2546,36 +2558,89 @@ def PaymentCreateView(request, pk=None):
|
||||
if form.is_valid():
|
||||
amount = form.cleaned_data.get("amount")
|
||||
invoice = form.cleaned_data.get("invoice")
|
||||
bill = form.cleaned_data.get("bill")
|
||||
payment_method = form.cleaned_data.get("payment_method")
|
||||
model = invoice if invoice else bill
|
||||
if not model.is_approved():
|
||||
model.mark_as_approved(user_model=entity.admin)
|
||||
try:
|
||||
if invoice:
|
||||
set_invoice_payment(dealer,entity,invoice,amount,payment_method)
|
||||
elif bill:
|
||||
set_bill_payment(dealer,entity,bill,amount,payment_method)
|
||||
return redirect(redirect_url, pk=model.pk)
|
||||
vat_amount = 0
|
||||
total_amount = 0
|
||||
|
||||
if invoice.terms == "on_receipt":
|
||||
for x in invoice.get_itemtxs_data()[0].all():
|
||||
vat_amount += models.Car.objects.get(
|
||||
vin=x.item_model.name
|
||||
).finances.vat_amount * Decimal(x.quantity)
|
||||
total_amount += Decimal(x.unit_cost) * Decimal(x.quantity)
|
||||
|
||||
grand_total = total_amount - Decimal(vat_amount)
|
||||
|
||||
ledger = LedgerModel.objects.filter(
|
||||
name=str(invoice.pk), entity=entity
|
||||
).first()
|
||||
journal = JournalEntryModel.objects.create(
|
||||
posted=False,
|
||||
description=f"Payment for Invoice {invoice.invoice_number}",
|
||||
ledger=ledger,
|
||||
locked=False,
|
||||
origin="Payment",
|
||||
)
|
||||
credit_account = entity.get_default_coa_accounts().get(
|
||||
name="Sales Revenue"
|
||||
)
|
||||
debit_account = None
|
||||
if payment_method == "cash":
|
||||
debit_account = entity.get_default_coa_accounts().get(
|
||||
name="Cash", active=True
|
||||
)
|
||||
elif payment_method == "credit":
|
||||
debit_account = entity.get_default_coa_accounts().get(
|
||||
name="Accounts Receivable", active=True
|
||||
)
|
||||
else:
|
||||
debit_account = entity.get_default_coa_accounts().get(
|
||||
name="Cash in Bank", active=True
|
||||
)
|
||||
|
||||
vat_payable_account = entity.get_default_coa_accounts().get(
|
||||
name="VAT Payable", active=True
|
||||
)
|
||||
TransactionModel.objects.create(
|
||||
journal_entry=journal,
|
||||
account=debit_account, # Debit Cash
|
||||
amount=amount, # Payment amount
|
||||
tx_type="debit",
|
||||
description="Payment Received",
|
||||
)
|
||||
|
||||
TransactionModel.objects.create(
|
||||
journal_entry=journal,
|
||||
account=credit_account, # Credit Accounts Receivable
|
||||
amount=grand_total, # Payment amount
|
||||
tx_type="credit",
|
||||
description="Payment Received",
|
||||
)
|
||||
|
||||
if vat_amount > 0:
|
||||
TransactionModel.objects.create(
|
||||
journal_entry=journal,
|
||||
account=vat_payable_account, # Credit VAT Payable
|
||||
amount=vat_amount,
|
||||
tx_type="credit",
|
||||
description="VAT Payable on Invoice",
|
||||
)
|
||||
|
||||
invoice.make_payment(amount)
|
||||
invoice.save()
|
||||
except Exception as e:
|
||||
messages.error(request, f"Error creating payment: {str(e)}")
|
||||
else:
|
||||
messages.error(request, f"Invalid form data: {str(form.errors)}")
|
||||
# return redirect(redirect_url, pk=model.pk)
|
||||
messages.error(request, f"Invalid form data: {str(form.errors)}")
|
||||
return redirect("invoice_detail", pk=invoice.pk)
|
||||
form = forms.PaymentForm()
|
||||
form.initial["amount"] = model.amount_due - model.amount_paid
|
||||
|
||||
if model:
|
||||
if isinstance(model, InvoiceModel):
|
||||
form.initial["invoice"] = model
|
||||
form.fields['bill'].widget = HiddenInput()
|
||||
|
||||
elif isinstance(model, BillModel):
|
||||
form.initial["bill"] = model
|
||||
form.fields['invoice'].widget = HiddenInput()
|
||||
form.initial["amount"] = invoice.amount_due
|
||||
|
||||
if invoice:
|
||||
form.initial["invoice"] = invoice
|
||||
return render(
|
||||
request, "sales/payments/payment_form.html", {"model": model, "form": form}
|
||||
request, "sales/payments/payment_form.html", {"invoice": invoice, "form": form}
|
||||
)
|
||||
|
||||
|
||||
@ -2941,7 +3006,7 @@ class ItemExpenseListView(ListView):
|
||||
|
||||
|
||||
class BillListView(ListView):
|
||||
model = BillModel
|
||||
model = ItemModel
|
||||
template_name = "ledger/bills/bill_list.html"
|
||||
context_object_name = "bills"
|
||||
paginate_by = 20
|
||||
@ -2951,279 +3016,23 @@ class BillListView(ListView):
|
||||
return dealer.entity.get_bills()
|
||||
|
||||
|
||||
class BillDetailView(LoginRequiredMixin, DetailView):
|
||||
class BillCreateView(LoginRequiredMixin,SuccessMessageMixin,CreateView):
|
||||
model = BillModel
|
||||
template_name = "ledger/bills/bill_detail.html"
|
||||
context_object_name = "bill"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
bill = kwargs.get("object")
|
||||
|
||||
if bill.get_itemtxs_data():
|
||||
txs = bill.get_itemtxs_data()[0]
|
||||
car_and_item_info = [
|
||||
{
|
||||
"car": models.Car.objects.get(vin=x.item_model.name),
|
||||
"total": models.Car.objects.get(
|
||||
vin=x.item_model.name
|
||||
).finances.cost_price
|
||||
* Decimal(x.quantity),
|
||||
"itemmodel": x,
|
||||
}
|
||||
for x in txs
|
||||
]
|
||||
grand_total = sum(
|
||||
Decimal(
|
||||
models.Car.objects.get(vin=x.item_model.name).finances.cost_price
|
||||
)
|
||||
* Decimal(x.quantity)
|
||||
for x in txs
|
||||
)
|
||||
|
||||
kwargs["car_and_item_info"] = car_and_item_info
|
||||
kwargs["grand_total"] = grand_total
|
||||
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
class InReviewBillView(LoginRequiredMixin, UpdateView):
|
||||
model = BillModel
|
||||
form_class = InReviewBillModelUpdateForm
|
||||
template_name = "ledger/bills/bill_update_form.html"
|
||||
form_class = BillModelCreateForm
|
||||
template_name = "ledger/bills/bill_form.html"
|
||||
success_url = reverse_lazy("bill_list")
|
||||
success_message = _("Bill updated successfully.")
|
||||
context_object_name = "bill"
|
||||
|
||||
success_message = _("Bill created successfully.")
|
||||
|
||||
def get_form_kwargs(self):
|
||||
dealer = get_user_type(self.request)
|
||||
kwargs = super().get_form_kwargs()
|
||||
dealer = get_user_type(self.request)
|
||||
kwargs["entity_model"] = dealer.entity
|
||||
kwargs["user_model"] = dealer.entity.admin
|
||||
return kwargs
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy("bill_detail", kwargs={"pk": self.kwargs["pk"]})
|
||||
|
||||
def form_valid(self, form):
|
||||
dealer = get_user_type(self.request)
|
||||
form.instance.entity = dealer.entity
|
||||
self.object.mark_as_review()
|
||||
return super().form_valid(form)
|
||||
|
||||
class ApprovedBillModelView(LoginRequiredMixin, UpdateView):
|
||||
model = BillModel
|
||||
form_class = ApprovedBillModelUpdateForm
|
||||
template_name = "ledger/bills/bill_update_form.html"
|
||||
success_url = reverse_lazy("bill_list")
|
||||
success_message = _("Bill updated successfully.")
|
||||
context_object_name = "bill"
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
dealer = get_user_type(self.request)
|
||||
kwargs["entity_model"] = dealer.entity
|
||||
kwargs["user_model"] = dealer.entity.admin
|
||||
return kwargs
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy("bill_detail", kwargs={"pk": self.kwargs["pk"]})
|
||||
|
||||
def form_valid(self, form):
|
||||
dealer = get_user_type(self.request)
|
||||
form.instance.entity = dealer.entity
|
||||
if not self.object.is_approved():
|
||||
self.object.mark_as_approved(user_model=dealer.entity.admin)
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
def bill_mark_as_approved(request,pk):
|
||||
bill = get_object_or_404(BillModel,pk=pk)
|
||||
if request.method == "POST":
|
||||
dealer = get_user_type(request)
|
||||
if bill.is_approved():
|
||||
messages.error(request, _("Bill is already approved."))
|
||||
return redirect("bill_detail",pk=bill.pk)
|
||||
bill.mark_as_approved(user_model=dealer.entity.admin)
|
||||
bill.save()
|
||||
messages.success(request, _("Bill marked as approved successfully."))
|
||||
return redirect("bill_detail",pk=bill.pk)
|
||||
|
||||
def bill_mark_as_paid(request,pk):
|
||||
bill = get_object_or_404(BillModel,pk=pk)
|
||||
if request.method == "POST":
|
||||
dealer = get_user_type(request)
|
||||
if bill.is_paid():
|
||||
messages.error(request, _("Bill is already paid."))
|
||||
return redirect("bill_detail",pk=bill.pk)
|
||||
if bill.amount_due == bill.amount_paid:
|
||||
bill.mark_as_paid(user_model=dealer.entity.admin)
|
||||
bill.save()
|
||||
bill.ledger.lock_journal_entries()
|
||||
bill.ledger.post_journal_entries()
|
||||
|
||||
bill.ledger.post()
|
||||
bill.ledger.save()
|
||||
messages.success(request, _("Bill marked as paid successfully."))
|
||||
else:
|
||||
messages.error(request, _("Amount paid is not equal to amount due."))
|
||||
return redirect("bill_detail",pk=bill.pk)
|
||||
|
||||
# def get_context_data(self, **kwargs):
|
||||
# def form_valid(self, form):
|
||||
# dealer = get_user_type(self.request)
|
||||
# context = super().get_context_data(**kwargs)
|
||||
# context['entity_model'] = dealer.entity
|
||||
# context['user_model'] = dealer.entity.admin
|
||||
|
||||
# return context
|
||||
|
||||
# class BillCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
||||
# model = BillModel
|
||||
# form_class = BillModelCreateForm
|
||||
# template_name = "ledger/bills/bill_form.html"
|
||||
# success_url = reverse_lazy("bill_list")
|
||||
# success_message = _("Bill created successfully.")
|
||||
|
||||
# def get_form_kwargs(self):
|
||||
# dealer = get_user_type(self.request)
|
||||
# kwargs = super().get_form_kwargs()
|
||||
# kwargs["entity_model"] = dealer.entity
|
||||
# return kwargs
|
||||
|
||||
|
||||
# def form_valid(self, form):
|
||||
# dealer = get_user_type(self.request)
|
||||
# form.instance.entity = dealer.entity
|
||||
# ledger = dealer.entity.create_ledger(
|
||||
# name=f"Bill for Vendor {form.instance.vendor.vendor_name}", posted=True
|
||||
# )
|
||||
# form.instance.ledger = ledger
|
||||
# return super().form_valid(form)
|
||||
@login_required
|
||||
def bill_create(request):
|
||||
dealer = get_user_type(request)
|
||||
entity = dealer.entity
|
||||
|
||||
if request.method == "POST":
|
||||
data = json.loads(request.body)
|
||||
vendor_id = data.get("vendor")
|
||||
terms = data.get("terms")
|
||||
vendor = entity.get_vendors().filter(pk=vendor_id).first()
|
||||
|
||||
items = data.get("item", [])
|
||||
quantities = data.get("quantity", [])
|
||||
|
||||
if not all([items, quantities]):
|
||||
return JsonResponse(
|
||||
{"status": "error", "message": "Items and Quantities are required"},
|
||||
status=400,
|
||||
)
|
||||
if isinstance(quantities, list):
|
||||
if "0" in quantities:
|
||||
return JsonResponse(
|
||||
{"status": "error", "message": "Quantity must be greater than zero"}
|
||||
)
|
||||
else:
|
||||
if int(quantities) <= 0:
|
||||
return JsonResponse(
|
||||
{"status": "error", "message": "Quantity must be greater than zero"}
|
||||
)
|
||||
|
||||
bill = entity.create_bill(vendor_model=vendor, terms=terms)
|
||||
if isinstance(items, list):
|
||||
item_quantity_map = {}
|
||||
for item, quantity in zip(items, quantities):
|
||||
if item in item_quantity_map:
|
||||
item_quantity_map[item] += int(quantity)
|
||||
else:
|
||||
item_quantity_map[item] = int(quantity)
|
||||
item_list = list(item_quantity_map.keys())
|
||||
quantity_list = list(item_quantity_map.values())
|
||||
|
||||
items_list = [
|
||||
{"item_id": item_list[i], "quantity": quantity_list[i]}
|
||||
for i in range(len(item_list))
|
||||
]
|
||||
items_txs = []
|
||||
for item in items_list:
|
||||
item_instance = ItemModel.objects.get(pk=item.get("item_id"))
|
||||
car = models.Car.objects.get(vin=item_instance.name)
|
||||
quantity = Decimal(item.get("quantity"))
|
||||
items_txs.append(
|
||||
{
|
||||
"item_number": item_instance.item_number,
|
||||
"quantity": quantity,
|
||||
"unit_cost": car.finances.cost_price,
|
||||
"total_amount": car.finances.cost_price * quantity,
|
||||
}
|
||||
)
|
||||
|
||||
bill_itemtxs = {
|
||||
item.get("item_number"): {
|
||||
"unit_cost": item.get("unit_cost"),
|
||||
"quantity": item.get("quantity"),
|
||||
"total_amount": item.get("total_amount"),
|
||||
}
|
||||
for item in items_txs
|
||||
}
|
||||
else:
|
||||
item = entity.get_items_all().filter(pk=items).first()
|
||||
instance = models.Car.objects.get(vin=item.name)
|
||||
bill_itemtxs = {
|
||||
item.item_number: {
|
||||
"unit_cost": instance.finances.cost_price,
|
||||
"quantity": Decimal(quantities),
|
||||
"total_amount": instance.finances.cost_price * Decimal(quantities),
|
||||
}
|
||||
}
|
||||
|
||||
bill_itemtxs = bill.migrate_itemtxs(
|
||||
itemtxs=bill_itemtxs,
|
||||
commit=True,
|
||||
operation=BillModel.ITEMIZE_APPEND,
|
||||
)
|
||||
|
||||
url = reverse("bill_detail", kwargs={"pk": bill.pk})
|
||||
return JsonResponse(
|
||||
{
|
||||
"status": "success",
|
||||
"message": "Estimate created successfully!",
|
||||
"url": f"{url}",
|
||||
}
|
||||
)
|
||||
|
||||
form = forms.BillModelCreateForm(entity_model=entity)
|
||||
form.initial.update(
|
||||
{
|
||||
"cash_account": entity.get_default_coa_accounts().get(name="Cash"),
|
||||
"prepaid_account": entity.get_default_coa_accounts().get(
|
||||
name="Prepaid Expenses"
|
||||
),
|
||||
"unearned_account": entity.get_default_coa_accounts().get(
|
||||
name="Accounts Payable"
|
||||
),
|
||||
}
|
||||
)
|
||||
car_list = models.Car.objects.filter(dealer=dealer)
|
||||
context = {
|
||||
"form": form,
|
||||
"items": [
|
||||
{
|
||||
"car": x,
|
||||
"product": entity.get_items_all()
|
||||
.filter(item_role=ItemModel.ITEM_ROLE_PRODUCT, name=x.vin)
|
||||
.first(),
|
||||
}
|
||||
for x in car_list
|
||||
],
|
||||
}
|
||||
|
||||
return render(request, "ledger/bills/bill_form.html", context)
|
||||
|
||||
|
||||
def BillDeleteView(request, pk):
|
||||
bill = get_object_or_404(BillModel, pk=pk)
|
||||
bill.delete()
|
||||
return redirect("bill_list")
|
||||
# form.instance.entity = dealer.entity
|
||||
# return super().form_valid(form)
|
||||
|
||||
|
||||
class SubscriptionPlans(ListView):
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
121
requirements.txt
121
requirements.txt
@ -3,25 +3,25 @@ aiohttp==3.11.11
|
||||
aiohttp-retry==2.8.3
|
||||
aiosignal==1.3.2
|
||||
alabaster==1.0.0
|
||||
albucore==0.0.13
|
||||
albumentations==1.4.10
|
||||
albucore==0.0.23
|
||||
albumentations==2.0.0
|
||||
annotated-types==0.7.0
|
||||
anyio==4.6.2.post1
|
||||
anyio==4.8.0
|
||||
arabic-reshaper==3.0.0
|
||||
asgiref==3.8.1
|
||||
astor==0.8.1
|
||||
astroid==3.3.5
|
||||
astroid==3.3.8
|
||||
attrs==23.2.0
|
||||
autopep8==2.3.1
|
||||
babel==2.16.0
|
||||
autopep8==2.3.2
|
||||
Babel==2.15.0
|
||||
beautifulsoup4==4.12.3
|
||||
bleach==6.2.0
|
||||
blinker==1.9.0
|
||||
Brotli==1.1.0
|
||||
certifi==2024.8.30
|
||||
certifi==2024.12.14
|
||||
cffi==1.17.1
|
||||
chardet==5.2.0
|
||||
charset-normalizer==3.4.0
|
||||
charset-normalizer==3.4.1
|
||||
click==8.1.8
|
||||
colorama==0.4.6
|
||||
commonmark==0.9.1
|
||||
@ -29,128 +29,140 @@ contourpy==1.3.1
|
||||
crispy-bootstrap5==2024.10
|
||||
cryptography==44.0.0
|
||||
cssselect2==0.7.0
|
||||
ctranslate2==4.5.0
|
||||
cycler==0.12.1
|
||||
Cython==3.0.11
|
||||
decorator==5.1.1
|
||||
desert==2020.11.18
|
||||
dill==0.3.9
|
||||
distro==1.9.0
|
||||
dj-rest-auth==7.0.0
|
||||
dj-rest-auth==7.0.1
|
||||
dj-shop-cart==7.1.1
|
||||
Django==5.1.4
|
||||
django-allauth==65.3.0
|
||||
Django==5.1.5
|
||||
django-allauth==65.3.1
|
||||
django-appointment==3.7.4
|
||||
django-autoslug==1.9.9
|
||||
django-bootstrap5==24.3
|
||||
django-classy-tags==4.1.0
|
||||
django-cors-headers==4.6.0
|
||||
django-countries==7.6.1
|
||||
django-crispy-forms==2.3
|
||||
django-debug-toolbar==4.4.6
|
||||
django-debug-toolbar==5.0.1
|
||||
django-extensions==3.2.3
|
||||
django-filter==24.3
|
||||
django-formtools==2.5.1
|
||||
django-ledger==0.7.0
|
||||
django-ledger==0.7.3
|
||||
django-money==3.5.3
|
||||
django-nine==0.2.7
|
||||
django-nonefield==0.4
|
||||
django-phonenumber-field==8.0.0
|
||||
django-picklefield==3.2
|
||||
django-prometheus==2.3.1
|
||||
django-q2==1.7.6
|
||||
django-sekizai==4.1.0
|
||||
django-silk==5.3.1
|
||||
django-silk==5.3.2
|
||||
django-sms==0.7.0
|
||||
django-sslserver==0.22
|
||||
django-tables2==2.7.0
|
||||
django-tables2==2.7.5
|
||||
django-treebeard==4.7.1
|
||||
django-view-breadcrumbs==2.5.1
|
||||
djangocms-admin-style==3.3.1
|
||||
djangorestframework==3.15.2
|
||||
djangorestframework-simplejwt==5.3.1
|
||||
djangorestframework_simplejwt==5.4.0
|
||||
djangoviz==0.1.1
|
||||
docutils==0.21.2
|
||||
easy-thumbnails==2.10
|
||||
et_xmlfile==2.0.0
|
||||
Faker==33.1.0
|
||||
Faker==33.3.1
|
||||
filelock==3.16.1
|
||||
fire==0.7.0
|
||||
Flask==3.1.0
|
||||
fonttools==4.55.3
|
||||
frozenlist==1.5.0
|
||||
fsspec==2024.12.0
|
||||
gprof2dot==2024.6.6
|
||||
graphqlclient==0.2.4
|
||||
greenlet==3.1.1
|
||||
h11==0.14.0
|
||||
h2==4.1.0
|
||||
hpack==4.0.0
|
||||
hstspreload==2025.1.1
|
||||
httpcore==1.0.7
|
||||
httpx==0.28.0
|
||||
httpx==0.28.1
|
||||
hyperframe==6.0.1
|
||||
idna==3.10
|
||||
imageio==2.36.1
|
||||
imageio==2.37.0
|
||||
imagesize==1.4.1
|
||||
imgaug==0.4.0
|
||||
iso4217==1.12.20240625
|
||||
isodate==0.7.2
|
||||
isort==5.13.2
|
||||
itsdangerous==2.2.0
|
||||
Jinja2==3.1.4
|
||||
jiter==0.8.0
|
||||
Jinja2==3.1.5
|
||||
jiter==0.8.2
|
||||
joblib==1.4.2
|
||||
kiwisolver==1.4.8
|
||||
lazy_loader==0.4
|
||||
ledger==1.0.1
|
||||
libretranslatepy==2.1.4
|
||||
lmdb==1.6.2
|
||||
lxml==5.3.0
|
||||
Markdown==3.7
|
||||
markdown-it-py==3.0.0
|
||||
MarkupSafe==3.0.2
|
||||
marshmallow==3.23.2
|
||||
marshmallow==3.25.1
|
||||
matplotlib==3.10.0
|
||||
mccabe==0.7.0
|
||||
mdurl==0.1.2
|
||||
MouseInfo==0.1.3
|
||||
mpmath==1.3.0
|
||||
multidict==6.1.0
|
||||
mypy-extensions==1.0.0
|
||||
networkx==3.4.2
|
||||
newrelic==10.3.1
|
||||
newrelic==10.4.0
|
||||
nltk==3.9.1
|
||||
numpy==1.26.4
|
||||
libquadmath==2.2.2
|
||||
oauthlib==3.2.2
|
||||
ofxtools==0.9.5
|
||||
openai==1.56.2
|
||||
opencv-contrib-python==4.10.0.84
|
||||
opencv-python==4.10.0.84
|
||||
opencv-python-headless==4.10.0.84
|
||||
openai==1.59.8
|
||||
opencv-contrib-python==4.11.0.86
|
||||
opencv-python==4.11.0.86
|
||||
opencv-python-headless==4.11.0.86
|
||||
openpyxl==3.1.5
|
||||
opt-einsum==3.3.0
|
||||
opt_einsum==3.4.0
|
||||
outcome==1.3.0.post0
|
||||
packaging==24.2
|
||||
pandas==2.2.3
|
||||
pango==0.0.1
|
||||
pdfkit==1.0.0
|
||||
phonenumbers==8.13.51
|
||||
pillow==11.0.0
|
||||
phonenumbers==8.13.53
|
||||
pillow==11.1.0
|
||||
platformdirs==4.3.6
|
||||
prometheus_client==0.21.1
|
||||
propcache==0.2.1
|
||||
protobuf==5.29.3
|
||||
psycopg==3.2.3
|
||||
psycopg-binary==3.2.3
|
||||
psycopg-c==3.2.3
|
||||
psycopg==3.2.4
|
||||
psycopg-binary==3.2.4
|
||||
psycopg-c==3.2.4
|
||||
py-moneyed==3.0
|
||||
PyAutoGUI==0.9.54
|
||||
pyclipper==1.3.0.post6
|
||||
pycodestyle==2.12.1
|
||||
pycparser==2.22
|
||||
pydantic==2.10.3
|
||||
pydantic_core==2.27.1
|
||||
pydantic==2.10.5
|
||||
pydantic_core==2.27.2
|
||||
pydotplus==2.0.2
|
||||
pydyf==0.11.0
|
||||
PyGetWindow==0.0.9
|
||||
Pygments==2.18.0
|
||||
Pygments==2.19.1
|
||||
PyJWT==2.10.1
|
||||
pylint==3.3.2
|
||||
pylint==3.3.3
|
||||
PyMsgBox==1.0.9
|
||||
PyMySQL==1.1.1
|
||||
pyobjc-core==10.3.2
|
||||
pyobjc-framework-Cocoa==10.3.2
|
||||
pyobjc-framework-Quartz==10.3.2
|
||||
pyparsing==3.2.0
|
||||
pyobjc-core==11.0
|
||||
pyobjc-framework-Cocoa==11.0
|
||||
pyobjc-framework-Quartz==11.0
|
||||
pyparsing==3.2.1
|
||||
pyperclip==1.9.0
|
||||
pyphen==0.17.0
|
||||
pypng==0.20220715.0
|
||||
@ -166,30 +178,36 @@ python3-saml==1.16.0
|
||||
pytweening==1.2.0
|
||||
pytz==2024.2
|
||||
pyvin==0.0.2
|
||||
pywa==2.4.0
|
||||
pywa==2.7.0
|
||||
pywhat==5.1.0
|
||||
pywhatkit==5.4
|
||||
PyYAML==6.0.2
|
||||
pyzbar==0.1.9
|
||||
qrcode==8.0
|
||||
RapidFuzz==3.11.0
|
||||
regex==2024.11.6
|
||||
reportlab==4.2.5
|
||||
requests==2.32.3
|
||||
requests-oauthlib==2.0.0
|
||||
rfc3986==2.0.0
|
||||
rich==13.9.4
|
||||
rubicon-objc==0.4.9
|
||||
rubicon-objc==0.5.0
|
||||
sacremoses==0.1.1
|
||||
scikit-image==0.25.0
|
||||
scikit-learn==1.6.0
|
||||
scipy==1.14.1
|
||||
libomp runtime library==1.6.1
|
||||
libquadmath==1.15.1
|
||||
selenium==4.27.1
|
||||
sentencepiece==0.2.0
|
||||
shapely==2.0.6
|
||||
six==1.16.0
|
||||
six==1.17.0
|
||||
sniffio==1.3.1
|
||||
snowballstemmer==2.2.0
|
||||
sortedcontainers==2.4.0
|
||||
soupsieve==2.6
|
||||
SQLAlchemy==2.0.36
|
||||
sqlparse==0.5.2
|
||||
SQLAlchemy==2.0.37
|
||||
sqlparse==0.5.3
|
||||
stanza==1.10.1
|
||||
sympy==1.13.1
|
||||
tablib==3.7.0
|
||||
termcolor==2.5.0
|
||||
threadpoolctl==3.5.0
|
||||
@ -198,16 +216,17 @@ tinycss2==1.4.0
|
||||
tinyhtml5==2.0.0
|
||||
tomli==2.2.1
|
||||
tomlkit==0.13.2
|
||||
torch==2.5.1
|
||||
tqdm==4.67.1
|
||||
trio==0.28.0
|
||||
trio-websocket==0.11.1
|
||||
twilio==9.4.1
|
||||
twilio==9.4.3
|
||||
typing-inspect==0.9.0
|
||||
typing_extensions==4.12.2
|
||||
tzdata==2024.2
|
||||
Unidecode==1.3.8
|
||||
upgrade-requirements==1.7.0
|
||||
urllib3==2.2.3
|
||||
urllib3==2.3.0
|
||||
vin==0.6.2
|
||||
vininfo==1.8.0
|
||||
vishap==0.1.5
|
||||
|
||||
BIN
static/images/.DS_Store
vendored
BIN
static/images/.DS_Store
vendored
Binary file not shown.
@ -12,7 +12,13 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="Haikal - The Backbone of Car Qar: An innovative car inventory management system designed to streamline dealership operations. Manage inventory, sales, transfers, and accounting seamlessly with advanced analytics and intuitive tools. Inspired by Arabic origins, Haikal empowers businesses with precision and efficiency.">
|
||||
<title>{% block title %}{% trans 'HAIKAL' %}{% endblock %}</title>
|
||||
<title>
|
||||
{% block title %}
|
||||
|
||||
{% endblock %}
|
||||
{% block description %}
|
||||
{% endblock %}
|
||||
</title>
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{% static 'images/favicons/apple-touch-icon.png' %}">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="{% static 'images/favicons/favicon-32x32.png' %}">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="{% static 'images/favicons/favicon-16x16.png' %}">
|
||||
@ -41,24 +47,28 @@
|
||||
<link href="{% static 'css/user-rtl.min.css' %}" type="text/css" rel="stylesheet" id="user-style-rtl">
|
||||
{% endif %}
|
||||
|
||||
{% block extra_css %}{% endblock extra_css %}
|
||||
|
||||
{% block customCSS %}
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<main class="main" id="top">
|
||||
{% include 'header.html' %}
|
||||
<div class="content">
|
||||
|
||||
<div class="content">
|
||||
<section class="content">
|
||||
{% block content %}
|
||||
<!-- Main content goes here -->
|
||||
{% endblock %}
|
||||
|
||||
{% endblock content%}
|
||||
{% block body %}
|
||||
{% endblock body%}
|
||||
|
||||
</section>
|
||||
{% include 'footer.html' %}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
{% block extra_js %}{% endblock extra_js %}
|
||||
{% block customJS %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
{% load i18n static %}
|
||||
|
||||
<nav class="navbar navbar-vertical navbar-expand-lg">
|
||||
<div class="collapse navbar-collapse" id="navbarVerticalCollapse">
|
||||
<!-- scrollbar removed-->
|
||||
|
||||
@ -6,9 +6,7 @@
|
||||
height: auto;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
</style>
|
||||
{% include 'partials/form_errors.html' %}
|
||||
|
||||
<!-- JavaScript Section -->
|
||||
<script src="https://unpkg.com/@zxing/library@latest"></script>
|
||||
@ -17,24 +15,25 @@
|
||||
<div class="row g-3">
|
||||
<form method="post" id="carForm" class="form needs-validation" novalidate>
|
||||
{% csrf_token %}
|
||||
{% include 'partials/form_errors.html' %}
|
||||
<div class="d-flex flex-column min-vh-100">
|
||||
<div class="d-flex flex-column flex-sm-grow-1 p-4">
|
||||
<main class="d-grid gap-4 p-1">
|
||||
|
||||
<div class="row g-4">
|
||||
<h3 class="mb-3">{% trans 'Add Car' %}</h3>
|
||||
<h3 class="mb-3">{% trans 'Add Car' %}</h3>
|
||||
|
||||
<!-- VIN -->
|
||||
<div class="col-lg-4 col-xl-6">
|
||||
<div class="card h-100">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div>
|
||||
<div class="form-floating">
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
id="{{ form.vin.id_for_label }}" name="{{ form.vin.html_name }}" />
|
||||
<label for="{{ form.vin.id_for_label }}">{% trans 'VIN' %}</label>
|
||||
</div>
|
||||
<div class="form-floating">
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
id="{{ form.vin.id_for_label }}" name="{{ form.vin.html_name }}" required/>
|
||||
<label for="{{ form.vin.id_for_label }}">{% trans 'VIN' %}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group input-group-sm my-2">
|
||||
<button type="button"
|
||||
@ -61,7 +60,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="serie-row" class="col-6">
|
||||
|
||||
<div class="form-floating">
|
||||
<select class="form-select form-select-sm"
|
||||
id="{{ form.id_car_serie.id_for_label }}"
|
||||
@ -82,18 +80,15 @@
|
||||
</div>
|
||||
<div class="col-6" id="trim-row">
|
||||
<div class="form-floating">
|
||||
|
||||
<select class="form-select form-select-sm"
|
||||
id="{{ form.id_car_trim.id_for_label }}"
|
||||
name="{{ form.id_car_trim.html_name }}">
|
||||
<option value="">{% trans 'Select' %}</option>
|
||||
</select>
|
||||
<label for="{{ form.id_car_trim.id_for_label }}">{% trans 'trim'|capfirst %}</label>
|
||||
|
||||
</div>
|
||||
<select class="form-select form-select-sm"
|
||||
id="{{ form.id_car_trim.id_for_label }}"
|
||||
name="{{ form.id_car_trim.html_name }}">
|
||||
<option value="">{% trans 'Select' %}</option>
|
||||
</select>
|
||||
<label for="{{ form.id_car_trim.id_for_label }}">{% trans 'trim'|capfirst %}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row my-3">
|
||||
<div class="col-6" id="model-row">
|
||||
<div class="form-floating">
|
||||
@ -107,17 +102,16 @@
|
||||
</div>
|
||||
<div class="badge badge-phoenix fs-11 badge-phoenix-success m-1" id="generation-div"></div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="btn-group btn-group-sm justify-content-between align-items-center my-2">
|
||||
<div class="col-4 align-items-center">
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-phoenix-primary rounded-start"
|
||||
class="btn btn-sm btn-phoenix-success p-1"
|
||||
id="specification-btn"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#specificationsModal"
|
||||
disabled>{% trans 'specifications'|capfirst %}
|
||||
</button>
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-phoenix-warning rounded-end"
|
||||
class="btn btn-sm btn-phoenix-warning p-1"
|
||||
id="options-btn"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#equipmentOptionsModal"
|
||||
@ -125,17 +119,15 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="row g-3">
|
||||
<div class="col-lg-4 col-xl-6">
|
||||
<div class="row g-3">
|
||||
<!-- Vendor Field -->
|
||||
|
||||
<!--Vendor Field-->
|
||||
<div class="col-lg-4 col-xl-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-body">
|
||||
@ -145,15 +137,9 @@
|
||||
{% trans 'Vendor' %}
|
||||
</label>
|
||||
</div>
|
||||
{% if form.vendor.errors %}
|
||||
<div class="text-danger small">
|
||||
{{ form.vendor.errors|striptags }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stock Type Card -->
|
||||
<div class="col-lg-4 col-xl-4">
|
||||
<div class="card h-100">
|
||||
@ -164,12 +150,6 @@
|
||||
{% trans 'Stock Type'|capfirst %}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{% if form.stock_type.errors %}
|
||||
<div class="text-danger small">
|
||||
{{ form.stock_type.errors|striptags }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -181,11 +161,6 @@
|
||||
{{ form.mileage|add_class:"form-control form-control-sm" }}
|
||||
<label for="{{ form.mileage.id_for_label }}">{% trans 'Mileage'|capfirst %}</label>
|
||||
</div>
|
||||
{% if form.mileage.errors %}
|
||||
<div class="text-danger small">
|
||||
{{ form.mileage.errors|striptags }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -201,12 +176,6 @@
|
||||
{% trans 'Receiving Date' %}:
|
||||
</label>
|
||||
</div>
|
||||
{% if form.receiving_date.errors %}
|
||||
<div class="text-danger small">
|
||||
{{ form.receiving_date.errors|striptags }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -221,21 +190,13 @@
|
||||
</label>
|
||||
{{ form.remarks|add_class:"form-control form-control-sm" }}
|
||||
</div>
|
||||
{% if form.remarks.errors %}
|
||||
<div class="text-danger small">
|
||||
{{ form.remarks.errors|striptags }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Specifications Buttons -->
|
||||
|
||||
<!--Specifications Buttons-->
|
||||
<div class="btn-group mt-3">
|
||||
|
||||
<button type="submit" name="add_another" value="true" class="btn btn-sm btn-phoenix-success me-1">
|
||||
{% trans "Save and Add Another" %}
|
||||
</button>
|
||||
@ -243,7 +204,6 @@
|
||||
{% trans "Save and Go to Inventory" %}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
@ -264,7 +224,6 @@
|
||||
<h5 class="modal-title" id="specificationsModalLabel">
|
||||
<span class="ms-3 my-5">{% trans 'specifications'|capfirst %}</span><span class="ms-1 fw-light">{{ _("Details") }}</span>
|
||||
</h5>
|
||||
|
||||
<button class="btn btn-circle project-modal-btn position-absolute end-0 top-0 mt-3 me-3 bg-body-emphasis" data-bs-dismiss="modal">
|
||||
<span class="fa-solid fa-xmark text-body dark__text-gray-100"></span>
|
||||
</button>
|
||||
@ -318,7 +277,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Scanner Modal -->
|
||||
<!--Scanner Modal-->
|
||||
<div class="modal fade" id="scannerModal" tabindex="-1" aria-labelledby="scannerModalLabel">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content rounded-top-3">
|
||||
@ -334,7 +293,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- CAR FORM -->
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<div class="row my-5">
|
||||
<!-- Display Form Errors -->
|
||||
<div class="card shadow rounded">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<div class="card-header">
|
||||
<p class="mb-0">
|
||||
{% if customer.created %}
|
||||
<!--<i class="bi bi-pencil-square"></i>-->
|
||||
|
||||
@ -4,15 +4,14 @@
|
||||
{% block title %}{{ _("Bank Accounts") }}{% 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 "Bank Accounts" %}</h3>
|
||||
<a href="{% url 'bank_account_create' %}" class="btn btn-sm btn-success ">{% trans "Add Bank Account" %}</a>
|
||||
<div class="row mt-4">
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
|
||||
<h3 class="">{% trans "Bank Accounts" %}</h3>
|
||||
<a href="{% url 'bank_account_create' %}" class="btn btn-sm btn-phoenix-primary ">{% trans "Add Bank Account" %}</a>
|
||||
</div>
|
||||
<div class="mx-n4 px-4 mx-lg-n6 px-lg-6 bg-body-emphasis pt-7 border-y">
|
||||
|
||||
<div class="table-responsive mx-n1 px-1 scrollbar">
|
||||
|
||||
<div class="table-responsive px-1 scrollbar">
|
||||
<table class="table fs-9 mb-0 border-top border-translucent">
|
||||
<thead>
|
||||
<tr>
|
||||
@ -45,6 +44,6 @@
|
||||
</div>
|
||||
<div class="d-flex justify-content-center">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -1,273 +1,122 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{{ _("View Bill") }}{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="modal fade" id="mark_as_approved_Modal" tabindex="-1" aria-labelledby="confirmModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-primary">
|
||||
<h5 class="modal-title text-light" id="confirmModalLabel">{% trans 'Confirm' %}</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{% trans 'Are you sure' %}
|
||||
<div class="modal-footer">
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-danger"
|
||||
data-bs-dismiss="modal">
|
||||
{% trans 'No' %}
|
||||
</button>
|
||||
<form id="confirmForm" method="POST" action="{% url 'bill_mark_as_approved' bill.pk %}" class="d-inline">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-success btn-sm">{% trans "Yes" %}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ============================================-->
|
||||
<div class="modal fade" id="mark_as_paid_Modal" tabindex="-1" aria-labelledby="confirmModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-primary">
|
||||
<h5 class="modal-title text-light" id="confirmModalLabel">{% trans 'Confirm' %}</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{% trans 'Are you sure' %}
|
||||
<div class="modal-footer">
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-danger"
|
||||
data-bs-dismiss="modal">
|
||||
{% trans 'No' %}
|
||||
</button>
|
||||
<form id="confirmForm" method="POST" action="{% url 'bill_mark_as_paid' bill.pk %}" class="d-inline">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-success btn-sm">{% trans "Yes" %}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ============================================-->
|
||||
<!-- <section> begin ============================-->
|
||||
<section class="pt-5 pb-9 bg-body-emphasis dark__bg-gray-1200 border-top">
|
||||
<div class="row-small mt-3">
|
||||
<div class="d-flex justify-content-between align-items-end mb-4">
|
||||
<h2 class="mb-0">{% trans 'Bill' %}</h2>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
{% if bill.is_draft %}
|
||||
<a href="{% url 'in_review_bill' bill.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{% trans 'Review Bill' %}</span></a>
|
||||
{% endif %}
|
||||
{% if bill.is_review %}
|
||||
<button id="mark_bill_as_approved" class="btn btn-phoenix-secondary" data-bs-toggle="modal" data-bs-target="#mark_as_approved_Modal"><span class="d-none d-sm-inline-block">{% trans 'Mark as Approved' %}</span></button>
|
||||
{% endif %}
|
||||
{% if bill.is_approved %}
|
||||
<a href="{% url 'payment_create' bill.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{% trans 'Record Payment' %}</span></a>
|
||||
{% endif %}
|
||||
{% if bill.is_approved %}
|
||||
<button id="mark_bill_as_paid" class="btn btn-phoenix-secondary" data-bs-toggle="modal" data-bs-target="#mark_as_paid_Modal"><span class="d-none d-sm-inline-block">{% trans 'Mark as Paid' %}</span></button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ============================================-->
|
||||
<div class="card mb-5">
|
||||
<div class="card-body">
|
||||
<div class="row g-4 g-xl-1 g-xxl-3 justify-content-between">
|
||||
<div class="col-sm-auto">
|
||||
<div class="d-sm-block d-inline-flex d-md-flex flex-xl-column flex-xxl-row align-items-center align-items-xl-start align-items-xxl-center">
|
||||
<div class="d-flex bg-success-subtle rounded flex-center me-3 mb-sm-3 mb-md-0 mb-xl-3 mb-xxl-0" style="width:32px; height:32px"><span class="text-success-dark" data-feather="dollar-sign" style="width:24px; height:24px"></span></div>
|
||||
<div>
|
||||
<p class="fw-bold mb-1">{% trans 'Paid Amount' %}</p>
|
||||
<h4 class="fw-bolder text-nowrap {% if bill.is_paid %}text-success{% endif %}">${{bill.amount_paid}}</h4>
|
||||
<h6 class="fw-bolder text-nowrap">Owned <span class="fw-semibold text-nowrap text-success">${{bill.get_amount_open}}</span></h6>
|
||||
<div class="progress" style="height:17px">
|
||||
<div class="progress-bar fw-semibold bg-{% if bill.get_progress_percent < 100 %}secondary{% else %}success{% endif %} rounded-2" role="progressbar" style="width: {{bill.get_progress_percent}}%" aria-valuenow="{{bill.get_progress_percent}}" aria-valuemin="0" aria-valuemax="100">{{bill.get_progress_percent}}%</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if 'net' in bill.terms %}
|
||||
<div class="col-sm-auto">
|
||||
<div class="d-sm-block d-inline-flex d-md-flex flex-xl-column flex-xxl-row align-items-center align-items-xl-start align-items-xxl-center border-start-sm ps-sm-5 border-translucent">
|
||||
<div class="d-flex bg-primary-subtle rounded flex-center me-3 mb-sm-3 mb-md-0 mb-xl-3 mb-xxl-0" style="width:32px; height:32px"><span class="text-primary-dark" data-feather="layout" style="width:24px; height:24px"></span></div>
|
||||
<div>
|
||||
<p class="fw-bold mb-1"></p>
|
||||
<div class="fs-9 text-body-secondary fw-semibold mb-0 d-sm-block d-inline-flex d-md-flex flex-xl-column ">
|
||||
<table>
|
||||
<tr>
|
||||
<td>{% trans 'Terms' %}:</td>
|
||||
<td><span class="badge rounded bg-success">{{bill.bill_number}}</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Date Due' %}:</td>
|
||||
<td><span class="badge rounded bg-success">{{bill.date_due}}</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Due in Days' %}:</td>
|
||||
<td>
|
||||
<span class="badge rounded bg-success">{{bill.due_in_days}}</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Is Past Due' %}:</td>
|
||||
<td>
|
||||
{% if bill.is_past_due %}
|
||||
<span class="badge rounded bg-danger">{% trans 'Yes' %}</span>
|
||||
{% else %}
|
||||
<span class="badge rounded bg-success">{% trans 'No' %}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="col-sm-auto">
|
||||
<div class="d-sm-block d-inline-flex d-md-flex flex-xl-column flex-xxl-row align-items-center align-items-xl-start align-items-xxl-center border-start-sm ps-sm-5 border-translucent">
|
||||
<div class="d-flex bg-primary-subtle rounded flex-center me-3 mb-sm-3 mb-md-0 mb-xl-3 mb-xxl-0" style="width:32px; height:32px"><span class="text-primary-dark" data-feather="layout" style="width:24px; height:24px"></span></div>
|
||||
<div>
|
||||
<p class="fw-bold mb-1">{% trans 'Due Amount' %}</p>
|
||||
{% if bill.is_paid %}
|
||||
<s><h4 class="fw-bolder text-nowrap">${{bill.amount_due}} </h4></s>
|
||||
{% else %}
|
||||
<h4 class="fw-bolder text-nowrap">${{bill.amount_due}} </h4>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <section> begin ============================-->
|
||||
<div class="bg-body dark__bg-gray-1100 p-4 mb-4 rounded-2 text-body-tertiary">
|
||||
<div class="row g-4">
|
||||
<div class="col-12 col-lg-3">
|
||||
<div class="row g-4 g-lg-2">
|
||||
<div class="col-12 col-sm-6 col-lg-12">
|
||||
<div class="row align-items-center g-0">
|
||||
<div class="col-auto col-lg-6 col-xl-5">
|
||||
<h6 class="mb-0 me-3">{% trans "Bill Number" %} :</h6>
|
||||
</div>
|
||||
<div class="col-auto col-lg-6 col-xl-7">
|
||||
<p class="fs-9 text-body-secondary fw-semibold mb-0">#{{bill.bill_number}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 col-lg-12">
|
||||
<div class="row align-items-center g-0">
|
||||
<div class="col-auto col-lg-6 col-xl-5">
|
||||
<h6 class="me-3">{% trans "Bill Date" %} :</h6>
|
||||
</div>
|
||||
<div class="col-auto col-lg-6 col-xl-7">
|
||||
<p class="fs-9 text-body-secondary fw-semibold mb-0">{{bill.created}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 col-lg-5">
|
||||
<div class="row align-items-center g-0">
|
||||
<div class="col-auto col-lg-6 col-xl-5">
|
||||
<h6 class="mb-2 me-3">{% trans "Customer Name" %} :</h6>
|
||||
<p class="fs-9 text-body-secondary fw-semibold mb-0">{{bill.vendor.vendor_name}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 col-lg-4">
|
||||
<div class="row g-4">
|
||||
<div class="col-12 col-lg-6">
|
||||
<h6 class="mb-2"> {% trans "bill Status" %} :</h6>
|
||||
<div class="fs-9 text-body-secondary fw-semibold mb-0">
|
||||
{% if bill.bill_status == 'draft' %}
|
||||
<span class="badge text-bg-warning">{% trans "Draft" %}</span>
|
||||
{% elif bill.bill_status == 'in_review' %}
|
||||
<span class="badge text-bg-info">{% trans "In Review" %}</span>
|
||||
{% elif bill.bill_status == 'approved' %}
|
||||
<span class="badge text-bg-info">{% trans "Approved" %}</span>
|
||||
{% elif bill.bill_status == 'declined' %}
|
||||
<span class="badge text-bg-danger">{% trans "Declined" %}</span>
|
||||
{% elif bill.bill_status == 'paid' %}
|
||||
<span class="badge text-bg-success">{% trans "Paid" %}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-0">
|
||||
<div class="table-responsive scrollbar">
|
||||
<table id="bill-table" class="table fs-9 text-body mb-0">
|
||||
<thead class="bg-body-secondary">
|
||||
<tr>
|
||||
<th scope="col" style="width: 24px;">#</th>
|
||||
<th scope="col" style="min-width: 260px;">{% trans "Item" %}</th>
|
||||
<th scope="col" style="min-width: 60px;">{% trans "Quantity" %}</th>
|
||||
<th scope="col" style="min-width: 60px;">{% trans "Unit Price" %}</th>
|
||||
<th scope="col" style="min-width: 60px;">{% trans "Total" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in car_and_item_info %}
|
||||
<tr>
|
||||
<td class="">{{forloop.counter}}</td>
|
||||
<td class="">{{item.car.id_car_model}}</td>
|
||||
<td class="align-middle">{{item.itemmodel.quantity}}</td>
|
||||
<td class="align-middle ps-5">{{item.car.finances.cost_price}}</td>
|
||||
<td class="align-middle text-body-tertiary fw-semibold">{{item.total}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr class="bg-body-secondary total-sum">
|
||||
<td class="align-middle ps-4 fw-bolder text-body-highlight" colspan="4">{% trans "Grand Total" %}</td>
|
||||
<td class="align-middle text-start fw-bolder">
|
||||
<span id="grand-total">{{grand_total}}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% block title %}
|
||||
{{ page_title }}
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script>
|
||||
function calculateTotals() {
|
||||
const table = document.getElementById('estimate-table');
|
||||
const rows = table.getElementsByTagName('tbody')[0].rows;
|
||||
let grandTotal = 0;
|
||||
{% 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">
|
||||
<div class="modal-body d-flex justify-content-center">
|
||||
<h1 class="text-danger me-2"><i class="bi bi-exclamation-diamond-fill"></i></h1>
|
||||
<span class="text-danger">{% trans 'Are you sure you want to delete this account?' %}</span>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-sm btn-secondary" data-bs-dismiss="modal">{% trans 'No' %}</button>
|
||||
<div class="btn btn-sm btn-danger">
|
||||
<form action="{% url 'account_delete' account.pk %}" method="post">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-sm btn-danger">{% trans 'Yes' %}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
for (let row of rows) {
|
||||
// Ensure the row has the expected number of cells
|
||||
if (row.cells.length >= 5) {
|
||||
const quantity = parseFloat(row.cells[2].textContent); // Quantity column
|
||||
const unitPrice = parseFloat(row.cells[3].textContent); // Unit Price column
|
||||
<div class="row my-5">
|
||||
<div class="card rounded">
|
||||
<div class="card-header">
|
||||
<p class="mb-0">{{ header_title|upper }}</p>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<p>
|
||||
<strong>{{ _('Account Name') }}:</strong> {{ account.name }}
|
||||
</p>
|
||||
<p>
|
||||
<strong>{{ _('Account Code') }}:</strong> {{ account.code }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p>
|
||||
<strong>{{ _('Balance Type') }}:</strong> {{ account.balance_type }}
|
||||
</p>
|
||||
<p>
|
||||
<strong>{{ _('Active') }}:</strong> {{ account.active }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table class="table is-fullwidth is-narrow is-striped is-bordered django-ledger-table-bottom-margin-75">
|
||||
<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>
|
||||
<th class="has-text-centered">{{ _('Unit') }}</th>
|
||||
<th class="has-text-centered">{{ _('Actions') }}</th>
|
||||
</tr>
|
||||
|
||||
if (!isNaN(quantity) && !isNaN(unitPrice)) {
|
||||
const total = quantity * unitPrice;
|
||||
row.cells[4].textContent = total.toFixed(2); // Populate Total column
|
||||
grandTotal += total; // Add to grand total
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Display the grand total
|
||||
document.getElementById('grand-total').textContent = grandTotal.toFixed(2);
|
||||
}
|
||||
|
||||
|
||||
// Run the function on page load
|
||||
window.onload = calculateTotals;
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
{% 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>
|
||||
{% if tx.tx_type == 'debit' %}
|
||||
${{ tx.amount }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if tx.tx_type == 'credit' %}
|
||||
${{ tx.amount }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ tx.description }}</td>
|
||||
<td>{{ tx.journal_entry.entity_unit.name }}</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">Total</td>
|
||||
<td class="has-text-centered">${{ total_debits }}</td>
|
||||
<td class="has-text-centered">${{ total_credits }}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer d-flex">
|
||||
<a class="btn btn-sm btn-primary me-1" href="{% url 'account_update' account.pk %}">
|
||||
<!-- <i class="bi bi-pencil-square"></i> -->
|
||||
{{ _('Edit') }}
|
||||
</a>
|
||||
<a class="btn btn-sm btn-danger me-1" data-bs-toggle="modal" data-bs-target="#deleteModal">
|
||||
<!-- <i class="bi bi-trash-fill"></i> -->
|
||||
{{ _('Delete') }}
|
||||
</a>
|
||||
<a class="btn btn-sm btn-secondary" href="{% url 'account_list' %}">
|
||||
<!-- <i class="bi bi-arrow-left-square-fill"></i> -->
|
||||
{% trans 'Back to List' %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -1,159 +1,38 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_filters %}
|
||||
{% load i18n static %}
|
||||
|
||||
{% block title %}{{ _("Create Bill") }}{% endblock title %}
|
||||
{% block title %}{% trans "account" %}{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mt-4">
|
||||
<h3 class="text-center">{% trans "Create Bill" %}</h3>
|
||||
<form id="mainForm" method="post" class="needs-validation">
|
||||
{% csrf_token %}
|
||||
<div class="row g-3">
|
||||
{{ form|crispy }}
|
||||
<div class="row mt-5">
|
||||
<div id="formrow">
|
||||
<h3 class="text-start">Unit Items</h3>
|
||||
<div class="form-row row g-3 mb-3 mt-5">
|
||||
<div class="mb-2 col-sm-2">
|
||||
<select class="form-control item" name="item[]" required>
|
||||
{% for item in items %}
|
||||
<option value="{{ item.product.pk }}">{{ item.car.id_car_model }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-2 col-sm-2">
|
||||
<input class="form-control quantity" type="number" placeholder="Quantity" name="quantity[]" required>
|
||||
</div>
|
||||
<div class="mb-2 col-sm-1">
|
||||
<button class="btn btn-danger removeBtn">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<button id="addMoreBtn" class="btn btn-primary">Add More</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row my-5">
|
||||
<!-- Display Form Errors -->
|
||||
<div class="card shadow rounded">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<p class="mb-0">
|
||||
{% if account.created %}
|
||||
<!--<i class="bi bi-pencil-square"></i>-->
|
||||
{{ _("Edit Account") }}
|
||||
{% else %}
|
||||
<!--<i class="bi bi-person-plus"></i> -->
|
||||
{{ _("Add Account") }}
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Buttons -->
|
||||
<div class="mt-5 text-center">
|
||||
<button type="submit" class="btn btn-success me-2" {% if not items %}disabled{% endif %}>{% trans "Save" %}</button>
|
||||
<a href="{% url 'bill_list' %}" class="btn btn-secondary">{% trans "Cancel" %}</a>
|
||||
<div class="card-body">
|
||||
<form method="post" class="form" novalidate>
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
{% for error in form.errors %}
|
||||
<div class="text-danger">{{ error }}</div>
|
||||
{% endfor %}
|
||||
<div class="d-flex justify-content-end">
|
||||
<button class="btn btn-sm btn-success me-1" type="submit">
|
||||
{{ _("Save") }}
|
||||
</button>
|
||||
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-sm btn-danger">{% trans "Cancel"|capfirst %}</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script>
|
||||
const Toast = Swal.mixin({
|
||||
toast: true,
|
||||
position: "top-end",
|
||||
showConfirmButton: false,
|
||||
timer: 2000,
|
||||
timerProgressBar: false,
|
||||
didOpen: (toast) => {
|
||||
toast.onmouseenter = Swal.stopTimer;
|
||||
toast.onmouseleave = Swal.resumeTimer;
|
||||
}
|
||||
});
|
||||
// Add new form fields
|
||||
document.getElementById('addMoreBtn').addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
const formrow = document.getElementById('formrow');
|
||||
const newForm = document.createElement('div');
|
||||
newForm.className = 'form-row row g-3 mb-3 mt-5';
|
||||
newForm.innerHTML = `
|
||||
<div class="mb-2 col-sm-2">
|
||||
<select class="form-control item" name="item[]" required>
|
||||
{% for item in items %}
|
||||
<option value="{{ item.product.pk }}">{{ item.car.id_car_model }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-2 col-sm-2">
|
||||
<input class="form-control quantity" type="number" placeholder="Quantity" name="quantity[]" required>
|
||||
</div>
|
||||
<div class="mb-2 col-sm-1">
|
||||
<button class="btn btn-danger removeBtn">Remove</button>
|
||||
</div>
|
||||
`;
|
||||
formrow.appendChild(newForm);
|
||||
|
||||
// Add remove button functionality
|
||||
newForm.querySelector('.removeBtn').addEventListener('click', function() {
|
||||
newForm.remove();
|
||||
});
|
||||
});
|
||||
|
||||
// Add remove button functionality to the initial form
|
||||
document.querySelectorAll('.form-row').forEach(row => {
|
||||
row.querySelector('.removeBtn').addEventListener('click', function() {
|
||||
row.remove();
|
||||
});
|
||||
});
|
||||
|
||||
// Handle form submission
|
||||
document.getElementById('mainForm').addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
/*const titleInput = document.querySelector('[name="title"]');
|
||||
if (titleInput.value.length < 5) {
|
||||
notify("error", "Customer billte Title must be at least 5 characters long.");
|
||||
return; // Stop form submission
|
||||
}*/
|
||||
|
||||
// Collect all form data
|
||||
const formData = {
|
||||
csrfmiddlewaretoken: document.querySelector('[name=csrfmiddlewaretoken]').value,
|
||||
vendor: document.querySelector('[name=vendor]').value,
|
||||
xref: document.querySelector('[name=xref]').value,
|
||||
date_draft: document.querySelector('[name=date_draft]').value,
|
||||
terms: document.querySelector('[name=terms]').value,
|
||||
item: [],
|
||||
quantity: []
|
||||
};
|
||||
|
||||
|
||||
// Collect multi-value fields (e.g., item[], quantity[])
|
||||
document.querySelectorAll('[name="item[]"]').forEach(input => {
|
||||
formData.item.push(input.value);
|
||||
});
|
||||
document.querySelectorAll('[name="quantity[]"]').forEach(input => {
|
||||
formData.quantity.push(input.value);
|
||||
});
|
||||
console.log(formData);
|
||||
|
||||
try {
|
||||
// Send data to the server using fetch
|
||||
const response = await fetch("{% url 'bill_create' %}", {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRFToken': formData.csrfmiddlewaretoken,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(formData)
|
||||
});
|
||||
|
||||
// Parse the JSON response
|
||||
const data = await response.json();
|
||||
|
||||
// Handle the response
|
||||
if (data.status === "error") {
|
||||
notify("error", data.message); // Display an error message
|
||||
} else if (data.status === "success") {
|
||||
notify("success","Bill created successfully");
|
||||
setTimeout(() => {
|
||||
window.location.assign(data.url); // Redirect to the provided URL
|
||||
}, 1000);
|
||||
} else {
|
||||
notify("error","Unexpected response from the server");
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
notify("error", error);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock extra_js %}
|
||||
{% endblock %}
|
||||
@ -30,25 +30,30 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Customer Table -->
|
||||
<!-- Customer Table -->
|
||||
|
||||
{% if page_obj.object_list %}
|
||||
<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' %}
|
||||
{% trans 'Account Name' %}
|
||||
</th>
|
||||
<th class="border-top border-translucent">
|
||||
{% trans 'Bill Status' %}
|
||||
</th>
|
||||
{% trans 'Code' %}
|
||||
</th>
|
||||
<th class="border-top border-translucent text-end pe-3">
|
||||
{% trans 'Vendor' %}
|
||||
{% 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>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="list">
|
||||
<tbody class="list">
|
||||
{% for bill in bills %}
|
||||
<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">
|
||||
@ -73,16 +78,31 @@
|
||||
</div>
|
||||
</div>
|
||||
<tr>
|
||||
<td class="align-middle ps-3">{{ bill.bill_number }}</td>
|
||||
<td class="align-middle">{{ bill.bill_status }}</td>
|
||||
<td class="align-middle ps-3">{{ bill.name }}</td>
|
||||
<td class="align-middle">{{ bill.code }}</td>
|
||||
<td class="align-middle text-end py-3 pe-3">
|
||||
{{bill.vendor.vendor_name}}
|
||||
{% if bill.balance_type == 'debit' %}
|
||||
<div class="badge badge-phoenix fs-10 badge-phoenix-success">
|
||||
<span class="fw-bold">{{ _('Debit') }}</span><span class="ms-1 fas fa-arrow-circle-down"></span>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="badge badge-phoenix fs-10 badge-phoenix-danger">
|
||||
<span class="fw-bold">{{ _('Credit') }}</span><span class="ms-1 fas fa-arrow-circle-up"></span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="align-middle text-end py-3 pe-3">
|
||||
{% if bill.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>
|
||||
<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>
|
||||
<a href="{% url 'bill_detail' bill.uuid %}" class="dropdown-item text-success-dark">{% trans 'View' %}</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>
|
||||
@ -137,7 +157,8 @@
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_filters %}
|
||||
{% block title %}{% trans "Bill" %}{% endblock title %}
|
||||
{% block content %}
|
||||
<div class="row my-4">
|
||||
<h2>{% trans "Bill" %}</h2>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<button type="submit" class="btn btn-sm btn-primary">
|
||||
{% if bill.is_draft %}
|
||||
{% trans "Mark As Review" %}
|
||||
{% elif bill.is_review or bill.is_approved %}
|
||||
{% trans "Save" %}
|
||||
{% endif %}
|
||||
</button>
|
||||
<a href="{% url 'bill_detail' pk=bill.pk %}" class="btn btn-sm btn-secondary">{% trans "Cancel" %}</a>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -27,12 +27,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row my-5">
|
||||
<div class="card rounded">
|
||||
<div class="card-header">
|
||||
<p class="mb-0">{{ header_title|upper }}</p>
|
||||
<div class="row my-3">
|
||||
<div class="">
|
||||
<div class="">
|
||||
<p class="mb-3"></p>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<p>
|
||||
@ -44,7 +44,12 @@
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p>
|
||||
<strong>{{ _('Balance Type') }}:</strong> {{ account.balance_type }}
|
||||
{% if account.balance_type == 'debit' %}
|
||||
<strong>{{ _('Balance Type') }}:</strong><div class="badge badge-phoenix fs-10 badge-phoenix-success"> <span class="fw-bold">{{ _("Debit") }}</span><span class="ms-1 fas fa-arrow-circle-down"></span></div>
|
||||
{% else %}
|
||||
<strong>{{ _('Balance Type') }}:</strong><div class="badge badge-phoenix fs-10 badge-phoenix-danger"> <span class="fw-bold">{{ _("Credit") }}</span><span class="ms-1 fas fa-arrow-circle-up"></span></div>
|
||||
{% endif %}
|
||||
|
||||
</p>
|
||||
<p>
|
||||
<strong>{{ _('Active') }}:</strong> {{ account.active }}
|
||||
@ -52,15 +57,15 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table class="table is-fullwidth is-narrow is-striped is-bordered django-ledger-table-bottom-margin-75">
|
||||
<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>
|
||||
<th class="has-text-centered">{{ _('Unit') }}</th>
|
||||
|
||||
<th class="has-text-centered">{{ _('Actions') }}</th>
|
||||
</tr>
|
||||
|
||||
@ -70,16 +75,16 @@
|
||||
<td>{{ tx.journal_entry.timestamp }}</td>
|
||||
<td>
|
||||
{% if tx.tx_type == 'debit' %}
|
||||
${{ tx.amount }}
|
||||
{{ tx.amount }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if tx.tx_type == 'credit' %}
|
||||
${{ tx.amount }}
|
||||
{{ tx.amount }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ tx.description }}</td>
|
||||
<td>{{ tx.journal_entry.entity_unit.name }}</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>
|
||||
@ -92,9 +97,9 @@
|
||||
{% endfor %}
|
||||
<tr class="has-text-weight-bold">
|
||||
<td></td>
|
||||
<td class="has-text-right">Total</td>
|
||||
<td class="has-text-centered">${{ total_debits }}</td>
|
||||
<td class="has-text-centered">${{ total_credits }}</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">{{ total_debits }} {{ _("SAR") }}</span></td>
|
||||
<td class="has-text-centered"><span class="fw-bold fs-8">{{ total_credits }} {{ _("SAR") }}</span></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
@ -103,16 +108,16 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer d-flex">
|
||||
<a class="btn btn-sm btn-primary me-1" href="{% url 'account_update' account.pk %}">
|
||||
<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> -->
|
||||
{{ _('Edit') }}
|
||||
</a>
|
||||
<a class="btn btn-sm btn-danger me-1" data-bs-toggle="modal" data-bs-target="#deleteModal">
|
||||
<a class="btn btn-sm btn-phoenix-danger me-1" data-bs-toggle="modal" data-bs-target="#deleteModal">
|
||||
<!-- <i class="bi bi-trash-fill"></i> -->
|
||||
{{ _('Delete') }}
|
||||
</a>
|
||||
<a class="btn btn-sm btn-secondary" href="{% url 'account_list' %}">
|
||||
<a class="btn btn-sm btn-phoenix-secondary" href="{% url 'account_list' %}">
|
||||
<!-- <i class="bi bi-arrow-left-square-fill"></i> -->
|
||||
{% trans 'Back to List' %}
|
||||
</a>
|
||||
|
||||
@ -8,14 +8,12 @@
|
||||
</a>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="d-flex flex-column min-vh-100">
|
||||
<div class="d-flex flex-column flex-sm-grow-1 ms-sm-14 p-4">
|
||||
<main class="d-grid gap-4 p-1">
|
||||
|
||||
<!-- Search Bar -->
|
||||
<div class="row g-4">
|
||||
<div class="row mt-4">
|
||||
<h3 class="mb-3">{% trans "Accounts" %}</h3>
|
||||
<div class="col-12">
|
||||
<div class="row-fluid p-2">
|
||||
<form method="get">
|
||||
<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" %}
|
||||
@ -33,17 +31,12 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Customer Table -->
|
||||
|
||||
{% if page_obj.object_list %}
|
||||
|
||||
<div id="accountsTable">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm fs-9 mb-0">
|
||||
<div class="table-responsive px-1 scrollbar">
|
||||
<table class="table fs-9 mb-0 border-top border-translucent">
|
||||
<thead>
|
||||
|
||||
<tr class="bg-body-highlight">
|
||||
@ -140,9 +133,7 @@
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@ -46,7 +46,7 @@
|
||||
<td class="text-center">
|
||||
<a href="{% url 'estimate_detail' estimate.pk %}"
|
||||
class="btn btn-sm btn-phoenix-success">
|
||||
{% trans "view" %}
|
||||
{% trans "view"|capfirst %}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@ -4,15 +4,12 @@
|
||||
{% block title %}{{ _("Invoices") }}{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="d-flex flex-column min-vh-100">
|
||||
<div class="d-flex flex-column flex-sm-grow-1 ms-sm-14 p-4">
|
||||
<main class="d-grid gap-4 p-1">
|
||||
<div class="row mt-4">
|
||||
<h3 class="text-center">{% trans "Invoices" %}</h3>
|
||||
<div class="mx-n4 px-4 mx-lg-n6 px-lg-6 pt-7 border-y">
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm fs-9 mb-0">
|
||||
<div class="row mt-4">
|
||||
<h3 class="mb-3">{% trans "Invoices" %}</h3>
|
||||
|
||||
<div class="table-responsive px-1 scrollbar">
|
||||
<table class="table fs-9 mb-0 border-top border-translucent">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Invoice Number" %}</th>
|
||||
@ -78,8 +75,4 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -6,10 +6,10 @@
|
||||
{% block content %}
|
||||
<div class="row mt-4">
|
||||
<h3 class="text-center">{% trans "Invoices" %}</h3>
|
||||
<div class="mx-n4 px-4 mx-lg-n6 px-lg-6 bg-body-emphasis pt-7 border-y">
|
||||
|
||||
<div class="table-responsive mx-n1 px-1 scrollbar">
|
||||
<table class="table fs-9 mb-0 border-top border-translucent">
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm fs-9 mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Invoice Number" %}</th>
|
||||
@ -45,6 +45,6 @@
|
||||
</div>
|
||||
<div class="d-flex justify-content-center">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -5,10 +5,9 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="row mt-4">
|
||||
<h3 class="text-center">{% trans "Transactions" %}</h3>
|
||||
<div class="mx-n4 px-4 mx-lg-n6 px-lg-6 bg-body-emphasis pt-7 border-y">
|
||||
<h3 class="mb-3">{% trans "Transactions" %}</h3>
|
||||
|
||||
<div class="table-responsive mx-n1 px-1 scrollbar">
|
||||
<div class="table-responsive px-1 scrollbar">
|
||||
<table class="table fs-9 mb-0 border-top border-translucent">
|
||||
<thead>
|
||||
<tr>
|
||||
@ -42,6 +41,6 @@
|
||||
</div>
|
||||
<div class="d-flex justify-content-center">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -12,18 +12,18 @@
|
||||
</style>
|
||||
{% endblock extra_css %}
|
||||
{% block content %}
|
||||
<div class="row {% if model.is_paid %}paid{% endif %}">
|
||||
<div class="row {% if invoice.invoice_status == 'paid' %}paid{% endif %}">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
{% if model.is_paid %}
|
||||
{% if invoice.invoice_status == 'paid' %}
|
||||
<div class="card-header">{{ _("Payment Already Made") }}</div>
|
||||
{% else %}
|
||||
<div class="card-header">{{ _("Make Payment") }}</div>
|
||||
{% endif %}
|
||||
<div class="card-body">
|
||||
{% if model %}
|
||||
<form method="post" action="{% url 'payment_create' pk=model.pk %}">
|
||||
{% if invoice %}
|
||||
<form method="post" action="{% url 'payment_create' pk=invoice.pk %}">
|
||||
{% else %}
|
||||
<form method="post" action="{% url 'payment_create' %}">
|
||||
{% endif %}
|
||||
|
||||
@ -5,11 +5,12 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="row mt-4">
|
||||
<div class="d-flex justify-content-end align-content-end">
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<h3 class="mb-3">{% trans "Payments" %}</h3>
|
||||
<a href="{% url 'payment_create' %}" class="btn btn-sm btn-phoenix-success ">{% trans "Add Payment" %}</a>
|
||||
|
||||
</div>
|
||||
<h3 class="mb-3">{% trans "Payments" %}</h3>
|
||||
|
||||
<div class="table-responsive px-1 scrollbar">
|
||||
<table class="table fs-9 mb-0 border-top border-translucent">
|
||||
<thead>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user