diff --git a/car_inventory/urls.py b/car_inventory/urls.py
index dafe4c8a..e07626dc 100644
--- a/car_inventory/urls.py
+++ b/car_inventory/urls.py
@@ -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)
diff --git a/inventory/forms.py b/inventory/forms.py
index d5dfa247..3664c702 100644
--- a/inventory/forms.py
+++ b/inventory/forms.py
@@ -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}),
}
\ No newline at end of file
diff --git a/inventory/migrations/0008_saleorder_estimate_saleorder_invoice.py b/inventory/migrations/0008_saleorder_estimate_saleorder_invoice.py
new file mode 100644
index 00000000..4fa2cf08
--- /dev/null
+++ b/inventory/migrations/0008_saleorder_estimate_saleorder_invoice.py
@@ -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'),
+ ),
+ ]
diff --git a/inventory/migrations/0009_saleorder_formatted_order_id.py b/inventory/migrations/0009_saleorder_formatted_order_id.py
new file mode 100644
index 00000000..2287102e
--- /dev/null
+++ b/inventory/migrations/0009_saleorder_formatted_order_id.py
@@ -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,
+ ),
+ ]
diff --git a/inventory/migrations/0010_remove_saleorder_car_remove_saleorder_customer_and_more.py b/inventory/migrations/0010_remove_saleorder_car_remove_saleorder_customer_and_more.py
new file mode 100644
index 00000000..b52c89af
--- /dev/null
+++ b/inventory/migrations/0010_remove_saleorder_car_remove_saleorder_customer_and_more.py
@@ -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',
+ ),
+ ]
diff --git a/inventory/migrations/0011_alter_saleorder_estimate.py b/inventory/migrations/0011_alter_saleorder_estimate.py
new file mode 100644
index 00000000..0fb4bd20
--- /dev/null
+++ b/inventory/migrations/0011_alter_saleorder_estimate.py
@@ -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,
+ ),
+ ]
diff --git a/inventory/mixins.py b/inventory/mixins.py
index b7762382..db90e7d8 100644
--- a/inventory/mixins.py
+++ b/inventory/mixins.py
@@ -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'
{field}
')
return field
diff --git a/inventory/models.py b/inventory/models.py
index a901e9b9..14d6a75c 100644
--- a/inventory/models.py
+++ b/inventory/models.py
@@ -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
\ No newline at end of file
+ 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
\ No newline at end of file
diff --git a/inventory/templatetags/custom_filters.py b/inventory/templatetags/custom_filters.py
index c1f06443..fec36bb5 100644
--- a/inventory/templatetags/custom_filters.py
+++ b/inventory/templatetags/custom_filters.py
@@ -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(),
+ }
diff --git a/inventory/templatetags/tenhal_tag.py b/inventory/templatetags/tenhal_tag.py
new file mode 100644
index 00000000..915875a3
--- /dev/null
+++ b/inventory/templatetags/tenhal_tag.py
@@ -0,0 +1,908 @@
+# """
+# Django Ledger created by Miguel Sanda .
+# Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
+
+# Contributions to this module:
+# Miguel Sanda
+# """
+
+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,
+# }
diff --git a/inventory/urls.py b/inventory/urls.py
index 80ec7da9..7b323a58 100644
--- a/inventory/urls.py
+++ b/inventory/urls.py
@@ -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//update/", views.UserUpdateView.as_view(), name="user_update"),
@@ -403,6 +404,7 @@ urlpatterns = [
"sales/estimates//send_email", views.send_email_view, name="send_email"
),
path('sales/estimates//sale_order/', views.create_sale_order, name='create_sale_order'),
+ path('sales/estimates//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//balance-sheet/',
+ views.BaseBalanceSheetRedirectView.as_view(),
+ name='entity-bs'),
+ path('entity//balance-sheet/year//',
+ views.FiscalYearBalanceSheetViewBase.as_view(),
+ name='entity-bs-year'),
+ path('entity//balance-sheet/quarter///',
+ views.QuarterlyBalanceSheetView.as_view(),
+ name='entity-bs-quarter'),
+ path('entity//balance-sheet/month///',
+ views.MonthlyBalanceSheetView.as_view(),
+ name='entity-bs-month'),
+ path('entity//balance-sheet/date////',
+ views.DateBalanceSheetView.as_view(),
+ name='entity-bs-date'),
+
]
diff --git a/inventory/utils.py b/inventory/utils.py
index 2abf4681..c73ed419 100644
--- a/inventory/utils.py
+++ b/inventory/utils.py
@@ -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
\ No newline at end of file
+ return obj_dict
diff --git a/inventory/views.py b/inventory/views.py
index 8b9bd00d..70dc11c1 100644
--- a/inventory/views.py
+++ b/inventory/views.py
@@ -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.
+ """
diff --git a/scripts/report.py b/scripts/report.py
new file mode 100644
index 00000000..b8ccb1e3
--- /dev/null
+++ b/scripts/report.py
@@ -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)
\ No newline at end of file
diff --git a/scripts/run.py b/scripts/run.py
new file mode 100644
index 00000000..5350fcd8
--- /dev/null
+++ b/scripts/run.py
@@ -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)
diff --git a/templates/base.html b/templates/base.html
index 7599a954..1ff03af0 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -56,6 +56,7 @@
{% include 'header.html' %}
+ {% block period_navigation %}{% endblock period_navigation %}
{% block content %}
{% endblock content%}
diff --git a/templates/header.html b/templates/header.html
index fd7571f7..ad9c6581 100644
--- a/templates/header.html
+++ b/templates/header.html
@@ -105,7 +105,7 @@
-
+
{% trans "orders"|capfirst %}
@@ -236,6 +236,11 @@
{% trans 'bills'|capfirst %}
+
+
+ {% trans 'bills'|capfirst %}
+
+
diff --git a/templates/ledger/reports/balance_sheet.html b/templates/ledger/reports/balance_sheet.html
new file mode 100644
index 00000000..53d30094
--- /dev/null
+++ b/templates/ledger/reports/balance_sheet.html
@@ -0,0 +1,54 @@
+{% extends "base.html" %}
+{% load i18n %}
+{% load static %}
+{% load custom_filters %}
+
+
+{% block period_navigation %}
+ {% if entity %}
+
+
{% period_navigation 'entity-bs' %}
+
+ {% elif ledger %}
+
+
{% period_navigation 'ledger-bs' %}
+
+ {% elif unit_model %}
+
+
{% period_navigation 'unit-bs' %}
+
+ {% endif %}
+{% endblock %}
+
+
+{% block content %}
+
+
+
+ {% if entity %}
+
{{ entity.name }}
+ {% elif ledger %}
+
{{ ledger.name }}
+ {% elif unit_model %}
+
{{ ledger.name }}
+ {% endif %}
+
{% trans 'Balance Sheet' %}
+ {% if unit_model %}
+
{{ unit_model.name }} {% trans 'Unit' %}
+ {% endif %}
+
+ {% if quarter %}{{ year }} | Q{{ quarter }}
+ {% elif month %}{{ from_date | date:'F, Y' }}
+ {% else %}{% trans 'Fiscal Year' %} {{ year }}
+ {% endif %}
+
+
As of {{ to_date | date:'m/d/Y' }}
+
+
+
+
+ {% balance_sheet_statement io_model=object %}
+
+
+
+{% endblock %}
diff --git a/templates/ledger/reports/components/period_navigator.html b/templates/ledger/reports/components/period_navigator.html
new file mode 100644
index 00000000..674b3c7f
--- /dev/null
+++ b/templates/ledger/reports/components/period_navigator.html
@@ -0,0 +1,73 @@
+{% load django_ledger %}
+{% load trans from i18n %}
+
+
+
+
+
+
+ {% 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 %}
+
+
+
+
+
+
+
+
+
+ Quarter:
+ {% for q_url in quarter_urls %}
+ {{ q_url.quarter_name }}
+ {% if not forloop.last %}|{% endif %}
+ {% endfor %}
+
+
+
+
+
+
+ {% trans 'Month' %}:
+ {% for m_url in month_urls %}
+ {{ m_url.month_abbr }}
+ {% if not forloop.last %}|{% endif %}
+ {% endfor %}
+
+
+
+
+
+ {% if has_date %}
+
{{ from_date | date:"m/d/Y" }}
+ {% else %}
+
+ {{ from_date | date:"m/d/Y" }}
+ {% trans 'thru' %}
+ {{ to_date | date:"m/d/Y" }}
+
+ {% endif %}
+
+
+
+
+
+
+
+ {% date_picker date_navigation_url %}
+
+
+
\ No newline at end of file
diff --git a/templates/ledger/reports/income_statement.html b/templates/ledger/reports/income_statement.html
new file mode 100644
index 00000000..e69de29b
diff --git a/templates/ledger/reports/tags/balance_sheet_statement.html b/templates/ledger/reports/tags/balance_sheet_statement.html
new file mode 100644
index 00000000..581c24e2
--- /dev/null
+++ b/templates/ledger/reports/tags/balance_sheet_statement.html
@@ -0,0 +1,140 @@
+{% load django_ledger %}
+{% load i18n %}
+
+
+
+
+ {% for bs_role, bs_role_data in tx_digest.balance_sheet.items %}
+ {% if bs_role_data.is_block %}
+
+ {{ bs_role | upper }}
+
+ {% if tx_digest.by_unit %}
+
+ {% endif %}
+
+
+
+
+
+ {% trans 'Account Code' %}
+ {% trans 'Account Name' %}
+ {% if tx_digest.by_unit %}
+ {% trans 'Unit' %}
+ {% endif %}
+ {% trans 'Balance Type' %}
+ {% trans 'Balance Through' %} {{ tx_digest.to_date | date }}
+ {% trans 'Actions' %}
+
+
+ {% for acc_role, acc_data in bs_role_data.roles.items %}
+
+ {{ acc_data.role_name | upper }}
+
+ {% if tx_digest.by_unit %}
+
+ {% endif %}
+
+
+
+
+
+ {% for acc in acc_data.accounts %}
+
+ {{ acc.code }}
+ {{ acc.name }}
+ {% if tx_digest.by_unit %}
+ {% if acc.unit_name %}{{ acc.unit_name }}{% endif %}
+ {% endif %}
+ {{ acc.balance_type.0 | upper }}
+ {% currency_symbol %}{{ acc.balance | currency_format }}
+
+
+
+
+ {% trans 'Actions' %}
+ {% icon 'bi:arrow-down' 24 %}
+
+
+ {% comment %} {% endcomment %}
+
+
+
+ {% endfor %}
+
+
+
+ {{ acc_data.role_name | upper }} {% trans 'Total:' %}
+
+ {% if tx_digest.by_unit %}
+
+ {% endif %}
+
+ {% currency_symbol %}{{ acc_data.total_balance | currency_format }}
+
+
+
+ {% endfor %}
+
+
+ {% if bs_role != 'equity' %}
+
+ {% trans 'Total' %} {{ bs_role | upper }}
+
+ {% if tx_digest.by_unit %}
+
+ {% endif %}
+
+
+ {% currency_symbol %}{{ bs_role_data.total_balance | currency_format }}
+ {% endif %}
+
+
+ {% endif %}
+ {% endfor %}
+
+
+
+ {% trans 'Retained Earnings' %}
+
+ {% if tx_digest.by_unit %}
+
+ {% endif %}
+
+
+ {% currency_symbol %}{{ tx_digest.group_balance.GROUP_EARNINGS | currency_format }}
+
+
+
+ {% trans 'Total EQUITY' %}
+
+ {% if tx_digest.by_unit %}
+
+ {% endif %}
+
+
+ {% currency_symbol %}{{ tx_digest.group_balance.GROUP_EQUITY | currency_format }}
+
+
+ {% trans 'Total Equity + Liabilities' %}
+
+ {% if tx_digest.by_unit %}
+
+ {% endif %}
+
+
+ {% currency_symbol %}{{ tx_digest.group_balance.GROUP_LIABILITIES_EQUITY | currency_format }}
+
+
+
+
+
diff --git a/templates/sales/estimates/estimate_detail.html b/templates/sales/estimates/estimate_detail.html
index 1a5382ab..2b5a6fbe 100644
--- a/templates/sales/estimates/estimate_detail.html
+++ b/templates/sales/estimates/estimate_detail.html
@@ -45,7 +45,10 @@
{% elif estimate.status == 'in_review' %}
{% trans 'Mark As Accept' %}
{% elif estimate.status == 'approved' %}
+ {% trans 'Create Sale Order' %}
+ {% elif estimate.status == 'completed' %}
{% trans 'Create Invoice' %}
+ {% trans 'Preview Sale Order' %}
{% elif estimate.status == 'in_review' %}
{% trans 'Preview' %}
{% endif %}
diff --git a/templates/sales/estimates/sale_order_form.html b/templates/sales/estimates/sale_order_form.html
new file mode 100644
index 00000000..42ccf2ce
--- /dev/null
+++ b/templates/sales/estimates/sale_order_form.html
@@ -0,0 +1,157 @@
+{% extends 'base.html' %}
+{% load i18n static %}
+{% load crispy_forms_filters %}
+{% block title %}
+ {% trans 'Sale Order' %}
+{% endblock %}
+
+{% block content %}
+
+
+
+
+
+
+ {% if customer.created %}
+ {{ _('Edit Sale Order') }}
+ {% else %}
+ {{ _('Add Sale Order') }}
+ {% endif %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ :
+
+ {{ estimate.customer.customer_name }}
+
+
+
+
+
+
+ :
+
+ {{ estimate.customer.email }}
+
+
+
+
+
+
+ :
+
+ {{ estimate.customer.address_1 }}
+
+
+
+
+
+
+ :
+
+ ${{ data.grand_total }}
+
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
diff --git a/templates/sales/estimates/sale_order.html b/templates/sales/estimates/sale_order_preview.html
similarity index 83%
rename from templates/sales/estimates/sale_order.html
rename to templates/sales/estimates/sale_order_preview.html
index d38f5631..eecde802 100644
--- a/templates/sales/estimates/sale_order.html
+++ b/templates/sales/estimates/sale_order_preview.html
@@ -123,7 +123,7 @@
-{% if estimate.status != "in_review" %}
+{% if not estimate.is_completed %}
@@ -208,44 +208,6 @@
{% endcomment %}
-
-
-
-
-
-
- {% trans 'Are you sure you want to accept this estimate?' %}
-
-
-
-
-
-
-
-
-
-
-
-
- {% trans 'Are you sure you want to reject this estimate?' %}
-
-
-
-
-
-
+
+ {% trans "VIN" %}
{% trans "Make" %}
{% trans "Model" %}
{% trans "Year" %}
- {% trans "VIN" %}
{% trans "Quantity" %}
{% trans "Unit Price" %}
{% trans "Total" %}
- {% for item in items %}
+ {% for car in data.cars %}
- {{ item.item_model.additional_info.car_info.make }}
- {{ item.item_model.additional_info.car_info.model }}
- {{ item.item_model.additional_info.car_info.year }}
- {{ item.item_model.additional_info.car_info.vin }}
- {{ item.ce_quantity }}
- {{ item.item_model.additional_info.car_finance.selling_price }}
- {{ item.total }}
+ {{ car.vin }}
+ {{ car.make }}
+ {{ car.model }}
+ {{ car.year }}
+ {{ car.quantity }}
+ {{ car.unit_price }}
+ {{ car.total }}
{% endfor %}
@@ -297,10 +260,10 @@
-
{% trans "VAT" %} ({{vat}}%): ${{vat_amount}}
+
{% trans "VAT" %} ({{vat}}%): ${{data.vat}}
{% trans "Additional Services" %}:
- {% for service in additional_services %}
+ {% for service in data.additional_services %}
{{service.name}} - ${{service.price}}
{% endfor %}
@@ -308,7 +271,7 @@
-
{%trans "Total Amount" %}: ${{total}}
+
{%trans "Total Amount" %}: ${{data.grand_total}}
diff --git a/templates/sales/orders/order_list.html b/templates/sales/orders/order_list.html
new file mode 100644
index 00000000..98bf9d63
--- /dev/null
+++ b/templates/sales/orders/order_list.html
@@ -0,0 +1,48 @@
+{% extends "base.html" %}
+{% load i18n static %}
+
+{% block title %}{{ _("Orders") }}{% endblock title %}
+
+{% block content %}
+
+
{% trans "Orders" %}
+
+
+
+ {% if is_paginated %}
+ {% include 'partials/pagination.html' %}
+ {% endif %}
+
+
+
+{% endblock %}
\ No newline at end of file