update dashboard
This commit is contained in:
parent
ecf1c375c1
commit
564a724663
@ -18,6 +18,7 @@ from django_ledger.models import (
|
|||||||
CustomerModel,
|
CustomerModel,
|
||||||
ItemModelQuerySet,
|
ItemModelQuerySet,
|
||||||
)
|
)
|
||||||
|
from django_ledger.io.io_core import get_localdate
|
||||||
from django.db.models import Sum
|
from django.db.models import Sum
|
||||||
from decimal import Decimal, InvalidOperation
|
from decimal import Decimal, InvalidOperation
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
@ -318,6 +319,18 @@ class AdditionalServices(models.Model, LocalizedNameMixin):
|
|||||||
blank=True,
|
blank=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return {
|
||||||
|
"name": self.name,
|
||||||
|
"price": str(self.price),
|
||||||
|
"price_": str(self.price_),
|
||||||
|
"taxable": self.taxable,
|
||||||
|
"uom": self.uom,
|
||||||
|
}
|
||||||
|
@property
|
||||||
|
def price_(self):
|
||||||
|
vat = VatRate.objects.filter(is_active=True).first()
|
||||||
|
return Decimal(self.price + (self.price * vat.rate)) if self.taxable else self.price
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Additional Services")
|
verbose_name = _("Additional Services")
|
||||||
verbose_name_plural = _("Additional Services")
|
verbose_name_plural = _("Additional Services")
|
||||||
@ -515,24 +528,23 @@ class CarFinance(models.Model):
|
|||||||
verbose_name=_("Discount Amount"),
|
verbose_name=_("Discount Amount"),
|
||||||
default=Decimal("0.00"),
|
default=Decimal("0.00"),
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def total(self):
|
def total(self):
|
||||||
if self.additional_services.count() > 0:
|
|
||||||
return self.selling_price + sum(
|
|
||||||
x.price for x in self.additional_services.all()
|
|
||||||
)
|
|
||||||
return self.selling_price
|
return self.selling_price
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
def total_additionals(self):
|
||||||
|
return sum(x.price_ for x in self.additional_services.all())
|
||||||
|
@property
|
||||||
def total_discount(self):
|
def total_discount(self):
|
||||||
if self.discount_amount > 0:
|
if self.discount_amount > 0:
|
||||||
return self.total - self.discount_amount
|
return self.selling_price - self.discount_amount
|
||||||
return self.total
|
return self.selling_price
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def total_vat(self):
|
def total_vat(self):
|
||||||
return self.total_discount + self.vat_amount
|
return round(self.total_discount + self.vat_amount + self.total_additionals,2)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def vat_amount(self):
|
def vat_amount(self):
|
||||||
@ -1574,7 +1586,8 @@ class SaleOrder(models.Model):
|
|||||||
next_id = last_order.id + 1
|
next_id = last_order.id + 1
|
||||||
else:
|
else:
|
||||||
next_id = 1
|
next_id = 1
|
||||||
self.formatted_order_id = f"{next_id:05d}"
|
year = get_localdate().year
|
||||||
|
self.formatted_order_id = f"O-{year}-{next_id:09d}"
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"Sale Order for {self.full_name}"
|
return f"Sale Order for {self.full_name}"
|
||||||
|
|||||||
@ -158,23 +158,12 @@ def create_ledger_entity(sender, instance, created, **kwargs):
|
|||||||
balance_type="debit",
|
balance_type="debit",
|
||||||
active=True,
|
active=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Inventory Account
|
|
||||||
asset_ca_inventory = entity.create_account(
|
|
||||||
coa_model=coa,
|
|
||||||
code="1106",
|
|
||||||
role=roles.ASSET_CA_INVENTORY,
|
|
||||||
name=_("Inventory"),
|
|
||||||
balance_type="debit",
|
|
||||||
active=True,
|
|
||||||
)
|
|
||||||
# asset_ca_inventory.role_default = True
|
|
||||||
# asset_ca_inventory.save()
|
|
||||||
|
|
||||||
# VAT Payable Account
|
# VAT Payable Account
|
||||||
liability_ltl_vat_receivable = entity.create_account(
|
liability_ltl_vat_receivable = entity.create_account(
|
||||||
coa_model=coa,
|
coa_model=coa,
|
||||||
code="1107",
|
code="1106",
|
||||||
role=roles.ASSET_CA_RECEIVABLES,
|
role=roles.ASSET_CA_RECEIVABLES,
|
||||||
name=_("VAT Receivable"),
|
name=_("VAT Receivable"),
|
||||||
balance_type="debit",
|
balance_type="debit",
|
||||||
@ -707,7 +696,7 @@ def update_item_model_cost(sender, instance, created, **kwargs):
|
|||||||
product.default_amount = instance.selling_price
|
product.default_amount = instance.selling_price
|
||||||
product.additional_info = {}
|
product.additional_info = {}
|
||||||
product.additional_info.update({"car_finance":instance.to_dict()})
|
product.additional_info.update({"car_finance":instance.to_dict()})
|
||||||
product.additional_info.update({"additional_services": [to_dict(service) for service in instance.additional_services.all()]})
|
product.additional_info.update({"additional_services": [service.to_dict() for service in instance.additional_services.all()]})
|
||||||
product.save()
|
product.save()
|
||||||
print(f"Inventory item updated with CarFinance data for Car: {instance.car}")
|
print(f"Inventory item updated with CarFinance data for Car: {instance.car}")
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
from random import randint
|
||||||
from django import template
|
from django import template
|
||||||
from calendar import month_abbr
|
from calendar import month_abbr
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
@ -36,6 +37,7 @@ def attr(field, args):
|
|||||||
|
|
||||||
@register.inclusion_tag('ledger/reports/components/period_navigator.html', takes_context=True)
|
@register.inclusion_tag('ledger/reports/components/period_navigator.html', takes_context=True)
|
||||||
def period_navigation(context, base_url: str):
|
def period_navigation(context, base_url: str):
|
||||||
|
print(context, base_url)
|
||||||
kwargs = dict()
|
kwargs = dict()
|
||||||
entity_slug = context['view'].kwargs['entity_slug']
|
entity_slug = context['view'].kwargs['entity_slug']
|
||||||
kwargs['entity_slug'] = entity_slug
|
kwargs['entity_slug'] = entity_slug
|
||||||
@ -147,7 +149,7 @@ def balance_sheet_statement(context, io_model, to_date=None):
|
|||||||
'tx_digest': io_digest.get_io_data(),
|
'tx_digest': io_digest.get_io_data(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@register.inclusion_tag('django_ledger/financial_statements/tags/income_statement.html', takes_context=True)
|
@register.inclusion_tag('ledger/reports/tags/income_statement.html', takes_context=True)
|
||||||
def income_statement_table(context, io_model, from_date=None, to_date=None):
|
def income_statement_table(context, io_model, from_date=None, to_date=None):
|
||||||
user_model = context['user']
|
user_model = context['user']
|
||||||
activity = context['request'].GET.get('activity')
|
activity = context['request'].GET.get('activity')
|
||||||
@ -178,3 +180,53 @@ def income_statement_table(context, io_model, from_date=None, to_date=None):
|
|||||||
'user_model': user_model,
|
'user_model': user_model,
|
||||||
'tx_digest': io_digest.get_io_data()
|
'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/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
|
||||||
|
}
|
||||||
|
|||||||
@ -1,9 +1,18 @@
|
|||||||
import json
|
import json
|
||||||
from . import models as m
|
from . import models as m
|
||||||
|
from datetime import datetime
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django_ledger import models as lm
|
from django_ledger import models as lm
|
||||||
from django.test import Client, TestCase
|
from django.test import Client, TestCase
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
from django_ledger.io.io_core import get_localdate
|
||||||
|
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
from decimal import Decimal
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
from inventory.models import VatRate
|
||||||
|
from inventory.utils import CarFinanceCalculator
|
||||||
|
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
@ -52,7 +61,7 @@ class ModelTest(TestCase):
|
|||||||
id_car_serie=self.car_serie,
|
id_car_serie=self.car_serie,
|
||||||
year=2020,
|
year=2020,
|
||||||
id_car_trim=self.trim,
|
id_car_trim=self.trim,
|
||||||
receiving_date="2020-01-01",
|
receiving_date=get_localdate(),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.car_finances = m.CarFinance.objects.create(
|
self.car_finances = m.CarFinance.objects.create(
|
||||||
@ -78,7 +87,7 @@ class ModelTest(TestCase):
|
|||||||
self.assertIsNotNone(dealer.entity)
|
self.assertIsNotNone(dealer.entity)
|
||||||
self.assertEqual(dealer.entity.name, dealer.name)
|
self.assertEqual(dealer.entity.name, dealer.name)
|
||||||
|
|
||||||
self.assertEqual(dealer.entity.get_all_accounts().count(), 19)
|
self.assertEqual(dealer.entity.get_all_accounts().count(), 57)
|
||||||
self.assertEqual(dealer.entity.get_uom_all().count(), 16)
|
self.assertEqual(dealer.entity.get_uom_all().count(), 16)
|
||||||
|
|
||||||
def test_car_creation_creates_product(self):
|
def test_car_creation_creates_product(self):
|
||||||
@ -232,4 +241,164 @@ class AuthenticationTest(TestCase):
|
|||||||
|
|
||||||
# Check the response
|
# Check the response
|
||||||
self.assertEqual(response.status_code, 400)
|
self.assertEqual(response.status_code, 400)
|
||||||
self.assertIn("error", response.json()) # Assuming the view returns an error for missing fields
|
self.assertIn("error", response.json()) # Assuming the view returns an error for missing fields
|
||||||
|
|
||||||
|
|
||||||
|
class CarFinanceCalculatorTests(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
# Common setup for all tests
|
||||||
|
self.mock_model = MagicMock()
|
||||||
|
self.vat_rate = VatRate.objects.create(rate=Decimal('0.20'), is_active=True)
|
||||||
|
|
||||||
|
def test_no_active_vat_rate_raises_error(self):
|
||||||
|
VatRate.objects.all().delete() # Ensure no active VAT
|
||||||
|
with self.assertRaises(ObjectDoesNotExist):
|
||||||
|
CarFinanceCalculator(self.mock_model)
|
||||||
|
|
||||||
|
def test_vat_rate_retrieval(self):
|
||||||
|
calculator = CarFinanceCalculator(self.mock_model)
|
||||||
|
self.assertEqual(calculator.vat_rate, Decimal('0.20'))
|
||||||
|
|
||||||
|
def test_item_transactions_retrieval(self):
|
||||||
|
mock_item = MagicMock()
|
||||||
|
self.mock_model.get_itemtxs_data.return_value = [MagicMock(all=lambda: [mock_item])]
|
||||||
|
calculator = CarFinanceCalculator(self.mock_model)
|
||||||
|
self.assertEqual(calculator.item_transactions, [mock_item])
|
||||||
|
|
||||||
|
def test_get_car_data(self):
|
||||||
|
mock_item = MagicMock()
|
||||||
|
mock_item.ce_quantity = 2
|
||||||
|
mock_item.item_model = MagicMock()
|
||||||
|
mock_item.item_model.item_number = '123'
|
||||||
|
mock_item.item_model.additional_info = {
|
||||||
|
CarFinanceCalculator.CAR_FINANCE_KEY: {
|
||||||
|
'selling_price': '10000',
|
||||||
|
'cost_price': '8000',
|
||||||
|
'discount_amount': '500',
|
||||||
|
'total_vat': '2000'
|
||||||
|
},
|
||||||
|
CarFinanceCalculator.CAR_INFO_KEY: {
|
||||||
|
'vin': 'VIN123',
|
||||||
|
'make': 'Toyota',
|
||||||
|
'model': 'Camry',
|
||||||
|
'year': 2020,
|
||||||
|
'trim': 'LE',
|
||||||
|
'mileage': 15000
|
||||||
|
},
|
||||||
|
CarFinanceCalculator.ADDITIONAL_SERVICES_KEY: [
|
||||||
|
{'name': 'Service 1', 'price': '200', 'taxable': True, 'price_': '240'}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
calculator = CarFinanceCalculator(self.mock_model)
|
||||||
|
car_data = calculator._get_car_data(mock_item)
|
||||||
|
|
||||||
|
self.assertEqual(car_data['item_number'], '123')
|
||||||
|
self.assertEqual(car_data['vin'], 'VIN123')
|
||||||
|
self.assertEqual(car_data['make'], 'Toyota')
|
||||||
|
self.assertEqual(car_data['selling_price'], '10000')
|
||||||
|
self.assertEqual(car_data['unit_price'], Decimal('10000'))
|
||||||
|
self.assertEqual(car_data['quantity'], 2)
|
||||||
|
self.assertEqual(car_data['total'], Decimal('20000'))
|
||||||
|
self.assertEqual(car_data['total_vat'], '2000')
|
||||||
|
self.assertEqual(car_data['additional_services'], [{'name': 'Service 1', 'price': '200', 'taxable': True, 'price_': '240'}])
|
||||||
|
|
||||||
|
def test_get_additional_services(self):
|
||||||
|
mock_item1 = MagicMock()
|
||||||
|
mock_item1.item_model.additional_info = {
|
||||||
|
CarFinanceCalculator.ADDITIONAL_SERVICES_KEY: [
|
||||||
|
{'name': 'Service 1', 'price': '100', 'taxable': True, 'price_': '120'}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
mock_item2 = MagicMock()
|
||||||
|
mock_item2.item_model.additional_info = {
|
||||||
|
CarFinanceCalculator.ADDITIONAL_SERVICES_KEY: [
|
||||||
|
{'name': 'Service 2', 'price': '200', 'taxable': False, 'price_': '200'}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
self.mock_model.get_itemtxs_data.return_value = [MagicMock(all=lambda: [mock_item1, mock_item2])]
|
||||||
|
calculator = CarFinanceCalculator(self.mock_model)
|
||||||
|
services = calculator._get_additional_services()
|
||||||
|
|
||||||
|
self.assertEqual(len(services), 2)
|
||||||
|
self.assertEqual(services[0]['name'], 'Service 1')
|
||||||
|
self.assertEqual(services[1]['name'], 'Service 2')
|
||||||
|
self.assertEqual(services[0]['price_'], '120')
|
||||||
|
self.assertEqual(services[1]['price_'], '200')
|
||||||
|
|
||||||
|
def test_calculate_totals(self):
|
||||||
|
mock_item1 = MagicMock()
|
||||||
|
mock_item1.ce_quantity = 2
|
||||||
|
mock_item1.item_model.additional_info = {
|
||||||
|
CarFinanceCalculator.CAR_FINANCE_KEY: {
|
||||||
|
'selling_price': '10000',
|
||||||
|
'discount_amount': '500'
|
||||||
|
},
|
||||||
|
CarFinanceCalculator.ADDITIONAL_SERVICES_KEY: [
|
||||||
|
{'price_': '100'},
|
||||||
|
{'price_': '200'}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
mock_item2 = MagicMock()
|
||||||
|
mock_item2.quantity = 3
|
||||||
|
mock_item2.item_model.additional_info = {
|
||||||
|
CarFinanceCalculator.CAR_FINANCE_KEY: {
|
||||||
|
'selling_price': '20000',
|
||||||
|
'discount_amount': '1000'
|
||||||
|
},
|
||||||
|
CarFinanceCalculator.ADDITIONAL_SERVICES_KEY: [
|
||||||
|
{'price_': '300'}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
self.mock_model.get_itemtxs_data.return_value = [MagicMock(all=lambda: [mock_item1, mock_item2])]
|
||||||
|
calculator = CarFinanceCalculator(self.mock_model)
|
||||||
|
totals = calculator.calculate_totals()
|
||||||
|
|
||||||
|
expected_total_price = (Decimal('10000') * 2 + Decimal('20000') * 3) - (Decimal('500') + Decimal('1000'))
|
||||||
|
expected_vat = expected_total_price * Decimal('0.15')
|
||||||
|
expected_additionals = Decimal('100') + Decimal('200') + Decimal('300')
|
||||||
|
expected_grand_total = (expected_total_price + expected_vat + expected_additionals).quantize(Decimal('0.00'))
|
||||||
|
|
||||||
|
self.assertEqual(totals['total_price'], expected_total_price)
|
||||||
|
self.assertEqual(totals['total_discount'], Decimal('1500'))
|
||||||
|
self.assertEqual(totals['total_vat_amount'], expected_vat)
|
||||||
|
self.assertEqual(totals['total_additionals'], expected_additionals)
|
||||||
|
self.assertEqual(totals['grand_total'], expected_grand_total)
|
||||||
|
|
||||||
|
def test_get_finance_data(self):
|
||||||
|
mock_item = MagicMock()
|
||||||
|
mock_item.ce_quantity = 1
|
||||||
|
mock_item.item_model = MagicMock()
|
||||||
|
mock_item.item_model.item_number = '456'
|
||||||
|
mock_item.item_model.additional_info = {
|
||||||
|
CarFinanceCalculator.CAR_FINANCE_KEY: {
|
||||||
|
'selling_price': '15000',
|
||||||
|
'discount_amount': '1000',
|
||||||
|
'total_vat': '2800'
|
||||||
|
},
|
||||||
|
CarFinanceCalculator.CAR_INFO_KEY: {
|
||||||
|
'vin': 'VIN456',
|
||||||
|
'make': 'Honda',
|
||||||
|
'model': 'Civic',
|
||||||
|
'year': 2021
|
||||||
|
},
|
||||||
|
CarFinanceCalculator.ADDITIONAL_SERVICES_KEY: [
|
||||||
|
{'name': 'Service', 'price': '150', 'taxable': True, 'price_': '180'}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
self.mock_model.get_itemtxs_data.return_value = [MagicMock(all=lambda: [mock_item])]
|
||||||
|
calculator = CarFinanceCalculator(self.mock_model)
|
||||||
|
finance_data = calculator.get_finance_data()
|
||||||
|
|
||||||
|
self.assertEqual(len(finance_data['cars']), 1)
|
||||||
|
self.assertEqual(finance_data['quantity'], 1)
|
||||||
|
self.assertEqual(finance_data['total_price'], Decimal('14000')) # 15000 - 1000
|
||||||
|
self.assertEqual(finance_data['total_vat'], Decimal('14000') + (Decimal('14000') * Decimal('0.20')))
|
||||||
|
self.assertEqual(finance_data['total_vat_amount'], Decimal('14000') * Decimal('0.20'))
|
||||||
|
self.assertEqual(finance_data['total_additionals'], Decimal('180'))
|
||||||
|
self.assertEqual(finance_data['additionals'][0]['name'], 'Service')
|
||||||
|
self.assertEqual(finance_data['vat'], Decimal('0.20'))
|
||||||
@ -554,15 +554,59 @@ urlpatterns = [
|
|||||||
path('entity/<slug:entity_slug>/income-statement/year/<int:year>/',
|
path('entity/<slug:entity_slug>/income-statement/year/<int:year>/',
|
||||||
views.FiscalYearIncomeStatementViewBase.as_view(),
|
views.FiscalYearIncomeStatementViewBase.as_view(),
|
||||||
name='entity-ic-year'),
|
name='entity-ic-year'),
|
||||||
# path('entity/<slug:entity_slug>/income-statement/quarter/<int:year>/<int:quarter>/',
|
path('entity/<slug:entity_slug>/income-statement/quarter/<int:year>/<int:quarter>/',
|
||||||
# views.QuarterlyIncomeStatementView.as_view(),
|
views.QuarterlyIncomeStatementView.as_view(),
|
||||||
# name='entity-ic-quarter'),
|
name='entity-ic-quarter'),
|
||||||
# path('entity/<slug:entity_slug>/income-statement/month/<int:year>/<int:month>/',
|
path('entity/<slug:entity_slug>/income-statement/month/<int:year>/<int:month>/',
|
||||||
# views.MonthlyIncomeStatementView.as_view(),
|
views.MonthlyIncomeStatementView.as_view(),
|
||||||
# name='entity-ic-month'),
|
name='entity-ic-month'),
|
||||||
# path('entity/<slug:entity_slug>/income-statement/date/<int:year>/<int:month>/<int:day>/',
|
path('entity/<slug:entity_slug>/income-statement/date/<int:year>/<int:month>/<int:day>/',
|
||||||
# views.MonthlyIncomeStatementView.as_view(),
|
views.MonthlyIncomeStatementView.as_view(),
|
||||||
# name='entity-ic-date'),
|
name='entity-ic-date'),
|
||||||
|
# CASH FLOW STATEMENTS...
|
||||||
|
# Entities...
|
||||||
|
path('entity/<slug:entity_slug>/cash-flow-statement/',
|
||||||
|
views.BaseCashFlowStatementRedirectViewBase.as_view(),
|
||||||
|
name='entity-cf'),
|
||||||
|
path('entity/<slug:entity_slug>/cash-flow-statement/year/<int:year>/',
|
||||||
|
views.FiscalYearCashFlowStatementViewBase.as_view(),
|
||||||
|
name='entity-cf-year'),
|
||||||
|
path('entity/<slug:entity_slug>/cash-flow-statement/quarter/<int:year>/<int:quarter>/',
|
||||||
|
views.QuarterlyCashFlowStatementView.as_view(),
|
||||||
|
name='entity-cf-quarter'),
|
||||||
|
path('entity/<slug:entity_slug>/cash-flow-statement/month/<int:year>/<int:month>/',
|
||||||
|
views.MonthlyCashFlowStatementView.as_view(),
|
||||||
|
name='entity-cf-month'),
|
||||||
|
path('entity/<slug:entity_slug>/cash-flow-statement/date/<int:year>/<int:month>/<int:day>/',
|
||||||
|
views.DateCashFlowStatementView.as_view(),
|
||||||
|
name='entity-cf-date'),
|
||||||
|
#Dashboard
|
||||||
|
# DASHBOARD Views...
|
||||||
|
path('<slug:entity_slug>/dashboard/',
|
||||||
|
views.EntityModelDetailHandlerViewBase.as_view(),
|
||||||
|
name='entity-dashboard'),
|
||||||
|
path('<slug:entity_slug>/dashboard/year/<int:year>/',
|
||||||
|
views.FiscalYearEntityModelDashboardView.as_view(),
|
||||||
|
name='entity-dashboard-year'),
|
||||||
|
path('<slug:entity_slug>/dashboard/quarter/<int:year>/<int:quarter>/',
|
||||||
|
views.QuarterlyEntityDashboardView.as_view(),
|
||||||
|
name='entity-dashboard-quarter'),
|
||||||
|
path('<slug:entity_slug>/dashboard/month/<int:year>/<int:month>/',
|
||||||
|
views.MonthlyEntityDashboardView.as_view(),
|
||||||
|
name='entity-dashboard-month'),
|
||||||
|
path('<slug:entity_slug>/dashboard/date/<int:year>/<int:month>/<int:day>/',
|
||||||
|
views.DateEntityDashboardView.as_view(),
|
||||||
|
name='entity-dashboard-date'),
|
||||||
|
#dashboard api
|
||||||
|
path('entity/<slug:entity_slug>/data/net-payables/',
|
||||||
|
views.PayableNetAPIView.as_view(),
|
||||||
|
name='entity-json-net-payables'),
|
||||||
|
path('entity/<slug:entity_slug>/data/net-receivables/',
|
||||||
|
views.ReceivableNetAPIView.as_view(),
|
||||||
|
name='entity-json-net-receivables'),
|
||||||
|
path('entity/<slug:entity_slug>/data/pnl/',
|
||||||
|
views.PnLAPIView.as_view(),
|
||||||
|
name='entity-json-pnl'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -147,7 +147,7 @@ def get_car_finance_data(model):
|
|||||||
if i.item_model.additional_info["additional_services"]:
|
if i.item_model.additional_info["additional_services"]:
|
||||||
additional_services.extend(
|
additional_services.extend(
|
||||||
[
|
[
|
||||||
{"name": x.name, "price": x.price}
|
{"name": x.get("name"), "price": x.get("price")}
|
||||||
for x in i.item_model.additional_info["additional_services"]
|
for x in i.item_model.additional_info["additional_services"]
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@ -281,45 +281,25 @@ def get_financial_values(model):
|
|||||||
|
|
||||||
|
|
||||||
def set_invoice_payment(dealer, entity, invoice, amount, payment_method):
|
def set_invoice_payment(dealer, entity, invoice, amount, payment_method):
|
||||||
vat_amount = 0
|
|
||||||
total_amount = 0
|
|
||||||
|
|
||||||
calculator = CarFinanceCalculator(invoice)
|
calculator = CarFinanceCalculator(invoice)
|
||||||
finance_data = calculator.get_finance_data()
|
finance_data = calculator.get_finance_data()
|
||||||
# if invoice.terms == "on_receipt":
|
|
||||||
# for x in invoice.get_itemtxs_data()[0].all():
|
|
||||||
# total_amount += Decimal(x.unit_cost) * Decimal(x.quantity)
|
|
||||||
|
|
||||||
ledger = LedgerModel.objects.filter(
|
|
||||||
name__icontains=str(invoice.pk), entity=entity
|
|
||||||
).first()
|
|
||||||
journal = JournalEntryModel.objects.create(
|
journal = JournalEntryModel.objects.create(
|
||||||
posted=False,
|
posted=False,
|
||||||
description=f"Payment for Invoice {invoice.invoice_number}",
|
description=f"Payment for Invoice {invoice.invoice_number}",
|
||||||
ledger=ledger,
|
ledger=invoice.ledger,
|
||||||
locked=False,
|
locked=False,
|
||||||
origin="Payment",
|
origin="Payment",
|
||||||
)
|
)
|
||||||
|
|
||||||
credit_account = entity.get_default_coa_accounts().get(name="Sales Revenue")
|
credit_account = entity.get_default_coa_accounts().get(name="Sales Revenue")
|
||||||
debit_account = None
|
debit_account = entity.get_default_coa_accounts().get(name="Cash", active=True)
|
||||||
if payment_method == "cash":
|
vat_payable_account = entity.get_default_coa_accounts().get(name="VAT Payable", active=True)
|
||||||
debit_account = entity.get_default_coa_accounts().get(name="Cash", active=True)
|
|
||||||
elif payment_method == "credit":
|
|
||||||
debit_account = entity.get_default_coa_accounts().get(
|
|
||||||
name="Accounts Receivable", active=True
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
debit_account = entity.get_default_coa_accounts().get(
|
|
||||||
name="Cash in Bank", active=True
|
|
||||||
)
|
|
||||||
|
|
||||||
vat_payable_account = entity.get_default_coa_accounts().get(
|
|
||||||
name="VAT Payable", active=True
|
|
||||||
)
|
|
||||||
TransactionModel.objects.create(
|
TransactionModel.objects.create(
|
||||||
journal_entry=journal,
|
journal_entry=journal,
|
||||||
account=debit_account, # Debit Cash
|
account=debit_account, # Debit Account
|
||||||
amount=finance_data["grand_total"], # Payment amount
|
amount=Decimal(finance_data["grand_total"]),
|
||||||
tx_type="debit",
|
tx_type="debit",
|
||||||
description="Payment Received",
|
description="Payment Received",
|
||||||
)
|
)
|
||||||
@ -327,7 +307,7 @@ def set_invoice_payment(dealer, entity, invoice, amount, payment_method):
|
|||||||
TransactionModel.objects.create(
|
TransactionModel.objects.create(
|
||||||
journal_entry=journal,
|
journal_entry=journal,
|
||||||
account=credit_account, # Credit Accounts Receivable
|
account=credit_account, # Credit Accounts Receivable
|
||||||
amount=finance_data["total_price"], # Payment amount
|
amount=Decimal(finance_data["total_price"] + finance_data["total_additionals"]),
|
||||||
tx_type="credit",
|
tx_type="credit",
|
||||||
description="Payment Received",
|
description="Payment Received",
|
||||||
)
|
)
|
||||||
@ -336,7 +316,7 @@ def set_invoice_payment(dealer, entity, invoice, amount, payment_method):
|
|||||||
TransactionModel.objects.create(
|
TransactionModel.objects.create(
|
||||||
journal_entry=journal,
|
journal_entry=journal,
|
||||||
account=vat_payable_account, # Credit VAT Payable
|
account=vat_payable_account, # Credit VAT Payable
|
||||||
amount=finance_data["total_vat_amount"],
|
amount=finance_data.get("total_vat_amount"),
|
||||||
tx_type="credit",
|
tx_type="credit",
|
||||||
description="VAT Payable on Invoice",
|
description="VAT Payable on Invoice",
|
||||||
)
|
)
|
||||||
@ -413,147 +393,304 @@ def transfer_to_dealer(request, cars, to_dealer, remarks=None):
|
|||||||
car.dealer = to_dealer
|
car.dealer = to_dealer
|
||||||
car.save()
|
car.save()
|
||||||
|
|
||||||
|
class CarTransfer:
|
||||||
|
def __init__(self, car, transfer):
|
||||||
|
self.car = car
|
||||||
|
self.transfer = transfer
|
||||||
|
self.from_dealer = transfer.from_dealer
|
||||||
|
self.to_dealer = transfer.to_dealer
|
||||||
|
self.customer = None
|
||||||
|
self.invoice = None
|
||||||
|
self.ledger = None
|
||||||
|
self.item = None
|
||||||
|
self.product = None
|
||||||
|
self.vendor = None
|
||||||
|
self.bill = None
|
||||||
|
|
||||||
def transfer_car(car, transfer):
|
def transfer_car(self):
|
||||||
from_dealer = transfer.from_dealer
|
self._create_customer()
|
||||||
to_dealer = transfer.to_dealer
|
self._create_invoice()
|
||||||
# add transfer.to_dealer as customer in transfer.from_dealer entity
|
self._create_product_in_receiver_ledger()
|
||||||
|
self._create_vendor_and_bill()
|
||||||
|
self._finalize_car_transfer()
|
||||||
|
return True
|
||||||
|
|
||||||
customer = (
|
def _create_customer(self):
|
||||||
from_dealer.entity.get_customers().filter(email=to_dealer.user.email).first()
|
self.customer = self._find_or_create_customer()
|
||||||
)
|
if self.customer is None:
|
||||||
if not customer:
|
self.customer = self._create_new_customer()
|
||||||
customer = from_dealer.entity.create_customer(
|
|
||||||
|
def _find_or_create_customer(self):
|
||||||
|
return self.from_dealer.entity.get_customers().filter(email=self.to_dealer.user.email).first()
|
||||||
|
|
||||||
|
def _create_new_customer(self):
|
||||||
|
customer = self.from_dealer.entity.create_customer(
|
||||||
customer_model_kwargs={
|
customer_model_kwargs={
|
||||||
"customer_name": to_dealer.name,
|
"customer_name": self.to_dealer.name,
|
||||||
"email": to_dealer.user.email,
|
"email": self.to_dealer.user.email,
|
||||||
"address_1": to_dealer.address,
|
"address_1": self.to_dealer.address,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
customer.additional_info = {}
|
||||||
customer.additional_info.update({"type": "organization"})
|
customer.additional_info.update({"type": "organization"})
|
||||||
customer.save()
|
customer.save()
|
||||||
|
return customer
|
||||||
|
|
||||||
invoice = from_dealer.entity.create_invoice(
|
def _create_invoice(self):
|
||||||
customer_model=customer,
|
self.invoice = self.from_dealer.entity.create_invoice(
|
||||||
terms=InvoiceModel.TERMS_NET_30,
|
customer_model=self.customer,
|
||||||
cash_account=from_dealer.entity.get_default_coa_accounts().get(
|
terms=InvoiceModel.TERMS_NET_30,
|
||||||
name="Cash", active=True
|
cash_account=self.from_dealer.entity.get_default_coa_accounts().get(name="Cash", active=True),
|
||||||
),
|
prepaid_account=self.from_dealer.entity.get_default_coa_accounts().get(name="Accounts Receivable", active=True),
|
||||||
prepaid_account=from_dealer.entity.get_default_coa_accounts().get(
|
coa_model=self.from_dealer.entity.get_default_coa(),
|
||||||
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
|
|
||||||
ledger.save()
|
|
||||||
invoice.save()
|
|
||||||
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,
|
|
||||||
"quantity": transfer.quantity,
|
|
||||||
"total_amount": transfer.total_price,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
invoice_itemtxs = invoice.migrate_itemtxs(
|
|
||||||
itemtxs=invoice_itemtxs,
|
|
||||||
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
|
|
||||||
uom = to_dealer.entity.get_uom_all().filter(name=item.uom.name).first()
|
|
||||||
|
|
||||||
# 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(),
|
|
||||||
)
|
|
||||||
|
|
||||||
product.additional_info.update({"car_info": car.to_dict()})
|
|
||||||
product.save()
|
|
||||||
|
|
||||||
# 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
|
self.ledger = self.from_dealer.entity.create_ledger(name=str(self.invoice.pk))
|
||||||
|
self.invoice.ledgar = self.ledger
|
||||||
|
self.ledger.invoicemodel = self.invoice
|
||||||
|
self.ledger.save()
|
||||||
|
self.invoice.save()
|
||||||
|
|
||||||
bill = to_dealer.entity.create_bill(
|
self._add_car_item_to_invoice()
|
||||||
vendor_model=vendor,
|
|
||||||
terms=BillModel.TERMS_NET_30,
|
def _add_car_item_to_invoice(self):
|
||||||
cash_account=to_dealer.entity.get_default_coa_accounts().get(
|
self.item = self.from_dealer.entity.get_items_products().filter(name=self.car.vin).first()
|
||||||
name="Cash", active=True
|
if not self.item:
|
||||||
),
|
return
|
||||||
prepaid_account=to_dealer.entity.get_default_coa_accounts().get(
|
|
||||||
name="Prepaid Expenses", active=True
|
invoice_itemtxs = {
|
||||||
),
|
self.item.item_number: {
|
||||||
coa_model=to_dealer.entity.get_default_coa(),
|
"unit_cost": self.transfer.total_price,
|
||||||
)
|
"quantity": self.transfer.quantity,
|
||||||
bill.additional_info = {}
|
"total_amount": self.transfer.total_price,
|
||||||
bill_itemtxs = {
|
}
|
||||||
product.item_number: {
|
|
||||||
"unit_cost": transfer.total_price,
|
|
||||||
"quantity": transfer.quantity,
|
|
||||||
"total_amount": transfer.total_price,
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
bill_itemtxs = bill.migrate_itemtxs(
|
invoice_itemtxs = self.invoice.migrate_itemtxs(
|
||||||
itemtxs=bill_itemtxs, commit=True, operation=BillModel.ITEMIZE_APPEND
|
itemtxs=invoice_itemtxs,
|
||||||
)
|
commit=True,
|
||||||
|
operation=InvoiceModel.ITEMIZE_APPEND,
|
||||||
|
)
|
||||||
|
self.invoice.save()
|
||||||
|
self.invoice.mark_as_review()
|
||||||
|
self.invoice.mark_as_approved(self.from_dealer.entity.slug, self.from_dealer.entity.admin)
|
||||||
|
self.invoice.save()
|
||||||
|
|
||||||
bill.additional_info.update({"car_info": car.to_dict()})
|
def _create_product_in_receiver_ledger(self):
|
||||||
bill.additional_info.update({"car_finance": car.finances.to_dict()})
|
uom = self.to_dealer.entity.get_uom_all().filter(name=self.item.uom.name).first()
|
||||||
|
self.product = self.to_dealer.entity.create_item_product(
|
||||||
|
name=self.item.name,
|
||||||
|
uom_model=uom,
|
||||||
|
item_type=self.item.item_type,
|
||||||
|
coa_model=self.to_dealer.entity.get_default_coa(),
|
||||||
|
)
|
||||||
|
|
||||||
bill.mark_as_review()
|
self.product.additional_info.update({"car_info": self.car.to_dict()})
|
||||||
bill.mark_as_approved(to_dealer.entity.slug, to_dealer.entity.admin)
|
self.product.save()
|
||||||
bill.save()
|
|
||||||
|
|
||||||
car.dealer = to_dealer
|
def _create_vendor_and_bill(self):
|
||||||
car.vendor = vendor
|
self.vendor = self._find_or_create_vendor()
|
||||||
car.receiving_date = datetime.datetime.now()
|
self.bill = self.to_dealer.entity.create_bill(
|
||||||
car.finances.additional_services.clear()
|
vendor_model=self.vendor,
|
||||||
if hasattr(car, "custom_cards"):
|
terms=BillModel.TERMS_NET_30,
|
||||||
car.custom_cards.delete()
|
cash_account=self.to_dealer.entity.get_default_coa_accounts().get(name="Cash", active=True),
|
||||||
|
prepaid_account=self.to_dealer.entity.get_default_coa_accounts().get(name="Prepaid Expenses", active=True),
|
||||||
|
coa_model=self.to_dealer.entity.get_default_coa(),
|
||||||
|
)
|
||||||
|
|
||||||
car.finances.cost_price = transfer.total_price
|
self._add_car_item_to_bill()
|
||||||
car.finances.selling_price = 0
|
|
||||||
car.finances.discount_amount = 0
|
|
||||||
car.finances.save()
|
|
||||||
car.location.owner = to_dealer
|
|
||||||
car.location.showroom = to_dealer
|
|
||||||
car.location.description = ""
|
|
||||||
car.location.save()
|
|
||||||
car.status = models.CarStatusChoices.AVAILABLE
|
|
||||||
transfer.status = models.CarTransferStatusChoices.success
|
|
||||||
transfer.active = False
|
|
||||||
transfer.save()
|
|
||||||
car.save()
|
|
||||||
|
|
||||||
return True
|
def _find_or_create_vendor(self):
|
||||||
# pay the pill
|
vendor = self.to_dealer.entity.get_vendors().filter(vendor_name=self.from_dealer.name).first()
|
||||||
# set_bill_payment(to_dealer,to_dealer.entity,bill,transfer.total_price,"credit")
|
if not vendor:
|
||||||
|
vendor = VendorModel.objects.create(
|
||||||
|
entity_model=self.to_dealer.entity,
|
||||||
|
vendor_name=self.from_dealer.name,
|
||||||
|
additional_info={"info": to_dict(self.from_dealer)},
|
||||||
|
)
|
||||||
|
return vendor
|
||||||
|
|
||||||
|
def _add_car_item_to_bill(self):
|
||||||
|
bill_itemtxs = {
|
||||||
|
self.product.item_number: {
|
||||||
|
"unit_cost": self.transfer.total_price,
|
||||||
|
"quantity": self.transfer.quantity,
|
||||||
|
"total_amount": self.transfer.total_price,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bill_itemtxs = self.bill.migrate_itemtxs(
|
||||||
|
itemtxs=bill_itemtxs, commit=True, operation=BillModel.ITEMIZE_APPEND
|
||||||
|
)
|
||||||
|
self.bill.additional_info = {}
|
||||||
|
self.bill.additional_info.update({"car_info": self.car.to_dict()})
|
||||||
|
self.bill.additional_info.update({"car_finance": self.car.finances.to_dict()})
|
||||||
|
|
||||||
|
self.bill.mark_as_review()
|
||||||
|
self.bill.mark_as_approved(self.to_dealer.entity.slug, self.to_dealer.entity.admin)
|
||||||
|
self.bill.save()
|
||||||
|
|
||||||
|
def _finalize_car_transfer(self):
|
||||||
|
self.car.dealer = self.to_dealer
|
||||||
|
self.car.vendor = self.vendor
|
||||||
|
self.car.receiving_date = datetime.datetime.now()
|
||||||
|
self.car.finances.additional_services.clear()
|
||||||
|
if hasattr(self.car, "custom_cards"):
|
||||||
|
self.car.custom_cards.delete()
|
||||||
|
|
||||||
|
self.car.finances.cost_price = self.transfer.total_price
|
||||||
|
self.car.finances.selling_price = 0
|
||||||
|
self.car.finances.discount_amount = 0
|
||||||
|
self.car.finances.save()
|
||||||
|
self.car.location.owner = self.to_dealer
|
||||||
|
self.car.location.showroom = self.to_dealer
|
||||||
|
self.car.location.description = ""
|
||||||
|
self.car.location.save()
|
||||||
|
self.car.status = models.CarStatusChoices.AVAILABLE
|
||||||
|
self.transfer.status = models.CarTransferStatusChoices.success
|
||||||
|
self.transfer.active = False
|
||||||
|
self.transfer.save()
|
||||||
|
self.car.save()
|
||||||
|
|
||||||
|
|
||||||
|
# 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()
|
||||||
|
# )
|
||||||
|
# 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,
|
||||||
|
# }
|
||||||
|
# )
|
||||||
|
# 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
|
||||||
|
# ),
|
||||||
|
# coa_model=from_dealer.entity.get_default_coa(),
|
||||||
|
# )
|
||||||
|
|
||||||
|
# ledger = from_dealer.entity.create_ledger(name=str(invoice.pk))
|
||||||
|
# invoice.ledgar = ledger
|
||||||
|
# ledger.invoicemodel = invoice
|
||||||
|
# ledger.save()
|
||||||
|
# invoice.save()
|
||||||
|
# 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,
|
||||||
|
# "quantity": transfer.quantity,
|
||||||
|
# "total_amount": transfer.total_price,
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
|
||||||
|
# invoice_itemtxs = invoice.migrate_itemtxs(
|
||||||
|
# itemtxs=invoice_itemtxs,
|
||||||
|
# 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
|
||||||
|
# uom = to_dealer.entity.get_uom_all().filter(name=item.uom.name).first()
|
||||||
|
|
||||||
|
# # 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(),
|
||||||
|
# )
|
||||||
|
|
||||||
|
# product.additional_info.update({"car_info": car.to_dict()})
|
||||||
|
# product.save()
|
||||||
|
|
||||||
|
# # 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
|
||||||
|
|
||||||
|
# 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
|
||||||
|
# ),
|
||||||
|
# coa_model=to_dealer.entity.get_default_coa(),
|
||||||
|
# )
|
||||||
|
# bill.additional_info = {}
|
||||||
|
# bill_itemtxs = {
|
||||||
|
# product.item_number: {
|
||||||
|
# "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.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
|
||||||
|
# car.finances.save()
|
||||||
|
# car.location.owner = to_dealer
|
||||||
|
# car.location.showroom = to_dealer
|
||||||
|
# car.location.description = ""
|
||||||
|
# car.location.save()
|
||||||
|
# car.status = models.CarStatusChoices.AVAILABLE
|
||||||
|
# transfer.status = models.CarTransferStatusChoices.success
|
||||||
|
# transfer.active = False
|
||||||
|
# transfer.save()
|
||||||
|
# car.save()
|
||||||
|
|
||||||
|
# return True
|
||||||
|
|
||||||
|
|
||||||
def to_dict(obj):
|
def to_dict(obj):
|
||||||
@ -628,9 +765,10 @@ class CarFinanceCalculator:
|
|||||||
"additional_services": self._get_nested_value(item, self.ADDITIONAL_SERVICES_KEY),
|
"additional_services": self._get_nested_value(item, self.ADDITIONAL_SERVICES_KEY),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _get_additional_services(self):
|
def _get_additional_services(self):
|
||||||
return [
|
return [
|
||||||
{"name": service.name, "price": service.price}
|
{"name": service.get('name'), "price": service.get('price'), "taxable": service.get('taxable'),"price_": service.get('price_')}
|
||||||
for item in self.item_transactions
|
for item in self.item_transactions
|
||||||
for service in self._get_nested_value(item, self.ADDITIONAL_SERVICES_KEY) or []
|
for service in self._get_nested_value(item, self.ADDITIONAL_SERVICES_KEY) or []
|
||||||
]
|
]
|
||||||
@ -638,26 +776,28 @@ class CarFinanceCalculator:
|
|||||||
def calculate_totals(self):
|
def calculate_totals(self):
|
||||||
total_price = sum(
|
total_price = sum(
|
||||||
Decimal(self._get_nested_value(item, self.CAR_FINANCE_KEY, 'selling_price')) *
|
Decimal(self._get_nested_value(item, self.CAR_FINANCE_KEY, 'selling_price')) *
|
||||||
Decimal(self._get_quantity(item))
|
int(self._get_quantity(item))
|
||||||
for item in self.item_transactions
|
for item in self.item_transactions
|
||||||
)
|
)
|
||||||
|
total_additionals = sum(Decimal(x.get('price_')) for x in self._get_additional_services())
|
||||||
total_vat_amount = total_price * self.vat_rate
|
|
||||||
total_discount = sum(
|
total_discount = sum(
|
||||||
Decimal(self._get_nested_value(item, self.CAR_FINANCE_KEY, 'discount_amount'))
|
Decimal(self._get_nested_value(item, self.CAR_FINANCE_KEY, 'discount_amount'))
|
||||||
for item in self.item_transactions
|
for item in self.item_transactions
|
||||||
)
|
)
|
||||||
|
total_price_discounted = total_price - total_discount
|
||||||
|
total_vat_amount = total_price_discounted * self.vat_rate
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"total_price": total_price,
|
"total_price": total_price_discounted,
|
||||||
"total_vat_amount": total_vat_amount,
|
"total_vat_amount": total_vat_amount,
|
||||||
"total_discount": total_discount,
|
"total_discount": total_discount,
|
||||||
"grand_total": (total_price + total_vat_amount) - total_discount ,
|
"total_additionals": total_additionals,
|
||||||
|
"grand_total": round(total_price_discounted + total_vat_amount + total_additionals, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_finance_data(self):
|
def get_finance_data(self):
|
||||||
totals = self.calculate_totals()
|
totals = self.calculate_totals()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"cars": [self._get_car_data(item) for item in self.item_transactions],
|
"cars": [self._get_car_data(item) for item in self.item_transactions],
|
||||||
"quantity": sum(self._get_quantity(item) for item in self.item_transactions),
|
"quantity": sum(self._get_quantity(item) for item in self.item_transactions),
|
||||||
@ -665,6 +805,7 @@ class CarFinanceCalculator:
|
|||||||
"total_vat": totals['total_vat_amount'] + totals['total_price'],
|
"total_vat": totals['total_vat_amount'] + totals['total_price'],
|
||||||
"total_vat_amount": totals['total_vat_amount'],
|
"total_vat_amount": totals['total_vat_amount'],
|
||||||
"total_discount": totals['total_discount'],
|
"total_discount": totals['total_discount'],
|
||||||
|
"total_additionals": totals['total_additionals'],
|
||||||
"grand_total": totals['grand_total'],
|
"grand_total": totals['grand_total'],
|
||||||
"additionals": self.additional_services,
|
"additionals": self.additional_services,
|
||||||
"vat": self.vat_rate,
|
"vat": self.vat_rate,
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
from calendar import month_name
|
||||||
|
from random import randint
|
||||||
from rich import print
|
from rich import print
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from django.core.paginator import Paginator
|
from django.core.paginator import Paginator
|
||||||
@ -95,7 +97,7 @@ from .utils import (
|
|||||||
set_bill_payment,
|
set_bill_payment,
|
||||||
set_invoice_payment,
|
set_invoice_payment,
|
||||||
to_dict,
|
to_dict,
|
||||||
transfer_car,
|
CarTransfer,
|
||||||
)
|
)
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from allauth.account import views
|
from allauth.account import views
|
||||||
@ -105,7 +107,7 @@ import cv2
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from pyzbar.pyzbar import decode
|
from pyzbar.pyzbar import decode
|
||||||
from django.core.files.storage import default_storage
|
from django.core.files.storage import default_storage
|
||||||
|
from django_ledger.utils import accruable_net_summary
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
@ -879,7 +881,9 @@ def car_transfer_accept_reject(request, car_pk, transfer_pk):
|
|||||||
elif status == "accepted":
|
elif status == "accepted":
|
||||||
transfer.status = "accept"
|
transfer.status = "accept"
|
||||||
transfer.save()
|
transfer.save()
|
||||||
success = transfer_car(car, transfer)
|
transfer_process = CarTransfer(car, transfer)
|
||||||
|
success = transfer_process.transfer_car()
|
||||||
|
# success = CarTransfer(car, transfer)
|
||||||
if success:
|
if success:
|
||||||
messages.success(request, _("Car Transfer Completed successfully."))
|
messages.success(request, _("Car Transfer Completed successfully."))
|
||||||
models.Notification.objects.create(
|
models.Notification.objects.create(
|
||||||
@ -1050,7 +1054,9 @@ class CustomerListView(LoginRequiredMixin, ListView):
|
|||||||
query = self.request.GET.get("q")
|
query = self.request.GET.get("q")
|
||||||
dealer = get_user_type(self.request)
|
dealer = get_user_type(self.request)
|
||||||
|
|
||||||
customers = dealer.entity.get_customers().filter(active=True,additional_info__type="customer")
|
customers = dealer.entity.get_customers().filter(
|
||||||
|
active=True, additional_info__type="customer"
|
||||||
|
)
|
||||||
|
|
||||||
if query:
|
if query:
|
||||||
customers = customers.filter(
|
customers = customers.filter(
|
||||||
@ -1182,8 +1188,12 @@ def CustomerUpdateView(request, pk):
|
|||||||
instance.save()
|
instance.save()
|
||||||
messages.success(request, _("Customer updated successfully."))
|
messages.success(request, _("Customer updated successfully."))
|
||||||
return redirect("customer_list")
|
return redirect("customer_list")
|
||||||
else:
|
else:
|
||||||
form = forms.CustomerForm(initial=customer.additional_info["customer_info"] if "customer_info" in customer.additional_info else {})
|
form = forms.CustomerForm(
|
||||||
|
initial=customer.additional_info["customer_info"]
|
||||||
|
if "customer_info" in customer.additional_info
|
||||||
|
else {}
|
||||||
|
)
|
||||||
return render(request, "customers/customer_form.html", {"form": form})
|
return render(request, "customers/customer_form.html", {"form": form})
|
||||||
|
|
||||||
|
|
||||||
@ -1722,7 +1732,11 @@ class OrganizationListView(LoginRequiredMixin, ListView):
|
|||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
dealer = get_user_type(self.request)
|
dealer = get_user_type(self.request)
|
||||||
return dealer.entity.get_customers().filter(additional_info__type="organization",active=True).all()
|
return (
|
||||||
|
dealer.entity.get_customers()
|
||||||
|
.filter(additional_info__type="organization", active=True)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class OrganizationDetailView(DetailView):
|
class OrganizationDetailView(DetailView):
|
||||||
@ -1734,16 +1748,16 @@ class OrganizationDetailView(DetailView):
|
|||||||
def OrganizationCreateView(request):
|
def OrganizationCreateView(request):
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
form = forms.OrganizationForm(request.POST)
|
form = forms.OrganizationForm(request.POST)
|
||||||
|
|
||||||
#upload logo
|
# upload logo
|
||||||
image = request.FILES.get('logo')
|
image = request.FILES.get("logo")
|
||||||
file_name = default_storage.save('images/{}'.format(image.name), image)
|
file_name = default_storage.save("images/{}".format(image.name), image)
|
||||||
file_url = default_storage.url(file_name)
|
file_url = default_storage.url(file_name)
|
||||||
|
|
||||||
organization_dict = {
|
organization_dict = {
|
||||||
x: request.POST[x] for x in request.POST if x != "csrfmiddlewaretoken"
|
x: request.POST[x] for x in request.POST if x != "csrfmiddlewaretoken"
|
||||||
}
|
}
|
||||||
dealer = get_user_type(request)
|
dealer = get_user_type(request)
|
||||||
|
|
||||||
instance = dealer.entity.create_customer(
|
instance = dealer.entity.create_customer(
|
||||||
customer_model_kwargs={
|
customer_model_kwargs={
|
||||||
@ -1763,26 +1777,29 @@ def OrganizationCreateView(request):
|
|||||||
else:
|
else:
|
||||||
form = forms.OrganizationForm()
|
form = forms.OrganizationForm()
|
||||||
return render(request, "organizations/organization_form.html", {"form": form})
|
return render(request, "organizations/organization_form.html", {"form": form})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def OrganizationUpdateView(request,pk):
|
def OrganizationUpdateView(request, pk):
|
||||||
organization = get_object_or_404(CustomerModel, pk=pk)
|
organization = get_object_or_404(CustomerModel, pk=pk)
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
form = forms.OrganizationForm(request.POST)
|
form = forms.OrganizationForm(request.POST)
|
||||||
|
|
||||||
organization_dict = {
|
organization_dict = {
|
||||||
x: request.POST[x] for x in request.POST if x != "csrfmiddlewaretoken"
|
x: request.POST[x] for x in request.POST if x != "csrfmiddlewaretoken"
|
||||||
}
|
}
|
||||||
dealer = get_user_type(request)
|
dealer = get_user_type(request)
|
||||||
|
|
||||||
instance = dealer.entity.get_customers().get(pk=organization.additional_info['organization_info']['pk'])
|
instance = dealer.entity.get_customers().get(
|
||||||
|
pk=organization.additional_info["organization_info"]["pk"]
|
||||||
|
)
|
||||||
instance.customer_name = organization_dict["name"]
|
instance.customer_name = organization_dict["name"]
|
||||||
instance.address_1 = organization_dict["address"]
|
instance.address_1 = organization_dict["address"]
|
||||||
instance.phone = organization_dict["phone_number"]
|
instance.phone = organization_dict["phone_number"]
|
||||||
instance.email = organization_dict["email"]
|
instance.email = organization_dict["email"]
|
||||||
|
|
||||||
organization_dict["logo"] = organization.additional_info['organization_info']['logo']
|
organization_dict["logo"] = organization.additional_info["organization_info"][
|
||||||
|
"logo"
|
||||||
|
]
|
||||||
organization_dict["pk"] = str(instance.pk)
|
organization_dict["pk"] = str(instance.pk)
|
||||||
instance.additional_info["organization_info"] = organization_dict
|
instance.additional_info["organization_info"] = organization_dict
|
||||||
instance.additional_info["type"] = "organization"
|
instance.additional_info["type"] = "organization"
|
||||||
@ -1790,10 +1807,12 @@ def OrganizationUpdateView(request,pk):
|
|||||||
messages.success(request, _("Organization created successfully."))
|
messages.success(request, _("Organization created successfully."))
|
||||||
return redirect("organization_list")
|
return redirect("organization_list")
|
||||||
else:
|
else:
|
||||||
form = forms.OrganizationForm(initial=organization.additional_info["organization_info"] or {})
|
form = forms.OrganizationForm(
|
||||||
form.fields.pop('logo', None)
|
initial=organization.additional_info["organization_info"] or {}
|
||||||
|
)
|
||||||
|
form.fields.pop("logo", None)
|
||||||
return render(request, "organizations/organization_form.html", {"form": form})
|
return render(request, "organizations/organization_form.html", {"form": form})
|
||||||
|
|
||||||
|
|
||||||
class OrganizationDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
|
class OrganizationDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
|
||||||
model = models.Organization
|
model = models.Organization
|
||||||
@ -2121,16 +2140,21 @@ class AccountCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
|||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
dealer = get_user_type(self.request)
|
dealer = get_user_type(self.request)
|
||||||
form.instance.entity_model = dealer.entity
|
form.instance.entity_model = dealer.entity
|
||||||
|
form.instance.coa_model = dealer.entity.get_default_coa()
|
||||||
form.instance.depth = 0
|
form.instance.depth = 0
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
dealer = get_user_type(self.request)
|
dealer = get_user_type(self.request)
|
||||||
entity = dealer.entity
|
kwargs = super().get_form_kwargs()
|
||||||
kwargs = super().get_form_kwargs()
|
kwargs["coa_model"] = dealer.entity.get_default_coa()
|
||||||
|
|
||||||
kwargs["coa_model"] = entity.get_default_coa()
|
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
def get_form(self, form_class=None):
|
||||||
|
form = super().get_form(form_class)
|
||||||
|
entity = get_user_type(self.request).entity
|
||||||
|
form.initial['coa_model'] = entity.get_default_coa()
|
||||||
|
return form
|
||||||
|
|
||||||
|
|
||||||
class AccountDetailView(LoginRequiredMixin, DetailView):
|
class AccountDetailView(LoginRequiredMixin, DetailView):
|
||||||
@ -2188,13 +2212,11 @@ class AccountUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
|||||||
@login_required
|
@login_required
|
||||||
def account_delete(request, pk):
|
def account_delete(request, pk):
|
||||||
account = get_object_or_404(AccountModel, pk=pk)
|
account = get_object_or_404(AccountModel, pk=pk)
|
||||||
if request.method == "POST":
|
|
||||||
account.delete()
|
account.delete()
|
||||||
messages.success(request, "Account deleted successfully.")
|
messages.success(request, "Account deleted successfully.")
|
||||||
return redirect("account_list")
|
return redirect("account_list")
|
||||||
return render(
|
|
||||||
request, "ledger/coa_accounts/account_delete.html", {"account": account}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Estimates
|
# Estimates
|
||||||
@ -2207,7 +2229,11 @@ class EstimateListView(LoginRequiredMixin, ListView):
|
|||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
dealer = get_user_type(self.request)
|
dealer = get_user_type(self.request)
|
||||||
entity = dealer.entity
|
entity = dealer.entity
|
||||||
return entity.get_estimates()
|
status = self.request.GET.get('status')
|
||||||
|
queryset = entity.get_estimates()
|
||||||
|
if status:
|
||||||
|
queryset = queryset.filter(status=status)
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
# class EstimateCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
# class EstimateCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
||||||
@ -2352,7 +2378,9 @@ def create_estimate(request):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
form = forms.EstimateModelCreateForm(entity_slug=entity.slug, user_model=entity.admin)
|
form = forms.EstimateModelCreateForm(
|
||||||
|
entity_slug=entity.slug, user_model=entity.admin
|
||||||
|
)
|
||||||
form.fields["customer"].queryset = entity.get_customers().filter(active=True)
|
form.fields["customer"].queryset = entity.get_customers().filter(active=True)
|
||||||
car_list = models.Car.objects.filter(
|
car_list = models.Car.objects.filter(
|
||||||
dealer=dealer, finances__selling_price__gt=0
|
dealer=dealer, finances__selling_price__gt=0
|
||||||
@ -2369,7 +2397,7 @@ def create_estimate(request):
|
|||||||
for x in car_list
|
for x in car_list
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
return render(request, "sales/estimates/estimate_form.html", context)
|
return render(request, "sales/estimates/estimate_form.html", context)
|
||||||
|
|
||||||
|
|
||||||
@ -2380,11 +2408,10 @@ class EstimateDetailView(LoginRequiredMixin, DetailView):
|
|||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
estimate = kwargs.get("object")
|
estimate = kwargs.get("object")
|
||||||
if estimate.get_itemtxs_data():
|
if estimate.get_itemtxs_data():
|
||||||
calculator = CarFinanceCalculator(estimate)
|
calculator = CarFinanceCalculator(estimate)
|
||||||
finance_data = calculator.get_finance_data()
|
finance_data = calculator.get_finance_data()
|
||||||
kwargs['data'] = finance_data
|
kwargs["data"] = finance_data
|
||||||
print(finance_data)
|
|
||||||
kwargs["invoice"] = (
|
kwargs["invoice"] = (
|
||||||
InvoiceModel.objects.all().filter(ce_model=estimate).first()
|
InvoiceModel.objects.all().filter(ce_model=estimate).first()
|
||||||
)
|
)
|
||||||
@ -2404,23 +2431,29 @@ def create_sale_order(request, pk):
|
|||||||
estimate.save()
|
estimate.save()
|
||||||
messages.success(request, "Sale Order created successfully")
|
messages.success(request, "Sale Order created successfully")
|
||||||
return redirect("estimate_detail", pk=pk)
|
return redirect("estimate_detail", pk=pk)
|
||||||
|
|
||||||
form = forms.SaleOrderForm()
|
form = forms.SaleOrderForm()
|
||||||
form.fields["estimate"].queryset = EstimateModel.objects.filter(pk=pk)
|
form.fields["estimate"].queryset = EstimateModel.objects.filter(pk=pk)
|
||||||
form.initial['estimate'] = estimate
|
form.initial["estimate"] = estimate
|
||||||
# data = get_car_finance_data(estimate)
|
# data = get_car_finance_data(estimate)
|
||||||
calculator = CarFinanceCalculator(estimate)
|
calculator = CarFinanceCalculator(estimate)
|
||||||
finance_data = calculator.get_finance_data()
|
finance_data = calculator.get_finance_data()
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
"sales/estimates/sale_order_form.html",
|
"sales/estimates/sale_order_form.html",
|
||||||
{"form": form, "estimate": estimate, "items": items,"data": finance_data},
|
{"form": form, "estimate": estimate, "items": items, "data": finance_data},
|
||||||
)
|
)
|
||||||
|
|
||||||
def preview_sale_order(request,pk):
|
|
||||||
estimate = get_object_or_404(EstimateModel,pk=pk)
|
def preview_sale_order(request, pk):
|
||||||
|
estimate = get_object_or_404(EstimateModel, pk=pk)
|
||||||
data = get_car_finance_data(estimate)
|
data = get_car_finance_data(estimate)
|
||||||
return render(request,'sales/estimates/sale_order_preview.html',{'order':estimate.sale_orders.first(),"data":data,"estimate":estimate})
|
return render(
|
||||||
|
request,
|
||||||
|
"sales/estimates/sale_order_preview.html",
|
||||||
|
{"order": estimate.sale_orders.first(), "data": data, "estimate": estimate},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class PaymentRequest(LoginRequiredMixin, DetailView):
|
class PaymentRequest(LoginRequiredMixin, DetailView):
|
||||||
model = EstimateModel
|
model = EstimateModel
|
||||||
@ -2510,10 +2543,13 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView):
|
|||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
invoice = kwargs.get("object")
|
invoice = kwargs.get("object")
|
||||||
|
|
||||||
if invoice.get_itemtxs_data():
|
if invoice.get_itemtxs_data():
|
||||||
calculator = CarFinanceCalculator(invoice)
|
calculator = CarFinanceCalculator(invoice)
|
||||||
finance_data = calculator.get_finance_data()
|
finance_data = calculator.get_finance_data()
|
||||||
print((finance_data["total_vat_amount"]+finance_data["total_price"]) == finance_data["grand_total"])
|
print(
|
||||||
|
(finance_data["total_vat_amount"] + finance_data["total_price"])
|
||||||
|
== finance_data["grand_total"]
|
||||||
|
)
|
||||||
kwargs["data"] = finance_data
|
kwargs["data"] = finance_data
|
||||||
kwargs["payments"] = JournalEntryModel.objects.filter(
|
kwargs["payments"] = JournalEntryModel.objects.filter(
|
||||||
ledger=invoice.ledger
|
ledger=invoice.ledger
|
||||||
@ -2613,12 +2649,12 @@ def invoice_create(request, pk):
|
|||||||
ledger.invoicemodel = invoice
|
ledger.invoicemodel = invoice
|
||||||
ledger.save()
|
ledger.save()
|
||||||
invoice.save()
|
invoice.save()
|
||||||
|
|
||||||
# unit_items = estimate.get_itemtxs_data()[0]
|
# unit_items = estimate.get_itemtxs_data()[0]
|
||||||
# vat = models.VatRate.objects.filter(is_active=True).first()
|
# vat = models.VatRate.objects.filter(is_active=True).first()
|
||||||
calculator = CarFinanceCalculator(estimate)
|
calculator = CarFinanceCalculator(estimate)
|
||||||
finance_data = calculator.get_finance_data()
|
finance_data = calculator.get_finance_data()
|
||||||
|
|
||||||
# total = 0
|
# total = 0
|
||||||
# discount_amount = 0
|
# discount_amount = 0
|
||||||
|
|
||||||
@ -3143,7 +3179,7 @@ class BillDetailView(LoginRequiredMixin, DetailView):
|
|||||||
bill = kwargs.get("object")
|
bill = kwargs.get("object")
|
||||||
if bill.get_itemtxs_data():
|
if bill.get_itemtxs_data():
|
||||||
txs = bill.get_itemtxs_data()[0]
|
txs = bill.get_itemtxs_data()[0]
|
||||||
|
|
||||||
car_and_item_info = [
|
car_and_item_info = [
|
||||||
{
|
{
|
||||||
"car": models.Car.objects.get(vin=x.item_model.name),
|
"car": models.Car.objects.get(vin=x.item_model.name),
|
||||||
@ -3164,7 +3200,6 @@ class BillDetailView(LoginRequiredMixin, DetailView):
|
|||||||
)
|
)
|
||||||
vat = models.VatRate.objects.filter(is_active=True).first()
|
vat = models.VatRate.objects.filter(is_active=True).first()
|
||||||
if vat:
|
if vat:
|
||||||
|
|
||||||
grand_total += round(Decimal(grand_total) * Decimal(vat.rate), 2)
|
grand_total += round(Decimal(grand_total) * Decimal(vat.rate), 2)
|
||||||
kwargs["car_and_item_info"] = car_and_item_info
|
kwargs["car_and_item_info"] = car_and_item_info
|
||||||
kwargs["grand_total"] = grand_total
|
kwargs["grand_total"] = grand_total
|
||||||
@ -3390,16 +3425,14 @@ def bill_create(request):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
car_list = models.Car.objects.filter(
|
car_list = models.Car.objects.filter(
|
||||||
dealer=dealer, finances__selling_price__gt=0,status="available"
|
dealer=dealer, finances__selling_price__gt=0, status="available"
|
||||||
)
|
)
|
||||||
context = {
|
context = {
|
||||||
"form": form,
|
"form": form,
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"car": x,
|
"car": x,
|
||||||
"product": entity.get_items_products()
|
"product": entity.get_items_products().filter(name=x.vin).first(),
|
||||||
.filter(name=x.vin)
|
|
||||||
.first(),
|
|
||||||
}
|
}
|
||||||
for x in car_list
|
for x in car_list
|
||||||
],
|
],
|
||||||
@ -3422,11 +3455,12 @@ class SubscriptionPlans(ListView):
|
|||||||
|
|
||||||
# orders
|
# orders
|
||||||
|
|
||||||
|
|
||||||
class OrderListView(ListView):
|
class OrderListView(ListView):
|
||||||
model = models.SaleOrder
|
model = models.SaleOrder
|
||||||
template_name = "sales/orders/order_list.html"
|
template_name = "sales/orders/order_list.html"
|
||||||
context_object_name = "orders"
|
context_object_name = "orders"
|
||||||
|
|
||||||
|
|
||||||
# email
|
# email
|
||||||
def send_email_view(request, pk):
|
def send_email_view(request, pk):
|
||||||
@ -3498,39 +3532,53 @@ def custom_bad_request_view(request, exception=None):
|
|||||||
return render(request, "errors/400.html", {})
|
return render(request, "errors/400.html", {})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# from django_ledger.io.io_core import get_localdate
|
# from django_ledger.io.io_core import get_localdate
|
||||||
# from django_ledger.views.mixins import (DjangoLedgerSecurityMixIn)
|
# from django_ledger.views.mixins import (DjangoLedgerSecurityMixIn)
|
||||||
# from django.views.generic import RedirectView
|
# from django.views.generic import RedirectView
|
||||||
from django_ledger.views.financial_statement import FiscalYearBalanceSheetView,BaseIncomeStatementRedirectView,FiscalYearIncomeStatementView
|
from django_ledger.views.financial_statement import (
|
||||||
|
FiscalYearBalanceSheetView,
|
||||||
|
BaseIncomeStatementRedirectView,
|
||||||
|
FiscalYearIncomeStatementView,
|
||||||
|
BaseCashFlowStatementRedirectView,
|
||||||
|
FiscalYearCashFlowStatementView,
|
||||||
|
)
|
||||||
|
from django_ledger.views.entity import EntityModelDetailBaseView,EntityModelDetailHandlerView
|
||||||
from django.views.generic import DetailView, RedirectView
|
from django.views.generic import DetailView, RedirectView
|
||||||
|
|
||||||
from django_ledger.io.io_core import get_localdate
|
from django_ledger.io.io_core import get_localdate
|
||||||
from django_ledger.models import EntityModel, EntityUnitModel
|
from django_ledger.models import EntityModel, EntityUnitModel
|
||||||
from django_ledger.views.mixins import (
|
from django_ledger.views.mixins import (
|
||||||
QuarterlyReportMixIn, YearlyReportMixIn,
|
QuarterlyReportMixIn,
|
||||||
MonthlyReportMixIn, DateReportMixIn, DjangoLedgerSecurityMixIn, EntityUnitMixIn,
|
YearlyReportMixIn,
|
||||||
BaseDateNavigationUrlMixIn, PDFReportMixIn
|
MonthlyReportMixIn,
|
||||||
|
DateReportMixIn,
|
||||||
|
DjangoLedgerSecurityMixIn,
|
||||||
|
EntityUnitMixIn,
|
||||||
|
BaseDateNavigationUrlMixIn,
|
||||||
|
PDFReportMixIn,
|
||||||
)
|
)
|
||||||
# BALANCE SHEET -----------
|
# BALANCE SHEET -----------
|
||||||
|
|
||||||
class BaseBalanceSheetRedirectView(DjangoLedgerSecurityMixIn, RedirectView):
|
|
||||||
|
|
||||||
|
class BaseBalanceSheetRedirectView(DjangoLedgerSecurityMixIn, RedirectView):
|
||||||
def get_redirect_url(self, *args, **kwargs):
|
def get_redirect_url(self, *args, **kwargs):
|
||||||
year = get_localdate().year
|
year = get_localdate().year
|
||||||
return reverse('entity-bs-year',
|
return reverse(
|
||||||
kwargs={
|
"entity-bs-year",
|
||||||
'entity_slug': self.kwargs['entity_slug'],
|
kwargs={"entity_slug": self.kwargs["entity_slug"], "year": year},
|
||||||
'year': year
|
)
|
||||||
})
|
|
||||||
|
|
||||||
class FiscalYearBalanceSheetViewBase(FiscalYearBalanceSheetView):
|
class FiscalYearBalanceSheetViewBase(FiscalYearBalanceSheetView):
|
||||||
template_name = "ledger/reports/balance_sheet.html"
|
template_name = "ledger/reports/balance_sheet.html"
|
||||||
|
|
||||||
|
|
||||||
class QuarterlyBalanceSheetView(FiscalYearBalanceSheetViewBase, QuarterlyReportMixIn):
|
class QuarterlyBalanceSheetView(FiscalYearBalanceSheetViewBase, QuarterlyReportMixIn):
|
||||||
"""
|
"""
|
||||||
Quarter Balance Sheet View.
|
Quarter Balance Sheet View.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class MonthlyBalanceSheetView(FiscalYearBalanceSheetViewBase, MonthlyReportMixIn):
|
class MonthlyBalanceSheetView(FiscalYearBalanceSheetViewBase, MonthlyReportMixIn):
|
||||||
"""
|
"""
|
||||||
Monthly Balance Sheet View.
|
Monthly Balance Sheet View.
|
||||||
@ -3542,33 +3590,264 @@ class DateBalanceSheetView(FiscalYearBalanceSheetViewBase, DateReportMixIn):
|
|||||||
Date Balance Sheet View.
|
Date Balance Sheet View.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class BaseIncomeStatementRedirectViewBase(BaseIncomeStatementRedirectView):
|
|
||||||
|
|
||||||
|
# Income Statement -----------
|
||||||
|
|
||||||
|
|
||||||
|
class BaseIncomeStatementRedirectViewBase(BaseIncomeStatementRedirectView):
|
||||||
def get_redirect_url(self, *args, **kwargs):
|
def get_redirect_url(self, *args, **kwargs):
|
||||||
year = get_localdate().year
|
year = get_localdate().year
|
||||||
dealer = get_user_type(self.request)
|
dealer = get_user_type(self.request)
|
||||||
return reverse('entity-ic-year',
|
return reverse(
|
||||||
kwargs={
|
"entity-ic-year", kwargs={"entity_slug": dealer.entity.slug, "year": year}
|
||||||
'entity_slug': dealer.entity.slug,
|
)
|
||||||
'year': year
|
|
||||||
})
|
|
||||||
|
|
||||||
class FiscalYearIncomeStatementViewBase(FiscalYearIncomeStatementView):
|
class FiscalYearIncomeStatementViewBase(FiscalYearIncomeStatementView):
|
||||||
template_name = "ledger/reports/income_statement.html"
|
template_name = "ledger/reports/income_statement.html"
|
||||||
|
|
||||||
class QuarterlyIncomeStatementView(FiscalYearIncomeStatementView, QuarterlyReportMixIn):
|
|
||||||
|
class QuarterlyIncomeStatementView(
|
||||||
|
FiscalYearIncomeStatementViewBase, QuarterlyReportMixIn
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Quarter Income Statement View.
|
Quarter Income Statement View.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class MonthlyIncomeStatementView(FiscalYearIncomeStatementView, MonthlyReportMixIn):
|
class MonthlyIncomeStatementView(FiscalYearIncomeStatementViewBase, MonthlyReportMixIn):
|
||||||
"""
|
"""
|
||||||
Monthly Income Statement View.
|
Monthly Income Statement View.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class DateModelIncomeStatementView(FiscalYearIncomeStatementView, DateReportMixIn):
|
class DateModelIncomeStatementView(FiscalYearIncomeStatementViewBase, DateReportMixIn):
|
||||||
"""
|
"""
|
||||||
Date Income Statement View.
|
Date Income Statement View.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# Cash Flow -----------
|
||||||
|
|
||||||
|
|
||||||
|
class BaseCashFlowStatementRedirectViewBase(BaseCashFlowStatementRedirectView):
|
||||||
|
def get_redirect_url(self, *args, **kwargs):
|
||||||
|
year = get_localdate().year
|
||||||
|
dealer = get_user_type(self.request)
|
||||||
|
return reverse(
|
||||||
|
"entity-cf-year", kwargs={"entity_slug": dealer.entity.slug, "year": year}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class FiscalYearCashFlowStatementViewBase(FiscalYearCashFlowStatementView):
|
||||||
|
template_name = "ledger/reports/cash_flow_statement.html"
|
||||||
|
|
||||||
|
|
||||||
|
class QuarterlyCashFlowStatementView(
|
||||||
|
FiscalYearCashFlowStatementViewBase, QuarterlyReportMixIn
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Quarter Cash Flow Statement View.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class MonthlyCashFlowStatementView(
|
||||||
|
FiscalYearCashFlowStatementViewBase, MonthlyReportMixIn
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Monthly Cash Flow Statement View.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class DateCashFlowStatementView(FiscalYearCashFlowStatementViewBase, DateReportMixIn):
|
||||||
|
"""
|
||||||
|
Date Cash Flow Statement View.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# Dashboard
|
||||||
|
|
||||||
|
class EntityModelDetailHandlerViewBase(EntityModelDetailHandlerView):
|
||||||
|
|
||||||
|
def get_redirect_url(self, *args, **kwargs):
|
||||||
|
loc_date = get_localdate()
|
||||||
|
dealer = get_user_type(self.request)
|
||||||
|
unit_slug = self.get_unit_slug()
|
||||||
|
if unit_slug:
|
||||||
|
return reverse('unit-dashboard-month',
|
||||||
|
kwargs={
|
||||||
|
'entity_slug': dealer.entity.slug,
|
||||||
|
'unit_slug': unit_slug,
|
||||||
|
'year': loc_date.year,
|
||||||
|
'month': loc_date.month,
|
||||||
|
})
|
||||||
|
return reverse('entity-dashboard-month',
|
||||||
|
kwargs={
|
||||||
|
'entity_slug': dealer.entity.slug,
|
||||||
|
'year': loc_date.year,
|
||||||
|
'month': loc_date.month,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
class EntityModelDetailBaseViewBase(EntityModelDetailBaseView):
|
||||||
|
template_name = "ledger/reports/dashboard.html"
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
dealer = get_user_type(self.request)
|
||||||
|
entity_model: EntityModel = dealer.entity
|
||||||
|
context['page_title'] = entity_model.name
|
||||||
|
context['header_title'] = entity_model.name
|
||||||
|
context['header_subtitle'] = _('Dashboard')
|
||||||
|
context['header_subtitle_icon'] = 'mdi:monitor-dashboard'
|
||||||
|
|
||||||
|
unit_slug = context.get('unit_slug', self.get_unit_slug())
|
||||||
|
KWARGS = dict(entity_slug=self.kwargs['entity_slug'])
|
||||||
|
|
||||||
|
if unit_slug:
|
||||||
|
KWARGS['unit_slug'] = unit_slug
|
||||||
|
|
||||||
|
url_pointer = 'entity' if not unit_slug else 'unit'
|
||||||
|
context['pnl_chart_id'] = f'djl-entity-pnl-chart-{randint(10000, 99999)}'
|
||||||
|
context['pnl_chart_endpoint'] = reverse(f'django_ledger:{url_pointer}-json-pnl', kwargs=KWARGS)
|
||||||
|
context['payables_chart_id'] = f'djl-entity-payables-chart-{randint(10000, 99999)}'
|
||||||
|
context['payables_chart_endpoint'] = reverse(f'django_ledger:{url_pointer}-json-net-payables', kwargs=KWARGS)
|
||||||
|
context['receivables_chart_id'] = f'djl-entity-receivables-chart-{randint(10000, 99999)}'
|
||||||
|
context['receivables_chart_endpoint'] = reverse(f'django_ledger:{url_pointer}-json-net-receivables',
|
||||||
|
kwargs=KWARGS)
|
||||||
|
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class FiscalYearEntityModelDashboardView(EntityModelDetailBaseViewBase):
|
||||||
|
"""
|
||||||
|
Entity Fiscal Year Dashboard View.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class QuarterlyEntityDashboardView(FiscalYearEntityModelDashboardView, QuarterlyReportMixIn):
|
||||||
|
"""
|
||||||
|
Entity Quarterly Dashboard View.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class MonthlyEntityDashboardView(FiscalYearEntityModelDashboardView, MonthlyReportMixIn):
|
||||||
|
"""
|
||||||
|
Monthly Entity Dashboard View.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class DateEntityDashboardView(FiscalYearEntityModelDashboardView, DateReportMixIn):
|
||||||
|
"""
|
||||||
|
Date-specific Entity Dashboard View.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class PayableNetAPIView(DjangoLedgerSecurityMixIn, EntityUnitMixIn, View):
|
||||||
|
http_method_names = ['get']
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
if request.user.is_authenticated:
|
||||||
|
dealer = get_user_type(request)
|
||||||
|
bill_qs = BillModel.objects.for_entity(
|
||||||
|
entity_slug=dealer.entity.slug,
|
||||||
|
user_model=dealer.entity.admin,
|
||||||
|
).unpaid()
|
||||||
|
|
||||||
|
# todo: implement this...
|
||||||
|
# unit_slug = self.get_unit_slug()
|
||||||
|
# if unit_slug:
|
||||||
|
# bill_qs.filter(ledger__journal_entry__entity_unit__slug__exact=unit_slug)
|
||||||
|
|
||||||
|
net_summary = accruable_net_summary(bill_qs)
|
||||||
|
net_payables = {
|
||||||
|
'net_payable_data': net_summary
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonResponse({
|
||||||
|
'results': net_payables
|
||||||
|
})
|
||||||
|
|
||||||
|
return JsonResponse({
|
||||||
|
'message': 'Unauthorized'
|
||||||
|
}, status=401)
|
||||||
|
|
||||||
|
class ReceivableNetAPIView(DjangoLedgerSecurityMixIn, EntityUnitMixIn, View):
|
||||||
|
http_method_names = ['get']
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
if request.user.is_authenticated:
|
||||||
|
dealer = get_user_type(request)
|
||||||
|
invoice_qs = InvoiceModel.objects.for_entity(
|
||||||
|
entity_slug=dealer.entity.slug,
|
||||||
|
user_model=dealer.entity.admin,
|
||||||
|
).unpaid()
|
||||||
|
|
||||||
|
# todo: implement this...
|
||||||
|
# unit_slug = self.get_unit_slug()
|
||||||
|
# if unit_slug:
|
||||||
|
# invoice_qs.filter(ledger__journal_entry__entity_unit__slug__exact=unit_slug)
|
||||||
|
|
||||||
|
net_summary = accruable_net_summary(invoice_qs)
|
||||||
|
|
||||||
|
net_receivable = {
|
||||||
|
'net_receivable_data': net_summary
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonResponse({
|
||||||
|
'results': net_receivable
|
||||||
|
})
|
||||||
|
|
||||||
|
return JsonResponse({
|
||||||
|
'message': 'Unauthorized'
|
||||||
|
}, status=401)
|
||||||
|
|
||||||
|
class PnLAPIView(DjangoLedgerSecurityMixIn, EntityUnitMixIn, View):
|
||||||
|
http_method_names = ['get']
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
if request.user.is_authenticated:
|
||||||
|
dealer = get_user_type(request)
|
||||||
|
entity = EntityModel.objects.for_user(
|
||||||
|
user_model=dealer.entity.admin).get(
|
||||||
|
slug__exact=dealer.entity.slug)
|
||||||
|
|
||||||
|
unit_slug = self.get_unit_slug()
|
||||||
|
|
||||||
|
io_digest = entity.digest(
|
||||||
|
user_model=self.request.user,
|
||||||
|
unit_slug=unit_slug,
|
||||||
|
equity_only=True,
|
||||||
|
signs=False,
|
||||||
|
by_period=True,
|
||||||
|
process_groups=True,
|
||||||
|
from_date=self.request.GET.get('fromDate'),
|
||||||
|
to_date=self.request.GET.get('toDate'),
|
||||||
|
|
||||||
|
# todo: For PnL to display proper period values must not use closing entries.
|
||||||
|
use_closing_entries=False
|
||||||
|
)
|
||||||
|
|
||||||
|
io_data = io_digest.get_io_data()
|
||||||
|
group_balance_by_period = io_data['group_balance_by_period']
|
||||||
|
group_balance_by_period = dict(sorted((k, v) for k, v in group_balance_by_period.items()))
|
||||||
|
|
||||||
|
entity_data = {
|
||||||
|
f'{month_name[k[1]]} {k[0]}': {d: float(f) for d, f in v.items()} for k, v in
|
||||||
|
group_balance_by_period.items()}
|
||||||
|
|
||||||
|
entity_pnl = {
|
||||||
|
'entity_slug': entity.slug,
|
||||||
|
'entity_name': entity.name,
|
||||||
|
'pnl_data': entity_data
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonResponse({
|
||||||
|
'results': entity_pnl
|
||||||
|
})
|
||||||
|
|
||||||
|
return JsonResponse({
|
||||||
|
'message': 'Unauthorized'
|
||||||
|
}, status=401)
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
from django_ledger.models.invoice import InvoiceModel
|
||||||
|
from django_ledger.utils import accruable_net_summary
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from django_ledger.models import EstimateModel,EntityModel
|
from django_ledger.models import EstimateModel,EntityModel
|
||||||
from rich import print
|
from rich import print
|
||||||
@ -28,18 +30,23 @@ def run():
|
|||||||
|
|
||||||
# # print(bs_report)
|
# # print(bs_report)
|
||||||
# print(ic_report.get_report_data())
|
# print(ic_report.get_report_data())
|
||||||
estimate = EstimateModel.objects.first()
|
# estimate = EstimateModel.objects.first()
|
||||||
calculator = CarFinanceCalculator(estimate)
|
# calculator = CarFinanceCalculator(estimate)
|
||||||
finance_data = calculator.get_finance_data()
|
# finance_data = calculator.get_finance_data()
|
||||||
|
|
||||||
|
|
||||||
invoice_itemtxs = {
|
# invoice_itemtxs = {
|
||||||
i.get("item_number"): {
|
# i.get("item_number"): {
|
||||||
"unit_cost": i.get("total_price"),
|
# "unit_cost": i.get("total_price"),
|
||||||
"quantity": i.get("quantity"),
|
# "quantity": i.get("quantity"),
|
||||||
"total_amount": i.get("total_vat"),
|
# "total_amount": i.get("total_vat"),
|
||||||
}
|
# }
|
||||||
for i in finance_data.get("cars")
|
# for i in finance_data.get("cars")
|
||||||
}
|
# }
|
||||||
|
# invoice = InvoiceModel.objects.first()
|
||||||
print(finance_data)
|
entity = EntityModel.objects.filter(name="ismail").first()
|
||||||
|
invoice_qs = InvoiceModel.objects.for_entity(
|
||||||
|
entity_slug=entity.slug,
|
||||||
|
user_model=entity.admin,
|
||||||
|
).unpaid()
|
||||||
|
print(accruable_net_summary(invoice_qs))
|
||||||
2
static/js/djetler.bundle.js
Normal file
2
static/js/djetler.bundle.js
Normal file
File diff suppressed because one or more lines are too long
@ -25,7 +25,9 @@
|
|||||||
<link rel="shortcut icon" type="image/x-icon" href="{% static 'images/favicons/favicon.ico' %}">
|
<link rel="shortcut icon" type="image/x-icon" href="{% static 'images/favicons/favicon.ico' %}">
|
||||||
<link rel="manifest" href="{% static 'images/favicons/manifest.json' %}">
|
<link rel="manifest" href="{% static 'images/favicons/manifest.json' %}">
|
||||||
<meta name="msapplication-TileImage" content="{% static 'images/logos/logo-d.png' %}">
|
<meta name="msapplication-TileImage" content="{% static 'images/logos/logo-d.png' %}">
|
||||||
<meta name="theme-color" content="#ffffff">
|
<meta name="theme-color" content="#ffffff">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
|
||||||
|
|
||||||
<script src="{% static 'vendors/simplebar/simplebar.min.js' %}"></script>
|
<script src="{% static 'vendors/simplebar/simplebar.min.js' %}"></script>
|
||||||
<script src="{% static 'js/config.js' %}"></script>
|
<script src="{% static 'js/config.js' %}"></script>
|
||||||
<script src="{% static 'js/sweetalert2.all.min.js' %}"></script>
|
<script src="{% static 'js/sweetalert2.all.min.js' %}"></script>
|
||||||
@ -36,9 +38,9 @@
|
|||||||
<link href="https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@300;400;600;700;800;900&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@300;400;600;700;800;900&display=swap" rel="stylesheet">
|
||||||
<link href="{% static 'vendors/simplebar/simplebar.min.css' %}" rel="stylesheet">
|
<link href="{% static 'vendors/simplebar/simplebar.min.css' %}" rel="stylesheet">
|
||||||
<link href="{% static 'css/sweetalert2.min.css' %}" rel="stylesheet">
|
<link href="{% static 'css/sweetalert2.min.css' %}" rel="stylesheet">
|
||||||
<link href="{% static 'vendors/flatpickr/flatpickr.min.css' %}" rel="stylesheet">
|
<link href="{% static 'vendors/flatpickr/flatpickr.min.css' %}" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="https://unicons.iconscout.com/release/v4.0.8/css/line.css">
|
<link rel="stylesheet" href="https://unicons.iconscout.com/release/v4.0.8/css/line.css">
|
||||||
<link href="{% static 'css/custom.css' %}" rel="stylesheet">
|
<link href="{% static 'css/custom.css' %}" rel="stylesheet">
|
||||||
{% if LANGUAGE_CODE == 'en' %}
|
{% if LANGUAGE_CODE == 'en' %}
|
||||||
<link href="{% static 'css/theme.min.css' %}" type="text/css" rel="stylesheet" id="style-default">
|
<link href="{% static 'css/theme.min.css' %}" type="text/css" rel="stylesheet" id="style-default">
|
||||||
<link href="{% static 'css/user.min.css' %}" type="text/css" rel="stylesheet" id="user-style-default">
|
<link href="{% static 'css/user.min.css' %}" type="text/css" rel="stylesheet" id="user-style-default">
|
||||||
@ -46,34 +48,45 @@
|
|||||||
<link href="{% static 'css/theme-rtl.min.css' %}" type="text/css" rel="stylesheet" id="style-rtl">
|
<link href="{% static 'css/theme-rtl.min.css' %}" type="text/css" rel="stylesheet" id="style-rtl">
|
||||||
<link href="{% static 'css/user-rtl.min.css' %}" type="text/css" rel="stylesheet" id="user-style-rtl">
|
<link href="{% static 'css/user-rtl.min.css' %}" type="text/css" rel="stylesheet" id="user-style-rtl">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"
|
||||||
integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g=="
|
integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g=="
|
||||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{% block customCSS %}
|
{% block customCSS %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<main class="main" id="top">
|
<main class="main" id="top">
|
||||||
{% include 'header.html' %}
|
{% include 'header.html' %}
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
{% block period_navigation %}{% endblock period_navigation %}
|
||||||
{% block period_navigation %}{% endblock period_navigation %}
|
{% block content %}{% endblock content%}
|
||||||
<section class="content">
|
{% block body %}{% endblock body%}
|
||||||
{% block content %}
|
{% include 'footer.html' %}
|
||||||
{% endblock content%}
|
</div>
|
||||||
{% block body %}
|
|
||||||
{% endblock body%}
|
|
||||||
|
|
||||||
|
|
||||||
{% include 'footer.html' %}
|
|
||||||
</div>
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
{% block customJS %}
|
{% block customJS %}
|
||||||
{% endblock %}
|
<script src="{% static 'django_ledger/bundle/djetler.bundle.js' %}"></script>
|
||||||
|
<script>
|
||||||
|
{% if entity_slug %}
|
||||||
|
let entitySlug = "{{ view.kwargs.entity_slug }}"
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if from_date and to_date %}
|
||||||
|
let fromDate = "{{ from_date | date:'Y-m-d' }}";
|
||||||
|
let toDate = "{{ to_date | date:'Y-m-d' }}";
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if date_navigation_url %}
|
||||||
|
let dateNavigationUrl = "{{ date_navigation_url }}"
|
||||||
|
let datePickers = document.querySelectorAll("[id^='djl-datepicker']")
|
||||||
|
datePickers.forEach(dp => djLedger.getCalendar(dp.attributes.id.value, dateNavigationUrl))
|
||||||
|
{% endif %}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
<!-- ===============================================-->
|
<!-- ===============================================-->
|
||||||
<!-- JavaScripts-->
|
<!-- JavaScripts-->
|
||||||
@ -89,6 +102,7 @@
|
|||||||
<script src="{% static 'vendors/dayjs/dayjs.min.js' %}"></script>
|
<script src="{% static 'vendors/dayjs/dayjs.min.js' %}"></script>
|
||||||
<script src="{% static 'js/phoenix.js' %}"></script>
|
<script src="{% static 'js/phoenix.js' %}"></script>
|
||||||
<script src="{% static 'vendors/echarts/echarts.min.js' %}"></script>
|
<script src="{% static 'vendors/echarts/echarts.min.js' %}"></script>
|
||||||
|
<script src="{% static 'js/crm-analytics.js' %}"></script>
|
||||||
<script src="{% static 'js/travel-agency-dashboard.js' %}"></script>
|
<script src="{% static 'js/travel-agency-dashboard.js' %}"></script>
|
||||||
<script src="{% static 'js/main.js' %}"></script>
|
<script src="{% static 'js/main.js' %}"></script>
|
||||||
<script src="{% static 'vendors/mapbox-gl/mapbox-gl.js' %}"></script>
|
<script src="{% static 'vendors/mapbox-gl/mapbox-gl.js' %}"></script>
|
||||||
@ -96,11 +110,10 @@
|
|||||||
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
|
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
|
||||||
<script src="{% static 'vendors/swiper/swiper-bundle.min.js' %}"></script>
|
<script src="{% static 'vendors/swiper/swiper-bundle.min.js' %}"></script>
|
||||||
<script src="{% static 'vendors/flatpickr/flatpickr.min.js' %}"></script>
|
<script src="{% static 'vendors/flatpickr/flatpickr.min.js' %}"></script>
|
||||||
|
<script src="{% static 'django_ledger/bundle/djetler.bundle.js' %}"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{% if messages %}
|
{% if messages %}
|
||||||
{% for message in messages %}
|
{% for message in messages %}
|
||||||
|
|
||||||
@ -121,17 +134,6 @@
|
|||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
/*const Toast = Swal.mixin({
|
|
||||||
toast: true,
|
|
||||||
position: "top-end",
|
|
||||||
showConfirmButton: false,
|
|
||||||
timer: 2000,
|
|
||||||
timerProgressBar: false,
|
|
||||||
didOpen: (toast) => {
|
|
||||||
toast.onmouseenter = Swal.stopTimer;
|
|
||||||
toast.onmouseleave = Swal.resumeTimer;
|
|
||||||
}
|
|
||||||
});*/
|
|
||||||
function notify(tag,msg){
|
function notify(tag,msg){
|
||||||
Toast.fire({
|
Toast.fire({
|
||||||
icon: tag,
|
icon: tag,
|
||||||
@ -139,9 +141,20 @@ function notify(tag,msg){
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{% if entity_slug %}
|
||||||
|
let entitySlug = "{{ view.kwargs.entity_slug }}"
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if from_date and to_date %}
|
||||||
|
let fromDate = "{{ from_date | date:'Y-m-d' }}";
|
||||||
|
let toDate = "{{ to_date | date:'Y-m-d' }}";
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
</script>
|
{% if date_navigation_url %}
|
||||||
|
let dateNavigationUrl = "{{ date_navigation_url }}"
|
||||||
|
let datePickers = document.querySelectorAll("[id^='djl-datepicker']")
|
||||||
|
datePickers.forEach(dp => djLedger.getCalendar(dp.attributes.id.value, dateNavigationUrl))
|
||||||
|
{% endif %}
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@ -251,6 +251,24 @@
|
|||||||
<ul class="nav collapse parent" data-bs-parent="#navbarVerticalCollapse" id="nv-reports">
|
<ul class="nav collapse parent" data-bs-parent="#navbarVerticalCollapse" id="nv-reports">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
|
<a class="nav-link" href="{% url 'entity-dashboard' request.user.dealer.entity.slug %}">
|
||||||
|
{% else %}
|
||||||
|
<a class="nav-link" href="#">
|
||||||
|
{% endif %}
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<span class="nav-link-icon"><span data-feather="package"></span></span><span class="nav-link-text">{% trans 'Dashboard'|capfirst %}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{% if request.user.is_authenticated %}
|
||||||
|
<a class="nav-link" href="{% url 'entity-cf' request.user.dealer.entity.slug %}">
|
||||||
|
{% else %}
|
||||||
|
<a class="nav-link" href="#">
|
||||||
|
{% endif %}
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<span class="nav-link-icon"><span data-feather="package"></span></span><span class="nav-link-text">{% trans 'Cash Flow'|capfirst %}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{% if request.user.is_authenticated %}
|
||||||
<a class="nav-link" href="{% url 'entity-ic' request.user.dealer.entity.slug %}">
|
<a class="nav-link" href="{% url 'entity-ic' request.user.dealer.entity.slug %}">
|
||||||
{% else %}
|
{% else %}
|
||||||
<a class="nav-link" href="#">
|
<a class="nav-link" href="#">
|
||||||
|
|||||||
@ -145,7 +145,7 @@
|
|||||||
{% for service in car.finances.additional_services.all %}
|
{% for service in car.finances.additional_services.all %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{service.name}}</td>
|
<td>{{service.name}}</td>
|
||||||
<td>{{ service.price }}</td>
|
<td>{{ service.price_ }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -312,10 +312,8 @@
|
|||||||
<a class="btn btn-sm btn-phoenix-success" href="{% url 'transfer_detail' car.get_transfer.pk %}">Approve</a>
|
<a class="btn btn-sm btn-phoenix-success" href="{% url 'transfer_detail' car.get_transfer.pk %}">Approve</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% if car.get_transfer.status == "draft" %}
|
<a class="btn btn-sm btn-phoenix-success" href="{% url 'transfer_detail' car.get_transfer.pk %}?action=cancel">Cancel</a>
|
||||||
<a class="btn btn-sm btn-phoenix-success" href="{% url 'transfer_detail' car.get_transfer.pk %}?action=cancel">Cancel</a>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
52
templates/ledger/reports/cash_flow_statement.html
Normal file
52
templates/ledger/reports/cash_flow_statement.html
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load static %}
|
||||||
|
{% load custom_filters %}
|
||||||
|
|
||||||
|
{% block period_navigation %}
|
||||||
|
{% if unit_model and entity %}
|
||||||
|
<div class="col-12">{% period_navigation 'unit-cf' %}</div>
|
||||||
|
{% elif entity %}
|
||||||
|
<div class="col-12">{% period_navigation 'entity-cf' %}</div>
|
||||||
|
{% elif ledger %}
|
||||||
|
<div class="col-12">{% period_navigation 'ledger-cf' %}</div>
|
||||||
|
{% elif unit_model %}
|
||||||
|
<div class="col-12">{% period_navigation 'unit-cf' %}</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body text-center">
|
||||||
|
<div class="container mb-4">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
{% if unit_model and entity %}
|
||||||
|
<h1 class="display-4 font-weight-light">{{ entity.name }}</h1>
|
||||||
|
{% elif entity %}
|
||||||
|
<h1 class="display-4 font-weight-light">{{ entity.name }}</h1>
|
||||||
|
{% elif ledger %}
|
||||||
|
<h1 class="display-4 font-weight-light">{{ ledger.name }}</h1>
|
||||||
|
{% endif %}
|
||||||
|
<h1 class="display-4 font-weight-bold">{% trans 'Cash Flow Statement' %}</h1>
|
||||||
|
{% if unit_model %}
|
||||||
|
<h3 class="h4 font-weight-bold text-success">{{ unit_model.name }} {% trans 'Unit' %}</h3>
|
||||||
|
{% endif %}
|
||||||
|
<h2 class="display-4 font-weight-light">
|
||||||
|
{% if quarter %}{{ year }} | Q{{ quarter }}
|
||||||
|
{% elif month %}{{ start_date | date:'F, Y' }}
|
||||||
|
{% else %}{% trans 'Fiscal Year' %} {{ year }}
|
||||||
|
{% endif %}</h2>
|
||||||
|
<h3 class="h4 font-italic font-weight-light">
|
||||||
|
{{ from_date | date:'m/d/Y' }} - {{ to_date | date:'m/d/Y' }}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% cash_flow_statement io_model=object %}
|
||||||
|
<a class="btn btn-primary w-100 my-2"
|
||||||
|
href="{{ request.path }}?format=pdf">{% trans 'Download PDF' %}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@ -16,10 +16,16 @@
|
|||||||
<!-- Year Navigation -->
|
<!-- Year Navigation -->
|
||||||
<div class="text-center mb-3">
|
<div class="text-center mb-3">
|
||||||
<p class="mb-1">
|
<p class="mb-1">
|
||||||
<span class="fw-bold">Year:</span>
|
<span class="fw-bold">Year: </span>
|
||||||
<a href="{{ previous_year_url }}" class="text-decoration-none me-2"><< {{ previous_year }}</a>
|
<a href="{{ previous_year_url }}" class="text-decoration-none me-2">
|
||||||
<a href="{{ current_year_url }}" class="text-decoration-none me-2">{{ year }}</a>
|
<span class="fas fa-chevron-left"> </span>
|
||||||
<a href="{{ next_year_url }}" class="text-decoration-none">{{ next_year }} >></a>
|
<span class="fas fa-chevron-left"> </span>
|
||||||
|
{{ previous_year }}</a>
|
||||||
|
<a href="{{ current_year_url }}" class="text-decoration-none me-2 fw-bolder">{{ year }}</a>
|
||||||
|
<a href="{{ next_year_url }}" class="text-decoration-none">{{ next_year }}
|
||||||
|
<span class="fas fa-chevron-right"> </span>
|
||||||
|
<span class="fas fa-chevron-right"> </span>
|
||||||
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -66,8 +72,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Date Picker -->
|
<!-- Date Picker -->
|
||||||
<div class="text-center">
|
|
||||||
{% date_picker date_navigation_url %}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
140
templates/ledger/reports/dashboard-copy.html
Normal file
140
templates/ledger/reports/dashboard-copy.html
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load static %}
|
||||||
|
{% load django_ledger %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div id="chart" class="echart-basic-bar-chart-example" style="min-height:300px"></div>
|
||||||
|
|
||||||
|
<section class="py-5">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-3 d-none d-lg-block">
|
||||||
|
<!-- Sidebar (if needed) -->
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-9">
|
||||||
|
<div class="row row-cols-1 row-cols-md-2 g-4">
|
||||||
|
<div class="col">
|
||||||
|
{% include 'django_ledger/includes/widget_bs.html' with tx_digest=tx_digest %}
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
{% include 'django_ledger/includes/widget_ic.html' with tx_digest=equity_digest %}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-body">
|
||||||
|
{% chart_container pnl_chart_id pnl_chart_endpoint %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
{% include 'django_ledger/includes/widget_ratios.html' with tx_digest=tx_digest %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{# RECEIVABLES SECTION START #}
|
||||||
|
<section class="bg-light py-5">
|
||||||
|
<div class="container">
|
||||||
|
<div class="text-center mb-5">
|
||||||
|
<h1 class="display-5 fw-bold">{% trans 'Receivables' %}</h1>
|
||||||
|
<p class="lead">
|
||||||
|
<i class="bi bi-building me-2"></i>{{ entity.name }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
|
||||||
|
{% if invoices %}
|
||||||
|
<div class="col">
|
||||||
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-body">
|
||||||
|
{% chart_container receivables_chart_id receivables_chart_endpoint %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% for invoice in invoices %}
|
||||||
|
<div class="col">
|
||||||
|
{% include 'django_ledger/invoice/includes/card_invoice.html' with invoice=invoice entity_slug=entity.slug style='dashboard' %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
<div class="col">
|
||||||
|
{% include 'django_ledger/invoice/includes/card_invoice.html' with create_invoice=True entity_slug=entity.slug %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{# RECEIVABLES SECTION END #}
|
||||||
|
|
||||||
|
{# PAYABLES SECTION START #}
|
||||||
|
<section class="bg-light py-5">
|
||||||
|
<div class="container">
|
||||||
|
<div class="text-center mb-5">
|
||||||
|
<h1 class="display-5 fw-bold">{% trans 'Payables' %}</h1>
|
||||||
|
<p class="lead">
|
||||||
|
<i class="bi bi-building me-2"></i>{{ entity.name }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
|
||||||
|
{% if bills %}
|
||||||
|
<div class="col">
|
||||||
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-body">
|
||||||
|
{% chart_container payables_chart_id payables_chart_endpoint %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% for bill in bills %}
|
||||||
|
<div class="col">
|
||||||
|
{% include 'django_ledger/bills/includes/card_bill.html' with bill=bill entity_slug=entity.slug style='dashboard' %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
<div class="col">
|
||||||
|
{% include 'django_ledger/bills/includes/card_bill.html' with create_bill=True entity_slug=entity.slug style='dashboard' %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{# PAYABLES SECTION END #}
|
||||||
|
{{payables_chart_endpoint}}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block customJS %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
// Initialize the echarts instance based on the prepared dom
|
||||||
|
let data = []
|
||||||
|
async function get_data(){
|
||||||
|
const response = await fetch("{{ rec }}");
|
||||||
|
data = await response.json();
|
||||||
|
console.log(data);
|
||||||
|
}
|
||||||
|
// Specify the configuration items and data for the chart
|
||||||
|
var myChart = echarts.init(document.getElementById('chart'));
|
||||||
|
var option = {
|
||||||
|
title: {
|
||||||
|
text: 'ECharts Getting Started Example'
|
||||||
|
},
|
||||||
|
tooltip: {},
|
||||||
|
legend: {
|
||||||
|
data: ['sales']
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
data: ['Shirts', 'Cardigans', 'Chiffons', 'Pants', 'Heels', 'Socks']
|
||||||
|
},
|
||||||
|
yAxis: {},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: 'sales',
|
||||||
|
type: 'bar',
|
||||||
|
data: [5, 20, 36, 10, 10, 20]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Display the chart using the configuration items and data just specified.
|
||||||
|
myChart.setOption(option);
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
525
templates/ledger/reports/dashboard.html
Normal file
525
templates/ledger/reports/dashboard.html
Normal file
@ -0,0 +1,525 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load static %}
|
||||||
|
{% load django_ledger %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<main class="main" id="top">
|
||||||
|
<div class="pb-6">
|
||||||
|
<div class="row align-items-center justify-content-between g-3 mb-6">
|
||||||
|
<div class="col-12 col-md-auto">
|
||||||
|
<h2 class="mb-0"></h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="px-3 mb-6">
|
||||||
|
<div class="row justify-content-between">
|
||||||
|
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-end-xxl-0 border-bottom-xxl-0 border-end border-bottom pb-4 pb-xxl-0 "><i class="fa-solid fa-landmark-dome fs-5 lh-1 text-primary"></i>
|
||||||
|
<h1 class="fs-5 pt-3">{% currency_symbol %}{{ tx_digest.group_balance.GROUP_ASSETS | currency_format }}</h1>
|
||||||
|
<p class="fs-9 mb-0">Assets</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-end-xxl-0 border-bottom-xxl-0 border-end-md border-bottom pb-4 pb-xxl-0"><i class="fa-solid fa-weight-hanging fs-5 lh-1 text-primary"></i>
|
||||||
|
<h1 class="fs-5 pt-3">{% currency_symbol %}{{ tx_digest.group_balance.GROUP_LIABILITIES | currency_format }}</h1>
|
||||||
|
<p class="fs-9 mb-0">Liabilities</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-bottom-xxl-0 border-bottom border-end border-end-md-0 pb-4 pb-xxl-0 pt-4 pt-md-0"><i class="fa-solid fa-scale-balanced fs-5 lh-1 text-primary"></i>
|
||||||
|
<h1 class="fs-5 pt-3">{% currency_symbol %}{{ tx_digest.group_balance.GROUP_EQUITY | currency_format }}</h1>
|
||||||
|
<p class="fs-9 mb-0">Equity</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-end-md border-end-xxl-0 border-bottom border-bottom-md-0 pb-4 pb-xxl-0 pt-4 pt-xxl-0"><i class="fa-solid fs-5 lh-1 text-success fa-money-bill"></i>
|
||||||
|
<h1 class="fs-5 pt-3">{% currency_symbol %}{{ tx_digest.role_balance.ASSET_CA_CASH | currency_format }}</h1>
|
||||||
|
<p class="fs-9 mb-0">Cash</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-auto">
|
||||||
|
<h2 class="mb-0"></h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="px-3 mb-6">
|
||||||
|
<div class="row justify-content-between">
|
||||||
|
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-end-xxl-0 border-bottom-xxl-0 border-end border-bottom pb-4 pb-xxl-0 "><i class="fa-solid fa-coins fs-5 lh-1 text-primary"></i>
|
||||||
|
<h1 class="fs-5 pt-3">{% currency_symbol %}{{ tx_digest.group_balance.GROUP_INCOME | currency_format }}</h1>
|
||||||
|
<p class="fs-9 mb-0">Revenue</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-end-xxl-0 border-bottom-xxl-0 border-end-md border-bottom pb-4 pb-xxl-0"><i class="fa-solid fa-hand-holding-dollar fs-5 lh-1 text-danger"></i>
|
||||||
|
<h1 class="fs-5 pt-3">{% currency_symbol %}{{ tx_digest.group_balance.GROUP_EXPENSES | reverse_sign | currency_format }}</h1>
|
||||||
|
<p class="fs-9 mb-0">Expenses</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-bottom-xxl-0 border-bottom border-end border-end-md-0 pb-4 pb-xxl-0 pt-4 pt-md-0"><i class="fa-solid fa-sack-dollar fs-5 lh-1 text-primary"></i>
|
||||||
|
<h1 class="fs-5 pt-3">{% currency_symbol %}{{ tx_digest.group_balance.GROUP_EARNINGS | currency_format }}</h1>
|
||||||
|
<p class="fs-9 mb-0">Earnings (Loss)</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-end-md border-end-xxl-0 border-bottom border-bottom-md-0 pb-4 pb-xxl-0 pt-4 pt-xxl-0"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mx-n4 px-4 mx-lg-n6 px-lg-6 bg-body-emphasis pt-6 pb-3 border-y">
|
||||||
|
<div class="row gx-6">
|
||||||
|
<div class="col-12 col-md-6 col-lg-12 col-xl-6 mb-5 mb-md-3 mb-lg-5 mb-xl-2 mb-xxl-3">
|
||||||
|
<div class="scrollbar">
|
||||||
|
<h3>Net Receivables</h3>
|
||||||
|
<p class="text-body-tertiary">According to the sales data.</p>
|
||||||
|
<div id="net_receivable_chart" class="echart-doughnut-rounded-chart-example" style="min-height:320px"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6 col-lg-12 col-xl-6 mb-1 mb-sm-0">
|
||||||
|
|
||||||
|
<div class="row g-3 align-items-center">
|
||||||
|
|
||||||
|
<div class="col-sm-4 col-md-12 col-lg-8 col-xl-12 col-xxl-12 d-flex justify-content-end-xxl mt-0">
|
||||||
|
<div class="d-flex flex-1 justify-content-center d-sm-block d-md-flex d-lg-block d-xl-flex d-xxl-block gap-3">
|
||||||
|
|
||||||
|
<div class="mb-4 me-6 me-sm-0 me-md-6 me-lg-0 me-xl-6 me-xxl-0">
|
||||||
|
<h3 class="">Financial Analysis</h3>
|
||||||
|
<h4>Solvency:</h4>
|
||||||
|
<h5 class="progress-bar">Current
|
||||||
|
Ratio: {{ tx_digest.ratios.current_ratio | currency_format }}</h5>
|
||||||
|
<div class="progress" style="height:12px">
|
||||||
|
<progress
|
||||||
|
class="progress-bar {{ tx_digest.ratios.current_ratio | fin_ratio_threshold_class:'current_ratio' }}"
|
||||||
|
value="{{ tx_digest.ratios.current_ratio }}"
|
||||||
|
max="{% fin_ratio_max_value ratio='current_ratio' %}">
|
||||||
|
{{ tx_digest.ratios.current_ratio }}</progress></div>
|
||||||
|
<h5 class="progress-bar">Quick
|
||||||
|
Ratio: {{ tx_digest.ratios.quick_ratio | currency_format }} </h5>
|
||||||
|
<div class="progress" style="height:12px">
|
||||||
|
<progress
|
||||||
|
class="progress-bar {{ tx_digest.ratios.quick_ratio | fin_ratio_threshold_class:'quick_ratio' }}"
|
||||||
|
value="{{ tx_digest.ratios.quick_ratio }}"
|
||||||
|
max="{% fin_ratio_max_value ratio='quick_ratio' %}">
|
||||||
|
{{ tx_digest.ratios.current_ratio }}</progress></div>
|
||||||
|
|
||||||
|
<h4>Leverage:</h4>
|
||||||
|
<h5 class="progress-bar">Debt to
|
||||||
|
Equity: {{ tx_digest.ratios.debt_to_equity | currency_format }} </h5>
|
||||||
|
<div class="progress" style="height:12px">
|
||||||
|
<progress
|
||||||
|
class="progress-bar {{ tx_digest.ratios.debt_to_equity | fin_ratio_threshold_class:'debt_to_equity' }}"
|
||||||
|
value="{{ tx_digest.ratios.debt_to_equity }}"
|
||||||
|
max="{% fin_ratio_max_value ratio='debt_to_equity' %}">
|
||||||
|
{{ tx_digest.ratios.debt_to_equity }}</progress></div>
|
||||||
|
|
||||||
|
|
||||||
|
<h4>Profitability:</h4>
|
||||||
|
<h5 class="progress-bar">Return on
|
||||||
|
Equity: {{ tx_digest.ratios.return_on_equity | percentage }} </h5>
|
||||||
|
<div class="progress" style="height:12px">
|
||||||
|
<progress
|
||||||
|
class="progress-bar bg-info {{ tx_digest.ratios.quick_ratio | fin_ratio_threshold_class:'quick_ratio' }}"
|
||||||
|
value="{{ tx_digest.ratios.return_on_equity }}"
|
||||||
|
max="{% fin_ratio_max_value ratio='return_on_equity' %}">
|
||||||
|
{{ tx_digest.ratios.return_on_equity }}</progress></div>
|
||||||
|
|
||||||
|
<h5 class="progress-bar">Return on
|
||||||
|
Assets: {{ tx_digest.ratios.return_on_assets | percentage }} </h5>
|
||||||
|
<div class="progress" style="height:12px">
|
||||||
|
<progress
|
||||||
|
class="progress-bar {{ tx_digest.ratios.return_on_assets | fin_ratio_threshold_class:'return_on_assets' }}"
|
||||||
|
value="{{ tx_digest.ratios.return_on_assets }}"
|
||||||
|
max="{% fin_ratio_max_value ratio='return_on_assets' %}">{{ tx_digest.ratios.return_on_assets }}</progress></div>
|
||||||
|
|
||||||
|
<h5 class="progress-bar">Net Profit
|
||||||
|
Margin: {{ tx_digest.ratios.net_profit_margin | percentage }} </h5>
|
||||||
|
<div class="progress" style="height:12px">
|
||||||
|
<progress
|
||||||
|
class="progress-bar {{ tx_digest.ratios.net_profit_margin | fin_ratio_threshold_class:'net_profit_margin' }}"
|
||||||
|
value="{{ tx_digest.ratios.net_profit_margin }}"
|
||||||
|
max="{% fin_ratio_max_value ratio='net_profit_margin' %}">{{ tx_digest.ratios.net_profit_margin }}</progress></div>
|
||||||
|
|
||||||
|
<h5 class="progress-bar">Gross Profit
|
||||||
|
Margin: {{ tx_digest.ratios.gross_profit_margin | percentage }} </h5>
|
||||||
|
<div class="progress" style="height:12px">
|
||||||
|
<progress
|
||||||
|
class="progress-bar {{ tx_digest.ratios.gross_profit_margin | fin_ratio_threshold_class:'gross_profit_margin' }}"
|
||||||
|
value="{{ tx_digest.ratios.gross_profit_margin }}"
|
||||||
|
max="{% fin_ratio_max_value ratio='gross_profit_margin' %}">{{ tx_digest.ratios.gross_profit_margin }}</progress></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row pt-6 gy-7 gx-6">
|
||||||
|
<div class="col-12 col-md-12">
|
||||||
|
<div class="row justify-content-between mb-4">
|
||||||
|
<div class="col-12">
|
||||||
|
<h3></h3>
|
||||||
|
<div class="col-6 col-md-3">
|
||||||
|
<div class="">
|
||||||
|
<label class="form-label" for="datepicker">Start Date</label>
|
||||||
|
<input id="startDate" class="form-control datetimepicker flatpickr-input" pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}" type="date" placeholder="dd/mm/yyyy" data-options="{"disableMobile":true,"dateFormat":"Y-m-d"}" readonly="readonly">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="form-label" for="datepicker">End Date</label>
|
||||||
|
<input id="endDate" class="form-control datetimepicker flatpickr-input" pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}" type="date" placeholder="dd/mm/yyyy" data-options="{"disableMobile":true,"dateFormat":"Y-m-d"}" readonly="readonly">
|
||||||
|
</div>
|
||||||
|
<button id="apply_filter" class="btn btn-primary mt-3" type="submit">Apply</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="net_receivable_chart1" class="echart-doughnut-rounded-chart-example col-12" style="min-height:450px"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer class="footer position-absolute">
|
||||||
|
<div class="row g-0 justify-content-between align-items-center h-100">
|
||||||
|
<div class="col-12 col-sm-auto text-center">
|
||||||
|
<p class="mb-0 mt-2 mt-sm-0 text-body">Thank you for creating with Phoenix<span class="d-none d-sm-inline-block"></span><span class="d-none d-sm-inline-block mx-1">|</span><br class="d-sm-none" />2024 ©<a class="mx-1" href="https://themewagon.com">Themewagon</a></p>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-sm-auto text-center">
|
||||||
|
<p class="mb-0 text-body-tertiary text-opacity-85">v1.20.1</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
<div class="modal fade" id="searchBoxModal" tabindex="-1" aria-hidden="true" data-bs-backdrop="true" data-phoenix-modal="data-phoenix-modal" style="--phoenix-backdrop-opacity: 1;">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content mt-15 rounded-pill">
|
||||||
|
<div class="modal-body p-0">
|
||||||
|
<div class="search-box navbar-top-search-box" data-list='{"valueNames":["title"]}' style="width: auto;">
|
||||||
|
<form class="position-relative" data-bs-toggle="search" data-bs-display="static">
|
||||||
|
<input class="form-control search-input fuzzy-search rounded-pill form-control-lg" type="search" placeholder="Search..." aria-label="Search" />
|
||||||
|
<span class="fas fa-search search-box-icon"></span>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
<div class="btn-close position-absolute end-0 top-50 translate-middle cursor-pointer shadow-none" data-bs-dismiss="search">
|
||||||
|
<button class="btn btn-link p-0" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="dropdown-menu border start-0 py-0 overflow-hidden w-100">
|
||||||
|
<div class="scrollbar-overlay" style="max-height: 30rem;">
|
||||||
|
<div class="list pb-3">
|
||||||
|
<h6 class="dropdown-header text-body-highlight fs-10 py-2">24 <span class="text-body-quaternary">results</span></h6>
|
||||||
|
<hr class="my-0" />
|
||||||
|
<h6 class="dropdown-header text-body-highlight fs-9 border-bottom border-translucent py-2 lh-sm">Recently Searched </h6>
|
||||||
|
<div class="py-2"><a class="dropdown-item" href="../../apps/e-commerce/landing/product-details.html">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
|
||||||
|
<div class="fw-normal text-body-highlight title"><span class="fa-solid fa-clock-rotate-left" data-fa-transform="shrink-2"></span> Store Macbook</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-item" href="../../apps/e-commerce/landing/product-details.html">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
|
||||||
|
<div class="fw-normal text-body-highlight title"> <span class="fa-solid fa-clock-rotate-left" data-fa-transform="shrink-2"></span> MacBook Air - 13″</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<hr class="my-0" />
|
||||||
|
<h6 class="dropdown-header text-body-highlight fs-9 border-bottom border-translucent py-2 lh-sm">Products</h6>
|
||||||
|
<div class="py-2"><a class="dropdown-item py-2 d-flex align-items-center" href="../../apps/e-commerce/landing/product-details.html">
|
||||||
|
<div class="file-thumbnail me-2"><img class="h-100 w-100 object-fit-cover rounded-3" src="../../assets/img/products/60x60/3.png" alt="" /></div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<h6 class="mb-0 text-body-highlight title">MacBook Air - 13″</h6>
|
||||||
|
<p class="fs-10 mb-0 d-flex text-body-tertiary"><span class="fw-medium text-body-tertiary text-opactity-85">8GB Memory - 1.6GHz - 128GB Storage</span></p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-item py-2 d-flex align-items-center" href="../../apps/e-commerce/landing/product-details.html">
|
||||||
|
<div class="file-thumbnail me-2"><img class="img-fluid" src="../../assets/img/products/60x60/3.png" alt="" /></div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<h6 class="mb-0 text-body-highlight title">MacBook Pro - 13″</h6>
|
||||||
|
<p class="fs-10 mb-0 d-flex text-body-tertiary"><span class="fw-medium text-body-tertiary text-opactity-85">30 Sep at 12:30 PM</span></p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<hr class="my-0" />
|
||||||
|
<h6 class="dropdown-header text-body-highlight fs-9 border-bottom border-translucent py-2 lh-sm">Quick Links</h6>
|
||||||
|
<div class="py-2"><a class="dropdown-item" href="../../apps/e-commerce/landing/product-details.html">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
|
||||||
|
<div class="fw-normal text-body-highlight title"><span class="fa-solid fa-link text-body" data-fa-transform="shrink-2"></span> Support MacBook House</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-item" href="../../apps/e-commerce/landing/product-details.html">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
|
||||||
|
<div class="fw-normal text-body-highlight title"> <span class="fa-solid fa-link text-body" data-fa-transform="shrink-2"></span> Store MacBook″</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<hr class="my-0" />
|
||||||
|
<h6 class="dropdown-header text-body-highlight fs-9 border-bottom border-translucent py-2 lh-sm">Files</h6>
|
||||||
|
<div class="py-2"><a class="dropdown-item" href="../../apps/e-commerce/landing/product-details.html">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
|
||||||
|
<div class="fw-normal text-body-highlight title"><span class="fa-solid fa-file-zipper text-body" data-fa-transform="shrink-2"></span> Library MacBook folder.rar</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-item" href="../../apps/e-commerce/landing/product-details.html">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
|
||||||
|
<div class="fw-normal text-body-highlight title"> <span class="fa-solid fa-file-lines text-body" data-fa-transform="shrink-2"></span> Feature MacBook extensions.txt</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-item" href="../../apps/e-commerce/landing/product-details.html">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
|
||||||
|
<div class="fw-normal text-body-highlight title"> <span class="fa-solid fa-image text-body" data-fa-transform="shrink-2"></span> MacBook Pro_13.jpg</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<hr class="my-0" />
|
||||||
|
<h6 class="dropdown-header text-body-highlight fs-9 border-bottom border-translucent py-2 lh-sm">Members</h6>
|
||||||
|
<div class="py-2"><a class="dropdown-item py-2 d-flex align-items-center" href="../../pages/members.html">
|
||||||
|
<div class="avatar avatar-l status-online me-2 text-body">
|
||||||
|
<img class="rounded-circle " src="../../assets/img/team/40x40/10.webp" alt="" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<h6 class="mb-0 text-body-highlight title">Carry Anna</h6>
|
||||||
|
<p class="fs-10 mb-0 d-flex text-body-tertiary">anna@technext.it</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-item py-2 d-flex align-items-center" href="../../pages/members.html">
|
||||||
|
<div class="avatar avatar-l me-2 text-body">
|
||||||
|
<img class="rounded-circle " src="../../assets/img/team/40x40/12.webp" alt="" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<h6 class="mb-0 text-body-highlight title">John Smith</h6>
|
||||||
|
<p class="fs-10 mb-0 d-flex text-body-tertiary">smith@technext.it</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<hr class="my-0" />
|
||||||
|
<h6 class="dropdown-header text-body-highlight fs-9 border-bottom border-translucent py-2 lh-sm">Related Searches</h6>
|
||||||
|
<div class="py-2"><a class="dropdown-item" href="../../apps/e-commerce/landing/product-details.html">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
|
||||||
|
<div class="fw-normal text-body-highlight title"><span class="fa-brands fa-firefox-browser text-body" data-fa-transform="shrink-2"></span> Search in the Web MacBook</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-item" href="../../apps/e-commerce/landing/product-details.html">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
|
||||||
|
<div class="fw-normal text-body-highlight title"> <span class="fa-brands fa-chrome text-body" data-fa-transform="shrink-2"></span> Store MacBook″</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-center">
|
||||||
|
<p class="fallback fw-bold fs-7 d-none">No Result Found.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
var navbarTopStyle = window.config.config.phoenixNavbarTopStyle;
|
||||||
|
var navbarTop = document.querySelector('.navbar-top');
|
||||||
|
if (navbarTopStyle === 'darker') {
|
||||||
|
navbarTop.setAttribute('data-navbar-appearance', 'darker');
|
||||||
|
}
|
||||||
|
|
||||||
|
var navbarVerticalStyle = window.config.config.phoenixNavbarVerticalStyle;
|
||||||
|
var navbarVertical = document.querySelector('.navbar-vertical');
|
||||||
|
if (navbarVertical && navbarVerticalStyle === 'darker') {
|
||||||
|
navbarVertical.setAttribute('data-navbar-appearance', 'darker');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<div class="support-chat-container">
|
||||||
|
<div class="container-fluid support-chat">
|
||||||
|
<div class="card bg-body-emphasis">
|
||||||
|
<div class="card-header d-flex flex-between-center px-4 py-3 border-bottom border-translucent">
|
||||||
|
<h5 class="mb-0 d-flex align-items-center gap-2">Demo widget<span class="fa-solid fa-circle text-success fs-11"></span></h5>
|
||||||
|
<div class="btn-reveal-trigger">
|
||||||
|
<button class="btn btn-link p-0 dropdown-toggle dropdown-caret-none transition-none d-flex" type="button" id="support-chat-dropdown" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h text-body"></span></button>
|
||||||
|
<div class="dropdown-menu dropdown-menu-end py-2" aria-labelledby="support-chat-dropdown"><a class="dropdown-item" href="#!">Request a callback</a><a class="dropdown-item" href="#!">Search in chat</a><a class="dropdown-item" href="#!">Show history</a><a class="dropdown-item" href="#!">Report to Admin</a><a class="dropdown-item btn-support-chat" href="#!">Close Support</a></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body chat p-0">
|
||||||
|
<div class="d-flex flex-column-reverse scrollbar h-100 p-3">
|
||||||
|
<div class="text-end mt-6"><a class="mb-2 d-inline-flex align-items-center text-decoration-none text-body-emphasis bg-body-hover rounded-pill border border-primary py-2 ps-4 pe-3" href="#!">
|
||||||
|
<p class="mb-0 fw-semibold fs-9">I need help with something</p><span class="fa-solid fa-paper-plane text-primary fs-9 ms-3"></span>
|
||||||
|
</a><a class="mb-2 d-inline-flex align-items-center text-decoration-none text-body-emphasis bg-body-hover rounded-pill border border-primary py-2 ps-4 pe-3" href="#!">
|
||||||
|
<p class="mb-0 fw-semibold fs-9">I can’t reorder a product I previously ordered</p><span class="fa-solid fa-paper-plane text-primary fs-9 ms-3"></span>
|
||||||
|
</a><a class="mb-2 d-inline-flex align-items-center text-decoration-none text-body-emphasis bg-body-hover rounded-pill border border-primary py-2 ps-4 pe-3" href="#!">
|
||||||
|
<p class="mb-0 fw-semibold fs-9">How do I place an order?</p><span class="fa-solid fa-paper-plane text-primary fs-9 ms-3"></span>
|
||||||
|
</a><a class="false d-inline-flex align-items-center text-decoration-none text-body-emphasis bg-body-hover rounded-pill border border-primary py-2 ps-4 pe-3" href="#!">
|
||||||
|
<p class="mb-0 fw-semibold fs-9">My payment method not working</p><span class="fa-solid fa-paper-plane text-primary fs-9 ms-3"></span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="text-center mt-auto">
|
||||||
|
<div class="avatar avatar-3xl status-online"><img class="rounded-circle border border-3 border-light-subtle" src="../../assets/img/team/30.webp" alt="" /></div>
|
||||||
|
<h5 class="mt-2 mb-3">Eric</h5>
|
||||||
|
<p class="text-center text-body-emphasis mb-0">Ask us anything – we’ll get back to you here or by email within 24 hours.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer d-flex align-items-center gap-2 border-top border-translucent ps-3 pe-4 py-3">
|
||||||
|
<div class="d-flex align-items-center flex-1 gap-3 border border-translucent rounded-pill px-4">
|
||||||
|
<input class="form-control outline-none border-0 flex-1 fs-9 px-0" type="text" placeholder="Write message" />
|
||||||
|
<label class="btn btn-link d-flex p-0 text-body-quaternary fs-9 border-0" for="supportChatPhotos"><span class="fa-solid fa-image"></span></label>
|
||||||
|
<input class="d-none" type="file" accept="image/*" id="supportChatPhotos" />
|
||||||
|
<label class="btn btn-link d-flex p-0 text-body-quaternary fs-9 border-0" for="supportChatAttachment"> <span class="fa-solid fa-paperclip"></span></label>
|
||||||
|
<input class="d-none" type="file" id="supportChatAttachment" />
|
||||||
|
</div>
|
||||||
|
<button class="btn p-0 border-0 send-btn"><span class="fa-solid fa-paper-plane fs-9"></span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-support-chat p-0 border border-translucent"><span class="fs-8 btn-text text-primary text-nowrap">Chat demo</span><span class="ping-icon-wrapper mt-n4 ms-n6 mt-sm-0 ms-sm-2 position-absolute position-sm-relative"><span class="ping-icon-bg"></span><span class="fa-solid fa-circle ping-icon"></span></span><span class="fa-solid fa-headset text-primary fs-8 d-sm-none"></span><span class="fa-solid fa-chevron-down text-primary fs-7"></span></button>
|
||||||
|
</main>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block customJS %}
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.6.0/echarts.min.js" integrity="sha512-XSmbX3mhrD2ix5fXPTRQb2FwK22sRMVQTpBP2ac8hX7Dh/605hA2QDegVWiAvZPiXIxOV0CbkmUjGionDpbCmw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
let myChart;
|
||||||
|
let myChart1;
|
||||||
|
|
||||||
|
function use_chart(type, chart_id, title, labels, values, update = false) {
|
||||||
|
if (chart_id === 'net_receivable_chart') {
|
||||||
|
if (!myChart) {
|
||||||
|
myChart = echarts.init(document.getElementById(chart_id));
|
||||||
|
}
|
||||||
|
const { getColor, getData } = window.phoenix.utils;
|
||||||
|
var option = {
|
||||||
|
title: {
|
||||||
|
text: title
|
||||||
|
},
|
||||||
|
tooltip: {},
|
||||||
|
legend: {
|
||||||
|
data: ['Net Receivables']
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
data: labels
|
||||||
|
},
|
||||||
|
yAxis: {},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: type,
|
||||||
|
radius: ['40%', '70%'],
|
||||||
|
center: window.innerWidth < 530 ? ['65%', '55%'] : ['50%', '55%'],
|
||||||
|
avoidLabelOverlap: false,
|
||||||
|
itemStyle: {
|
||||||
|
borderRadius: 10,
|
||||||
|
borderColor: getColor('body-highlight-bg'),
|
||||||
|
borderWidth: 2
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
|
position: 'center'
|
||||||
|
},
|
||||||
|
labelLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
data: values
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
if (update) {
|
||||||
|
myChart.setOption(option);
|
||||||
|
} else {
|
||||||
|
myChart.clear();
|
||||||
|
myChart.setOption(option);
|
||||||
|
}
|
||||||
|
} else if (chart_id === 'net_receivable_chart1') {
|
||||||
|
if (!myChart1) {
|
||||||
|
myChart1 = echarts.init(document.getElementById(chart_id));
|
||||||
|
}
|
||||||
|
const { getColor, getData } = window.phoenix.utils;
|
||||||
|
var option = {
|
||||||
|
title: {
|
||||||
|
text: title
|
||||||
|
},
|
||||||
|
tooltip: {},
|
||||||
|
legend: {
|
||||||
|
data: ['Net Receivables']
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
data: labels
|
||||||
|
},
|
||||||
|
yAxis: {},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: type,
|
||||||
|
data: values
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
if (update) {
|
||||||
|
myChart1.setOption(option);
|
||||||
|
} else {
|
||||||
|
myChart.clear();
|
||||||
|
myChart1.setOption(option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = []
|
||||||
|
const apply_filter = document.getElementById('apply_filter');
|
||||||
|
|
||||||
|
async function get_data(){
|
||||||
|
const response = await fetch("{% url 'entity-json-net-receivables' request.user.dealer.entity.slug %}");
|
||||||
|
data = await response.json();
|
||||||
|
const keys = Object.keys(data.results.net_receivable_data);
|
||||||
|
const labels = keys;
|
||||||
|
const values = keys.map(key => data.results.net_receivable_data[key]);
|
||||||
|
use_chart('pie','net_receivable_chart','Net Receivables',labels,values)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchData() {
|
||||||
|
const start_date = document.getElementById('startDate').value;
|
||||||
|
const end_date = document.getElementById('endDate').value;
|
||||||
|
let url = ''
|
||||||
|
if(!start_date || !end_date){
|
||||||
|
url = "{% url 'entity-json-pnl' request.user.dealer.entity.slug %}";
|
||||||
|
}else{
|
||||||
|
url = `{% url 'entity-json-pnl' request.user.dealer.entity.slug %}?fromDate=${start_date}&toDate=${end_date}`;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const response = await fetch(url);
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
const months = Object.keys(data.results.pnl_data);
|
||||||
|
const labels = Object.keys(data.results.pnl_data[months[0]]);
|
||||||
|
const values = labels.map(label => data.results.pnl_data[months[0]][label]);
|
||||||
|
|
||||||
|
use_chart('bar','net_receivable_chart1','Net Receivables',labels,values,true);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching data:", error);
|
||||||
|
dataContainer.innerHTML = "<p>Error fetching data.</p>";
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apply_filter.addEventListener('click', () => {
|
||||||
|
fetchData();
|
||||||
|
});
|
||||||
|
|
||||||
|
get_data()
|
||||||
|
fetchData()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
chart: {
|
||||||
|
type: 'line'
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
name: 'sales',
|
||||||
|
data: [30,40,35,50,49,60,70,91,125]
|
||||||
|
}],
|
||||||
|
xaxis: {
|
||||||
|
categories: [1991,1992,1993,1994,1995,1996,1997, 1998,1999]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var chart = new ApexCharts(document.querySelector("#chart"), options);
|
||||||
|
|
||||||
|
chart.render();
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
@ -5,36 +5,36 @@
|
|||||||
|
|
||||||
{% block period_navigation %}
|
{% block period_navigation %}
|
||||||
{% if unit_model %}
|
{% if unit_model %}
|
||||||
<div class="column is-12">{% period_navigation 'unit-ic' %}</div>
|
<div class="col-12">{% period_navigation 'unit-ic' %}</div>
|
||||||
{% elif entity %}
|
{% elif entity %}
|
||||||
<div class="column is-12">{% period_navigation 'entity-ic' %}</div>
|
<div class="col-12">{% period_navigation 'entity-ic' %}</div>
|
||||||
{% elif ledger %}
|
{% elif ledger %}
|
||||||
<div class="column is-12">{% period_navigation 'ledger-ic' %}</div>
|
<div class="col-12">{% period_navigation 'ledger-ic' %}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-content has-text-centered">
|
<div class="card-body text-center">
|
||||||
<div class="container mb-4">
|
<div class="container mb-4">
|
||||||
<div class="columns">
|
<div class="row">
|
||||||
<div class="column">
|
<div class="col">
|
||||||
{% if entity %}
|
{% if entity %}
|
||||||
<h1 class="is-size-2 has-text-weight-light">{{ entity.name }}</h1>
|
<h1 class="display-4 font-weight-light">{{ entity.name }}</h1>
|
||||||
{% elif ledger %}
|
{% elif ledger %}
|
||||||
<h1 class="is-size-2 has-text-weight-light">{{ ledger.name }}</h1>
|
<h1 class="display-4 font-weight-light">{{ ledger.name }}</h1>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if unit_model %}
|
{% if unit_model %}
|
||||||
<h3 class="is-size-4 has-text-weight-medium is-italic">{{ unit_model.name }} {% trans 'Unit' %}</h3>
|
<h3 class="h4 font-weight-medium font-italic">{{ unit_model.name }} {% trans 'Unit' %}</h3>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<h1 class="is-size-2 has-text-weight-bold">{% trans 'Income Statement' %}</h1>
|
<h1 class="display-4 font-weight-bold">{% trans 'Income Statement' %}</h1>
|
||||||
<h2 class="is-size-2 has-text-weight-light">
|
<h2 class="display-4 font-weight-light">
|
||||||
{% if quarter %}{{ year }} | Q{{ quarter }}
|
{% if quarter %}{{ year }} | Q{{ quarter }}
|
||||||
{% elif month %}{{ start_date | date:'F, Y' }}
|
{% elif month %}{{ start_date | date:'F, Y' }}
|
||||||
{% else %}Fiscal Year {{ year }}
|
{% else %}Fiscal Year {{ year }}
|
||||||
{% endif %}</h2>
|
{% endif %}</h2>
|
||||||
<h3 class="is-size-4 is-italic has-font-weight-light">
|
<h3 class="h4 font-italic font-weight-light">
|
||||||
{{ from_date | date:'m/d/Y' }} - {{ to_date | date:'m/d/Y' }}
|
{{ from_date | date:'m/d/Y' }} - {{ to_date | date:'m/d/Y' }}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
@ -42,22 +42,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% income_statement_table io_model=object %}
|
{% income_statement_table io_model=object %}
|
||||||
|
|
||||||
{% if ledger %}
|
<a class="btn btn-primary w-100 my-2"
|
||||||
<a class="button is-fullwidth is-dark my-2"
|
|
||||||
href="{% url 'django_ledger:ledger-list' entity_slug=view.kwargs.entity_slug %}">Go
|
|
||||||
Back</a>
|
|
||||||
{% elif entity %}
|
|
||||||
<a class="button is-fullwidth is-dark my-2"
|
|
||||||
href="{% url 'django_ledger:entity-dashboard' entity_slug=view.kwargs.entity_slug %}">Go
|
|
||||||
Back</a>
|
|
||||||
{% endif %}
|
|
||||||
<a class="button is-fullwidth is-light my-2"
|
|
||||||
href="?by_unit=1">{% trans 'By Unit' %}</a>
|
|
||||||
|
|
||||||
<a class="button is-fullwidth is-link my-2"
|
|
||||||
href="{{ request.path }}?format=pdf">{% trans 'Download PDF' %}</a>
|
href="{{ request.path }}?format=pdf">{% trans 'Download PDF' %}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -2,41 +2,27 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
<table class="table is-fullwidth is-narrow is-striped">
|
<table class="table is-fullwidth is-narrow is-striped has-text-centered">
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for bs_role, bs_role_data in tx_digest.balance_sheet.items %}
|
{% for bs_role, bs_role_data in tx_digest.balance_sheet.items %}
|
||||||
{% if bs_role_data.is_block %}
|
{% if bs_role_data.is_block %}
|
||||||
<tr>
|
<tr class="has-background-light">
|
||||||
<td><h2 class="is-size-3">{{ bs_role | upper }}</h2></td>
|
<td colspan="6" class="has-text-weight-bold is-size-3">{{ bs_role | upper }}</td>
|
||||||
<td></td>
|
|
||||||
{% if tx_digest.by_unit %}
|
|
||||||
<td></td>
|
|
||||||
{% endif %}
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr class="has-background-dark has-text-white">
|
||||||
<th class="has-text-centered">{% trans 'Account Code' %}</th>
|
<th>{% trans 'Account Code' %}</th>
|
||||||
<th class="has-text-centered">{% trans 'Account Name' %}</th>
|
<th>{% trans 'Account Name' %}</th>
|
||||||
{% if tx_digest.by_unit %}
|
{% if tx_digest.by_unit %}
|
||||||
<th class="has-text-centered">{% trans 'Unit' %}</th>
|
<th>{% trans 'Unit' %}</th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<th class="has-text-centered">{% trans 'Balance Type' %}</th>
|
<th>{% trans 'Balance Type' %}</th>
|
||||||
<th class="has-text-centered">{% trans 'Balance Through' %} {{ tx_digest.to_date | date }}</th>
|
<th>{% trans 'Balance Through' %} {{ tx_digest.to_date | date }}</th>
|
||||||
<th class="has-text-centered">{% trans 'Actions' %}</th>
|
<th>{% trans '' %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
{% for acc_role, acc_data in bs_role_data.roles.items %}
|
{% for acc_role, acc_data in bs_role_data.roles.items %}
|
||||||
<tr class="has-background-grey-light">
|
<tr class="has-background-grey-light has-text-weight-bold">
|
||||||
<td class="p-3 has-text-weight-bold">{{ acc_data.role_name | upper }}</td>
|
<td colspan="6">{{ acc_data.role_name | upper }}</td>
|
||||||
<td></td>
|
|
||||||
{% if tx_digest.by_unit %}
|
|
||||||
<td></td>
|
|
||||||
{% endif %}
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
{% for acc in acc_data.accounts %}
|
{% for acc in acc_data.accounts %}
|
||||||
@ -44,30 +30,11 @@
|
|||||||
<td>{{ acc.code }}</td>
|
<td>{{ acc.code }}</td>
|
||||||
<td class="has-text-left">{{ acc.name }}</td>
|
<td class="has-text-left">{{ acc.name }}</td>
|
||||||
{% if tx_digest.by_unit %}
|
{% if tx_digest.by_unit %}
|
||||||
<td>{% if acc.unit_name %}{{ acc.unit_name }}{% endif %}</td>
|
<td>{{ acc.unit_name|default:"" }}</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<td class="has-text-centered">{{ acc.balance_type.0 | upper }}</td>
|
<td>{{ acc.balance_type.0 | upper }}</td>
|
||||||
<td class="has-text-right">{% currency_symbol %}{{ acc.balance | currency_format }}</td>
|
<td class="has-text-right">{% currency_symbol %}{{ acc.balance | currency_format }}</td>
|
||||||
<td>
|
<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>
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
@ -78,63 +45,58 @@
|
|||||||
{% if tx_digest.by_unit %}
|
{% if tx_digest.by_unit %}
|
||||||
<td></td>
|
<td></td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<td class="has-text-right">
|
<td class="has-text-right">{% currency_symbol %}{{ acc_data.total_balance | currency_format }}</td>
|
||||||
{% currency_symbol %}{{ acc_data.total_balance | currency_format }}</td>
|
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
|
||||||
{% if bs_role != 'equity' %}
|
{% if bs_role != 'equity' %}
|
||||||
<tr class="has-text-weight-bold is-size-5">
|
<tr class="has-text-weight-bold is-size-5 has-background-light">
|
||||||
<td>{% trans 'Total' %} {{ bs_role | upper }}</td>
|
<td>{% trans 'Total' %} {{ bs_role | upper }}</td>
|
||||||
<td></td>
|
|
||||||
{% if tx_digest.by_unit %}
|
|
||||||
<td></td>
|
<td></td>
|
||||||
{% endif %}
|
{% if tx_digest.by_unit %}
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
{% endif %}
|
||||||
<td>{% currency_symbol %}{{ bs_role_data.total_balance | currency_format }}</td>
|
<td></td>
|
||||||
|
<td class="has-text-right">{% currency_symbol %}{{ bs_role_data.total_balance | currency_format }}</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
<tr class="has-text-weight-bold is-size-5 has-background-grey-lighter">
|
||||||
<tr class="has-text-weight-bold is-size-5">
|
|
||||||
<td>{% trans 'Retained Earnings' %}</td>
|
<td>{% trans 'Retained Earnings' %}</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
{% if tx_digest.by_unit %}
|
{% if tx_digest.by_unit %}
|
||||||
<td></td>
|
<td></td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<td></td>
|
<td></td>
|
||||||
|
<td class="has-text-right">{% currency_symbol %}{{ tx_digest.group_balance.GROUP_EARNINGS | currency_format }}</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td>{% currency_symbol %}{{ tx_digest.group_balance.GROUP_EARNINGS | currency_format }}</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr class="has-text-weight-bold is-size-5">
|
<tr class="has-text-weight-bold is-size-5 has-background-light">
|
||||||
<td>{% trans 'Total EQUITY' %}</td>
|
<td>{% trans 'Total EQUITY' %}</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
{% if tx_digest.by_unit %}
|
{% if tx_digest.by_unit %}
|
||||||
<td></td>
|
<td></td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<td></td>
|
<td></td>
|
||||||
|
<td class="has-text-right">{% currency_symbol %}{{ tx_digest.group_balance.GROUP_EQUITY | currency_format }}</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td>{% currency_symbol %}{{ tx_digest.group_balance.GROUP_EQUITY | currency_format }}</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="has-text-weight-bold is-size-5">
|
|
||||||
|
<tr class="has-text-weight-bold is-size-5 has-background-dark has-text-white">
|
||||||
<td>{% trans 'Total Equity + Liabilities' %}</td>
|
<td>{% trans 'Total Equity + Liabilities' %}</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
{% if tx_digest.by_unit %}
|
{% if tx_digest.by_unit %}
|
||||||
<td></td>
|
<td></td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<td></td>
|
<td></td>
|
||||||
|
<td class="has-text-right">{% currency_symbol %}{{ tx_digest.group_balance.GROUP_LIABILITIES_EQUITY | currency_format }}</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td>{% currency_symbol %}{{ tx_digest.group_balance.GROUP_LIABILITIES_EQUITY | currency_format }}</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
362
templates/ledger/reports/tags/income_statement.html
Normal file
362
templates/ledger/reports/tags/income_statement.html
Normal file
@ -0,0 +1,362 @@
|
|||||||
|
{% load django_ledger %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
|
||||||
|
<div class="table-container">
|
||||||
|
<table class="table is-fullwidth is-narrow is-striped">
|
||||||
|
<tr>
|
||||||
|
<th>{% trans 'Account Number' %}</th>
|
||||||
|
<th>{% trans 'Description' %}</th>
|
||||||
|
{% if tx_digest.by_unit %}
|
||||||
|
<th>{% trans 'Unit' %}</th>
|
||||||
|
{% endif %}
|
||||||
|
<th>{% trans 'Balance Type' %}</th>
|
||||||
|
<th>{% trans 'Balance' %}</th>
|
||||||
|
<th>{% trans '' %}</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{# OPERATING INCOME #}
|
||||||
|
<tr>
|
||||||
|
<td><h2 class="is-size-3 has-text-left">{% trans 'Operating Revenues' %}</h2></td>
|
||||||
|
<td></td>
|
||||||
|
{% if tx_digest.by_unit %}
|
||||||
|
<td></td>
|
||||||
|
{% endif %}
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{% for acc in tx_digest.income_statement.operating.revenues %}
|
||||||
|
<tr>
|
||||||
|
<td class="has-text-right">{{ acc.code }}</td>
|
||||||
|
<td>{{ acc.name }}</td>
|
||||||
|
{% if tx_digest.by_unit %}
|
||||||
|
<td>{% if acc.unit_name %}{{ acc.unit_name }}{% endif %}</td>
|
||||||
|
{% endif %}
|
||||||
|
<td class="has-text-centered">
|
||||||
|
{% if acc.balance_type == 'debit' %}
|
||||||
|
<span class="icon">{% icon 'bi:arrow-bar-down' 24 %}</span>
|
||||||
|
{% elif acc.balance_type == 'credit' %}
|
||||||
|
<span class="icon">{% icon 'bi:arrow-bar-up' 24 %}</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>{% currency_symbol %}{{ acc.balance | currency_format }}</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
{% if tx_digest.by_unit %}
|
||||||
|
<td></td>
|
||||||
|
{% endif %}
|
||||||
|
<td></td>
|
||||||
|
<td><h2 class="is-size-5 has-text-right">{% trans 'Net Operating Revenues' %}</h2></td>
|
||||||
|
<td class="is-size-5 has-text-weight-bold">
|
||||||
|
{% currency_symbol %}{{ tx_digest.income_statement.operating.net_operating_revenue | currency_format }}</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{# COGS #}
|
||||||
|
<tr>
|
||||||
|
<td><h2 class="is-size-3 has-text-left">{% trans 'Less: Cost of Goods Sold' %}</h2></td>
|
||||||
|
<td></td>
|
||||||
|
{% if tx_digest.by_unit %}
|
||||||
|
<td></td>
|
||||||
|
{% endif %}
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
{% for acc in tx_digest.income_statement.operating.cogs %}
|
||||||
|
<tr>
|
||||||
|
<td class="has-text-right">{{ acc.code }}</td>
|
||||||
|
<td>{{ acc.name }}</td>
|
||||||
|
{% if tx_digest.by_unit %}
|
||||||
|
<td>{% if acc.unit_name %}{{ acc.unit_name }}{% endif %}</td>
|
||||||
|
{% endif %}
|
||||||
|
<td class="has-text-centered">
|
||||||
|
{% if acc.balance_type == 'debit' %}
|
||||||
|
<span class="icon">{% icon 'bi:arrow-bar-down' 24 %}</span>
|
||||||
|
{% elif acc.balance_type == 'credit' %}
|
||||||
|
<span class="icon">{% icon 'bi:arrow-bar-up' 24 %}</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>{% currency_symbol %}{{ acc.balance | reverse_sign | 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>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
{% if tx_digest.by_unit %}
|
||||||
|
<td></td>
|
||||||
|
{% endif %}
|
||||||
|
<td></td>
|
||||||
|
<td><h2 class="is-size-5 has-text-right">{% trans 'Net COGS' %}</h2></td>
|
||||||
|
<td class="is-size-5 has-text-weight-bold">
|
||||||
|
{% currency_symbol %}{{ tx_digest.income_statement.operating.net_cogs | currency_format }}</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{# GROSS PROFIT #}
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
{% if tx_digest.by_unit %}
|
||||||
|
<td></td>
|
||||||
|
{% endif %}
|
||||||
|
<td></td>
|
||||||
|
<td><h2 class="is-size-4 has-text-right">{% trans 'Gross Profit' %}</h2></td>
|
||||||
|
<td class="is-size-4 has-text-weight-bold">
|
||||||
|
{% currency_symbol %}{{ tx_digest.income_statement.operating.gross_profit | currency_format }}</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{# OPERATING EXPENSES #}
|
||||||
|
<tr>
|
||||||
|
<td><h2 class="is-size-3 has-text-left">{% trans 'Operating Expenses' %}</h2></td>
|
||||||
|
<td></td>
|
||||||
|
{% if tx_digest.by_unit %}
|
||||||
|
<td></td>
|
||||||
|
{% endif %}
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
{% for acc in tx_digest.income_statement.operating.expenses %}
|
||||||
|
<tr>
|
||||||
|
<td class="has-text-right">{{ acc.code }}</td>
|
||||||
|
<td>{{ acc.name }}</td>
|
||||||
|
{% if tx_digest.by_unit %}
|
||||||
|
<td>{% if acc.unit_name %}{{ acc.unit_name }}{% endif %}</td>
|
||||||
|
{% endif %}
|
||||||
|
<td class="has-text-centered">
|
||||||
|
{% if acc.balance_type == 'debit' %}
|
||||||
|
<span class="icon">{% icon 'bi:arrow-bar-down' 24 %}</span>
|
||||||
|
{% elif acc.balance_type == 'credit' %}
|
||||||
|
<span class="icon">{% icon 'bi:arrow-bar-up' 24 %}</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>{% currency_symbol %}{{ acc.balance | reverse_sign | 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>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
{% if tx_digest.by_unit %}
|
||||||
|
<td></td>
|
||||||
|
{% endif %}
|
||||||
|
<td></td>
|
||||||
|
<td><h2 class="is-size-5 has-text-right">{% trans 'Net Operating Expenses' %}</h2></td>
|
||||||
|
<td class="is-size-5 has-text-weight-bold">
|
||||||
|
{% currency_symbol %}{{ tx_digest.income_statement.operating.net_operating_expenses | reverse_sign | currency_format }}</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
|
{# NET OPERATING INCOME #}
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
{% if tx_digest.by_unit %}
|
||||||
|
<td></td>
|
||||||
|
{% endif %}
|
||||||
|
<td></td>
|
||||||
|
<td><h2 class="is-size-4 has-text-right">{% trans 'Net Operating Income (Loss)' %}</h2></td>
|
||||||
|
<td class="is-size-3">
|
||||||
|
{% currency_symbol %}{{ tx_digest.income_statement.operating.net_operating_income| currency_format }}</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{# OTHER REVENUES #}
|
||||||
|
<tr>
|
||||||
|
<td><h2 class="is-size-3 has-text-left">{% trans 'Other Revenues' %}</h2></td>
|
||||||
|
<td></td>
|
||||||
|
{% if tx_digest.by_unit %}
|
||||||
|
<td></td>
|
||||||
|
{% endif %}
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
{% for acc in tx_digest.income_statement.other.revenues %}
|
||||||
|
<tr>
|
||||||
|
<td class="has-text-right">{{ acc.code }}</td>
|
||||||
|
<td>{{ acc.name }}</td>
|
||||||
|
{% if tx_digest.by_unit %}
|
||||||
|
<td>{% if acc.unit_name %}{{ acc.unit_name }}{% endif %}</td>
|
||||||
|
{% endif %}
|
||||||
|
<td class="has-text-centered">
|
||||||
|
{% if acc.balance_type == 'debit' %}
|
||||||
|
<span class="icon">{% icon 'bi:arrow-bar-down' 24 %}</span>
|
||||||
|
{% elif acc.balance_type == 'credit' %}
|
||||||
|
<span class="icon">{% icon 'bi:arrow-bar-up' 24 %}</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td class="is-size-5">{% 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>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
{% if tx_digest.by_unit %}
|
||||||
|
<td></td>
|
||||||
|
{% endif %}
|
||||||
|
<td></td>
|
||||||
|
<td><h2 class="is-size-5 has-text-right">{% trans 'Net Other Revenues' %}</h2></td>
|
||||||
|
<td class="is-size-5 has-text-weight-bold">
|
||||||
|
{% currency_symbol %}{{ tx_digest.income_statement.other.net_other_revenues | currency_format }}</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
|
{# OTHER EXPENSES #}
|
||||||
|
<tr>
|
||||||
|
<td><h2 class="is-size-3 has-text-left">{% trans 'Other Expenses' %}</h2></td>
|
||||||
|
<td></td>
|
||||||
|
{% if tx_digest.by_unit %}
|
||||||
|
<td></td>
|
||||||
|
{% endif %}
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
{% for acc in tx_digest.income_statement.other.expenses %}
|
||||||
|
<tr>
|
||||||
|
<td class="has-text-right">{{ acc.code }}</td>
|
||||||
|
<td>{{ acc.name }}</td>
|
||||||
|
{% if tx_digest.by_unit %}
|
||||||
|
<td>{% if acc.unit_name %}{{ acc.unit_name }}{% endif %}</td>
|
||||||
|
{% endif %}
|
||||||
|
<td class="has-text-centered">
|
||||||
|
{% if acc.balance_type == 'debit' %}
|
||||||
|
<span class="icon">{% icon 'bi:arrow-bar-down' 24 %}</span>
|
||||||
|
{% elif acc.balance_type == 'credit' %}
|
||||||
|
<span class="icon">{% icon 'bi:arrow-bar-up' 24 %}</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td class="is-size-5">{% currency_symbol %}{{ acc.balance | reverse_sign | 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>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
{% if tx_digest.by_unit %}
|
||||||
|
<td></td>
|
||||||
|
{% endif %}
|
||||||
|
<td></td>
|
||||||
|
<td><h2 class="is-size-5 has-text-right">{% trans 'Net Other Expenses' %}</h2></td>
|
||||||
|
<td class="is-size-5 has-text-weight-bold">
|
||||||
|
{% currency_symbol %}{{ tx_digest.income_statement.other.net_other_expenses | currency_format }}</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{# NET OTHER INCOME/LOSS #}
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
{% if tx_digest.by_unit %}
|
||||||
|
<td></td>
|
||||||
|
{% endif %}
|
||||||
|
<td></td>
|
||||||
|
<td><h2 class="is-size-4 has-text-right">{% trans 'Net Other Income (Loss)' %}</h2></td>
|
||||||
|
<td class="is-size-3">
|
||||||
|
{% currency_symbol %}{{ tx_digest.income_statement.other.net_other_income | currency_format }}</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
|
{# NET INCOME #}
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
{% if tx_digest.by_unit %}
|
||||||
|
<td></td>
|
||||||
|
{% endif %}
|
||||||
|
<td>
|
||||||
|
<h4 class="is-size-4 has-text-right">{{ tx_digest.from_date | date }} {% trans 'through' %} {{ tx_digest.to_date | date }}</h4>
|
||||||
|
</td>
|
||||||
|
<td><h2 class="is-size-3 has-text-right">{% trans 'Net Income' %}</h2></td>
|
||||||
|
<td class="is-size-3 has-text-weight-bold">
|
||||||
|
{% currency_symbol %}{{ tx_digest.income_statement.net_income| currency_format }}</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
@ -29,9 +29,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- ============================================-->
|
|
||||||
<!-- ============================================-->
|
|
||||||
<!-- ============================================-->
|
<!-- ============================================-->
|
||||||
<!-- <section> begin ============================-->
|
<!-- <section> begin ============================-->
|
||||||
<section class="pt-5 pb-9 bg-body-emphasis dark__bg-gray-1200 border-top">
|
<section class="pt-5 pb-9 bg-body-emphasis dark__bg-gray-1200 border-top">
|
||||||
@ -154,21 +151,20 @@
|
|||||||
<tr class="bg-body-secondary total-sum">
|
<tr class="bg-body-secondary total-sum">
|
||||||
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="4">{% trans "Additional Services" %}</td>
|
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="4">{% trans "Additional Services" %}</td>
|
||||||
<td class="align-middle text-start fw-semibold">
|
<td class="align-middle text-start fw-semibold">
|
||||||
{% for service in data.additional_services %}
|
{% for service in data.additionals %}
|
||||||
<small><span class="fw-semibold">+ {{service.name}} - {{service.price}}</span></small><br>
|
<small><span class="fw-semibold">+ {{service.name}} - {{service.total}}</span></small><br>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="bg-body-secondary total-sum">
|
<tr class="bg-body-secondary total-sum">
|
||||||
<td class="align-middle ps-4 fw-bolder text-body-highlight" colspan="4">{% trans "Grand Total" %}</td>
|
<td class="align-middle ps-4 fw-bolder text-body-highlight" colspan="4">{% trans "Grand Total" %}</td>
|
||||||
<td class="align-middle text-start fw-bolder">
|
<td class="align-middle text-start fw-bolder">
|
||||||
<span id="grand-total">{{data.grand_total}}</span>
|
<span id="grand-total">{{data.grand_total}}</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- end of .row-->
|
<!-- end of .row-->
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user