sale order
This commit is contained in:
parent
149c68cfae
commit
7142975004
@ -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)
|
||||
|
||||
@ -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}),
|
||||
}
|
||||
@ -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'),
|
||||
),
|
||||
]
|
||||
19
inventory/migrations/0009_saleorder_formatted_order_id.py
Normal file
19
inventory/migrations/0009_saleorder_formatted_order_id.py
Normal 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,
|
||||
),
|
||||
]
|
||||
@ -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',
|
||||
),
|
||||
]
|
||||
21
inventory/migrations/0011_alter_saleorder_estimate.py
Normal file
21
inventory/migrations/0011_alter_saleorder_estimate.py
Normal 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,
|
||||
),
|
||||
]
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
@ -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(),
|
||||
}
|
||||
|
||||
908
inventory/templatetags/tenhal_tag.py
Normal file
908
inventory/templatetags/tenhal_tag.py
Normal 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,
|
||||
# }
|
||||
@ -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'),
|
||||
|
||||
]
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
9
scripts/report.py
Normal 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
70
scripts/run.py
Normal 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)
|
||||
@ -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%}
|
||||
|
||||
@ -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>
|
||||
|
||||
54
templates/ledger/reports/balance_sheet.html
Normal file
54
templates/ledger/reports/balance_sheet.html
Normal 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 %}
|
||||
73
templates/ledger/reports/components/period_navigator.html
Normal file
73
templates/ledger/reports/components/period_navigator.html
Normal 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>
|
||||
0
templates/ledger/reports/income_statement.html
Normal file
0
templates/ledger/reports/income_statement.html
Normal file
140
templates/ledger/reports/tags/balance_sheet_statement.html
Normal file
140
templates/ledger/reports/tags/balance_sheet_statement.html
Normal 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>
|
||||
@ -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 %}
|
||||
|
||||
157
templates/sales/estimates/sale_order_form.html
Normal file
157
templates/sales/estimates/sale_order_form.html
Normal 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 %}
|
||||
@ -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 -->
|
||||
48
templates/sales/orders/order_list.html
Normal file
48
templates/sales/orders/order_list.html
Normal 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 %}
|
||||
Loading…
x
Reference in New Issue
Block a user