haikal/inventory/tasks.py
2025-08-05 19:48:48 +03:00

1322 lines
42 KiB
Python

import base64
import logging
from plans.models import Plan
from django.urls import reverse
from django.conf import settings
from django.utils import timezone
from django.db import transaction
from django_ledger.io import roles
from django_q.tasks import async_task
from django.core.mail import send_mail
from appointment.models import StaffMember
from django.utils.translation import activate
from django.core.files.base import ContentFile
from django.contrib.auth import get_user_model
from allauth.account.models import EmailAddress
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import User, Group, Permission
from inventory.models import DealerSettings, Dealer,Schedule,Notification
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
def create_settings(pk):
instance = Dealer.objects.get(pk=pk)
DealerSettings.objects.create(
dealer=instance,
invoice_cash_account=instance.entity.get_all_accounts()
.filter(role=roles.ASSET_CA_CASH)
.first(),
invoice_prepaid_account=instance.entity.get_all_accounts()
.filter(role=roles.ASSET_CA_RECEIVABLES)
.first(),
invoice_unearned_account=instance.entity.get_all_accounts()
.filter(role=roles.LIABILITY_CL_DEFERRED_REVENUE)
.first(),
bill_cash_account=instance.entity.get_all_accounts()
.filter(role=roles.ASSET_CA_CASH)
.first(),
bill_prepaid_account=instance.entity.get_all_accounts()
.filter(role=roles.ASSET_CA_PREPAID)
.first(),
bill_unearned_account=instance.entity.get_all_accounts()
.filter(role=roles.LIABILITY_CL_ACC_PAYABLE)
.first(),
)
def create_coa_accounts(instance):
entity = instance.entity
coa = entity.get_default_coa()
accounts_data = [
# Current Assets (must start with 1)
{
"code": "1010",
"name": "Cash on Hand",
"role": roles.ASSET_CA_CASH,
"balance_type": roles.DEBIT,
"locked": False,
"default": True, # Default for ASSET_CA_CASH
},
{
"code": "1020",
"name": "Bank",
"role": roles.ASSET_CA_CASH,
"balance_type": roles.DEBIT,
"locked": False,
"default": False,
},
{
"code": "1030",
"name": "Accounts Receivable",
"role": roles.ASSET_CA_RECEIVABLES,
"balance_type": roles.DEBIT,
"locked": False,
"default": True, # Default for ASSET_CA_RECEIVABLES
},
{
"code": "1040",
"name": "Inventory (Cars)",
"role": roles.ASSET_CA_INVENTORY,
"balance_type": roles.DEBIT,
"locked": False,
"default": True, # Default for ASSET_CA_INVENTORY
},
{
"code": "1045",
"name": "Spare Parts Inventory",
"role": roles.ASSET_CA_INVENTORY,
"balance_type": roles.DEBIT,
"locked": False,
"default": False,
},
{
"code": "1050",
"name": "Employee Advances",
"role": roles.ASSET_CA_RECEIVABLES,
"balance_type": roles.DEBIT,
"locked": False,
"default": False,
},
{
"code": "1060",
"name": "Prepaid Expenses",
"role": roles.ASSET_CA_PREPAID,
"balance_type": roles.DEBIT,
"locked": False,
"default": True, # Default for ASSET_CA_PREPAID
},
{
"code": "1070",
"name": "Notes Receivable",
"role": roles.ASSET_LTI_NOTES_RECEIVABLE,
"balance_type": roles.DEBIT,
"locked": False,
"default": True, # Default for ASSET_LTI_NOTES_RECEIVABLE
},
# Fixed Assets (must also start with 1)
{
"code": "1110",
"name": "Lands",
"role": roles.ASSET_LTI_LAND,
"balance_type": roles.DEBIT,
"locked": False,
"default": True, # Default for ASSET_LTI_LAND
},
{
"code": "1111",
"name": "Buildings",
"role": roles.ASSET_PPE_BUILDINGS,
"balance_type": roles.DEBIT,
"locked": False,
"default": True, # Default for ASSET_PPE_BUILDINGS
},
{
"code": "1112",
"name": "Company Vehicles",
"role": roles.ASSET_PPE_EQUIPMENT,
"balance_type": roles.DEBIT,
"locked": False,
"default": True, # Default for ASSET_PPE_EQUIPMENT
},
{
"code": "1113",
"name": "Equipment & Tools",
"role": roles.ASSET_PPE_EQUIPMENT,
"balance_type": roles.DEBIT,
"locked": False,
"default": False,
},
{
"code": "1114",
"name": "Furniture & Fixtures",
"role": roles.ASSET_PPE_EQUIPMENT,
"balance_type": roles.DEBIT,
"locked": False,
"default": False,
},
{
"code": "1115",
"name": "Other Fixed Assets",
"role": roles.ASSET_PPE_EQUIPMENT,
"balance_type": roles.DEBIT,
"locked": False,
"default": False,
},
{
"code": "1120",
"name": "Long-term Investments",
"role": roles.ASSET_LTI_SECURITIES,
"balance_type": roles.DEBIT,
"locked": False,
"default": True, # Default for ASSET_LTI_SECURITIES
},
{
"code": "1130",
"name": "Intangible Assets",
"role": roles.ASSET_INTANGIBLE_ASSETS,
"balance_type": roles.DEBIT,
"locked": False,
"default": True, # Default for ASSET_INTANGIBLE_ASSETS
},
# Current Liabilities (must start with 2)
{
"code": "2010",
"name": "Accounts Payable",
"role": roles.LIABILITY_CL_ACC_PAYABLE,
"balance_type": roles.CREDIT,
"locked": True,
"default": True, # Default for LIABILITY_CL_ACC_PAYABLE
},
{
"code": "2020",
"name": "Notes Payable",
"role": roles.LIABILITY_CL_ST_NOTES_PAYABLE,
"balance_type": roles.CREDIT,
"locked": False,
"default": True, # Default for LIABILITY_CL_ST_NOTES_PAYABLE
},
{
"code": "2030",
"name": "Short-term Loans",
"role": roles.LIABILITY_CL_ST_NOTES_PAYABLE,
"balance_type": roles.CREDIT,
"locked": False,
"default": False,
},
{
"code": "2040",
"name": "Employee Payables",
"role": roles.LIABILITY_CL_WAGES_PAYABLE,
"balance_type": roles.CREDIT,
"locked": False,
"default": True, # Default for LIABILITY_CL_WAGES_PAYABLE
},
{
"code": "2050",
"name": "Accrued Expenses",
"role": roles.LIABILITY_CL_OTHER,
"balance_type": roles.CREDIT,
"locked": False,
"default": True, # Default for LIABILITY_CL_OTHER
},
{
"code": "2060",
"name": "Accrued Taxes",
"role": roles.LIABILITY_CL_TAXES_PAYABLE,
"balance_type": roles.CREDIT,
"locked": False,
"default": False, # Default for LIABILITY_CL_TAXES_PAYABLE
},
{
"code": "2070",
"name": "Provisions",
"role": roles.LIABILITY_CL_OTHER,
"balance_type": roles.CREDIT,
"locked": False,
"default": False,
},
# Long-term Liabilities (must also start with 2)
{
"code": "2103",
"name": "Deferred Revenue",
"role": roles.LIABILITY_CL_DEFERRED_REVENUE,
"balance_type": roles.CREDIT,
"locked": False,
"default": True, # Default for LIABILITY_CL_DEFERRED_REVENUE
},
{
"code": "2200",
"name": "Tax Payable",
"role": roles.LIABILITY_CL_TAXES_PAYABLE,
"balance_type": roles.CREDIT,
"locked": False,
"default": True,
},
{
"code": "2210",
"name": "Long-term Bank Loans",
"role": roles.LIABILITY_LTL_NOTES_PAYABLE,
"balance_type": roles.CREDIT,
"locked": False,
"default": True, # Default for LIABILITY_LTL_NOTES_PAYABLE
},
{
"code": "2220",
"name": "Lease Liabilities",
"role": roles.LIABILITY_LTL_NOTES_PAYABLE,
"balance_type": roles.CREDIT,
"locked": False,
"default": False,
},
{
"code": "2230",
"name": "Other Long-term Liabilities",
"role": roles.LIABILITY_LTL_NOTES_PAYABLE,
"balance_type": roles.CREDIT,
"locked": False,
"default": False,
},
# Equity (must start with 3)
{
"code": "3010",
"name": "Capital",
"role": roles.EQUITY_CAPITAL,
"balance_type": roles.CREDIT,
"locked": True,
"default": True, # Default for EQUITY_CAPITAL
},
{
"code": "3020",
"name": "Statutory Reserve",
"role": roles.EQUITY_ADJUSTMENT,
"balance_type": roles.CREDIT,
"locked": False,
"default": True, # Default for EQUITY_ADJUSTMENT
},
{
"code": "3030",
"name": "Retained Earnings",
"role": roles.EQUITY_ADJUSTMENT,
"balance_type": roles.CREDIT,
"locked": False,
"default": False,
},
{
"code": "3040",
"name": "Profit & Loss for the Period",
"role": roles.EQUITY_ADJUSTMENT,
"balance_type": roles.CREDIT,
"locked": False,
"default": False,
},
# Revenue (must start with 4)
{
"code": "4010",
"name": "Car Sales",
"role": roles.INCOME_OPERATIONAL,
"balance_type": roles.CREDIT,
"locked": True,
"default": True, # Default for INCOME_OPERATIONAL
},
{
"code": "4020",
"name": "After-Sales Services",
"role": roles.INCOME_OPERATIONAL,
"balance_type": roles.CREDIT,
"locked": False,
"default": False,
},
{
"code": "4030",
"name": "Car Rental Income",
"role": roles.INCOME_PASSIVE,
"balance_type": roles.CREDIT,
"locked": False,
"default": True, # Default for INCOME_PASSIVE
},
{
"code": "4040",
"name": "Other Income",
"role": roles.INCOME_OTHER,
"balance_type": roles.CREDIT,
"locked": False,
"default": True, # Default for INCOME_OTHER
},
# Expenses (must start with 5 for COGS, 6 for others)
{
"code": "5010",
"name": "Cost of Goods Sold",
"role": roles.COGS,
"balance_type": roles.DEBIT,
"locked": True,
"default": True, # Default for COGS
},
{
"code": "5015",
"name": "Spare Parts Cost Consumed",
"role": roles.COGS,
"balance_type": roles.DEBIT,
"locked": False,
"default": False,
},
{
"code": "6010",
"name": "Salaries & Wages",
"role": roles.EXPENSE_OPERATIONAL,
"balance_type": roles.DEBIT,
"locked": False,
"default": True, # Default for EXPENSE_OPERATIONAL
},
{
"code": "6020",
"name": "Rent",
"role": roles.EXPENSE_OPERATIONAL,
"balance_type": roles.DEBIT,
"locked": False,
"default": False,
},
{
"code": "6030",
"name": "Utilities",
"role": roles.EXPENSE_OPERATIONAL,
"balance_type": roles.DEBIT,
"locked": False,
"default": False,
},
{
"code": "6040",
"name": "Advertising & Marketing",
"role": roles.EXPENSE_OPERATIONAL,
"balance_type": roles.DEBIT,
"locked": False,
"default": False,
},
{
"code": "6050",
"name": "Maintenance",
"role": roles.EXPENSE_OPERATIONAL,
"balance_type": roles.DEBIT,
"locked": False,
"default": False,
},
{
"code": "6060",
"name": "Operating Expenses",
"role": roles.EXPENSE_OPERATIONAL,
"balance_type": roles.DEBIT,
"locked": False,
"default": False,
},
{
"code": "6070",
"name": "Depreciation",
"role": roles.EXPENSE_DEPRECIATION,
"balance_type": roles.DEBIT,
"locked": False,
"default": True, # Default for EXPENSE_DEPRECIATION
},
{
"code": "6080",
"name": "Fees & Taxes",
"role": roles.EXPENSE_OPERATIONAL,
"balance_type": roles.DEBIT,
"locked": False,
"default": False,
},
{
"code": "6090",
"name": "Bank Charges",
"role": roles.EXPENSE_OPERATIONAL,
"balance_type": roles.DEBIT,
"locked": False,
"default": False,
},
{
"code": "6100",
"name": "Other Expenses",
"role": roles.EXPENSE_OTHER,
"balance_type": roles.DEBIT,
"locked": False,
"default": True, # Default for EXPENSE_OTHER
},
]
for account_data in accounts_data:
try:
account = entity.create_account(
coa_model=coa,
code=account_data["code"],
name=_(account_data["name"]),
role=_(account_data["role"]),
balance_type=_(account_data["balance_type"]),
active=True,
)
account.role_default = account_data["default"]
account.save()
logger.info(f"Created default account: {account}")
except Exception as e:
logger.error(f"Error creating default account: {account_data['code']}, {e}")
def create_coa_accounts1(pk):
with transaction.atomic():
instance = Dealer.objects.select_for_update().get(pk=pk)
entity = instance.entity
coa = entity.get_default_coa()
# Cash Account
asset_ca_cash = entity.create_account(
coa_model=coa,
code="1101",
role=roles.ASSET_CA_CASH,
name=_("Cash"),
balance_type="debit",
active=True,
)
asset_ca_cash.role_default = True
asset_ca_cash.save()
# Accounts Receivable Account
asset_ca_receivables = entity.create_account(
coa_model=coa,
code="1102",
role=roles.ASSET_CA_RECEIVABLES,
name=_("Accounts Receivable"),
balance_type="debit",
active=True,
)
asset_ca_receivables.role_default = True
asset_ca_receivables.save()
# Inventory Account
asset_ca_inventory = entity.create_account(
coa_model=coa,
code="1103",
role=roles.ASSET_CA_INVENTORY,
name=_("Inventory"),
balance_type="debit",
active=True,
)
asset_ca_inventory.role_default = True
asset_ca_inventory.save()
# 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()
# Employee Expenses Account
asset_ca_prepaid_employee = entity.create_account(
coa_model=coa,
code="1105",
role=roles.ASSET_CA_PREPAID,
name=_("Employee Advance"),
balance_type="debit",
active=True,
)
# VAT Payable Account
liability_ltl_vat_receivable = entity.create_account(
coa_model=coa,
code="1106",
role=roles.ASSET_CA_RECEIVABLES,
name=_("VAT Receivable"),
balance_type="debit",
active=True,
)
# Buildings Accumulated Depreciation Account
asset_ppe_buildings_accum_depreciation = entity.create_account(
coa_model=coa,
code="1201",
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()
# intangible Account
asset_lti_land_intangable = entity.create_account(
coa_model=coa,
code="1202",
role=roles.ASSET_INTANGIBLE_ASSETS,
name=_("Intangible Assets"),
balance_type="debit",
active=True,
)
asset_lti_land_intangable.role_default = True
asset_lti_land_intangable.save()
# investment property Account
asset_lti_land_investment = entity.create_account(
coa_model=coa,
code="1204",
role=roles.ASSET_LTI_SECURITIES,
name=_("Investments"),
balance_type="debit",
active=True,
)
asset_lti_land_investment.role_default = True
asset_lti_land_investment.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()
# Accounts Payable Account
liability_cl_acc_payable = entity.create_account(
coa_model=coa,
code="2101",
role=roles.LIABILITY_CL_ACC_PAYABLE,
name=_("Accounts Payable"),
balance_type="credit",
active=True,
)
liability_cl_acc_payable.role_default = True
liability_cl_acc_payable.save()
# Deferred Revenue Account
liability_cl_def_rev = entity.create_account(
coa_model=coa,
code="2103",
role=roles.LIABILITY_CL_DEFERRED_REVENUE,
name=_("Deferred Revenue"),
balance_type="credit",
active=True,
)
liability_cl_def_rev.role_default = True
liability_cl_def_rev.save()
# 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()
# VAT Payable Account
liability_ltl_vat_payable = entity.create_account(
coa_model=coa,
code="2106",
role=roles.LIABILITY_CL_OTHER,
name=_("VAT Payable"),
balance_type="credit",
active=True,
)
# taxes Payable Account
liability_ltl_taxes_payable = entity.create_account(
coa_model=coa,
code="2107",
role=roles.LIABILITY_CL_OTHER,
name=_("Taxes Payable"),
balance_type="credit",
active=True,
)
# social insurance Payable Account
liability_ltl_social_insurance_payable = entity.create_account(
coa_model=coa,
code="2108",
role=roles.LIABILITY_LTL_NOTES_PAYABLE,
name=_("Social Insurance Payable"),
balance_type="credit",
active=True,
)
# End of Service Benefits
entity.create_account(
coa_model=coa,
code="2202",
role=roles.LIABILITY_LTL_NOTES_PAYABLE,
name=_("End of Service Benefits"),
balance_type="credit",
active=True,
)
# Mortgage Payable Account
liability_ltl_mortgage_payable = entity.create_account(
coa_model=coa,
code="2203",
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()
# Capital
equity_capital = entity.create_account(
coa_model=coa,
code="3101",
role=roles.EQUITY_CAPITAL,
name=_("Registered Capital"),
balance_type="credit",
active=True,
)
equity_capital.role_default = True
equity_capital.save()
entity.create_account(
coa_model=coa,
code="3102",
role=roles.EQUITY_CAPITAL,
name=_("Additional Paid-In Capital"),
balance_type="credit",
active=True,
)
# Other Equity
other_equity = entity.create_account(
coa_model=coa,
code="3201",
role=roles.EQUITY_COMMON_STOCK,
name=_("Opening Balances"),
balance_type="credit",
active=True,
)
other_equity.role_default = True
other_equity.save()
# Reserves
reserve = entity.create_account(
coa_model=coa,
code="3301",
role=roles.EQUITY_ADJUSTMENT,
name=_("Statutory Reserve"),
balance_type="credit",
active=True,
)
reserve.role_default = True
reserve.save()
entity.create_account(
coa_model=coa,
code="3302",
role=roles.EQUITY_ADJUSTMENT,
name=_("Foreign Currency Translation Reserve"),
balance_type="credit",
active=True,
)
# Retained Earnings Account
equity_retained_earnings = entity.create_account(
coa_model=coa,
code="3401",
role=roles.EQUITY_PREFERRED_STOCK,
name=_("Operating Profits and Losses"),
balance_type="credit",
active=True,
)
equity_retained_earnings.role_default = True
equity_retained_earnings.save()
equity_retained_earnings_losses = entity.create_account(
coa_model=coa,
code="3402",
role=roles.EQUITY_PREFERRED_STOCK,
name=_("Retained Earnings (or Losses)"),
balance_type="credit",
active=True,
)
# Sales Revenue Account
income_operational = entity.create_account(
coa_model=coa,
code="4101",
role=roles.INCOME_OPERATIONAL,
name=_("Sales Revenue"),
balance_type="credit",
active=True,
)
income_operational.role_default = True
income_operational.save()
# 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()
# Uneared Income Account
income_unearned = entity.create_account(
coa_model=coa,
code="4103",
role=roles.INCOME_OTHER,
name=_("Unearned Income"),
balance_type="credit",
active=True,
)
# Operating Revenues
entity.create_account(
coa_model=coa,
code="4104",
role=roles.INCOME_OPERATIONAL,
name=_("Sales/Service Revenue"),
balance_type="credit",
active=True,
)
# Non-Operating Revenues
entity.create_account(
coa_model=coa,
code="4201",
role=roles.INCOME_OTHER,
name=_("Non-Operating Revenues"),
balance_type="credit",
active=True,
)
# Cost of Goods Sold (COGS) Account
expense_cogs = entity.create_account(
coa_model=coa,
code="5101",
role=roles.COGS,
name=_("Cost of Goods Sold"),
balance_type="debit",
active=True,
)
expense_cogs.role_default = True
expense_cogs.save()
# accrued Expenses Account
expense_cogs = entity.create_account(
coa_model=coa,
code="6117",
role=roles.EXPENSE_OPERATIONAL,
name=_("Accrued Expenses"),
balance_type="debit",
active=True,
)
# accrued salaries Account
expense_cogs = entity.create_account(
coa_model=coa,
code="6118",
role=roles.EXPENSE_OPERATIONAL,
name=_("Accrued Salaries"),
balance_type="debit",
active=True,
)
# Rent Expense Account
expense_rent = entity.create_account(
coa_model=coa,
code="6102",
role=roles.EXPENSE_OPERATIONAL,
name=_("Rent Expense"),
balance_type="debit",
active=True,
)
# expense_rent.role_default = True
# expense_rent.save()
# Salaries and Administrative Fees
expense_salaries = entity.create_account(
coa_model=coa,
code="6103",
role=roles.EXPENSE_OPERATIONAL,
name=_("Salaries and Administrative Fees"),
balance_type="debit",
active=True,
)
# Medical Insurance
expense_medical_insurance = entity.create_account(
coa_model=coa,
code="6104",
role=roles.EXPENSE_OPERATIONAL,
name=_("Medical Insurance"),
balance_type="debit",
active=True,
)
# Marketing and Advertising Expenses
expense_marketing = entity.create_account(
coa_model=coa,
code="6105",
role=roles.EXPENSE_OPERATIONAL,
name=_("Marketing and Advertising Expenses"),
balance_type="debit",
active=True,
)
# Commissions and Incentives
expense_commissions = entity.create_account(
coa_model=coa,
code="6106",
role=roles.EXPENSE_OPERATIONAL,
name=_("Commissions and Incentives"),
balance_type="debit",
active=True,
)
# Travel Tickets
expense_travel = entity.create_account(
coa_model=coa,
code="6107",
role=roles.EXPENSE_OPERATIONAL,
name=_("Travel Tickets"),
balance_type="debit",
active=True,
)
# Social Insurance
expense_other = entity.create_account(
coa_model=coa,
code="6108",
role=roles.EXPENSE_OPERATIONAL,
name=_("Social Insurance"),
balance_type="debit",
active=True,
)
# Government Fees
expense_other = entity.create_account(
coa_model=coa,
code="6109",
role=roles.EXPENSE_OPERATIONAL,
name=_("Government Fees"),
balance_type="debit",
active=True,
)
# Fees and Subscriptions
expense_other = entity.create_account(
coa_model=coa,
code="6110",
role=roles.EXPENSE_OPERATIONAL,
name=_("Fees and Subscriptions"),
balance_type="debit",
active=True,
)
# Office Services Expenses
expense_other = entity.create_account(
coa_model=coa,
code="6111",
role=roles.EXPENSE_OPERATIONAL,
name=_("Office Services Expenses"),
balance_type="debit",
active=True,
)
# Office Supplies and Printing
expense_other = entity.create_account(
coa_model=coa,
code="6112",
role=roles.EXPENSE_OPERATIONAL,
name=_("Office Supplies and Printing"),
balance_type="debit",
active=True,
)
# Hospitality Expenses
expense_other = entity.create_account(
coa_model=coa,
code="6113",
role=roles.EXPENSE_OPERATIONAL,
name=_("Hospitality Expenses"),
balance_type="debit",
active=True,
)
# Bank Commissions
expense_other = entity.create_account(
coa_model=coa,
code="6114",
role=roles.EXPENSE_OPERATIONAL,
name=_("Bank Commissions"),
balance_type="debit",
active=True,
)
# Other Expenses
expense_other = entity.create_account(
coa_model=coa,
code="6115",
role=roles.EXPENSE_OPERATIONAL,
name=_("Other Expenses"),
balance_type="debit",
active=True,
)
# Transportation Expenses
expense_other = entity.create_account(
coa_model=coa,
code="6116",
role=roles.EXPENSE_OPERATIONAL,
name=_("Transportation Expenses"),
balance_type="debit",
active=True,
)
# 5.1 Direct Costs
entity.create_account(
coa_model=coa,
code="6201",
role=roles.EXPENSE_OPERATIONAL,
name=_("Cost of Goods Sold"),
balance_type="debit",
active=True,
)
entity.create_account(
coa_model=coa,
code="6202",
role=roles.EXPENSE_OPERATIONAL,
name=_("Salaries and Wages"),
balance_type="debit",
active=True,
)
entity.create_account(
coa_model=coa,
code="6203",
role=roles.EXPENSE_OPERATIONAL,
name=_("Sales Commissions"),
balance_type="debit",
active=True,
)
entity.create_account(
coa_model=coa,
code="6204",
role=roles.EXPENSE_OPERATIONAL,
name=_("Shipping and Customs Clearance"),
balance_type="debit",
active=True,
)
# 5.3 Non-Operating Expenses
entity.create_account(
coa_model=coa,
code="6301",
role=roles.EXPENSE_OTHER,
name=_("Zakat"),
balance_type="debit",
active=True,
)
entity.create_account(
coa_model=coa,
code="6302",
role=roles.EXPENSE_OTHER,
name=_("Taxes"),
balance_type="debit",
active=True,
)
entity.create_account(
coa_model=coa,
code="6303",
role=roles.EXPENSE_OTHER,
name=_("Foreign Currency Translation"),
balance_type="debit",
active=True,
)
entity.create_account(
coa_model=coa,
code="6304",
role=roles.EXPENSE_OTHER,
name=_("Interest Expenses"),
balance_type="debit",
active=True,
)
# create_settings(instance.pk)
# @background
# def create_groups(instance):
# group_names = ["Inventory", "Accountant", "Sales"]
# for group_name in group_names:
# group, _ = Group.objects.get_or_create(name=f"{instance.pk}_{group_name}")
# group_manager,_ = CustomGroup.objects.get_or_create(name=group_name, dealer=instance, group=group)
# group_manager.set_default_permissions()
# instance.user.groups.add(group)
# @background
def create_accounts_for_make(dealer, makes):
entity = dealer.entity
coa = entity.get_default_coa()
name = ["Inventory", "Revenue", "Cogs"]
role = [roles.ASSET_CA_INVENTORY, roles.ASSET_CA_RECEIVABLES, roles.COGS]
balance_type = ["debit", "credit", "debit"]
for name, role, balance_type in zip(name, role, balance_type):
create_make_accounts(entity, coa, makes, name, role, balance_type)
def create_make_accounts(entity, coa, makes, name, role, balance_type):
for make in makes:
last_account = (
entity.get_all_accounts().filter(role=role).order_by("-created").first()
)
if len(last_account.code) == 4:
code = f"{int(last_account.code)}{1:03d}"
elif len(last_account.code) > 4:
code = f"{int(last_account.code) + 1}"
acc = (
entity.get_all_accounts()
.filter(
name=f"{name}:{make.name}",
role=role,
coa_model=coa,
balance_type=balance_type,
active=True,
)
.first()
)
if not acc:
acc = entity.create_account(
name=f"{name}:{make.name}",
code=code,
role=role,
coa_model=coa,
balance_type=balance_type,
active=True,
)
return acc
def send_email(from_, to_, subject, message):
subject = subject
message = message
from_email = from_
recipient_list = [to_]
async_task(send_mail, subject, message, from_email, recipient_list)
def create_user_dealer(email, password, name, arabic_name, phone, crn, vrn, address):
with transaction.atomic():
user = User.objects.create(username=email, email=email)
user.set_password(password)
user.save()
# EmailAddress.objects.create(user=user, email=email, verified=True, primary=True)
group = Group.objects.create(name=f"{user.pk}-Admin")
user.groups.add(group)
for perm in Permission.objects.filter(
content_type__app_label__in=["inventory", "django_ledger"]
):
group.permissions.add(perm)
# StaffMember.objects.create(user=user)
dealer = Dealer.objects.create(
user=user,
name=name,
arabic_name=arabic_name,
crn=crn,
vrn=vrn,
phone_number=phone,
address=address,
)
return dealer
# def create_groups(dealer_slug):
# from inventory.models import CustomGroup
# instance = Dealer.objects.get(slug=dealer_slug)
# def run():
# for group_name in ["Inventory", "Accountant", "Sales", "Manager"]:
# group, created = Group.objects.get_or_create(
# name=f"{instance.slug}_{group_name}"
# )
# group_manager, created = CustomGroup.objects.get_or_create(
# name=group_name, dealer=instance, group=group
# )
# if created:
# group_manager.set_default_permissions()
# instance.user.groups.add(group)
# transaction.on_commit(run)
def send_bilingual_reminder(user_id, plan_id, expiration_date, days_until_expire):
"""Send bilingual email reminder using Django-Q"""
try:
user = User.objects.get(id=user_id)
plan = Plan.objects.get(id=plan_id)
# Determine user language preference
user_language = getattr(user, 'language', settings.LANGUAGE_CODE)
activate(user_language)
# Context data
context = {
'user': user,
'plan': plan,
'expiration_date': expiration_date,
'days_until_expire': days_until_expire,
'SITE_NAME': settings.SITE_NAME,
'RENEWAL_URL': "url" ,#settings.RENEWAL_URL,
'direction': 'rtl' if user_language.startswith('ar') else 'ltr'
}
# Subject with translation
subject_en = f"Your {plan.name} subscription expires in {days_until_expire} days"
subject_ar = f"اشتراكك في {plan.name} ينتهي خلال {days_until_expire} أيام"
# Render templates
text_content = render_to_string([
f'emails/expiration_reminder_{user_language}.txt',
'emails/expiration_reminder.txt'
], context)
html_content = render_to_string([
f'emails/expiration_reminder_{user_language}.html',
'emails/expiration_reminder.html'
], context)
# Create email
email = EmailMultiAlternatives(
subject=subject_ar if user_language.startswith('ar') else subject_en,
body=text_content,
from_email=settings.DEFAULT_FROM_EMAIL,
to=[user.email]
)
email.attach_alternative(html_content, "text/html")
email.send()
return f"Sent to {user.email} in {user_language}"
except Exception as e:
logger.error(f"Email failed: {str(e)}")
raise
def handle_email_result(task):
"""Callback for email results"""
if task.success:
logger.info(f"Email task succeeded: {task.result}")
else:
logger.error(f"Email task failed: {task.result}")
def send_schedule_reminder_email(schedule_id):
"""
Sends an email reminder for a specific schedule.
This function is designed to be called by django-q.
"""
try:
schedule = Schedule.objects.get(pk=schedule_id)
# Ensure the user has an email and the schedule is not completed/canceled
if not schedule.scheduled_by.email or schedule.status in ["completed", "canceled"]:
logger.error(f"Skipping email for Schedule ID {schedule_id}: No email or schedule status is {schedule.status}.")
return
user_email = schedule.scheduled_by.email
Notification.objects.create(
user=schedule.scheduled_by,
message=_(
"""
Reminder: You have an appointment scheduled for {scheduled_type} After 15 minutes <a href="{url}" target="_blank">View</a>.
"""
).format(scheduled_type=schedule.scheduled_type, url=reverse("schedule_calendar", kwargs={"dealer_slug": schedule.dealer.slug})),)
# Prepare context for email templates
context = {
'schedule_purpose': schedule.purpose,
'scheduled_at': schedule.scheduled_at.astimezone(timezone.get_current_timezone()).strftime('%Y-%m-%d %H:%M %Z'), # Format with timezone
'schedule_type': schedule.scheduled_type,
'customer_name': schedule.customer.customer_name if schedule.customer else 'N/A',
'notes': schedule.notes,
'user_name': schedule.scheduled_by.get_full_name() or schedule.scheduled_by.email,
}
# Render email content from templates
html_message = render_to_string('emails/schedule_reminder.html', context)
plain_message = render_to_string('emails/schedule_reminder.txt', context)
send_mail(
f'Reminder: Your Upcoming Schedule - {schedule.purpose}',
plain_message,
settings.DEFAULT_FROM_EMAIL,
[user_email],
html_message=html_message,
)
logger.info(f"Successfully sent reminder email for Schedule ID: {schedule_id} to {user_email}")
except Schedule.DoesNotExist:
logger.info(f"Schedule with ID {schedule_id} does not exist. Cannot send reminder.")
except Exception as e:
logger.info(f"Error sending reminder email for Schedule ID {schedule_id}: {e}")
# Optional: A hook function to log the status of the email task (add to your_app/tasks.py)
def log_email_status(task):
"""
This function will be called by django-q after the send_schedule_reminder_email task completes.
It logs whether the task was successful or not.
"""
if task.success:
logger.info(f"Email task for Schedule ID {task.args[0]} completed successfully. Result: {task.result}")
else:
logger.error(f"Email task for Schedule ID {task.args[0]} failed. Error: {task.result}")