added test + expenses + some changes
This commit is contained in:
parent
9ccb432d19
commit
cc96947d18
@ -73,23 +73,6 @@ def create_car_location(sender, instance, created, **kwargs):
|
||||
except Exception as e:
|
||||
print(f"Failed to create CarLocation for car {instance.vin}: {e}")
|
||||
|
||||
|
||||
# @receiver(post_save, sender=models.CarReservation)
|
||||
# def update_car_status_on_reservation(sender, instance, created, **kwargs):
|
||||
# if created:
|
||||
# car = instance.car
|
||||
# car.status = models.CarStatusChoices.RESERVED
|
||||
# car.save()
|
||||
|
||||
|
||||
# @receiver(post_delete, sender=models.CarReservation)
|
||||
# def update_car_status_on_reservation_delete(sender, instance, **kwargs):
|
||||
# car = instance.car
|
||||
# if not car.is_reserved():
|
||||
# car.status = models.CarStatusChoices.AVAILABLE
|
||||
# car.save()
|
||||
|
||||
|
||||
# Create Entity
|
||||
@receiver(post_save, sender=models.Dealer)
|
||||
def create_ledger_entity(sender, instance, created, **kwargs):
|
||||
@ -108,16 +91,13 @@ def create_ledger_entity(sender, instance, created, **kwargs):
|
||||
coa = entity.create_chart_of_accounts(
|
||||
assign_as_default=True, commit=True, coa_name=_(f"{entity_name}-COA")
|
||||
)
|
||||
if coa:
|
||||
# entity.populate_default_coa(activate_accounts=True, coa_model=coa)
|
||||
print(f"Ledger entity created for Dealer: {instance.name}")
|
||||
|
||||
if coa:
|
||||
# Create unit of measures
|
||||
entity.create_uom(name="Unit", unit_abbr="unit")
|
||||
for u in models.UnitOfMeasure.choices:
|
||||
entity.create_uom(name=u[1], unit_abbr=u[0])
|
||||
|
||||
# Create Cash Account
|
||||
# Cash Account
|
||||
asset_ca_cash = entity.create_account(
|
||||
coa_model=coa,
|
||||
code="1010",
|
||||
@ -128,7 +108,8 @@ def create_ledger_entity(sender, instance, created, **kwargs):
|
||||
)
|
||||
asset_ca_cash.role_default = True
|
||||
asset_ca_cash.save()
|
||||
# Create Accounts Receivable Account
|
||||
|
||||
# Accounts Receivable Account
|
||||
asset_ca_receivables = entity.create_account(
|
||||
coa_model=coa,
|
||||
code="1102",
|
||||
@ -140,7 +121,7 @@ def create_ledger_entity(sender, instance, created, **kwargs):
|
||||
asset_ca_receivables.role_default = True
|
||||
asset_ca_receivables.save()
|
||||
|
||||
# Create Inventory Account
|
||||
# Inventory Account
|
||||
asset_ca_inventory = entity.create_account(
|
||||
coa_model=coa,
|
||||
code="1103",
|
||||
@ -149,11 +130,71 @@ def create_ledger_entity(sender, instance, created, **kwargs):
|
||||
balance_type="debit",
|
||||
active=True,
|
||||
)
|
||||
|
||||
asset_ca_inventory.role_default = True
|
||||
asset_ca_inventory.save()
|
||||
# Create Accounts Payable Account
|
||||
asset_ca_accounts_payable = entity.create_account(
|
||||
|
||||
# Prepaid Expenses Account
|
||||
asset_ca_prepaid = entity.create_account(
|
||||
coa_model=coa,
|
||||
code="1104",
|
||||
role=roles.ASSET_CA_PREPAID,
|
||||
name=_("Prepaid Expenses"),
|
||||
balance_type="debit",
|
||||
active=True,
|
||||
)
|
||||
asset_ca_prepaid.role_default = True
|
||||
asset_ca_prepaid.save()
|
||||
|
||||
# Notes Receivable Account
|
||||
asset_lti_notes_receivable = entity.create_account(
|
||||
coa_model=coa,
|
||||
code="1201",
|
||||
role=roles.ASSET_LTI_NOTES_RECEIVABLE,
|
||||
name=_("Notes Receivable"),
|
||||
balance_type="debit",
|
||||
active=True,
|
||||
)
|
||||
asset_lti_notes_receivable.role_default = True
|
||||
asset_lti_notes_receivable.save()
|
||||
|
||||
# Land Account
|
||||
asset_lti_land = entity.create_account(
|
||||
coa_model=coa,
|
||||
code="1202",
|
||||
role=roles.ASSET_LTI_LAND,
|
||||
name=_("Land"),
|
||||
balance_type="debit",
|
||||
active=True,
|
||||
)
|
||||
asset_lti_land.role_default = True
|
||||
asset_lti_land.save()
|
||||
|
||||
# Buildings Account
|
||||
asset_ppe_buildings = entity.create_account(
|
||||
coa_model=coa,
|
||||
code="1301",
|
||||
role=roles.ASSET_PPE_BUILDINGS,
|
||||
name=_("Buildings"),
|
||||
balance_type="debit",
|
||||
active=True,
|
||||
)
|
||||
asset_ppe_buildings.role_default = True
|
||||
asset_ppe_buildings.save()
|
||||
|
||||
# Buildings Accumulated Depreciation Account
|
||||
asset_ppe_buildings_accum_depreciation = entity.create_account(
|
||||
coa_model=coa,
|
||||
code="1302",
|
||||
role=roles.ASSET_PPE_BUILDINGS_ACCUM_DEPRECIATION,
|
||||
name=_("Buildings - Accum. Depreciation"),
|
||||
balance_type="credit",
|
||||
active=True,
|
||||
)
|
||||
asset_ppe_buildings_accum_depreciation.role_default = True
|
||||
asset_ppe_buildings_accum_depreciation.save()
|
||||
|
||||
# Accounts Payable Account
|
||||
liability_cl_acc_payable = entity.create_account(
|
||||
coa_model=coa,
|
||||
code="2101",
|
||||
role=roles.LIABILITY_CL_ACC_PAYABLE,
|
||||
@ -161,33 +202,83 @@ def create_ledger_entity(sender, instance, created, **kwargs):
|
||||
balance_type="credit",
|
||||
active=True,
|
||||
)
|
||||
liability_cl_acc_payable.role_default = True
|
||||
liability_cl_acc_payable.save()
|
||||
|
||||
# add Bank
|
||||
|
||||
# asset_ca_accounts_payable = entity.create_account(
|
||||
# coa_model=coa,
|
||||
# code="2101",
|
||||
# role=roles.LIABILITY_CL_ACC_PAYABLE,
|
||||
# name=_("Accounts Payable"),
|
||||
# balance_type="credit",
|
||||
# active=True,
|
||||
# )
|
||||
asset_ca_accounts_payable.role_default = True
|
||||
asset_ca_accounts_payable.save()
|
||||
# Create Equity Account
|
||||
asset_ca_equity = entity.create_account(
|
||||
# Deferred Revenue Account
|
||||
liability_cl_def_rev = entity.create_account(
|
||||
coa_model=coa,
|
||||
code="3101",
|
||||
role=roles.EQUITY_CAPITAL,
|
||||
name=_("Partners Current"),
|
||||
code="2103",
|
||||
role=roles.LIABILITY_CL_DEFERRED_REVENUE,
|
||||
name=_("Deferred Revenue"),
|
||||
balance_type="credit",
|
||||
active=True,
|
||||
)
|
||||
asset_ca_equity.role_default = True
|
||||
asset_ca_equity.save()
|
||||
liability_cl_def_rev.role_default = True
|
||||
liability_cl_def_rev.save()
|
||||
|
||||
# Create Sales Revenue Account
|
||||
asset_ca_revenue = entity.create_account(
|
||||
# Wages Payable Account
|
||||
liability_cl_wages_payable = entity.create_account(
|
||||
coa_model=coa,
|
||||
code="2102",
|
||||
role=roles.LIABILITY_CL_WAGES_PAYABLE,
|
||||
name=_("Wages Payable"),
|
||||
balance_type="credit",
|
||||
active=True,
|
||||
)
|
||||
liability_cl_wages_payable.role_default = True
|
||||
liability_cl_wages_payable.save()
|
||||
|
||||
# Long-Term Notes Payable Account
|
||||
liability_ltl_notes_payable = entity.create_account(
|
||||
coa_model=coa,
|
||||
code="2201",
|
||||
role=roles.LIABILITY_LTL_NOTES_PAYABLE,
|
||||
name=_("Long-Term Notes Payable"),
|
||||
balance_type="credit",
|
||||
active=True,
|
||||
)
|
||||
liability_ltl_notes_payable.role_default = True
|
||||
liability_ltl_notes_payable.save()
|
||||
|
||||
# Mortgage Payable Account
|
||||
liability_ltl_mortgage_payable = entity.create_account(
|
||||
coa_model=coa,
|
||||
code="2202",
|
||||
role=roles.LIABILITY_LTL_MORTGAGE_PAYABLE,
|
||||
name=_("Mortgage Payable"),
|
||||
balance_type="credit",
|
||||
active=True,
|
||||
)
|
||||
liability_ltl_mortgage_payable.role_default = True
|
||||
liability_ltl_mortgage_payable.save()
|
||||
|
||||
# Common Stock Account
|
||||
equity_common_stock = entity.create_account(
|
||||
coa_model=coa,
|
||||
code="3101",
|
||||
role=roles.EQUITY_COMMON_STOCK,
|
||||
name=_("Common Stock"),
|
||||
balance_type="credit",
|
||||
active=True,
|
||||
)
|
||||
equity_common_stock.role_default = True
|
||||
equity_common_stock.save()
|
||||
|
||||
# Retained Earnings Account
|
||||
equity_retained_earnings = entity.create_account(
|
||||
coa_model=coa,
|
||||
code="3102",
|
||||
role=roles.EQUITY_ADJUSTMENT,
|
||||
name=_("Retained Earnings"),
|
||||
balance_type="credit",
|
||||
active=True,
|
||||
)
|
||||
equity_retained_earnings.role_default = True
|
||||
equity_retained_earnings.save()
|
||||
|
||||
# Sales Revenue Account
|
||||
income_operational = entity.create_account(
|
||||
coa_model=coa,
|
||||
code="4101",
|
||||
role=roles.INCOME_OPERATIONAL,
|
||||
@ -195,11 +286,23 @@ def create_ledger_entity(sender, instance, created, **kwargs):
|
||||
balance_type="credit",
|
||||
active=True,
|
||||
)
|
||||
asset_ca_revenue.role_default = True
|
||||
asset_ca_revenue.save()
|
||||
income_operational.role_default = True
|
||||
income_operational.save()
|
||||
|
||||
# Create Cost of Goods Sold Account
|
||||
asset_ca_cogs = entity.create_account(
|
||||
# Interest Income Account
|
||||
income_interest = entity.create_account(
|
||||
coa_model=coa,
|
||||
code="4102",
|
||||
role=roles.INCOME_INTEREST,
|
||||
name=_("Interest Income"),
|
||||
balance_type="credit",
|
||||
active=True,
|
||||
)
|
||||
income_interest.role_default = True
|
||||
income_interest.save()
|
||||
|
||||
# Cost of Goods Sold (COGS) Account
|
||||
expense_cogs = entity.create_account(
|
||||
coa_model=coa,
|
||||
code="5101",
|
||||
role=roles.COGS,
|
||||
@ -207,42 +310,20 @@ def create_ledger_entity(sender, instance, created, **kwargs):
|
||||
balance_type="debit",
|
||||
active=True,
|
||||
)
|
||||
asset_ca_cogs.role_default = True
|
||||
asset_ca_cogs.save()
|
||||
expense_cogs.role_default = True
|
||||
expense_cogs.save()
|
||||
|
||||
# Create Rent Expense Account
|
||||
expense = entity.create_account(
|
||||
# Rent Expense Account
|
||||
expense_rent = entity.create_account(
|
||||
coa_model=coa,
|
||||
code="6101",
|
||||
code="6102",
|
||||
role=roles.EXPENSE_OPERATIONAL,
|
||||
name=_("Rent Expense"),
|
||||
balance_type="debit",
|
||||
active=True,
|
||||
)
|
||||
expense.role_default = True
|
||||
expense.save()
|
||||
|
||||
# Create Utilities Expense Account
|
||||
entity.create_account(
|
||||
coa_model=coa,
|
||||
code="6020",
|
||||
role=roles.EXPENSE_OPERATIONAL,
|
||||
name=_("Utilities Expense"),
|
||||
balance_type="debit",
|
||||
active=True,
|
||||
)
|
||||
# Create Deferred Revenue Account
|
||||
unearned_account = entity.create_account(
|
||||
coa_model=coa,
|
||||
code="2060",
|
||||
role=roles.LIABILITY_CL_DEFERRED_REVENUE,
|
||||
name=_("Deferred Revenue"),
|
||||
balance_type="credit",
|
||||
active=True,
|
||||
)
|
||||
unearned_account.role_default = True
|
||||
unearned_account.save()
|
||||
|
||||
expense_rent.role_default = True
|
||||
expense_rent.save()
|
||||
|
||||
# Create Vendor
|
||||
@receiver(post_save, sender=models.Vendor)
|
||||
@ -394,13 +475,11 @@ def log_opportunity_update(sender, instance, **kwargs):
|
||||
|
||||
@receiver(post_save, sender=models.AdditionalServices)
|
||||
def create_item_service(sender, instance, created, **kwargs):
|
||||
if created:
|
||||
|
||||
if created:
|
||||
entity = instance.dealer.entity
|
||||
uom = entity.get_uom_all().get(unit_abbr=instance.uom)
|
||||
cogs = entity.get_all_accounts().get(role=roles.COGS)
|
||||
|
||||
# price = (float(instance.price) * float(vat.rate)) + float(instance.price) if instance.taxable else instance.price
|
||||
service_model = ItemModel.objects.create(
|
||||
name=instance.name,
|
||||
uom=uom,
|
||||
|
||||
@ -1,3 +1,235 @@
|
||||
from django.test import TestCase
|
||||
import json
|
||||
from . import models as m
|
||||
from django.urls import reverse
|
||||
from django_ledger import models as lm
|
||||
from django.test import Client, TestCase
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
# Create your tests here.
|
||||
class ModelTest(TestCase):
|
||||
def setUp(self):
|
||||
email = "RkzgO@example.com"
|
||||
name = "John Doe"
|
||||
password = "password"
|
||||
crn = "123456789"
|
||||
vrn = "123456789"
|
||||
phone = "123456789"
|
||||
address = "123 Main St"
|
||||
arabic_name = "الاسم بالعربية"
|
||||
|
||||
self.vat = m.VatRate.objects.create(rate=0.15)
|
||||
|
||||
user = User.objects.create(username=email, email=email)
|
||||
user.set_password(password)
|
||||
user.save()
|
||||
|
||||
self.dealer = m.Dealer.objects.create(
|
||||
user=user,
|
||||
name=name,
|
||||
arabic_name=arabic_name,
|
||||
crn=crn,
|
||||
vrn=vrn,
|
||||
phone_number=phone,
|
||||
address=address,
|
||||
)
|
||||
|
||||
self.car_make = m.CarMake.objects.create(name="Make")
|
||||
self.car_model = m.CarModel.objects.create(
|
||||
name="Model", id_car_make=self.car_make
|
||||
)
|
||||
self.car_serie = m.CarSerie.objects.create(
|
||||
name="Serie", id_car_model=self.car_model
|
||||
)
|
||||
self.trim = m.CarTrim.objects.create(name="Trim", id_car_serie=self.car_serie)
|
||||
self.car = m.Car.objects.create(
|
||||
vin="123456789",
|
||||
dealer=self.dealer,
|
||||
id_car_make=self.car_make,
|
||||
id_car_model=self.car_model,
|
||||
id_car_serie=self.car_serie,
|
||||
year=2020,
|
||||
id_car_trim=self.trim,
|
||||
receiving_date="2020-01-01",
|
||||
)
|
||||
|
||||
self.car_finances = m.CarFinance.objects.create(
|
||||
car=self.car, selling_price=1000, cost_price=500, discount_amount=200
|
||||
)
|
||||
|
||||
def test_dealer_creation_creates_user_and_entity(self):
|
||||
dealer = self.dealer
|
||||
|
||||
self.assertEqual(User.objects.count(), 1)
|
||||
self.assertEqual(m.Dealer.objects.count(), 1)
|
||||
self.assertEqual(User.objects.first().username, "RkzgO@example.com")
|
||||
self.assertEqual(User.objects.first().email, "RkzgO@example.com")
|
||||
self.assertTrue(User.objects.first().check_password("password"))
|
||||
self.assertEqual(dealer.user, User.objects.first())
|
||||
self.assertEqual(dealer.name, "John Doe")
|
||||
self.assertEqual(dealer.arabic_name, "الاسم بالعربية")
|
||||
self.assertEqual(dealer.crn, "123456789")
|
||||
self.assertEqual(dealer.vrn, "123456789")
|
||||
self.assertEqual(dealer.phone_number, "123456789")
|
||||
self.assertEqual(dealer.address, "123 Main St")
|
||||
|
||||
self.assertIsNotNone(dealer.entity)
|
||||
self.assertEqual(dealer.entity.name, dealer.name)
|
||||
|
||||
self.assertEqual(dealer.entity.get_all_accounts().count(), 19)
|
||||
self.assertEqual(dealer.entity.get_uom_all().count(), 16)
|
||||
|
||||
def test_car_creation_creates_product(self):
|
||||
dealer = self.dealer
|
||||
|
||||
self.assertEqual(m.Car.objects.count(), 1)
|
||||
self.assertEqual(self.car.vin, "123456789")
|
||||
self.assertEqual(self.car.dealer, dealer)
|
||||
self.assertEqual(self.car.id_car_make, self.car_make)
|
||||
self.assertEqual(self.car.id_car_model, self.car_model)
|
||||
self.assertEqual(self.car.id_car_serie, self.car_serie)
|
||||
self.assertEqual(self.car.year, 2020)
|
||||
self.assertEqual(self.car.id_car_trim, self.trim)
|
||||
|
||||
product = dealer.entity.get_items_all().filter(name=self.car.vin).first()
|
||||
self.assertEqual(product.name, self.car.vin)
|
||||
|
||||
def test_car_finances_creation(self):
|
||||
self.assertEqual(m.CarFinance.objects.count(), 1)
|
||||
self.assertEqual(self.car_finances.car, self.car)
|
||||
self.assertEqual(self.car_finances.selling_price, 1000)
|
||||
self.assertEqual(self.car_finances.cost_price, 500)
|
||||
self.assertEqual(self.car_finances.discount_amount, 200)
|
||||
|
||||
def test_car_finance_total(self):
|
||||
self.assertEqual(m.CarFinance.objects.count(), 1)
|
||||
self.assertEqual(self.car_finances.total, 1000)
|
||||
self.assertEqual(self.car_finances.total_discount, 800)
|
||||
self.assertEqual(self.car_finances.total_vat, 920)
|
||||
|
||||
def test_car_additional_services_create_item_service(self):
|
||||
m.AdditionalServices.objects.create(
|
||||
name="Service",
|
||||
price=100,
|
||||
description="Description",
|
||||
dealer=self.dealer,
|
||||
taxable=True,
|
||||
uom=m.UnitOfMeasure.PIECE,
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
m.ItemModel.objects.filter(
|
||||
name="Service",
|
||||
default_amount=100,
|
||||
is_product_or_service=True,
|
||||
item_role="service",
|
||||
).count(),
|
||||
1,
|
||||
)
|
||||
|
||||
|
||||
class AuthenticationTest(TestCase):
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
self.url = reverse("account_signup")
|
||||
def test_login(self):
|
||||
url = reverse("account_login")
|
||||
response = self.client.post(url, {"email": "RkzgO@example.com", "password": "password"})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
def test_valid_data(self):
|
||||
# Create valid JSON data
|
||||
data = {
|
||||
"wizardValidationForm1": {
|
||||
"email": "test@example.com",
|
||||
"password": "password123",
|
||||
"confirm_password": "password123"
|
||||
},
|
||||
"wizardValidationForm2": {
|
||||
"name": "John Doe",
|
||||
"arabic_name": "جون دو",
|
||||
"phone_number": "1234567890"
|
||||
},
|
||||
"wizardValidationForm3": {
|
||||
"crn": "123456",
|
||||
"vrn": "789012",
|
||||
"address": "123 Main St"
|
||||
}
|
||||
}
|
||||
|
||||
# Send a POST request with the JSON data
|
||||
response = self.client.post(
|
||||
self.url,
|
||||
data=json.dumps(data),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
# Check the response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.json(), {'message': 'User created successfully.'})
|
||||
|
||||
|
||||
def test_passwords_do_not_match(self):
|
||||
# Create JSON data with mismatched passwords
|
||||
data = {
|
||||
"wizardValidationForm1": {
|
||||
"email": "test@example.com",
|
||||
"password": "password123",
|
||||
"confirm_password": "differentpassword"
|
||||
},
|
||||
"wizardValidationForm2": {
|
||||
"name": "John Doe",
|
||||
"arabic_name": "جون دو",
|
||||
"phone_number": "1234567890"
|
||||
},
|
||||
"wizardValidationForm3": {
|
||||
"crn": "123456",
|
||||
"vrn": "789012",
|
||||
"address": "123 Main St"
|
||||
}
|
||||
}
|
||||
|
||||
# Send a POST request with the JSON data
|
||||
response = self.client.post(
|
||||
self.url,
|
||||
data=json.dumps(data),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
# Check the response
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(response.json(), {"error": "Passwords do not match."})
|
||||
|
||||
def test_missing_required_fields(self):
|
||||
# Create JSON data with missing required fields
|
||||
data = {
|
||||
"wizardValidationForm1": {
|
||||
"email": "test@example.com",
|
||||
"password": "password123",
|
||||
# Missing "confirm_password"
|
||||
},
|
||||
"wizardValidationForm2": {
|
||||
"name": "John Doe",
|
||||
"arabic_name": "جون دو",
|
||||
"phone_number": "1234567890"
|
||||
},
|
||||
"wizardValidationForm3": {
|
||||
"crn": "123456",
|
||||
"vrn": "789012",
|
||||
"address": "123 Main St"
|
||||
}
|
||||
}
|
||||
|
||||
# Send a POST request with the JSON data
|
||||
response = self.client.post(
|
||||
self.url,
|
||||
data=json.dumps(data),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
# Check the response
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertIn("error", response.json()) # Assuming the view returns an error for missing fields
|
||||
@ -144,6 +144,10 @@ urlpatterns = [
|
||||
path('sales/invoices/<uuid:pk>/', views.InvoiceDetailView.as_view(), name='invoice_detail'),
|
||||
path('sales/invoices/<uuid:pk>/preview/', views.InvoicePreviewView.as_view(), name='invoice_preview'),
|
||||
path('sales/invoices/<uuid:pk>/invoice_mark_as/', views.invoice_mark_as, name='invoice_mark_as'),
|
||||
path('sales/invoices/<uuid:pk>/draft_invoice_update/', views.DraftInvoiceModelUpdateFormView.as_view(), name='draft_invoice_update'),
|
||||
path('sales/invoices/<uuid:pk>/approved_invoice_update/', views.ApprovedInvoiceModelUpdateFormView.as_view(), name='approved_invoice_update'),
|
||||
path('sales/invoices/<uuid:pk>/paid_invoice_update/', views.PaidInvoiceModelUpdateFormView.as_view(), name='paid_invoice_update'),
|
||||
|
||||
# path('sales/estimates/<uuid:pk>/preview/', views.EstimatePreviewView.as_view(), name='estimate_preview'),
|
||||
# path('send_email/<uuid:pk>', views.send_email, name='send_email'),
|
||||
|
||||
@ -158,11 +162,13 @@ urlpatterns = [
|
||||
# # Journal
|
||||
# path('sales/journal/<uuid:pk>/create/', views.JournalEntryCreateView.as_view(), name='journal_create'),
|
||||
|
||||
#Items
|
||||
# Items
|
||||
path('items/services/', views.ItemServiceListView.as_view(), name='item_service_list'),
|
||||
path('items/services/create/', views.ItemServiceCreateView.as_view(), name='item_service_create'),
|
||||
|
||||
|
||||
# Expanese
|
||||
path('items/expeneses/', views.ItemExpenseListView.as_view(), name='item_expense_list'),
|
||||
path('items/expeneses/create/', views.ItemExpenseCreateView.as_view(), name='item_expense_create'),
|
||||
path('items/expeneses/<uuid:pk>/update/', views.ItemExpenseUpdateView.as_view(), name='item_expense_update'),
|
||||
]
|
||||
|
||||
|
||||
|
||||
@ -9,16 +9,18 @@ from django.core.mail import send_mail
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from inventory.utilities.financials import get_financial_value
|
||||
from django_ledger.models.items import ItemModel
|
||||
from django_ledger.models import InvoiceModel, EstimateModel
|
||||
|
||||
|
||||
def get_jwt_token():
|
||||
url = 'https://carapi.app/api/auth/login'
|
||||
url = "https://carapi.app/api/auth/login"
|
||||
headers = {
|
||||
'accept': 'text/plain',
|
||||
'Content-Type': 'application/json',
|
||||
"accept": "text/plain",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
data = {
|
||||
"api_token": "f5204a00-6f31-4de2-96d8-ed998e0d230c",
|
||||
"api_secret": "8c11320781a5b8f4f327b6937e6f8241"
|
||||
"api_secret": "8c11320781a5b8f4f327b6937e6f8241",
|
||||
}
|
||||
try:
|
||||
response = requests.post(url, headers=headers, json=data)
|
||||
@ -30,9 +32,9 @@ def get_jwt_token():
|
||||
|
||||
|
||||
def localize_some_words():
|
||||
success = _('success')
|
||||
error = _('error')
|
||||
forget = _('Forgot Password?')
|
||||
success = _("success")
|
||||
error = _("error")
|
||||
forget = _("Forgot Password?")
|
||||
|
||||
return None
|
||||
|
||||
@ -42,9 +44,19 @@ def get_calculations(quotation):
|
||||
qc_len = quotation.quotation_cars.count()
|
||||
cars = [x.car for x in quotation.quotation_cars.all()]
|
||||
finances = models.CarFinance.objects.filter(car__in=cars)
|
||||
|
||||
|
||||
services = ItemModel.objects.filter(additional_finances__in=finances).all()
|
||||
data = [{"name":x.name,"price":x.default_amount,"total_price":x.default_amount * qc_len,"vated":float(x.default_amount) * 0.15 * float(qc_len),"total_price_vat":float(x.default_amount) + (float(x.default_amount) * 0.15 * float(qc_len))} for x in services]
|
||||
data = [
|
||||
{
|
||||
"name": x.name,
|
||||
"price": x.default_amount,
|
||||
"total_price": x.default_amount * qc_len,
|
||||
"vated": float(x.default_amount) * 0.15 * float(qc_len),
|
||||
"total_price_vat": float(x.default_amount)
|
||||
+ (float(x.default_amount) * 0.15 * float(qc_len)),
|
||||
}
|
||||
for x in services
|
||||
]
|
||||
context["services"] = data
|
||||
context["total_cost"] = 0
|
||||
context["total_vat"] = 0
|
||||
@ -52,7 +64,9 @@ def get_calculations(quotation):
|
||||
for k in context["services"]:
|
||||
context["total_cost"] += k["total_price"]
|
||||
context["total_vat"] += k["vated"]
|
||||
context["total_cost_vat"] = float(context["total_cost"])+float(context["total_vat"])
|
||||
context["total_cost_vat"] = float(context["total_cost"]) + float(
|
||||
context["total_vat"]
|
||||
)
|
||||
return context
|
||||
|
||||
|
||||
@ -66,12 +80,13 @@ def send_email(from_, to_, subject, message):
|
||||
|
||||
def get_user_type(request):
|
||||
dealer = ""
|
||||
if hasattr(request.user, 'dealer'):
|
||||
if hasattr(request.user, "dealer"):
|
||||
dealer = request.user.dealer
|
||||
elif hasattr(request.user, 'staff'):
|
||||
elif hasattr(request.user, "staff"):
|
||||
dealer = request.user.staff.dealer
|
||||
return dealer
|
||||
|
||||
|
||||
def get_dealer_from_instance(instance):
|
||||
if instance.dealer.staff:
|
||||
return instance.dealer
|
||||
@ -79,7 +94,7 @@ def get_dealer_from_instance(instance):
|
||||
return instance.dealer
|
||||
|
||||
|
||||
def reserve_car(car,request):
|
||||
def reserve_car(car, request):
|
||||
try:
|
||||
reserved_until = timezone.now() + timezone.timedelta(hours=24)
|
||||
models.CarReservation.objects.create(
|
||||
@ -97,5 +112,70 @@ def reserve_car(car,request):
|
||||
def calculate_vat_amount(amount):
|
||||
vat = models.VatRate.objects.filter(is_active=True).first()
|
||||
if vat:
|
||||
return ((amount * Decimal(vat.rate)).quantize(Decimal('0.01')),vat.rate)
|
||||
return amount
|
||||
return ((amount * Decimal(vat.rate)).quantize(Decimal("0.01")), vat.rate)
|
||||
return amount
|
||||
|
||||
|
||||
def get_financial_values(model):
|
||||
vat = models.VatRate.objects.filter(is_active=True).first()
|
||||
|
||||
if not model.get_itemtxs_data()[0].exists():
|
||||
return {
|
||||
"vat_amount": 0,
|
||||
"total": 0,
|
||||
"grand_total": 0,
|
||||
"discount_amount": 0,
|
||||
"vat": 0,
|
||||
"car_and_item_info": [],
|
||||
"additional_services": [],
|
||||
}
|
||||
|
||||
data = model.get_itemtxs_data()[0].all()
|
||||
|
||||
if isinstance(model, InvoiceModel):
|
||||
data = model.ce_model.get_itemtxs_data()[0].all()
|
||||
|
||||
car_and_item_info = [
|
||||
{
|
||||
"car": models.Car.objects.get(vin=x.item_model.name),
|
||||
"total": models.Car.objects.get(
|
||||
vin=x.item_model.name
|
||||
).finances.selling_price
|
||||
* Decimal(x.ce_quantity),
|
||||
"itemmodel": x,
|
||||
}
|
||||
for x in data
|
||||
]
|
||||
total = sum(
|
||||
Decimal(models.Car.objects.get(vin=x.item_model.name).finances.total)
|
||||
* Decimal(x.ce_quantity)
|
||||
for x in data
|
||||
)
|
||||
discount_amount = sum(
|
||||
models.CarFinance.objects.get(car__vin=i.item_model.name).discount_amount
|
||||
for i in data
|
||||
)
|
||||
|
||||
additional_services = []
|
||||
|
||||
for i in data:
|
||||
cf = models.CarFinance.objects.get(car__vin=i.item_model.name)
|
||||
if cf.additional_services.exists():
|
||||
additional_services.extend(
|
||||
[
|
||||
{"name": x.name, "price": x.price}
|
||||
for x in cf.additional_services.all()
|
||||
]
|
||||
)
|
||||
|
||||
grand_total = Decimal(total) - Decimal(discount_amount)
|
||||
vat_amount = round(Decimal(grand_total) * Decimal(vat.rate), 2)
|
||||
return {
|
||||
"car_and_item_info": car_and_item_info,
|
||||
"total": total,
|
||||
"discount_amount": discount_amount,
|
||||
"additional_services": additional_services,
|
||||
"grand_total": grand_total + vat_amount,
|
||||
"vat_amount": vat_amount,
|
||||
"vat": vat.rate,
|
||||
}
|
||||
|
||||
@ -16,10 +16,15 @@ from django_ledger.forms.bank_account import (
|
||||
BankAccountCreateForm,
|
||||
BankAccountUpdateForm,
|
||||
)
|
||||
from django_ledger.forms.invoice import (
|
||||
DraftInvoiceModelUpdateForm,
|
||||
ApprovedInvoiceModelUpdateForm,
|
||||
PaidInvoiceModelUpdateForm,
|
||||
)
|
||||
from django_ledger.forms.account import AccountModelCreateForm, AccountModelUpdateForm
|
||||
from django_ledger.forms.estimate import EstimateModelCreateForm
|
||||
from django_ledger.forms.invoice import InvoiceModelCreateForm
|
||||
from django_ledger.forms.item import ServiceCreateForm
|
||||
from django_ledger.forms.item import ServiceCreateForm,ExpenseItemCreateForm,ExpenseItemUpdateForm
|
||||
from django_ledger.forms.journal_entry import JournalEntryModelCreateForm
|
||||
from django_ledger.io import roles
|
||||
from django.contrib.admin.models import LogEntry
|
||||
@ -63,6 +68,7 @@ from django.contrib.auth.models import Group
|
||||
from .utils import (
|
||||
calculate_vat_amount,
|
||||
get_calculations,
|
||||
get_financial_values,
|
||||
reserve_car,
|
||||
send_email,
|
||||
get_user_type,
|
||||
@ -1670,7 +1676,7 @@ class AccountListView(LoginRequiredMixin, ListView):
|
||||
def get_queryset(self):
|
||||
entity = self.request.user.dealer.entity
|
||||
qs = entity.get_all_accounts()
|
||||
paginator = Paginator(qs, 10)
|
||||
paginator = Paginator(qs, 20)
|
||||
page_number = self.request.GET.get("page", 1) # Default to page 1
|
||||
page_obj = paginator.get_page(page_number)
|
||||
return page_obj
|
||||
@ -1904,51 +1910,15 @@ class EstimateDetailView(LoginRequiredMixin, DetailView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
estimate = kwargs.get("object")
|
||||
vat = models.VatRate.objects.filter(is_active=True).first()
|
||||
|
||||
if estimate.get_itemtxs_data():
|
||||
car_and_item_info = [
|
||||
{
|
||||
"car": models.Car.objects.get(vin=x.item_model.name),
|
||||
"total": models.Car.objects.get(
|
||||
vin=x.item_model.name
|
||||
).finances.selling_price
|
||||
* Decimal(x.ce_quantity),
|
||||
"itemmodel": x,
|
||||
}
|
||||
for x in estimate.get_itemtxs_data()[0].all()
|
||||
]
|
||||
total = sum(
|
||||
float(models.Car.objects.get(vin=x.item_model.name).finances.total)
|
||||
* float(x.ce_quantity)
|
||||
for x in estimate.get_itemtxs_data()[0].all()
|
||||
)
|
||||
discount_amount = sum(
|
||||
models.CarFinance.objects.get(
|
||||
car__vin=i.item_model.name
|
||||
).discount_amount
|
||||
for i in estimate.get_itemtxs_data()[0].all()
|
||||
)
|
||||
additional_services = []
|
||||
for i in estimate.get_itemtxs_data()[0].all():
|
||||
cf = models.CarFinance.objects.get(car__vin=i.item_model.name)
|
||||
if cf.additional_services.exists():
|
||||
additional_services.extend(
|
||||
[
|
||||
{"name": x.name, "price": x.price}
|
||||
for x in cf.additional_services.all()
|
||||
]
|
||||
)
|
||||
data = get_financial_values(estimate)
|
||||
|
||||
grand_total = float(total) - float(discount_amount)
|
||||
vat_amount = round(float(grand_total) * float(vat.rate), 2)
|
||||
|
||||
kwargs["vat_amount"] = vat_amount
|
||||
kwargs["car_and_item_info"] = car_and_item_info
|
||||
kwargs["total"] = grand_total + vat_amount
|
||||
kwargs["discount_amount"] = discount_amount
|
||||
kwargs["vat"] = vat.rate
|
||||
kwargs["additional_services"] = additional_services
|
||||
kwargs["vat_amount"] = data["vat_amount"]
|
||||
kwargs["total"] = data["grand_total"]
|
||||
kwargs["discount_amount"] = data["discount_amount"]
|
||||
kwargs["vat"] = data["vat"]
|
||||
kwargs["car_and_item_info"] = data["car_and_item_info"]
|
||||
kwargs["additional_services"] = data["additional_services"]
|
||||
kwargs["invoice"] = (
|
||||
InvoiceModel.objects.all().filter(ce_model=estimate).first()
|
||||
)
|
||||
@ -1976,51 +1946,15 @@ class EstimatePreviewView(LoginRequiredMixin, DetailView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
estimate = kwargs.get("object")
|
||||
vat = models.VatRate.objects.filter(is_active=True).first()
|
||||
if estimate.get_itemtxs_data():
|
||||
car_and_item_info = [
|
||||
{
|
||||
"car": models.Car.objects.get(vin=x.item_model.name),
|
||||
"total": models.Car.objects.get(
|
||||
vin=x.item_model.name
|
||||
).finances.selling_price
|
||||
* Decimal(x.ce_quantity),
|
||||
"itemmodel": x,
|
||||
}
|
||||
for x in estimate.get_itemtxs_data()[0].all()
|
||||
]
|
||||
total = sum(
|
||||
float(models.Car.objects.get(vin=x.item_model.name).finances.total)
|
||||
* float(x.ce_quantity)
|
||||
for x in estimate.get_itemtxs_data()[0].all()
|
||||
)
|
||||
discount_amount = sum(
|
||||
models.CarFinance.objects.get(
|
||||
car__vin=i.item_model.name
|
||||
).discount_amount
|
||||
for i in estimate.get_itemtxs_data()[0].all()
|
||||
)
|
||||
data = get_financial_values(estimate)
|
||||
|
||||
additional_services = []
|
||||
|
||||
for i in estimate.get_itemtxs_data()[0].all():
|
||||
cf = models.CarFinance.objects.get(car__vin=i.item_model.name)
|
||||
if cf.additional_services.exists():
|
||||
additional_services.extend(
|
||||
[
|
||||
{"name": x.name, "price": x.price}
|
||||
for x in cf.additional_services.all()
|
||||
]
|
||||
)
|
||||
|
||||
grand_total = float(total) - float(discount_amount)
|
||||
vat_amount = round(float(grand_total) * float(vat.rate), 2)
|
||||
|
||||
kwargs["vat_amount"] = vat_amount
|
||||
kwargs["total"] = grand_total + vat_amount
|
||||
kwargs["vat"] = vat.rate
|
||||
kwargs["car_and_item_info"] = car_and_item_info
|
||||
kwargs["additional_services"] = additional_services
|
||||
kwargs["vat_amount"] = data["vat_amount"]
|
||||
kwargs["total"] = data["grand_total"] + data["vat_amount"]
|
||||
kwargs["discount_amount"] = data["discount_amount"]
|
||||
kwargs["vat"] = data["vat"]
|
||||
kwargs["car_and_item_info"] = data["car_and_item_info"]
|
||||
kwargs["additional_services"] = data["additional_services"]
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
@ -2077,53 +2011,16 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
invoice = kwargs.get("object")
|
||||
vat = models.VatRate.objects.filter(is_active=True).first()
|
||||
|
||||
if invoice.get_itemtxs_data():
|
||||
car_and_item_info = [
|
||||
{
|
||||
"car": models.Car.objects.get(vin=x.item_model.name),
|
||||
"total": models.Car.objects.get(
|
||||
vin=x.item_model.name
|
||||
).finances.selling_price
|
||||
* Decimal(x.quantity),
|
||||
"itemmodel": x,
|
||||
}
|
||||
for x in invoice.get_itemtxs_data()[0].all()
|
||||
]
|
||||
total = sum(
|
||||
float(models.Car.objects.get(vin=x.item_model.name).finances.total)
|
||||
* float(x.quantity)
|
||||
for x in invoice.get_itemtxs_data()[0].all()
|
||||
)
|
||||
discount_amount = sum(
|
||||
models.CarFinance.objects.get(
|
||||
car__vin=i.item_model.name
|
||||
).discount_amount
|
||||
for i in invoice.get_itemtxs_data()[0].all()
|
||||
)
|
||||
data = get_financial_values(invoice)
|
||||
|
||||
additional_services = []
|
||||
|
||||
for i in invoice.get_itemtxs_data()[0].all():
|
||||
cf = models.CarFinance.objects.get(car__vin=i.item_model.name)
|
||||
if cf.additional_services.exists():
|
||||
additional_services.extend(
|
||||
[
|
||||
{"name": x.name, "price": x.price}
|
||||
for x in cf.additional_services.all()
|
||||
]
|
||||
)
|
||||
|
||||
grand_total = float(total) - float(discount_amount)
|
||||
vat_amount = round(float(grand_total) * float(vat.rate), 2)
|
||||
|
||||
kwargs["vat_amount"] = vat_amount
|
||||
kwargs["total"] = grand_total + vat_amount
|
||||
kwargs["discount_amount"] = discount_amount
|
||||
kwargs["vat"] = vat.rate
|
||||
kwargs["car_and_item_info"] = car_and_item_info
|
||||
kwargs["additional_services"] = additional_services
|
||||
kwargs["vat_amount"] = data["vat_amount"]
|
||||
kwargs["total"] = data["grand_total"]
|
||||
kwargs["discount_amount"] = data["discount_amount"]
|
||||
kwargs["vat"] = data["vat"]
|
||||
kwargs["car_and_item_info"] = data["car_and_item_info"]
|
||||
kwargs["additional_services"] = data["additional_services"]
|
||||
kwargs["payments"] = JournalEntryModel.objects.filter(
|
||||
ledger=invoice.ledger
|
||||
).all()
|
||||
@ -2131,6 +2028,65 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView):
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
class DraftInvoiceModelUpdateFormView(LoginRequiredMixin, UpdateView):
|
||||
model = InvoiceModel
|
||||
form_class = DraftInvoiceModelUpdateForm
|
||||
template_name = "sales/invoices/draft_invoice_update.html"
|
||||
success_url = reverse_lazy("invoice_list")
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
dealer = get_user_type(self.request.user.dealer)
|
||||
kwargs["entity_slug"] = dealer.entity
|
||||
kwargs["user_model"] = dealer.entity.admin
|
||||
return kwargs
|
||||
|
||||
|
||||
class ApprovedInvoiceModelUpdateFormView(LoginRequiredMixin, UpdateView):
|
||||
model = InvoiceModel
|
||||
form_class = ApprovedInvoiceModelUpdateForm
|
||||
template_name = "sales/invoices/approved_invoice_update.html"
|
||||
success_url = reverse_lazy("invoice_list")
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
dealer = get_user_type(self.request.user.dealer)
|
||||
kwargs["entity_slug"] = dealer.entity
|
||||
kwargs["user_model"] = dealer.entity.admin
|
||||
return kwargs
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy("invoice_detail", kwargs={"pk": self.object.pk})
|
||||
|
||||
|
||||
class PaidInvoiceModelUpdateFormView(LoginRequiredMixin, UpdateView):
|
||||
model = InvoiceModel
|
||||
form_class = PaidInvoiceModelUpdateForm
|
||||
template_name = "sales/invoices/paid_invoice_update.html"
|
||||
success_url = reverse_lazy("invoice_list")
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
dealer = get_user_type(self.request.user.dealer)
|
||||
kwargs["entity_slug"] = dealer.entity
|
||||
kwargs["user_model"] = dealer.entity.admin
|
||||
return kwargs
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy("invoice_detail", kwargs={"pk": self.object.pk})
|
||||
|
||||
def form_valid(self, form):
|
||||
invoice = form.save()
|
||||
|
||||
if invoice.get_amount_open() > 0:
|
||||
messages.error(self.request, "Invoice is not fully paid")
|
||||
return redirect("invoice_detail", pk=invoice.pk)
|
||||
else:
|
||||
invoice.post_ledger()
|
||||
invoice.save()
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
@login_required
|
||||
def invoice_mark_as(request, pk):
|
||||
invoice = get_object_or_404(InvoiceModel, pk=pk)
|
||||
@ -2143,6 +2099,7 @@ def invoice_mark_as(request, pk):
|
||||
messages.error(request, "invoice is not ready for approval")
|
||||
return redirect("invoice_detail", pk=invoice.pk)
|
||||
invoice.mark_as_approved(entity_slug=entity.slug, user_model=user)
|
||||
# invoice.post_ledger()
|
||||
invoice.save()
|
||||
ledger = (
|
||||
entity.get_ledgers().filter(name=f"Invoice {str(invoice.pk)}").first()
|
||||
@ -2168,18 +2125,11 @@ def invoice_create(request, pk):
|
||||
)
|
||||
if form.is_valid():
|
||||
invoice = form.save(commit=False)
|
||||
invoice_model = entity.create_invoice(
|
||||
customer_model=invoice.customer,
|
||||
terms=invoice.terms,
|
||||
cash_account=invoice.cash_account,
|
||||
prepaid_account=invoice.prepaid_account,
|
||||
coa_model=entity.get_default_coa(),
|
||||
)
|
||||
ledger = entity.create_ledger(name=f"Invoice {str(invoice_model.pk)}")
|
||||
invoice_model.ledgar = ledger
|
||||
ledger.invoicemodel = invoice_model
|
||||
ledger = entity.create_ledger(name=str(invoice.pk))
|
||||
invoice.ledgar = ledger
|
||||
ledger.invoicemodel = invoice
|
||||
ledger.save()
|
||||
invoice_model.save()
|
||||
invoice.save()
|
||||
|
||||
unit_items = estimate.get_itemtxs_data()[0]
|
||||
|
||||
@ -2205,18 +2155,18 @@ def invoice_create(request, pk):
|
||||
for i in itemtxs
|
||||
}
|
||||
|
||||
invoice_itemtxs = invoice_model.migrate_itemtxs(
|
||||
invoice_itemtxs = invoice.migrate_itemtxs(
|
||||
itemtxs=invoice_itemtxs,
|
||||
commit=True,
|
||||
operation=InvoiceModel.ITEMIZE_APPEND,
|
||||
)
|
||||
invoice_model.bind_estimate(estimate)
|
||||
invoice_model.mark_as_review()
|
||||
invoice.bind_estimate(estimate)
|
||||
invoice.mark_as_review()
|
||||
estimate.mark_as_completed()
|
||||
estimate.save()
|
||||
invoice_model.save()
|
||||
invoice.save()
|
||||
messages.success(request, "Invoice created successfully!")
|
||||
return redirect("invoice_detail", pk=invoice_model.pk)
|
||||
return redirect("invoice_detail", pk=invoice.pk)
|
||||
form.initial["customer"] = estimate.customer
|
||||
context = {
|
||||
"form": form,
|
||||
@ -2232,51 +2182,15 @@ class InvoicePreviewView(LoginRequiredMixin, DetailView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
invoice = kwargs.get("object")
|
||||
vat = models.VatRate.objects.filter(is_active=True).first()
|
||||
if invoice.get_itemtxs_data():
|
||||
car_and_item_info = [
|
||||
{
|
||||
"car": models.Car.objects.get(vin=x.item_model.name),
|
||||
"total": models.Car.objects.get(
|
||||
vin=x.item_model.name
|
||||
).finances.selling_price
|
||||
* Decimal(x.ce_quantity),
|
||||
"itemmodel": x,
|
||||
}
|
||||
for x in invoice.ce_model.get_itemtxs_data()[0].all()
|
||||
]
|
||||
total = sum(
|
||||
float(models.Car.objects.get(vin=x.item_model.name).finances.total)
|
||||
* float(x.ce_quantity)
|
||||
for x in invoice.ce_model.get_itemtxs_data()[0].all()
|
||||
)
|
||||
discount_amount = sum(
|
||||
models.CarFinance.objects.get(
|
||||
car__vin=i.item_model.name
|
||||
).discount_amount
|
||||
for i in invoice.ce_model.get_itemtxs_data()[0].all()
|
||||
)
|
||||
data = get_financial_values(invoice)
|
||||
|
||||
additional_services = []
|
||||
|
||||
for i in invoice.ce_model.get_itemtxs_data()[0].all():
|
||||
cf = models.CarFinance.objects.get(car__vin=i.item_model.name)
|
||||
if cf.additional_services.exists():
|
||||
additional_services.extend(
|
||||
[
|
||||
{"name": x.name, "price": x.price}
|
||||
for x in cf.additional_services.all()
|
||||
]
|
||||
)
|
||||
|
||||
grand_total = float(total) - float(discount_amount)
|
||||
vat_amount = round(float(grand_total) * float(vat.rate), 2)
|
||||
|
||||
kwargs["vat_amount"] = vat_amount
|
||||
kwargs["total"] = grand_total + vat_amount
|
||||
kwargs["vat"] = vat.rate
|
||||
kwargs["car_and_item_info"] = car_and_item_info
|
||||
kwargs["additional_services"] = additional_services
|
||||
kwargs["vat_amount"] = data["vat_amount"]
|
||||
kwargs["total"] = data["grand_total"] + data["vat_amount"]
|
||||
kwargs["discount_amount"] = data["discount_amount"]
|
||||
kwargs["vat"] = data["vat"]
|
||||
kwargs["car_and_item_info"] = data["car_and_item_info"]
|
||||
kwargs["additional_services"] = data["additional_services"]
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
@ -2310,8 +2224,8 @@ def PaymentCreateView(request, pk=None):
|
||||
ledger = None
|
||||
try:
|
||||
ledger = LedgerModel.objects.filter(
|
||||
name=f"Invoice {str(invoice.pk)}", entity=entity
|
||||
).first()
|
||||
name=str(invoice.pk), entity=entity
|
||||
).first()
|
||||
journal = JournalEntryModel.objects.create(
|
||||
posted=False,
|
||||
description=f"Payment for Invoice {invoice.invoice_number}",
|
||||
@ -2338,7 +2252,9 @@ def PaymentCreateView(request, pk=None):
|
||||
tx_type="credit",
|
||||
description="Payment Received",
|
||||
)
|
||||
journal.posted = True
|
||||
invoice.make_payment(amount)
|
||||
journal.save()
|
||||
invoice.save()
|
||||
|
||||
if invoice.amount_due == invoice.amount_paid:
|
||||
@ -2346,6 +2262,8 @@ def PaymentCreateView(request, pk=None):
|
||||
entity_slug=entity.slug, user_model=entity.admin
|
||||
)
|
||||
invoice.save()
|
||||
ledger.post()
|
||||
ledger.save()
|
||||
messages.success(request, "Payment created successfully!")
|
||||
return redirect("invoice_detail", pk=invoice.pk)
|
||||
except Exception as e:
|
||||
@ -2532,12 +2450,6 @@ class ItemServiceCreateView(CreateView):
|
||||
template_name = "items/service/service_create.html"
|
||||
success_url = reverse_lazy("item_service_list")
|
||||
|
||||
# def get_form_kwargs(self):
|
||||
# kwargs = super().get_form_kwargs()
|
||||
# kwargs["entity_slug"] = self.request.user.dealer.entity.slug
|
||||
# kwargs["user_model"] = self.request.user.dealer.entity.admin
|
||||
# return kwargs
|
||||
|
||||
def form_valid(self, form):
|
||||
vat = models.VatRate.objects.get(is_active=True)
|
||||
form.instance.dealer = get_user_type(self.request.user.dealer)
|
||||
@ -2557,6 +2469,54 @@ class ItemServiceListView(ListView):
|
||||
return items
|
||||
|
||||
|
||||
class ItemExpenseCreateView(CreateView):
|
||||
model = ItemModel
|
||||
form_class = ExpenseItemCreateForm
|
||||
template_name = "items/expenses/expense_create.html"
|
||||
success_url = reverse_lazy("item_expense_list")
|
||||
|
||||
def get_form_kwargs(self):
|
||||
dealer = get_user_type(self.request.user.dealer)
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs["entity_slug"] = dealer.entity.slug
|
||||
kwargs["user_model"] = dealer.entity.admin
|
||||
return kwargs
|
||||
|
||||
|
||||
def form_valid(self, form):
|
||||
dealer = get_user_type(self.request.user.dealer)
|
||||
form.instance.entity = dealer.entity
|
||||
return super().form_valid(form)
|
||||
|
||||
class ItemExpenseUpdateView(UpdateView):
|
||||
model = ItemModel
|
||||
form_class = ExpenseItemUpdateForm
|
||||
template_name = "items/expenses/expense_update.html"
|
||||
success_url = reverse_lazy("item_expense_list")
|
||||
|
||||
def get_form_kwargs(self):
|
||||
dealer = get_user_type(self.request.user.dealer)
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs["entity_slug"] = dealer.entity.slug
|
||||
kwargs["user_model"] = dealer.entity.admin
|
||||
return kwargs
|
||||
|
||||
|
||||
def form_valid(self, form):
|
||||
dealer = get_user_type(self.request.user.dealer)
|
||||
form.instance.entity = dealer.entity
|
||||
return super().form_valid(form)
|
||||
|
||||
class ItemExpenseListView(ListView):
|
||||
model = ItemModel
|
||||
template_name = "items/expenses/expenses_list.html"
|
||||
context_object_name = "expenses"
|
||||
|
||||
def get_queryset(self):
|
||||
dealer = get_user_type(self.request)
|
||||
items = dealer.entity.get_items_expenses()
|
||||
return items
|
||||
|
||||
class SubscriptionPlans(ListView):
|
||||
model = models.SubscriptionPlan
|
||||
template_name = "subscriptions/subscription_plan.html"
|
||||
@ -2616,6 +2576,8 @@ def send_email_view(request, pk):
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
# errors
|
||||
def custom_page_not_found_view(request, exception):
|
||||
return render(request, "errors/404.html", {})
|
||||
@ -2631,3 +2593,6 @@ def custom_permission_denied_view(request, exception=None):
|
||||
|
||||
def custom_bad_request_view(request, exception=None):
|
||||
return render(request, "errors/400.html", {})
|
||||
|
||||
|
||||
|
||||
|
||||
24
templates/items/expenses/expense_create.html
Normal file
24
templates/items/expenses/expense_create.html
Normal file
@ -0,0 +1,24 @@
|
||||
{% extends "base.html" %}
|
||||
{% load crispy_forms_filters %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% block title %}{{ _("Expenses") }}{% endblock title %}
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">{{ _("Add Expense") }}</div>
|
||||
<div class="card-body">
|
||||
<form method="post" action="">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<button type="submit" class="btn btn-primary">{% trans 'Save' %}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
||||
24
templates/items/expenses/expense_update.html
Normal file
24
templates/items/expenses/expense_update.html
Normal file
@ -0,0 +1,24 @@
|
||||
{% extends "base.html" %}
|
||||
{% load crispy_forms_filters %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% block title %}{{ _("Expenses") }}{% endblock title %}
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">{{ _("Update Expense") }}</div>
|
||||
<div class="card-body">
|
||||
<form method="post" action="">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<button type="submit" class="btn btn-primary">{% trans 'Save' %}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
||||
46
templates/items/expenses/expenses_list.html
Normal file
46
templates/items/expenses/expenses_list.html
Normal file
@ -0,0 +1,46 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n static %}
|
||||
|
||||
{% block title %}{{ _("Expenses") }}{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mt-4">
|
||||
<h3 class="text-center">{% trans "Expenses" %}</h3>
|
||||
<div class="mx-n4 px-4 mx-lg-n6 px-lg-6 bg-body-emphasis pt-7 border-y">
|
||||
|
||||
<div class="table-responsive mx-n1 px-1 scrollbar">
|
||||
<table class="table fs-9 mb-0 border-top border-translucent">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Item Number" %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Name" %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Unit of Measure" %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Action" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="list">
|
||||
{% for expense in expenses %}
|
||||
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
|
||||
<td class="align-middle product white-space-nowrap py-0">{{ expense.item_number }}</td>
|
||||
<td class="align-middle product white-space-nowrap">{{ expense.name }}</td>
|
||||
<td class="align-middle product white-space-nowrap">{{ expense.uom }}</td>
|
||||
<td class="text-center">
|
||||
<a href="{% url 'item_expense_update' expense.pk %}"
|
||||
class="btn btn-sm btn-phoenix-success">
|
||||
{% trans "Update" %}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="6" class="text-center">{% trans "No Invoice Found" %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="d-flex justify-content-center">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
24
templates/sales/invoices/approved_invoice_update.html
Normal file
24
templates/sales/invoices/approved_invoice_update.html
Normal file
@ -0,0 +1,24 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_filters %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">{{ _("Update Invoice") }}</div>
|
||||
<div class="card-body">
|
||||
<form method="post" action="">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<button type="submit" class="btn btn-primary">{% trans 'Save' %}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock content %}
|
||||
24
templates/sales/invoices/draft_invoice_update.html
Normal file
24
templates/sales/invoices/draft_invoice_update.html
Normal file
@ -0,0 +1,24 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_filters %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">{{ _("Update Invoice") }}</div>
|
||||
<div class="card-body">
|
||||
<form method="post" action="">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<button type="submit" class="btn btn-primary">{% trans 'Save' %}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock content %}
|
||||
@ -39,7 +39,10 @@
|
||||
<button id="accept_invoice" class="btn btn-phoenix-secondary" data-bs-toggle="modal" data-bs-target="#confirmModal"><span class="d-none d-sm-inline-block">{% trans 'Accept' %}</span></button>
|
||||
{% endif %}
|
||||
{% if invoice.invoice_status == 'approved' %}
|
||||
<a href="{% url 'payment_create' invoice.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{% trans 'Record Payment' %}</span></a>
|
||||
<a href="{% url 'payment_create' invoice.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{% trans 'Record Payment' %}</span></a>
|
||||
{% endif %}
|
||||
{% if invoice.get_amount_open == 0 %}
|
||||
<a href="{% url 'paid_invoice_update' invoice.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{% trans 'Mark As Paid' %}</span></a>
|
||||
{% endif %}
|
||||
<a href="{% url 'invoice_preview' invoice.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{% trans 'Preview' %}</span></a>
|
||||
</div>
|
||||
@ -55,6 +58,7 @@
|
||||
<div>
|
||||
<p class="fw-bold mb-1">{% trans 'Paid Amount' %}</p>
|
||||
<h4 class="fw-bolder text-nowrap">${{invoice.amount_paid}}</h4>
|
||||
<h6 class="fw-bolder text-nowrap">Owned <span class="fw-semibold text-nowrap text-success">${{invoice.get_amount_open}}</span></h6>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -148,7 +152,7 @@
|
||||
<tr>
|
||||
<td class="">{{forloop.counter}}</td>
|
||||
<td class="">{{item.car.id_car_model}}</td>
|
||||
<td class="align-middle">{{item.itemmodel.quantity}}</td>
|
||||
<td class="align-middle">{{item.itemmodel.ce_quantity}}</td>
|
||||
<td class="align-middle ps-5">{{item.car.finances.selling_price}}</td>
|
||||
<td class="align-middle text-body-tertiary fw-semibold">{{item.total}}</td>
|
||||
</tr>
|
||||
|
||||
24
templates/sales/invoices/paid_invoice_update.html
Normal file
24
templates/sales/invoices/paid_invoice_update.html
Normal file
@ -0,0 +1,24 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_filters %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">{{ _("Update Invoice") }}</div>
|
||||
<div class="card-body">
|
||||
<form method="post" action="">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<button type="submit" class="btn btn-primary">{% trans 'Save' %}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock content %}
|
||||
Loading…
x
Reference in New Issue
Block a user