sale order

This commit is contained in:
gitea 2025-01-27 15:41:30 +00:00
parent 149c68cfae
commit 7142975004
25 changed files with 1984 additions and 145 deletions

View File

@ -24,7 +24,7 @@ urlpatterns += i18n_patterns(
path('ledger/', include('django_ledger.urls', namespace='django_ledger')),
path("haikalbot/", include("haikalbot.urls")),
path('appointment/', include('appointment.urls')),
path('plans/', include('plans.urls')),
# path('plans/', include('plans.urls')),
)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@ -230,7 +230,7 @@ class CarUpdateForm(forms.ModelForm, AddClassMixin):
# ]
class CarFinanceForm(AddClassMixin, forms.ModelForm):
class CarFinanceForm(forms.ModelForm):
additional_finances = forms.ModelMultipleChoiceField(
queryset=AdditionalServices.objects.all(),
widget=forms.CheckboxSelectMultiple(attrs={"class": "form-check-input"}),
@ -689,8 +689,7 @@ class BillModelCreateForm(BillModelCreateFormBase):
class SaleOrderForm(forms.ModelForm):
class Meta:
model = SaleOrder
fields = '__all__'
widgets = {
'address': forms.Textarea(attrs={'rows': 3}),
fields = ['estimate','payment_method', 'comments']
widgets = {
'comments': forms.Textarea(attrs={'rows': 3}),
}

View File

@ -0,0 +1,25 @@
# Generated by Django 4.2.17 on 2025-01-27 08:29
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('django_ledger', '0017_alter_accountmodel_unique_together_and_more'),
('inventory', '0007_alter_cartransfer_status'),
]
operations = [
migrations.AddField(
model_name='saleorder',
name='estimate',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='sale_orders', to='django_ledger.estimatemodel', verbose_name='Estimate'),
),
migrations.AddField(
model_name='saleorder',
name='invoice',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='sale_orders', to='django_ledger.invoicemodel', verbose_name='Invoice'),
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 4.2.17 on 2025-01-27 08:57
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0008_saleorder_estimate_saleorder_invoice'),
]
operations = [
migrations.AddField(
model_name='saleorder',
name='formatted_order_id',
field=models.CharField(default=1, editable=False, max_length=10, unique=True),
preserve_default=False,
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 4.2.17 on 2025-01-27 09:02
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('inventory', '0009_saleorder_formatted_order_id'),
]
operations = [
migrations.RemoveField(
model_name='saleorder',
name='car',
),
migrations.RemoveField(
model_name='saleorder',
name='customer',
),
migrations.RemoveField(
model_name='saleorder',
name='trade_in',
),
]

View File

@ -0,0 +1,21 @@
# Generated by Django 4.2.17 on 2025-01-27 11:48
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('django_ledger', '0017_alter_accountmodel_unique_together_and_more'),
('inventory', '0010_remove_saleorder_car_remove_saleorder_customer_and_more'),
]
operations = [
migrations.AlterField(
model_name='saleorder',
name='estimate',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='sale_orders', to='django_ledger.estimatemodel', verbose_name='Estimate'),
preserve_default=False,
),
]

View File

@ -33,7 +33,7 @@ class AddClassMixin:
"""
field = super().__getitem__(name)
wrapper_class = field.field.widget.attrs.pop('wrapper_class', None)
if wrapper_class:
if wrapper_class:
field = forms.utils.safety.mark_safe(f'<div class="{wrapper_class}">{field}</div>')
return field

View File

@ -28,7 +28,7 @@ from sqlalchemy.orm.base import object_state
from .utilities.financials import get_financial_value, get_total, get_total_financials
from django.db.models import FloatField
from .mixins import LocalizedNameMixin
from django_ledger.models import EntityModel, ItemModel
from django_ledger.models import EntityModel, ItemModel,EstimateModel,InvoiceModel
from django_countries.fields import CountryField
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
@ -1540,34 +1540,55 @@ class UserActivityLog(models.Model):
def __str__(self):
return f"{self.user.email} - {self.action} - {self.timestamp}"
class SaleOrder(models.Model):
customer = models.ForeignKey(
Customer,
class SaleOrder(models.Model):
estimate = models.ForeignKey(
EstimateModel,
on_delete=models.CASCADE,
related_name="sale_orders",
verbose_name=_("Customer"),
verbose_name=_("Estimate")
)
car = models.ForeignKey(
Car,
invoice = models.ForeignKey(
InvoiceModel,
on_delete=models.CASCADE,
related_name="sale_orders",
verbose_name=_("Car"),
verbose_name=_("Invoice"),
null=True,
blank=True
)
payment_method = models.CharField(max_length=20, choices=[
('cash', 'Cash'),
('finance', 'Finance'),
('lease', 'Lease'),
])
trade_in = models.CharField(max_length=100, blank=True, null=True)
])
comments = models.TextField(blank=True, null=True)
formatted_order_id = models.CharField(max_length=10, unique=True, editable=False)
def save(self, *args, **kwargs):
if not self.formatted_order_id:
last_order = SaleOrder.objects.order_by('-id').first()
if last_order:
next_id = last_order.id + 1
else:
next_id = 1
self.formatted_order_id = f"{next_id:05d}"
super().save(*args, **kwargs)
def __str__(self):
return f"Sale Order for {self.full_name} - {self.make} {self.model}"
return f"Sale Order for {self.full_name}"
@property
def full_name(self):
return f"{self.customer.first_name} {self.customer.last_name}"
return f"{self.customer.customer_name}"
@property
def price(self):
return self.car.finances.selling_price
return self.car.finances.selling_price
@property
def items(self):
if self.estimate.get_itemtxs_data():
return self.estimate.get_itemtxs_data()[0]
return []
@property
def customer(self):
return self.estimate.customer

View File

@ -1,8 +1,10 @@
from django import template
from calendar import month_abbr
from django.urls import reverse
from django_ledger.io.io_core import get_localdate
register = template.Library()
@register.filter(name='percentage')
def percentage(value):
if value is not None:
@ -31,3 +33,116 @@ def attr(field, args):
attrs[definition.strip()] = True
return field.as_widget(attrs=attrs)
@register.inclusion_tag('ledger/reports/components/period_navigator.html', takes_context=True)
def period_navigation(context, base_url: str):
kwargs = dict()
entity_slug = context['view'].kwargs['entity_slug']
kwargs['entity_slug'] = entity_slug
if context['view'].kwargs.get('ledger_pk'):
kwargs['ledger_pk'] = context['view'].kwargs.get('ledger_pk')
if context['view'].kwargs.get('account_pk'):
kwargs['account_pk'] = context['view'].kwargs.get('account_pk')
if context['view'].kwargs.get('unit_slug'):
kwargs['unit_slug'] = context['view'].kwargs.get('unit_slug')
if context['view'].kwargs.get('coa_slug'):
kwargs['coa_slug'] = context['view'].kwargs.get('coa_slug')
ctx = dict()
ctx['year'] = context['year']
ctx['has_year'] = context.get('has_year')
ctx['has_quarter'] = context.get('has_quarter')
ctx['has_month'] = context.get('has_month')
ctx['has_date'] = context.get('has_date')
ctx['previous_year'] = context['previous_year']
kwargs['year'] = context['previous_year']
ctx['previous_year_url'] = reverse(f'{base_url}-year', kwargs=kwargs)
ctx['next_year'] = context['next_year']
kwargs['year'] = context['next_year']
ctx['next_year_url'] = reverse(f'{base_url}-year', kwargs=kwargs)
kwargs['year'] = context['year']
ctx['current_year_url'] = reverse(f'{base_url}-year', kwargs=kwargs)
dt = get_localdate()
KWARGS_CURRENT_MONTH = {
'entity_slug': context['view'].kwargs['entity_slug'],
'year': dt.year,
'month': dt.month
}
if 'unit_slug' in kwargs:
KWARGS_CURRENT_MONTH['unit_slug'] = kwargs['unit_slug']
if 'account_pk' in kwargs:
KWARGS_CURRENT_MONTH['account_pk'] = kwargs['account_pk']
if 'ledger_pk' in kwargs:
KWARGS_CURRENT_MONTH['ledger_pk'] = kwargs['ledger_pk']
if 'coa_slug' in kwargs:
KWARGS_CURRENT_MONTH['coa_slug'] = kwargs['coa_slug']
ctx['current_month_url'] = reverse(f'{base_url}-month',
kwargs=KWARGS_CURRENT_MONTH)
quarter_urls = list()
ctx['quarter'] = context.get('quarter')
for Q in range(1, 5):
kwargs['quarter'] = Q
quarter_urls.append({
'url': reverse(f'{base_url}-quarter', kwargs=kwargs),
'quarter': Q,
'quarter_name': f'Q{Q}'
})
del kwargs['quarter']
ctx['quarter_urls'] = quarter_urls
month_urls = list()
ctx['month'] = context.get('month')
for M in range(1, 13):
kwargs['month'] = M
month_urls.append({
'url': reverse(f'{base_url}-month', kwargs=kwargs),
'month': M,
'month_abbr': month_abbr[M]
})
ctx['month_urls'] = month_urls
ctx['from_date'] = context['from_date']
ctx['to_date'] = context['to_date']
ctx.update(kwargs)
ctx['date_navigation_url'] = context.get('date_navigation_url')
return ctx
@register.inclusion_tag('ledger/reports/tags/balance_sheet_statement.html', takes_context=True)
def balance_sheet_statement(context, io_model, to_date=None):
user_model = context['user']
activity = context['request'].GET.get('activity')
entity_slug = context['view'].kwargs.get('entity_slug')
if not to_date:
to_date = context['to_date']
io_digest = io_model.digest(
activity=activity,
user_model=user_model,
equity_only=False,
entity_slug=entity_slug,
unit_slug=context['unit_slug'],
by_unit=context['by_unit'],
to_date=to_date,
signs=True,
process_groups=True,
balance_sheet_statement=True)
return {
'entity_slug': entity_slug,
'user_model': user_model,
'tx_digest': io_digest.get_io_data(),
}

View File

@ -0,0 +1,908 @@
# """
# Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
# Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
# Contributions to this module:
# Miguel Sanda <msanda@arrobalytics.com>
# """
from calendar import month_abbr
from random import randint
from django import template
from django.db.models import Sum
from django.urls import reverse
from django.utils.formats import number_format
from django_ledger import __version__
from django_ledger.forms.app_filters import EntityFilterForm, ActivityFilterForm
from django_ledger.forms.feedback import BugReportForm, RequestNewFeatureForm
from django_ledger.io import CREDIT, DEBIT, ROLES_ORDER_ALL
from django_ledger.io.io_core import validate_activity, get_localdate
from django_ledger.models import TransactionModel, BillModel, InvoiceModel, EntityUnitModel
from django_ledger.settings import (
DJANGO_LEDGER_FINANCIAL_ANALYSIS, DJANGO_LEDGER_CURRENCY_SYMBOL,
DJANGO_LEDGER_SPACED_CURRENCY_SYMBOL)
from django_ledger.utils import get_default_entity_session_key, get_end_date_from_session
register = template.Library()
# @register.simple_tag(name='current_version')
# def current_version():
# return __version__
# @register.simple_tag(name='currency_symbol')
# def currency_symbol(spaced: bool = False):
# if spaced or DJANGO_LEDGER_SPACED_CURRENCY_SYMBOL:
# return f'{DJANGO_LEDGER_CURRENCY_SYMBOL} '
# return DJANGO_LEDGER_CURRENCY_SYMBOL
# @register.filter(name='absolute')
# def absolute(value):
# if value:
# if isinstance(value, str):
# value = float(value)
# return abs(value)
# @register.filter(name='reverse_sign')
# def reverse_sign(value):
# if value:
# if isinstance(value, str):
# value = float(value)
# return -value
# @register.filter(name='currency_format')
# def currency_format(value):
# if not value:
# value = 0.00
# return number_format(value, decimal_pos=2, use_l10n=True, force_grouping=True)
# @register.filter(name='percentage')
# def percentage(value):
# if value is not None:
# return '{0:,.2f}%'.format(value * 100)
# @register.filter(name='last_four')
# def last_four(value: str):
# if value:
# return '*' + value[-4:]
# return ''
# @register.inclusion_tag('django_ledger/components/icon.html')
# def icon(icon_name, size):
# return {
# 'icon': icon_name,
# 'size': size
# }
# @register.inclusion_tag('django_ledger/financial_statements/tags/balance_sheet_statement.html', takes_context=True)
# def balance_sheet_statement(context, io_model, to_date=None):
# user_model = context['user']
# activity = context['request'].GET.get('activity')
# entity_slug = context['view'].kwargs.get('entity_slug')
# if not to_date:
# to_date = context['to_date']
# io_digest = io_model.digest(
# activity=activity,
# user_model=user_model,
# equity_only=False,
# entity_slug=entity_slug,
# unit_slug=context['unit_slug'],
# by_unit=context['by_unit'],
# to_date=to_date,
# signs=True,
# process_groups=True,
# balance_sheet_statement=True)
# return {
# 'entity_slug': entity_slug,
# 'user_model': user_model,
# 'tx_digest': io_digest.get_io_data(),
# }
# @register.inclusion_tag('django_ledger/financial_statements/tags/cash_flow_statement.html', takes_context=True)
# def cash_flow_statement(context, io_model):
# user_model = context['user']
# entity_slug = context['view'].kwargs.get('entity_slug')
# from_date = context['from_date']
# to_date = context['to_date']
# io_digest = io_model.digest(
# cash_flow_statement=True,
# by_activity=True,
# user_model=user_model,
# equity_only=False,
# signs=True,
# entity_slug=entity_slug,
# unit_slug=context['unit_slug'],
# by_unit=context['by_unit'],
# from_date=from_date,
# to_date=to_date,
# process_groups=True)
# return {
# 'entity_slug': entity_slug,
# 'user_model': user_model,
# 'tx_digest': io_digest.get_io_data()
# }
# @register.inclusion_tag('django_ledger/financial_statements/tags/income_statement.html', takes_context=True)
# def income_statement_table(context, io_model, from_date=None, to_date=None):
# user_model = context['user']
# activity = context['request'].GET.get('activity')
# activity = validate_activity(activity, raise_404=True)
# entity_slug = context['view'].kwargs.get('entity_slug')
# if not from_date:
# from_date = context['from_date']
# if not to_date:
# to_date = context['to_date']
# io_digest = io_model.digest(
# activity=activity,
# user_model=user_model,
# entity_slug=entity_slug,
# unit_slug=context['unit_slug'],
# by_unit=context['by_unit'],
# from_date=from_date,
# to_date=to_date,
# equity_only=True,
# process_groups=True,
# income_statement=True,
# signs=True
# )
# return {
# 'entity_slug': entity_slug,
# 'user_model': user_model,
# 'tx_digest': io_digest.get_io_data()
# }
# @register.inclusion_tag('django_ledger/bank_account/tags/bank_accounts_table.html', takes_context=True)
# def bank_account_table(context, bank_account_qs):
# entity_slug = context['view'].kwargs['entity_slug']
# return {
# 'bank_account_qs': bank_account_qs,
# 'entity_slug': entity_slug
# }
# @register.inclusion_tag('django_ledger/data_import/tags/data_import_job_list_table.html', takes_context=True)
# def data_import_job_list_table(context):
# return context
# @register.inclusion_tag('django_ledger/data_import/tags/data_import_job_txs_table.html', takes_context=True)
# def data_import_job_txs_pending(context, staged_txs_formset):
# return {
# 'entity_slug': context['view'].kwargs['entity_slug'],
# 'job_pk': context['view'].kwargs['job_pk'],
# 'staged_txs_formset': staged_txs_formset
# }
# @register.inclusion_tag('django_ledger/data_import/tags/data_import_job_txs_imported.html', takes_context=True)
# def data_import_job_txs_imported(context, staged_txs_qs):
# imported_txs = [stx for stx in staged_txs_qs if stx.is_imported()]
# return {
# 'entity_slug': context['view'].kwargs['entity_slug'],
# 'job_pk': context['view'].kwargs['job_pk'],
# 'imported_txs': imported_txs
# }
# @register.inclusion_tag('django_ledger/journal_entry/tags/je_table.html', takes_context=True)
# def jes_table(context, journal_entry_qs, next_url=None):
# entity_slug = context['view'].kwargs['entity_slug']
# ledger_pk = context['view'].kwargs['ledger_pk']
# if not next_url:
# next_url = reverse('django_ledger:je-list',
# kwargs={
# 'entity_slug': entity_slug,
# 'ledger_pk': ledger_pk
# })
# return {
# 'jes': journal_entry_qs,
# 'entity_slug': entity_slug,
# 'ledger_pk': ledger_pk,
# 'next_url': next_url
# }
# @register.inclusion_tag('django_ledger/journal_entry/tags/je_txs_table.html')
# def journal_entry_txs_table(journal_entry_model, style='detail'):
# txs_queryset = journal_entry_model.transactionmodel_set.all().select_related('account').order_by('account__code')
# total_credits = sum(tx.amount for tx in txs_queryset if tx.tx_type == 'credit')
# total_debits = sum(tx.amount for tx in txs_queryset if tx.tx_type == 'debit')
# return {
# 'txs': txs_queryset,
# 'total_debits': total_debits,
# 'total_credits': total_credits,
# 'style': style
# }
# @register.inclusion_tag('django_ledger/journal_entry/tags/je_txs_table.html', takes_context=True)
# def bill_txs_table(context, bill_model: BillModel):
# # todo: move this to bill model...
# txs_queryset = TransactionModel.objects.for_bill(
# bill_model=bill_model.uuid,
# user_model=context['request'].user,
# entity_slug=context['view'].kwargs['entity_slug']
# ).select_related('journal_entry', 'journal_entry__entity_unit', 'account').order_by('-journal_entry__timestamp')
# total_credits = sum(tx.amount for tx in txs_queryset if tx.tx_type == CREDIT)
# total_debits = sum(tx.amount for tx in txs_queryset if tx.tx_type == DEBIT)
# return {
# 'style': 'detail',
# 'txs': txs_queryset,
# 'total_debits': total_debits,
# 'total_credits': total_credits
# }
# @register.inclusion_tag('django_ledger/journal_entry/tags/je_txs_table.html', takes_context=True)
# def invoice_txs_table(context, invoice_model: InvoiceModel):
# txs_queryset = TransactionModel.objects.for_invoice(
# invoice_model=invoice_model,
# user_model=context['request'].user,
# entity_slug=context['view'].kwargs['entity_slug']
# ).select_related('journal_entry', 'journal_entry__entity_unit', 'account').order_by('-journal_entry__timestamp')
# total_credits = sum(tx.amount for tx in txs_queryset if tx.tx_type == CREDIT)
# total_debits = sum(tx.amount for tx in txs_queryset if tx.tx_type == DEBIT)
# return {
# 'style': 'detail',
# 'txs': txs_queryset,
# 'total_debits': total_debits,
# 'total_credits': total_credits
# }
# @register.inclusion_tag('django_ledger/ledger/tags/ledgers_table.html', takes_context=True)
# def ledgers_table(context, ledger_model_qs):
# return {
# 'ledgers': ledger_model_qs,
# 'entity_slug': context['view'].kwargs['entity_slug'],
# }
# @register.inclusion_tag('django_ledger/invoice/tags/invoice_table.html', takes_context=True)
# def invoice_table(context, invoice_qs):
# return {
# 'invoices': invoice_qs,
# 'entity_slug': context['view'].kwargs['entity_slug']
# }
# @register.inclusion_tag('django_ledger/bills/tags/bill_table.html', takes_context=True)
# def bill_table(context, bill_qs):
# return {
# 'bills': bill_qs,
# 'entity_slug': context['view'].kwargs['entity_slug']
# }
# @register.inclusion_tag('django_ledger/closing_entry/tags/closing_entry_table.html', takes_context=True)
# def closing_entry_table(context, closing_entry_qs):
# return {
# 'closing_entry_list': closing_entry_qs,
# 'entity_slug': context['view'].kwargs['entity_slug']
# }
# @register.inclusion_tag('django_ledger/closing_entry/tags/closing_entry_txs_table.html', takes_context=True)
# def closing_entry_txs_table(context, closing_entry_txs_qs):
# ce_txs_list = list(closing_entry_txs_qs)
# ce_txs_list.sort(key=lambda ce_txs: ROLES_ORDER_ALL.index(ce_txs.account_model.role))
# return {
# 'ce_txs_list': ce_txs_list,
# 'entity_slug': context['view'].kwargs['entity_slug']
# }
# @register.inclusion_tag('django_ledger/purchase_order/includes/po_table.html', takes_context=True)
# def po_table(context, purchase_order_qs):
# return {
# 'po_list': purchase_order_qs,
# 'entity_slug': context['view'].kwargs['entity_slug']
# }
# @register.inclusion_tag('django_ledger/account/tags/accounts_table.html', takes_context=True)
# def accounts_table(context, accounts_qs, title=None):
# return {
# 'title': title,
# 'entity_slug': context['view'].kwargs['entity_slug'],
# 'accounts_gb': accounts_qs.gb_bs_role(),
# }
# @register.inclusion_tag('django_ledger/customer/tags/customer_table.html', takes_context=True)
# def customer_table(context):
# return context
# @register.inclusion_tag('django_ledger/vendor/tags/vendor_table.html', takes_context=True)
# def vendor_table(context):
# return context
# @register.inclusion_tag('django_ledger/account/tags/account_txs_table.html', takes_context=True)
# def account_txs_table(context, txs_qs):
# return {
# 'transactions': txs_qs,
# 'total_credits': sum(tx.amount for tx in txs_qs if tx.tx_type == 'credit'),
# 'total_debits': sum(tx.amount for tx in txs_qs if tx.tx_type == 'debit'),
# 'entity_slug': context['view'].kwargs['entity_slug'],
# 'account_pk': context['view'].kwargs['account_pk']
# }
# @register.inclusion_tag('django_ledger/components/breadcrumbs.html', takes_context=True)
# def nav_breadcrumbs(context):
# entity_slug = context['view'].kwargs.get('entity_slug')
# coa_slug = context['view'].kwargs.get('coa_slug')
# ledger_pk = context['view'].kwargs.get('entity_slug')
# account_pk = context['view'].kwargs.get('account_pk')
# return {
# 'entity_slug': entity_slug,
# 'coa_slug': coa_slug,
# 'ledger_pk': ledger_pk,
# 'account_pk': account_pk
# }
# @register.inclusion_tag('django_ledger/components/default_entity.html', takes_context=True)
# def default_entity(context):
# user = context['user']
# session_key = get_default_entity_session_key()
# session = context['request'].session
# session_entity_data = session.get(session_key)
# identity = randint(0, 1000000)
# try:
# entity_uuid = session_entity_data['entity_uuid']
# default_entity_form = EntityFilterForm(
# user_model=user,
# form_id=identity,
# current_entity_uuid=entity_uuid
# )
# except TypeError or KeyError:
# default_entity_form = EntityFilterForm(
# user_model=user,
# form_id=identity,
# )
# return {
# 'default_entity_form': default_entity_form,
# 'form_id': identity,
# }
# @register.simple_tag(takes_context=True)
# def session_entity_name(context, request=None):
# session_key = get_default_entity_session_key()
# if not request:
# request = context.get('request')
# try:
# session = request.session
# entity_name = session.get(session_key)['entity_name']
# except AttributeError:
# entity_name = 'Django Ledger'
# except KeyError:
# entity_name = 'Django Ledger'
# except TypeError:
# entity_name = 'Django Ledger'
# return entity_name
# # todo: rename template to activity_form_filter.
# @register.inclusion_tag('django_ledger/components/activity_form.html', takes_context=True)
# def activity_filter(context):
# request = context['request']
# activity = request.GET.get('activity')
# if activity:
# activity_form = ActivityFilterForm(initial={
# 'activity': activity
# })
# else:
# activity_form = ActivityFilterForm()
# return {
# 'activity_form': activity_form,
# 'form_path': context['request'].path
# }
# @register.inclusion_tag('django_ledger/components/date_picker.html', takes_context=True)
# def date_picker(context, nav_url=None, date_picker_id=None):
# try:
# entity_slug = context['view'].kwargs.get('entity_slug')
# except KeyError:
# entity_slug = context['entity_slug']
# if not date_picker_id:
# date_picker_id = f'djl-datepicker-{randint(10000, 99999)}'
# if 'date_picker_ids' not in context:
# context['date_picker_ids'] = list()
# context['date_picker_ids'].append(date_picker_id)
# date_navigation_url = nav_url if nav_url else context.get('date_navigation_url')
# return {
# 'entity_slug': entity_slug,
# 'date_picker_id': date_picker_id,
# 'date_navigation_url': date_navigation_url
# }
# @register.simple_tag(takes_context=True)
# def get_current_end_date_filter(context):
# entity_slug = context['view'].kwargs.get('entity_slug')
# return get_end_date_from_session(entity_slug, context['request'])
# @register.inclusion_tag('django_ledger/components/chart_container.html')
# def chart_container(chart_id, endpoint=None):
# return {
# 'chart_id': chart_id,
# 'endpoint': endpoint
# }
# @register.inclusion_tag('django_ledger/components/modals.html', takes_context=True)
# def modal_action(context, model, http_method: str = 'post', entity_slug: str = None):
# if not entity_slug:
# entity_slug = context['view'].kwargs['entity_slug']
# action_url = model.get_mark_as_paid_url(entity_slug=entity_slug)
# return {
# 'object': model,
# 'action_url': action_url,
# 'http_method': http_method,
# 'message': f'Do you want to mark {model.__class__._meta.verbose_name} {model.get_document_id()} as paid?'
# }
# @register.inclusion_tag('django_ledger/components/modals_v2.html', takes_context=True)
# def modal_action_v2(context, model, action_url: str, message: str, html_id: str, http_method: str = 'get'):
# return {
# 'object': model,
# 'action_url': action_url,
# 'http_method': http_method,
# 'message': message,
# 'html_id': html_id
# }
# @register.simple_tag
# def fin_ratio_max_value(ratio: str):
# params = DJANGO_LEDGER_FINANCIAL_ANALYSIS['ratios'][ratio]['ranges']
# return params['healthy']
# @register.filter
# def fin_ratio_threshold_class(value, ratio):
# if value:
# params = DJANGO_LEDGER_FINANCIAL_ANALYSIS['ratios'][ratio]
# ranges = params['ranges']
# if params['good_incremental']:
# if value <= ranges['critical']:
# return 'is-danger'
# elif value <= ranges['warning']:
# return 'is-warning'
# elif value <= ranges['watch']:
# return 'is-primary'
# return 'is-success'
# else:
# if value >= ranges['critical']:
# return 'is-danger'
# elif value >= ranges['warning']:
# return 'is-warning'
# elif value >= ranges['watch']:
# return 'is-primary'
# return 'is-success'
# @register.inclusion_tag('django_ledger/components/feedback_button.html', takes_context=True)
# def feedback_button(context, button_size_class: str = 'is-small', color_class: str = 'is-success', icon_id: str = None):
# bug_modal_html_id = f'djl-bug-button-{randint(10000, 99999)}'
# feature_modal_html_id = f'djl-feature-button-{randint(10000, 99999)}'
# bug_form = BugReportForm()
# feature_form = RequestNewFeatureForm()
# next_url = context['request'].path
# return {
# 'icon_id': icon_id,
# 'bug_modal_html_id': bug_modal_html_id,
# 'feature_modal_html_id': feature_modal_html_id,
# 'button_size_class': button_size_class,
# 'color_class': color_class,
# 'bug_form': bug_form,
# 'feature_form': feature_form,
# 'next_url': next_url
# }
@register.inclusion_tag('inventory/ledger/reports/components/period_navigator.html', takes_context=True)
def period_navigation(context, base_url: str):
kwargs = dict()
entity_slug = context['view'].kwargs['entity_slug']
kwargs['entity_slug'] = entity_slug
if context['view'].kwargs.get('ledger_pk'):
kwargs['ledger_pk'] = context['view'].kwargs.get('ledger_pk')
if context['view'].kwargs.get('account_pk'):
kwargs['account_pk'] = context['view'].kwargs.get('account_pk')
if context['view'].kwargs.get('unit_slug'):
kwargs['unit_slug'] = context['view'].kwargs.get('unit_slug')
if context['view'].kwargs.get('coa_slug'):
kwargs['coa_slug'] = context['view'].kwargs.get('coa_slug')
ctx = dict()
ctx['year'] = context['year']
ctx['has_year'] = context.get('has_year')
ctx['has_quarter'] = context.get('has_quarter')
ctx['has_month'] = context.get('has_month')
ctx['has_date'] = context.get('has_date')
ctx['previous_year'] = context['previous_year']
kwargs['year'] = context['previous_year']
ctx['previous_year_url'] = reverse(f'django_ledger:{base_url}-year', kwargs=kwargs)
ctx['next_year'] = context['next_year']
kwargs['year'] = context['next_year']
ctx['next_year_url'] = reverse(f'django_ledger:{base_url}-year', kwargs=kwargs)
kwargs['year'] = context['year']
ctx['current_year_url'] = reverse(f'django_ledger:{base_url}-year', kwargs=kwargs)
dt = get_localdate()
KWARGS_CURRENT_MONTH = {
'entity_slug': context['view'].kwargs['entity_slug'],
'year': dt.year,
'month': dt.month
}
if 'unit_slug' in kwargs:
KWARGS_CURRENT_MONTH['unit_slug'] = kwargs['unit_slug']
if 'account_pk' in kwargs:
KWARGS_CURRENT_MONTH['account_pk'] = kwargs['account_pk']
if 'ledger_pk' in kwargs:
KWARGS_CURRENT_MONTH['ledger_pk'] = kwargs['ledger_pk']
if 'coa_slug' in kwargs:
KWARGS_CURRENT_MONTH['coa_slug'] = kwargs['coa_slug']
ctx['current_month_url'] = reverse(f'django_ledger:{base_url}-month',
kwargs=KWARGS_CURRENT_MONTH)
quarter_urls = list()
ctx['quarter'] = context.get('quarter')
for Q in range(1, 5):
kwargs['quarter'] = Q
quarter_urls.append({
'url': reverse(f'django_ledger:{base_url}-quarter', kwargs=kwargs),
'quarter': Q,
'quarter_name': f'Q{Q}'
})
del kwargs['quarter']
ctx['quarter_urls'] = quarter_urls
month_urls = list()
ctx['month'] = context.get('month')
for M in range(1, 13):
kwargs['month'] = M
month_urls.append({
'url': reverse(f'django_ledger:{base_url}-month', kwargs=kwargs),
'month': M,
'month_abbr': month_abbr[M]
})
ctx['month_urls'] = month_urls
ctx['from_date'] = context['from_date']
ctx['to_date'] = context['to_date']
ctx.update(kwargs)
ctx['date_navigation_url'] = context.get('date_navigation_url')
return ctx
# @register.inclusion_tag('django_ledger/components/menu.html', takes_context=True)
# def navigation_menu(context, style):
# ENTITY_SLUG = context['view'].kwargs.get('entity_slug')
# ctx = dict()
# ctx['style'] = style
# if ENTITY_SLUG:
# ctx['entity_slug'] = ENTITY_SLUG
# nav_menu_links = [
# {
# 'type': 'link',
# 'title': 'Entity Dashboard',
# 'url': reverse('django_ledger:entity-dashboard', kwargs={'entity_slug': ENTITY_SLUG})
# },
# {
# 'type': 'links',
# 'title': 'Management',
# 'links': [
# {
# 'type': 'link',
# 'title': 'Vendors',
# 'url': reverse('django_ledger:vendor-list', kwargs={'entity_slug': ENTITY_SLUG})
# },
# {
# 'type': 'link',
# 'title': 'Customers',
# 'url': reverse('django_ledger:customer-list', kwargs={'entity_slug': ENTITY_SLUG})
# },
# {
# 'type': 'link',
# 'title': 'Bank Accounts',
# 'url': reverse('django_ledger:bank-account-list', kwargs={'entity_slug': ENTITY_SLUG})
# },
# {
# 'type': 'link',
# 'title': 'Estimates & Contracts',
# 'url': reverse('django_ledger:customer-estimate-list', kwargs={'entity_slug': ENTITY_SLUG})
# },
# {
# 'type': 'link',
# 'title': 'Bills',
# 'url': reverse('django_ledger:bill-list', kwargs={'entity_slug': ENTITY_SLUG})
# },
# {
# 'type': 'link',
# 'title': 'Invoices',
# 'url': reverse('django_ledger:invoice-list', kwargs={'entity_slug': ENTITY_SLUG})
# },
# {
# 'type': 'link',
# 'title': 'Purchase Orders',
# 'url': reverse('django_ledger:po-list', kwargs={'entity_slug': ENTITY_SLUG})
# },
# {
# 'type': 'link',
# 'title': 'Inventory',
# 'url': reverse('django_ledger:inventory-list', kwargs={'entity_slug': ENTITY_SLUG})
# },
# {
# 'type': 'link',
# 'title': 'Closing Entries',
# 'url': reverse('django_ledger:closing-entry-list', kwargs={'entity_slug': ENTITY_SLUG})
# }
# ]
# },
# {
# 'type': 'links',
# 'title': 'Your Lists',
# 'links': [
# {
# 'type': 'link',
# 'title': 'Entity Units',
# 'url': reverse('django_ledger:unit-list', kwargs={'entity_slug': ENTITY_SLUG})
# },
# {
# 'type': 'link',
# 'title': 'Products',
# 'url': reverse('django_ledger:product-list', kwargs={'entity_slug': ENTITY_SLUG})
# },
# {
# 'type': 'link',
# 'title': 'Services',
# 'url': reverse('django_ledger:service-list', kwargs={'entity_slug': ENTITY_SLUG})
# },
# {
# 'type': 'link',
# 'title': 'Business Expenses',
# 'url': reverse('django_ledger:expense-list', kwargs={'entity_slug': ENTITY_SLUG})
# },
# {
# 'type': 'link',
# 'title': 'Inventory Items',
# 'url': reverse('django_ledger:inventory-item-list', kwargs={'entity_slug': ENTITY_SLUG})
# },
# {
# 'type': 'link',
# 'title': 'Unit of Measures',
# 'url': reverse('django_ledger:uom-list', kwargs={'entity_slug': ENTITY_SLUG})
# },
# ]
# },
# {
# 'type': 'links',
# 'title': 'Reports',
# 'links': [
# {
# 'type': 'link',
# 'title': 'Balance Sheet',
# 'url': reverse('django_ledger:entity-bs', kwargs={'entity_slug': ENTITY_SLUG})
# },
# {
# 'type': 'link',
# 'title': 'Income Statement',
# 'url': reverse('django_ledger:entity-ic', kwargs={'entity_slug': ENTITY_SLUG})
# },
# {
# 'type': 'link',
# 'title': 'Cash Flow Statement',
# 'url': reverse('django_ledger:entity-cf', kwargs={'entity_slug': ENTITY_SLUG})
# },
# ]
# },
# {
# 'type': 'links',
# 'title': 'Accounting',
# 'links': [
# {
# 'type': 'link',
# 'title': 'Chart of Accounts',
# 'url': reverse('django_ledger:coa-list', kwargs={'entity_slug': ENTITY_SLUG})
# },
# {
# 'type': 'link',
# 'title': 'Ledgers',
# 'url': reverse('django_ledger:ledger-list-visible', kwargs={'entity_slug': ENTITY_SLUG})
# },
# {
# 'type': 'link',
# 'title': 'Data Import',
# 'url': reverse('django_ledger:data-import-jobs-list', kwargs={'entity_slug': ENTITY_SLUG})
# }
# ]
# },
# {
# 'type': 'links',
# 'title': 'Administration',
# 'links': [
# {
# 'type': 'link',
# 'title': 'My Entities',
# 'url': reverse('django_ledger:home')
# },
# {
# 'type': 'link',
# 'title': 'Entity Settings',
# 'url': reverse('django_ledger:entity-update', kwargs={'entity_slug': ENTITY_SLUG})
# }
# ]
# }
# ]
# ctx['links'] = nav_menu_links
# ctx['request'] = context['request']
# return ctx
# @register.inclusion_tag('django_ledger/product/tags/product_table.html', takes_context=True)
# def product_table(context, queryset):
# entity_slug = context['view'].kwargs['entity_slug']
# return {
# 'entity_slug': entity_slug,
# 'product_list': queryset
# }
# @register.inclusion_tag('django_ledger/service/tags/services_table.html', takes_context=True)
# def service_table(context, queryset):
# entity_slug = context['view'].kwargs['entity_slug']
# return {
# 'entity_slug': entity_slug,
# 'service_list': queryset
# }
# @register.inclusion_tag('django_ledger/expense/tags/expense_item_table.html', takes_context=True)
# def expense_item_table(context, queryset):
# entity_slug = context['view'].kwargs['entity_slug']
# return {
# 'entity_slug': entity_slug,
# 'expense_list': queryset
# }
# @register.inclusion_tag('django_ledger/inventory/tags/inventory_item_table.html', takes_context=True)
# def inventory_item_table(context, queryset):
# entity_slug = context['view'].kwargs['entity_slug']
# return {
# 'entity_slug': entity_slug,
# 'inventory_item_list': queryset
# }
# @register.inclusion_tag('django_ledger/invoice/tags/invoice_item_formset.html', takes_context=True)
# def invoice_item_formset_table(context, itemtxs_formset):
# return {
# 'entity_slug': context['view'].kwargs['entity_slug'],
# 'invoice_model': context['invoice'],
# 'total_amount__sum': context['total_amount__sum'],
# 'itemtxs_formset': itemtxs_formset,
# }
# @register.inclusion_tag('django_ledger/bills/tags/bill_item_formset.html', takes_context=True)
# def bill_item_formset_table(context, item_formset):
# return {
# 'entity_slug': context['view'].kwargs['entity_slug'],
# 'bill_pk': context['view'].kwargs['bill_pk'],
# 'total_amount__sum': context['total_amount__sum'],
# 'item_formset': item_formset,
# }
# @register.inclusion_tag('django_ledger/purchase_order/includes/po_item_formset.html', takes_context=True)
# def po_item_formset_table(context, po_model, itemtxs_formset):
# return {
# 'entity_slug': context['view'].kwargs['entity_slug'],
# 'po_model': po_model,
# 'itemtxs_formset': itemtxs_formset,
# }
# @register.inclusion_tag('django_ledger/uom/tags/uom_table.html', takes_context=True)
# def uom_table(context, uom_queryset):
# return {
# 'entity_slug': context['view'].kwargs['entity_slug'],
# 'uom_list': uom_queryset
# }
# @register.inclusion_tag('django_ledger/inventory/tags/inventory_table.html', takes_context=True)
# def inventory_table(context, queryset):
# ctx = {
# 'entity_slug': context['view'].kwargs['entity_slug'],
# 'inventory_list': queryset
# }
# ctx.update(queryset.aggregate(inventory_total_value=Sum('total_value')))
# return ctx
# @register.inclusion_tag('django_ledger/estimate/includes/estimate_table.html', takes_context=True)
# def customer_estimate_table(context, queryset):
# return {
# 'entity_slug': context['view'].kwargs['entity_slug'],
# 'ce_list': queryset
# }
# @register.inclusion_tag('django_ledger/estimate/includes/estimate_item_table.html', takes_context=True)
# def estimate_item_table(context, queryset):
# return {
# 'entity_slug': context['view'].kwargs['entity_slug'],
# 'ce_model': context['estimate_model'],
# 'ce_item_list': queryset
# }
# @register.inclusion_tag('django_ledger/purchase_order/tags/po_item_table.html', takes_context=True)
# def po_item_table(context, queryset):
# return {
# 'entity_slug': context['view'].kwargs['entity_slug'],
# 'po_model': context['po_model'],
# 'po_item_list': queryset
# }
# @register.inclusion_tag('django_ledger/estimate/tags/ce_item_formset.html', takes_context=True)
# def customer_estimate_item_formset(context, item_formset):
# return {
# 'entity_slug': context['view'].kwargs['entity_slug'],
# 'ce_pk': context['view'].kwargs['ce_pk'],
# 'ce_revenue_estimate__sum': context['ce_revenue_estimate__sum'],
# 'ce_cost_estimate__sum': context['ce_cost_estimate__sum'],
# 'item_formset': item_formset,
# }

View File

@ -279,6 +279,7 @@ urlpatterns = [
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"),
@ -403,6 +404,7 @@ urlpatterns = [
"sales/estimates/<uuid:pk>/send_email", views.send_email_view, name="send_email"
),
path('sales/estimates/<uuid:pk>/sale_order/', views.create_sale_order, name='create_sale_order'),
path('sales/estimates/<uuid:pk>/sale_order/preview/', views.preview_sale_order, name='preview_sale_order'),
# Invoice
path("sales/invoices/", views.InvoiceListView.as_view(), name="invoice_list"),
@ -523,6 +525,28 @@ urlpatterns = [
views.bill_mark_as_paid,
name="bill_mark_as_paid",
),
# orders
path("orders/", views.OrderListView.as_view(), name="order_list"),
# BALANCE SHEET Reports...
# Entities...
path('entity/<slug:entity_slug>/balance-sheet/',
views.BaseBalanceSheetRedirectView.as_view(),
name='entity-bs'),
path('entity/<slug:entity_slug>/balance-sheet/year/<int:year>/',
views.FiscalYearBalanceSheetViewBase.as_view(),
name='entity-bs-year'),
path('entity/<slug:entity_slug>/balance-sheet/quarter/<int:year>/<int:quarter>/',
views.QuarterlyBalanceSheetView.as_view(),
name='entity-bs-quarter'),
path('entity/<slug:entity_slug>/balance-sheet/month/<int:year>/<int:month>/',
views.MonthlyBalanceSheetView.as_view(),
name='entity-bs-month'),
path('entity/<slug:entity_slug>/balance-sheet/date/<int:year>/<int:month>/<int:day>/',
views.DateBalanceSheetView.as_view(),
name='entity-bs-date'),
]

View File

@ -15,7 +15,13 @@ from django.core.mail import send_mail
from django.utils.translation import gettext_lazy as _
from inventory.utilities.financials import get_financial_value
from django_ledger.models.items import ItemModel
from django_ledger.models import InvoiceModel, EstimateModel,BillModel,VendorModel,CustomerModel
from django_ledger.models import (
InvoiceModel,
EstimateModel,
BillModel,
VendorModel,
CustomerModel,
)
from decimal import Decimal
@ -123,6 +129,73 @@ def calculate_vat_amount(amount):
return amount
def get_car_finance_data(model):
vat = models.VatRate.objects.filter(is_active=True).first()
data = model.get_itemtxs_data()[0].all()
total = sum(
[
Decimal(item.item_model.additional_info["car_finance"]["selling_price"])
* Decimal(item.ce_quantity or item.quantity)
for item in data
]
)
additional_services = []
for i in data:
if i.item_model.additional_info["additional_services"]:
additional_services.extend(
[
{"name": x.name, "price": x.price}
for x in i.item_model.additional_info["additional_services"]
]
)
return {
"cars": [
{
"vin": x.item_model.additional_info["car_info"]["vin"],
"make": x.item_model.additional_info["car_info"]["make"],
"model": x.item_model.additional_info["car_info"]["model"],
"year": x.item_model.additional_info["car_info"]["year"],
"trim": x.item_model.additional_info["car_info"]["mileage"],
"cost_price": x.item_model.additional_info["car_finance"]["cost_price"],
"selling_price": x.item_model.additional_info["car_finance"][
"selling_price"
],
"discount": x.item_model.additional_info["car_finance"][
"discount_amount"
],
"quantity": x.ce_quantity or x.quantity,
"unit_price": Decimal(x.item_model.additional_info["car_finance"]["total"]),
"total": Decimal(x.item_model.additional_info["car_finance"]["total"]) * Decimal(x.quantity or x.ce_quantity),
"total_vat": x.item_model.additional_info["car_finance"]["total_vat"],
"additional_services": x.item_model.additional_info[
"additional_services"
],
}
for x in data
],
"quantity": sum((x.quantity or x.ce_quantity) for x in data),
"total_price": total,
"total_vat": (total * vat.rate) + total,
"total_discount": sum(
Decimal(x.item_model.additional_info["car_finance"]["discount_amount"])
for x in data
),
"grand_total": Decimal(total * vat.rate)
+ total
- Decimal(
sum(
Decimal(x.item_model.additional_info["car_finance"]["discount_amount"])
for x in data
)
),
"additionals": additional_services,
"vat": vat.rate,
}
def get_financial_values(model):
vat = models.VatRate.objects.filter(is_active=True).first()
@ -136,7 +209,7 @@ def get_financial_values(model):
"car_and_item_info": [],
"additional_services": [],
}
data = model.get_itemtxs_data()[0].all()
data = model.get_itemtxs_data()[0].all()
for item in data:
if not item.item_model.additional_info.get("car_finance"):
@ -149,44 +222,52 @@ def get_financial_values(model):
"car_and_item_info": [],
"additional_services": [],
}
if isinstance(model, InvoiceModel):
if model.ce_model:
data = model.ce_model.get_itemtxs_data()[0].all()
else:
data = model.get_itemtxs_data()[0].all()
total = sum([Decimal(item.item_model.additional_info["car_finance"]["selling_price"]) * Decimal(item.ce_quantity or item.quantity) for item in data])
total = sum(
[
Decimal(item.item_model.additional_info["car_finance"]["selling_price"])
* Decimal(item.ce_quantity or item.quantity)
for item in data
]
)
discount_amount = sum(
Decimal(i.item_model.additional_info['car_finance']["discount_amount"])
Decimal(i.item_model.additional_info["car_finance"]["discount_amount"])
for i in data
)
additional_services = []
for i in data:
if i.item_model.additional_info['additional_services']:
for i in data:
if i.item_model.additional_info["additional_services"]:
additional_services.extend(
[
{"name": x.name, "price": x.price}
for x in i.item_model.additional_info['additional_services']
for x in i.item_model.additional_info["additional_services"]
]
)
grand_total = Decimal(total) - Decimal(discount_amount)
vat_amount = round(Decimal(grand_total) * Decimal(vat.rate), 2)
car_and_item_info = [
{
"info": x.item_model.additional_info['car_info'],
"finances": x.item_model.additional_info['car_finance'],
"info": x.item_model.additional_info["car_info"],
"finances": x.item_model.additional_info["car_finance"],
"quantity": x.ce_quantity or x.quantity,
"total": Decimal(x.item_model.additional_info['car_finance']['selling_price'])
"total": Decimal(
x.item_model.additional_info["car_finance"]["selling_price"]
)
* Decimal(x.ce_quantity or x.quantity),
}
for x in data
]
return {
"total": total,
"discount_amount": discount_amount,
@ -245,7 +326,7 @@ def set_invoice_payment(dealer, entity, invoice, amount, payment_method):
tx_type="debit",
description="Payment Received",
)
# if total_amount + invoice.
TransactionModel.objects.create(
@ -288,7 +369,6 @@ def set_bill_payment(dealer, entity, bill, amount, payment_method):
name="Accounts Payable", active=True
)
TransactionModel.objects.create(
journal_entry=journal,
account=cash_account, # Debit Cash
@ -309,9 +389,9 @@ def set_bill_payment(dealer, entity, bill, amount, payment_method):
bill.save()
def transfer_to_dealer(request,cars, to_dealer, remarks=None):
def transfer_to_dealer(request, cars, to_dealer, remarks=None):
dealer = get_user_type(request)
if not cars:
raise ValueError("No cars selected for transfer.")
@ -337,33 +417,39 @@ def transfer_to_dealer(request,cars, to_dealer, remarks=None):
for car in cars:
car.dealer = to_dealer
car.save()
def transfer_car(car,transfer):
def transfer_car(car, transfer):
from_dealer = transfer.from_dealer
to_dealer = transfer.to_dealer
# add transfer.to_dealer as customer in transfer.from_dealer entity
customer = from_dealer.entity.get_customers().filter(
email=to_dealer.user.email).first()
customer = (
from_dealer.entity.get_customers().filter(email=to_dealer.user.email).first()
)
if not customer:
customer = from_dealer.entity.create_customer(
customer_model_kwargs={
"customer_name": to_dealer.name,
"email": to_dealer.user.email,
"address_1": to_dealer.address
"address_1": to_dealer.address,
}
)
customer.additional_info.update({"type":"organization"})
customer.save()
customer.additional_info.update({"type": "organization"})
customer.save()
invoice = from_dealer.entity.create_invoice(
customer_model=customer,
terms=InvoiceModel.TERMS_NET_30,
cash_account=from_dealer.entity.get_default_coa_accounts().get(name="Cash", active=True),
prepaid_account=from_dealer.entity.get_default_coa_accounts().get(name="Accounts Receivable", active=True),
cash_account=from_dealer.entity.get_default_coa_accounts().get(
name="Cash", active=True
),
prepaid_account=from_dealer.entity.get_default_coa_accounts().get(
name="Accounts Receivable", active=True
),
coa_model=from_dealer.entity.get_default_coa(),
)
ledger = from_dealer.entity.create_ledger(name=str(invoice.pk))
invoice.ledgar = ledger
ledger.invoicemodel = invoice
@ -372,7 +458,7 @@ def transfer_car(car,transfer):
item = from_dealer.entity.get_items_products().filter(name=car.vin).first()
if not item:
return
invoice_itemtxs = {
item.item_number: {
"unit_cost": transfer.total_price,
@ -386,68 +472,76 @@ def transfer_car(car,transfer):
commit=True,
operation=InvoiceModel.ITEMIZE_APPEND,
)
invoice.save()
invoice.mark_as_review()
invoice.mark_as_approved(from_dealer.entity.slug, from_dealer.entity.admin)
# invoice.mark_as_paid(from_dealer.entity.slug, from_dealer.entity.admin)
invoice.save()
#create car item product in to_dealer entity
# create car item product in to_dealer entity
uom = to_dealer.entity.get_uom_all().filter(name=item.uom.name).first()
#create item product in the reciever ledger
# create item product in the reciever ledger
product = to_dealer.entity.create_item_product(
name=item.name,
uom_model=uom,
item_type=item.item_type,
coa_model=to_dealer.entity.get_default_coa(),
name=item.name,
uom_model=uom,
item_type=item.item_type,
coa_model=to_dealer.entity.get_default_coa(),
)
product.additional_info.update({'car_info': car.to_dict()})
product.additional_info.update({"car_info": car.to_dict()})
product.save()
#add the sender as vendor and create a bill for it
# add the sender as vendor and create a bill for it
vendor = None
vendor = to_dealer.entity.get_vendors().filter(vendor_name=from_dealer.name).first()
if not vendor:
vendor = VendorModel.objects.create(entity_model=to_dealer.entity, vendor_name=from_dealer.name,additional_info={"info":to_dict(from_dealer)})
#transfer the car to to_dealer and create items record
vendor = VendorModel.objects.create(
entity_model=to_dealer.entity,
vendor_name=from_dealer.name,
additional_info={"info": to_dict(from_dealer)},
)
# transfer the car to to_dealer and create items record
bill = to_dealer.entity.create_bill(
vendor_model=vendor,
terms=BillModel.TERMS_NET_30,
cash_account=to_dealer.entity.get_default_coa_accounts().get(name="Cash", active=True),
prepaid_account=to_dealer.entity.get_default_coa_accounts().get(name="Prepaid Expenses", active=True),
cash_account=to_dealer.entity.get_default_coa_accounts().get(
name="Cash", active=True
),
prepaid_account=to_dealer.entity.get_default_coa_accounts().get(
name="Prepaid Expenses", active=True
),
coa_model=to_dealer.entity.get_default_coa(),
)
bill.additional_info = {}
bill_itemtxs = {
product.item_number: {
"unit_cost": transfer.total_price,
"unit_cost": transfer.total_price,
"quantity": transfer.quantity,
"total_amount": transfer.total_price,
}
}
bill_itemtxs = bill.migrate_itemtxs(itemtxs=bill_itemtxs,
commit=True,
operation=BillModel.ITEMIZE_APPEND)
bill.additional_info.update({'car_info': car.to_dict()})
bill.additional_info.update({'car_finance': car.finances.to_dict()})
}
bill_itemtxs = bill.migrate_itemtxs(
itemtxs=bill_itemtxs, commit=True, operation=BillModel.ITEMIZE_APPEND
)
bill.additional_info.update({"car_info": car.to_dict()})
bill.additional_info.update({"car_finance": car.finances.to_dict()})
bill.mark_as_review()
bill.mark_as_approved(to_dealer.entity.slug, to_dealer.entity.admin)
bill.save()
car.dealer = to_dealer
car.vendor = vendor
car.receiving_date = datetime.datetime.now()
car.finances.additional_services.clear()
if hasattr(car, "custom_cards"):
car.custom_cards.delete()
car.finances.cost_price = transfer.total_price
car.finances.selling_price = 0
car.finances.discount_amount = 0
@ -461,24 +555,25 @@ def transfer_car(car,transfer):
transfer.active = False
transfer.save()
car.save()
return True
#pay the pill
# pay the pill
# set_bill_payment(to_dealer,to_dealer.entity,bill,transfer.total_price,"credit")
def to_dict(obj):
obj_dict = vars(obj).copy()
if '_state' in vars(obj):
if "_state" in vars(obj):
del obj_dict["_state"]
for key, value in obj_dict.items():
if isinstance(value, datetime.datetime):
obj_dict[key] = value.strftime('%Y-%m-%d %H:%M:%S')
elif hasattr(value,'pk') or hasattr(value,'id'):
obj_dict[key] = value.strftime("%Y-%m-%d %H:%M:%S")
elif hasattr(value, "pk") or hasattr(value, "id"):
try:
obj_dict[key] = value.name
except AttributeError:
obj_dict[key] = str(value)
else:
obj_dict[key] = str(value)
return obj_dict
return obj_dict

View File

@ -84,6 +84,7 @@ from django.contrib.auth.models import Group
from .utils import (
calculate_vat_amount,
get_calculations,
get_car_finance_data,
get_financial_values,
reserve_car,
send_email,
@ -2399,15 +2400,26 @@ def create_sale_order(request, pk):
form = forms.SaleOrderForm(request.POST)
if form.is_valid():
form.save()
return redirect("success")
else:
form = forms.SaleOrderForm()
if not estimate.is_completed():
estimate.mark_as_completed()
estimate.save()
messages.success(request, "Sale Order created successfully")
return redirect("estimate_detail", pk=pk)
form = forms.SaleOrderForm()
form.fields["estimate"].queryset = EstimateModel.objects.filter(pk=pk)
form.initial['estimate'] = estimate
data = get_car_finance_data(estimate)
return render(
request,
"sales/estimates/sale_order.html",
{"form": form, "estimate": estimate, "items": items},
"sales/estimates/sale_order_form.html",
{"form": form, "estimate": estimate, "items": items,"data": data},
)
def preview_sale_order(request,pk):
estimate = get_object_or_404(EstimateModel,pk=pk)
data = get_car_finance_data(estimate)
return render(request,'sales/estimates/sale_order_preview.html',{'order':estimate.sale_orders.first(),"data":data,"estimate":estimate})
class PaymentRequest(LoginRequiredMixin, DetailView):
model = EstimateModel
@ -3406,6 +3418,13 @@ class SubscriptionPlans(ListView):
context_object_name = "plans"
# orders
class OrderListView(ListView):
model = models.SaleOrder
template_name = "sales/orders/order_list.html"
context_object_name = "orders"
# email
def send_email_view(request, pk):
estimate = get_object_or_404(EstimateModel, pk=pk)
@ -3474,3 +3493,48 @@ def custom_permission_denied_view(request, exception=None):
def custom_bad_request_view(request, exception=None):
return render(request, "errors/400.html", {})
# from django_ledger.io.io_core import get_localdate
# from django_ledger.views.mixins import (DjangoLedgerSecurityMixIn)
# from django.views.generic import RedirectView
from django_ledger.views.financial_statement import FiscalYearBalanceSheetView
from django.views.generic import DetailView, RedirectView
from django_ledger.io.io_core import get_localdate
from django_ledger.models import EntityModel, EntityUnitModel
from django_ledger.views.mixins import (
QuarterlyReportMixIn, YearlyReportMixIn,
MonthlyReportMixIn, DateReportMixIn, DjangoLedgerSecurityMixIn, EntityUnitMixIn,
BaseDateNavigationUrlMixIn, PDFReportMixIn
)
# BALANCE SHEET -----------
class BaseBalanceSheetRedirectView(DjangoLedgerSecurityMixIn, RedirectView):
def get_redirect_url(self, *args, **kwargs):
year = get_localdate().year
return reverse('entity-bs-year',
kwargs={
'entity_slug': self.kwargs['entity_slug'],
'year': year
})
class FiscalYearBalanceSheetViewBase(FiscalYearBalanceSheetView):
template_name = "ledger/reports/balance_sheet.html"
class QuarterlyBalanceSheetView(FiscalYearBalanceSheetViewBase, QuarterlyReportMixIn):
"""
Quarter Balance Sheet View.
"""
class MonthlyBalanceSheetView(FiscalYearBalanceSheetViewBase, MonthlyReportMixIn):
"""
Monthly Balance Sheet View.
"""
class DateBalanceSheetView(FiscalYearBalanceSheetViewBase, DateReportMixIn):
"""
Date Balance Sheet View.
"""

9
scripts/report.py Normal file
View File

@ -0,0 +1,9 @@
from django_ledger.report.balance_sheet import BalanceSheetReport
from django_ledger.models import EntityModel
def run():
entity = EntityModel.objects.first()
report = BalanceSheetReport(entity=entity)
print(report)

70
scripts/run.py Normal file
View File

@ -0,0 +1,70 @@
from decimal import Decimal
from django_ledger.models import EstimateModel
from rich import print
from inventory.models import VatRate
def run():
estimate = EstimateModel.objects.first()
vat = VatRate.objects.filter(is_active=True).first()
data = estimate.get_itemtxs_data()[0].all()
total = sum(
[
Decimal(item.item_model.additional_info["car_finance"]["selling_price"])
* Decimal(item.ce_quantity or item.quantity)
for item in data
]
)
additional_services = []
for i in data:
if i.item_model.additional_info["additional_services"]:
additional_services.extend(
[
{"name": x.name, "price": x.price}
for x in i.item_model.additional_info["additional_services"]
]
)
cars_info = {
"cars": [
{
"vin": x.item_model.additional_info["car_info"]["vin"],
"make": x.item_model.additional_info["car_info"]["make"],
"model": x.item_model.additional_info["car_info"]["model"],
"year": x.item_model.additional_info["car_info"]["year"],
"trim": x.item_model.additional_info["car_info"]["mileage"],
"cost_price": x.item_model.additional_info["car_finance"]["cost_price"],
"selling_price": x.item_model.additional_info["car_finance"][
"selling_price"
],
"discount": x.item_model.additional_info["car_finance"][
"discount_amount"
],
"total": x.item_model.additional_info["car_finance"]["total"],
"additional_services": x.item_model.additional_info[
"additional_services"
],
}
for x in data
],
"quantity": data.count(),
"total_price": total,
"total__vat": (total * vat.rate) + total,
"total_discount": sum(
Decimal(x.item_model.additional_info["car_finance"]["discount_amount"])
for x in data
),
"grand_total": Decimal(total * vat.rate)
+ total
- Decimal(
sum(
Decimal(x.item_model.additional_info["car_finance"]["discount_amount"])
for x in data
)
),
"additionals": additional_services,
}
print(cars_info)

View File

@ -56,6 +56,7 @@
<main class="main" id="top">
{% include 'header.html' %}
<div class="content">
{% block period_navigation %}{% endblock period_navigation %}
<section class="content">
{% block content %}
{% endblock content%}

View File

@ -105,7 +105,7 @@
<!-- more inner pages-->
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<a class="nav-link" href="{% url 'order_list' %}">
<div class="d-flex align-items-center">
<span class="nav-link-icon"><span class="fas fa-cart-plus"></span></span><span class="nav-link-text">{% trans "orders"|capfirst %}</span>
</div>
@ -236,6 +236,11 @@
<span class="nav-link-icon"><span data-feather="package"></span></span><span class="nav-link-text">{% trans 'bills'|capfirst %}</span>
</div>
</a>
<a class="nav-link" href="{% url 'entity-bs' request.user.dealer.entity.slug %}">
<div class="d-flex align-items-center">
<span class="nav-link-icon"><span data-feather="package"></span></span><span class="nav-link-text">{% trans 'bills'|capfirst %}</span>
</div>
</a>
</li>
</ul>
</div>

View File

@ -0,0 +1,54 @@
{% extends "base.html" %}
{% load i18n %}
{% load static %}
{% load custom_filters %}
{% block period_navigation %}
{% if entity %}
<div class="row mb-4">
<div class="col-12">{% period_navigation 'entity-bs' %}</div>
</div>
{% elif ledger %}
<div class="row mb-4">
<div class="col-12">{% period_navigation 'ledger-bs' %}</div>
</div>
{% elif unit_model %}
<div class="row mb-4">
<div class="col-12">{% period_navigation 'unit-bs' %}</div>
</div>
{% endif %}
{% endblock %}
{% block content %}
<div class="card shadow-sm border-0 p-6">
<div class="card-body">
<div class="text-center mb-5">
{% if entity %}
<h1 class="display-4 fw-light">{{ entity.name }}</h1>
{% elif ledger %}
<h1 class="display-4 fw-light">{{ ledger.name }}</h1>
{% elif unit_model %}
<h1 class="display-4 fw-light">{{ ledger.name }}</h1>
{% endif %}
<h1 class="display-4 fw-bold">{% trans 'Balance Sheet' %}</h1>
{% if unit_model %}
<h3 class="h2 fw-medium fst-italic">{{ unit_model.name }} {% trans 'Unit' %}</h3>
{% endif %}
<h2 class="display-6 fw-light">
{% if quarter %}{{ year }} | Q{{ quarter }}
{% elif month %}{{ from_date | date:'F, Y' }}
{% else %}{% trans 'Fiscal Year' %} {{ year }}
{% endif %}
</h2>
<p class="h5 fst-italic fw-light">As of {{ to_date | date:'m/d/Y' }}</p>
</div>
<!-- Balance Sheet Statement -->
<div class="table-responsive">
{% balance_sheet_statement io_model=object %}
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,73 @@
{% load django_ledger %}
{% load trans from i18n %}
<div class="card shadow-sm">
<div class="card-body">
<!-- Header Section -->
<div class="text-center mb-4">
<h2 class="display-4 fw-medium">
{% if has_year %}Fiscal Year {{ to_date.year }}{% endif %}
{% if has_month %}{{ to_date | date:"F Y" }}{% endif %}
{% if has_quarter %}Q{{ quarter }} {{ year }}{% endif %}
{% if has_date %}{{ to_date | date }}{% endif %}
</h2>
</div>
<!-- Year Navigation -->
<div class="text-center mb-3">
<p class="mb-1">
<span class="fw-bold">Year:</span>
<a href="{{ previous_year_url }}" class="text-decoration-none me-2"><< {{ previous_year }}</a>
<a href="{{ current_year_url }}" class="text-decoration-none me-2">{{ year }}</a>
<a href="{{ next_year_url }}" class="text-decoration-none">{{ next_year }} >></a>
</p>
</div>
<!-- Quarter Navigation -->
<div class="text-center mb-3">
<p class="mb-1">
<span class="fw-bold">Quarter:</span>
{% for q_url in quarter_urls %}
<a href="{{ q_url.url }}" class="text-decoration-none me-2">{{ q_url.quarter_name }}</a>
{% if not forloop.last %}|{% endif %}
{% endfor %}
</p>
</div>
<!-- Month Navigation -->
<div class="text-center mb-4">
<p class="mb-1">
<span class="fw-bold">{% trans 'Month' %}:</span>
{% for m_url in month_urls %}
<a href="{{ m_url.url }}" class="text-decoration-none me-2">{{ m_url.month_abbr }}</a>
{% if not forloop.last %}|{% endif %}
{% endfor %}
</p>
</div>
<!-- Date Range -->
<div class="text-center mb-4">
{% if has_date %}
<p class="fw-bold fst-italic">{{ from_date | date:"m/d/Y" }}</p>
{% else %}
<p class="fw-bold fst-italic">
{{ from_date | date:"m/d/Y" }}
{% trans 'thru' %}
{{ to_date | date:"m/d/Y" }}
</p>
{% endif %}
</div>
<!-- Current Month Link -->
<div class="text-center mb-4">
<a href="{{ current_month_url }}" class="btn btn-link text-decoration-none">
{% trans 'Go To Current Month' %}
</a>
</div>
<!-- Date Picker -->
<div class="text-center">
{% date_picker date_navigation_url %}
</div>
</div>
</div>

View File

@ -0,0 +1,140 @@
{% load django_ledger %}
{% load i18n %}
<div class="table-container">
<table class="table is-fullwidth is-narrow is-striped">
<tbody>
{% for bs_role, bs_role_data in tx_digest.balance_sheet.items %}
{% if bs_role_data.is_block %}
<tr>
<td><h2 class="is-size-3">{{ bs_role | upper }}</h2></td>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<th class="has-text-centered">{% trans 'Account Code' %}</th>
<th class="has-text-centered">{% trans 'Account Name' %}</th>
{% if tx_digest.by_unit %}
<th class="has-text-centered">{% trans 'Unit' %}</th>
{% endif %}
<th class="has-text-centered">{% trans 'Balance Type' %}</th>
<th class="has-text-centered">{% trans 'Balance Through' %} {{ tx_digest.to_date | date }}</th>
<th class="has-text-centered">{% trans 'Actions' %}</th>
</tr>
{% for acc_role, acc_data in bs_role_data.roles.items %}
<tr class="has-background-grey-light">
<td class="p-3 has-text-weight-bold">{{ acc_data.role_name | upper }}</td>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td></td>
<td></td>
<td></td>
</tr>
{% for acc in acc_data.accounts %}
<tr>
<td>{{ acc.code }}</td>
<td class="has-text-left">{{ acc.name }}</td>
{% if tx_digest.by_unit %}
<td>{% if acc.unit_name %}{{ acc.unit_name }}{% endif %}</td>
{% endif %}
<td class="has-text-centered">{{ acc.balance_type.0 | upper }}</td>
<td class="has-text-right">{% currency_symbol %}{{ acc.balance | currency_format }}</td>
<td>
<div class="dropdown is-hoverable" id="account-action-{{ account.uuid }}">
<div class="dropdown-trigger">
<button class="button is-small is-rounded"
aria-haspopup="true"
aria-controls="dropdown-menu">
<span>{% trans 'Actions' %}</span>
<span class="icon is-small">{% icon 'bi:arrow-down' 24 %}</span>
</button>
</div>
{% comment %} <div class="dropdown-menu" id="dropdown-menu-{{ acc.uuid }}" role="menu">
<div class="dropdown-content">
<a href="{% url 'django_ledger:account-detail' entity_slug=entity_slug coa_slug=acc.coa_slug account_pk=acc.account_uuid %}"
class="dropdown-item has-text-success">{% trans 'Detail' %}</a>
<a href="{% url 'django_ledger:account-update' entity_slug=entity_slug coa_slug=acc.coa_slug account_pk=acc.account_uuid %}"
class="dropdown-item has-text-warning">{% trans 'Update' %}</a>
</div>
</div> {% endcomment %}
</div>
</td>
</tr>
{% endfor %}
<tr class="has-text-weight-bold is-selected">
<td></td>
<td>{{ acc_data.role_name | upper }} {% trans 'Total:' %}</td>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td class="has-text-right">
{% currency_symbol %}{{ acc_data.total_balance | currency_format }}</td>
<td></td>
</tr>
{% endfor %}
{% if bs_role != 'equity' %}
<tr class="has-text-weight-bold is-size-5">
<td>{% trans 'Total' %} {{ bs_role | upper }}</td>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td></td>
<td></td>
<td>{% currency_symbol %}{{ bs_role_data.total_balance | currency_format }}</td>
{% endif %}
</tr>
{% endif %}
{% endfor %}
<tr class="has-text-weight-bold is-size-5">
<td>{% trans 'Retained Earnings' %}</td>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td></td>
<td></td>
<td>{% currency_symbol %}{{ tx_digest.group_balance.GROUP_EARNINGS | currency_format }}</td>
</tr>
<tr class="has-text-weight-bold is-size-5">
<td>{% trans 'Total EQUITY' %}</td>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td></td>
<td></td>
<td>{% currency_symbol %}{{ tx_digest.group_balance.GROUP_EQUITY | currency_format }}</td>
</tr>
<tr class="has-text-weight-bold is-size-5">
<td>{% trans 'Total Equity + Liabilities' %}</td>
<td></td>
{% if tx_digest.by_unit %}
<td></td>
{% endif %}
<td></td>
<td></td>
<td>{% currency_symbol %}{{ tx_digest.group_balance.GROUP_LIABILITIES_EQUITY | currency_format }}</td>
</tr>
</tbody>
</table>
</div>

View File

@ -45,7 +45,10 @@
{% elif estimate.status == 'in_review' %}
<button id="accept_estimate" onclick="setFormAction('approved')" class="btn btn-phoenix-secondary" data-bs-toggle="modal" data-bs-target="#confirmModal"><span class="d-none d-sm-inline-block">{% trans 'Mark As Accept' %}</span></button>
{% elif estimate.status == 'approved' %}
<a href="{% url 'create_sale_order' estimate.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{% trans 'Create Sale Order' %}</span></a>
{% elif estimate.status == 'completed' %}
<a href="{% url 'invoice_create' estimate.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{% trans 'Create Invoice' %}</span></a>
<a href="{% url 'preview_sale_order' estimate.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{% trans 'Preview Sale Order' %}</span></a>
{% elif estimate.status == 'in_review' %}
<a href="{% url 'estimate_preview' estimate.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{% trans 'Preview' %}</span></a>
{% endif %}

View File

@ -0,0 +1,157 @@
{% extends 'base.html' %}
{% load i18n static %}
{% load crispy_forms_filters %}
{% block title %}
{% trans 'Sale Order' %}
{% endblock %}
{% block content %}
<link rel="stylesheet" href="{% static 'flags/sprite.css' %}" />
<div class="row">
<div class="row mb-3">
<div class="col-sm-6 col-md-8">
<div class="d-sm-flex justify-content-between">
<h3 class="mb-3">
{% if customer.created %}
{{ _('Edit Sale Order') }}
{% else %}
{{ _('Add Sale Order') }}
{% endif %}
</h3>
</div>
</div>
</div>
<div class="row mb-3">
<div class="col-sm-5 col-md-6">
<form method="post" class="form row g-3 needs-validation" novalidate>
{% csrf_token %}
{{ form|crispy }}
<div class="col-12">
<button class="btn btn-primary" type="submit">{% trans 'Save' %}</button>
</div>
</form>
</div>
<div class="col-xl-4 col-xxl-4">
<div class="px-xl-4 mb-7">
<div class="row mx-0 mx-sm-3 mx-lg-0 px-lg-0">
<div class="col-sm-12 col-xxl-6 py-3">
<table class="w-100 table-stats table-stats">
<tr>
<th></th>
<th></th>
<th></th>
</tr>
<tr>
<td class="py-2">
<div class="d-inline-flex align-items-center">
<div class="d-flex bg-success-subtle rounded-circle flex-center me-3" style="width:24px; height:24px">
<span class="text-success-dark" data-feather="bar-chart-2" style="width:16px; height:16px"></span>
</div>
<p class="fw-bold mb-0">Customer Name</p>
</div>
</td>
<td class="py-2 d-none d-sm-block pe-sm-2">:</td>
<td class="py-2">
<p class="ps-6 ps-sm-0 fw-semibold mb-0 mb-0 pb-3 pb-sm-0">{{ estimate.customer.customer_name }}</p>
</td>
</tr>
<tr>
<td class="py-2">
<div class="d-flex align-items-center">
<div class="d-flex bg-info-subtle rounded-circle flex-center me-3" style="width:24px; height:24px">
<span class="text-info-dark" data-feather="trending-up" style="width:16px; height:16px"></span>
</div>
<p class="fw-bold mb-0">Email</p>
</div>
</td>
<td class="py-2 d-none d-sm-block pe-sm-2">:</td>
<td class="py-2">
<p class="ps-6 ps-sm-0 fw-semibold mb-0">{{ estimate.customer.email }}</p>
</td>
</tr>
<tr>
<td class="py-2">
<div class="d-flex align-items-center">
<div class="d-flex bg-info-subtle rounded-circle flex-center me-3" style="width:24px; height:24px">
<span class="text-info-dark" data-feather="trending-up" style="width:16px; height:16px"></span>
</div>
<p class="fw-bold mb-0">Address</p>
</div>
</td>
<td class="py-2 d-none d-sm-block pe-sm-2">:</td>
<td class="py-2">
<p class="ps-6 ps-sm-0 fw-semibold mb-0">{{ estimate.customer.address_1 }}</p>
</td>
</tr>
<tr>
<td class="py-2">
<div class="d-flex align-items-center">
<div class="d-flex bg-info-subtle rounded-circle flex-center me-3" style="width:24px; height:24px">
<span class="text-info-dark" data-feather="trending-up" style="width:16px; height:16px"></span>
</div>
<p class="fw-bold mb-0">Total Amount</p>
</div>
</td>
<td class="py-2 d-none d-sm-block pe-sm-2">:</td>
<td class="py-2">
<p class="ps-6 ps-sm-0 fw-semibold mb-0">${{ data.grand_total }}</p>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="border-top border-bottom border-translucent mt-10" id="leadDetailsTable">
<div class="table-responsive scrollbar mx-n1 px-1">
<table class="table fs-9 mb-0">
<thead>
<tr>
<th class="white-space-nowrap fs-9 align-middle ps-0" style="width:26px;">
<div class="form-check mb-0 fs-8">
<input class="form-check-input" type="checkbox" />
</div>
</th>
<th class="sort white-space-nowrap align-middle pe-3 ps-0 text-uppercase" scope="col" data-sort="name" style="width:20%; min-width:100px">Vin</th>
<th class="sort align-middle pe-6 text-uppercase" scope="col" data-sort="description" style="width:20%; max-width:60px">Make</th>
<th class="sort align-middle text-start text-uppercase" scope="col" data-sort="create_date" style="width:20%; min-width:115px">Model</th>
<th class="sort align-middle text-start text-uppercase" scope="col" data-sort="create_by" style="width:20%; min-width:150px">Year</th>
<th class="sort align-middle text-start text-uppercase" scope="col" data-sort="create_by" style="width:20%; min-width:150px">Unit Price</th>
<th class="align-middle pe-0 text-end" scope="col" style="width:15%;"></th>
</tr>
</thead>
<tbody class="list" id="lead-details-table-body">
{% for car in data.cars %}
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
<td class="fs-9 align-middle px-0 py-3">
<div class="form-check mb-0 fs-8">
<input class="form-check-input" type="checkbox" />
</div>
</td>
<td class="name align-middle white-space-nowrap py-2 ps-0">
<a class="d-flex align-items-center text-body-highlight" href="#!">
{% comment %} <div class="avatar avatar-m me-3 status-online">
<img class="rounded-circle" src="" alt="" />
</div> {% endcomment %}
<h6 class="mb-0 text-body-highlight fw-bold">{{car.vin}}</h6>
</a>
</td>
<td class="description align-middle white-space-nowrap text-start fw-bold text-body-tertiary py-2 pe-6">{{car.make}}</td>
<td class="create_by align-middle white-space-nowrap fw-semibold text-body-highlight">{{car.model}}</td>
<td class="create_by align-middle white-space-nowrap fw-semibold text-body-highlight">{{car.year}}</td>
<td class="last_activity align-middle text-center py-2">
<div class="d-flex align-items-center flex-1">
<span class="fw-bold fs-9 text-body">${{car.total_vat}}</span>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -123,7 +123,7 @@
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
</head>
<body>
{% if estimate.status != "in_review" %}
{% if not estimate.is_completed %}
<main class="main" id="top">
<div class="px-3">
<div class="row min-vh-100 flex-center p-5">
@ -208,44 +208,6 @@
</button>
</div> {% endcomment %}
<!-- Accept Modal -->
<div class="modal fade" id="acceptModal" tabindex="-1" aria-labelledby="acceptModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="acceptModalLabel">{% trans 'Accept Estimate' %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
{% trans 'Are you sure you want to accept this estimate?' %}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans 'Cancel' %}</button>
<a href="{% url 'estimate_mark_as' estimate.pk %}?mark=accepted" type="button" class="btn btn-success" id="confirmAccept">{% trans 'Accept' %}</a>
</div>
</div>
</div>
</div>
<!-- Reject Modal -->
<div class="modal fade" id="rejectModal" tabindex="-1" aria-labelledby="rejectModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="rejectModalLabel">{% trans 'Reject Estimate' %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
{% trans 'Are you sure you want to reject this estimate?' %}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans 'Cancel' %}</button>
<a href="{% url 'estimate_mark_as' estimate.pk %}?mark=rejected" type="button" class="btn btn-danger" id="confirmReject">{% trans 'Reject' %}</a>
</div>
</div>
</div>
</div>
<div class="estimate-row" id="estimate-content">
<!-- Header -->
<div class="estimate-header">
@ -265,30 +227,31 @@
<p><strong>{% trans "Terms" %} :</strong> {{estimate.terms|title}}</p>
</div>
<!-- Items Table -->
<div class="estimate-table">
<table class="table table-bordered">
<thead>
<tr>
<th>{% trans "VIN" %}</th>
<th>{% trans "Make" %}</th>
<th>{% trans "Model" %}</th>
<th>{% trans "Year" %}</th>
<th>{% trans "VIN" %}</th>
<th class="text-center">{% trans "Quantity" %}</th>
<th class="text-center">{% trans "Unit Price" %}</th>
<th class="text-center">{% trans "Total" %}</th>
</tr>
</thead>
<tbody>
{% for item in items %}
{% for car in data.cars %}
<tr>
<td>{{ item.item_model.additional_info.car_info.make }}</td>
<td>{{ item.item_model.additional_info.car_info.model }}</td>
<td>{{ item.item_model.additional_info.car_info.year }}</td>
<td>{{ item.item_model.additional_info.car_info.vin }}</td>
<td class="text-center">{{ item.ce_quantity }}</td>
<td class="text-center">{{ item.item_model.additional_info.car_finance.selling_price }}</td>
<td class="highlight fw-semibold text-center">{{ item.total }}</td>
<td>{{ car.vin }}</td>
<td>{{ car.make }}</td>
<td>{{ car.model }}</td>
<td>{{ car.year }}</td>
<td class="text-center">{{ car.quantity }}</td>
<td class="text-center">{{ car.unit_price }}</td>
<td class="highlight fw-semibold text-center">{{ car.total }}</td>
</tr>
{% endfor %}
</tbody>
@ -297,10 +260,10 @@
<!-- Additional Charges (VAT and Services) -->
<div class="additional-charges">
<p><strong>{% trans "VAT" %} ({{vat}}%):</strong> <span class="highlight">${{vat_amount}}</span></p>
<p><strong>{% trans "VAT" %} ({{vat}}%):</strong> <span class="highlight">${{data.vat}}</span></p>
<p><strong>{% trans "Additional Services" %}:</strong>
<br>
{% for service in additional_services %}
{% for service in data.additional_services %}
<span class="highlight">{{service.name}} - ${{service.price}}</span><br>
{% endfor %}
</p>
@ -308,7 +271,7 @@
<!-- Total -->
<div class="estimate-total">
<p><strong>{%trans "Total Amount" %}:</strong> <span class="highlight">${{total}}</span></p>
<p><strong>{%trans "Total Amount" %}:</strong> <span class="highlight">${{data.grand_total}}</span></p>
</div>
<!-- Footer Note -->

View File

@ -0,0 +1,48 @@
{% extends "base.html" %}
{% load i18n static %}
{% block title %}{{ _("Orders") }}{% endblock title %}
{% block content %}
<div class="row mt-4">
<h3 class="mb-3">{% trans "Orders" %}</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 "Order Number" %}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Customer" %}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "For Estimate" %}</th>
</tr>
</thead>
<tbody class="list">
{% for order in orders %}
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
<td class="align-middle product white-space-nowrap py-0">{{ order.formatted_order_id }}</td>
<td class="align-middle product white-space-nowrap py-0">{{ order.estimate.customer.customer_name }}</td>
<td class="align-middle product white-space-nowrap">{{ order.estimate }}</td>
<td class="text-center">
{% comment %} <a href="{% url 'estimate_detail' estimate.pk %}"
class="btn btn-sm btn-phoenix-success">
{% trans "view"|capfirst %}
</a> {% endcomment %}
</td>
</tr>
{% empty %}
<tr>
<td colspan="6" class="text-center">{% trans "No Quotations Found" %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="d-flex justify-content-center">
{% if is_paginated %}
{% include 'partials/pagination.html' %}
{% endif %}
</div>
</div>
{% endblock %}