This commit is contained in:
ismail 2025-09-02 14:09:28 +03:00
parent 05b18e9da3
commit a079ceec6d
9 changed files with 298 additions and 63 deletions

View File

@ -6,17 +6,64 @@ logger = logging.getLogger(__name__)
def check_create_coa_accounts(task):
logger.info("Checking if all accounts are created")
instance = task.kwargs["dealer"]
entity = instance.entity
coa = entity.get_default_coa()
"""
Hook to verify account creation and handle failures
"""
if task.success:
logger.info("Account creation task completed successfully")
return
for account_data in get_accounts_data():
if entity.get_all_accounts().filter(code=account_data["code"]).exists():
logger.info(f"Default account already exists: {account_data['code']}")
continue
logger.info(f"Default account does not exist: {account_data['code']}")
create_account(entity, coa, account_data)
logger.warning("Account creation task failed, checking status...")
try:
dealer_id = task.kwargs.get('dealer_id')
if not dealer_id:
logger.error("No dealer_id in task kwargs")
return
from .models import Dealer
instance = Dealer.objects.select_related('entity').get(id=dealer_id)
entity = instance.entity
if not entity:
logger.error(f"No entity for dealer {dealer_id}")
return
coa = entity.get_default_coa()
if not coa:
logger.error(f"No COA for entity {entity.id}")
return
# Check which accounts are missing and create them
from .utils import get_accounts_data, create_account
missing_accounts = []
for account_data in get_accounts_data():
if not entity.get_all_accounts().filter(code=account_data["code"]).exists():
missing_accounts.append(account_data)
logger.info(f"Missing account: {account_data['code']}")
if missing_accounts:
logger.info(f"Creating {len(missing_accounts)} missing accounts")
for account_data in missing_accounts:
create_account(entity, coa, account_data)
else:
logger.info("All accounts are already created")
except Exception as e:
logger.error(f"Error in check_create_coa_accounts hook: {e}")
# def check_create_coa_accounts(task):
# logger.info("Checking if all accounts are created")
# instance = task.kwargs["dealer"]
# entity = instance.entity
# coa = entity.get_default_coa()
# for account_data in get_accounts_data():
# if entity.get_all_accounts().filter(code=account_data["code"]).exists():
# logger.info(f"Default account already exists: {account_data['code']}")
# continue
# logger.info(f"Default account does not exist: {account_data['code']}")
# create_account(entity, coa, account_data)
def print_results(task):

View File

@ -685,7 +685,7 @@ class Car(Base):
)
#
additional_services = models.ManyToManyField(
AdditionalServices, related_name="additionals", blank=True, null=True
AdditionalServices, related_name="additionals"
)
cost_price = models.DecimalField(
max_digits=14,

View File

@ -135,55 +135,101 @@ def create_car_location(sender, instance, created, **kwargs):
print(f"Failed to create CarLocation for car {instance.vin}: {e}")
# Create Entity
@receiver(post_save, sender=models.Dealer)
def create_ledger_entity(sender, instance, created, **kwargs):
"""
Signal handler for creating ledger entities and initializing accounts for a new Dealer instance upon creation.
This signal is triggered when a new Dealer instance is saved to the database. It performs the following actions:
1. Creates a ledger entity for the Dealer with necessary configurations.
2. Generates a chart of accounts (COA) for the entity and assigns it as the default.
3. Creates predefined unit of measures (UOMs) related to the entity.
4. Initializes and assigns default accounts under various roles (e.g., assets, liabilities) for the entity with their
respective configurations (e.g., account code, balance type).
This function ensures all necessary financial records and accounts are set up when a new Dealer is added, preparing the
system for future financial transactions and accounting operations.
:param sender: The model class that sent the signal (in this case, Dealer).
:param instance: The instance of the model being saved.
:param created: A boolean indicating whether a new record was created.
:param kwargs: Additional keyword arguments passed by the signal.
:return: None
"""
if created:
entity_name = instance.user.dealer.name
entity = EntityModel.create_entity(
name=entity_name,
admin=instance.user,
use_accrual_method=True,
fy_start_month=1,
)
try:
# Use transaction to ensure atomicity
with transaction.atomic():
entity_name = instance.user.dealer.name
entity = EntityModel.create_entity(
name=entity_name,
admin=instance.user,
use_accrual_method=True,
fy_start_month=1,
)
if entity:
instance.entity = entity
instance.save()
coa = entity.create_chart_of_accounts(
assign_as_default=True, commit=True, coa_name=_(f"{entity_name}-COA")
)
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])
if entity:
instance.entity = entity
instance.save(update_fields=['entity'])
# Create COA accounts, background task
# Create COA synchronously first
coa = entity.create_chart_of_accounts(
assign_as_default=True, commit=True,
coa_name=_(f"{entity_name}-COA")
)
if coa:
# Create essential UOMs synchronously
entity.create_uom(name="Unit", unit_abbr="unit")
# Schedule async task after successful synchronous operations
async_task(
func="inventory.tasks.create_coa_accounts",
dealer=instance,
dealer_id=instance.id, # Pass ID instead of object
hook="inventory.hooks.check_create_coa_accounts",
ack_failure=True, # Ensure task failures are acknowledged
sync=False # Explicitly set to async
)
except Exception as e:
logger.error(f"Failed to create ledger entity for dealer {instance.id}: {e}")
# Schedule retry task
async_task(
func="inventory.tasks.retry_entity_creation",
dealer_id=instance.id,
retry_count=0
)
# Create Entity
# @receiver(post_save, sender=models.Dealer)
# def create_ledger_entity(sender, instance, created, **kwargs):
# """
# Signal handler for creating ledger entities and initializing accounts for a new Dealer instance upon creation.
# This signal is triggered when a new Dealer instance is saved to the database. It performs the following actions:
# 1. Creates a ledger entity for the Dealer with necessary configurations.
# 2. Generates a chart of accounts (COA) for the entity and assigns it as the default.
# 3. Creates predefined unit of measures (UOMs) related to the entity.
# 4. Initializes and assigns default accounts under various roles (e.g., assets, liabilities) for the entity with their
# respective configurations (e.g., account code, balance type).
# This function ensures all necessary financial records and accounts are set up when a new Dealer is added, preparing the
# system for future financial transactions and accounting operations.
# :param sender: The model class that sent the signal (in this case, Dealer).
# :param instance: The instance of the model being saved.
# :param created: A boolean indicating whether a new record was created.
# :param kwargs: Additional keyword arguments passed by the signal.
# :return: None
# """
# if created:
# entity_name = instance.user.dealer.name
# entity = EntityModel.create_entity(
# name=entity_name,
# admin=instance.user,
# use_accrual_method=True,
# fy_start_month=1,
# )
# if entity:
# instance.entity = entity
# instance.save()
# coa = entity.create_chart_of_accounts(
# assign_as_default=True, commit=True, coa_name=_(f"{entity_name}-COA")
# )
# 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 COA accounts, background task
# async_task(
# func="inventory.tasks.create_coa_accounts",
# dealer=instance,
# hook="inventory.hooks.check_create_coa_accounts",
# )
# async_task('inventory.tasks.check_create_coa_accounts', instance, schedule_type='O', schedule_time=timedelta(seconds=20))
# create_settings(instance.pk)

View File

@ -62,14 +62,117 @@ def create_settings(pk):
)
def create_coa_accounts(**kwargs):
logger.info("creating all accounts are created")
instance = kwargs.get("dealer")
entity = instance.entity
coa = entity.get_default_coa()
def create_coa_accounts(dealer_id, **kwargs):
"""
Create COA accounts with retry logic and proper error handling
"""
from .models import Dealer
from .utils import create_account, get_accounts_data
for account_data in get_accounts_data():
create_account(entity, coa, account_data)
max_retries = 3
retry_delay = 2 # seconds
for attempt in range(max_retries):
try:
logger.info(f"Attempt {attempt + 1} to create accounts for dealer {dealer_id}")
# Get fresh instance from database
instance = Dealer.objects.select_related('entity').get(id=dealer_id)
entity = instance.entity
if not entity:
logger.error(f"No entity found for dealer {dealer_id}")
return False
coa = entity.get_default_coa()
if not coa:
logger.error(f"No COA found for entity {entity.id}")
return False
logger.info("Creating default accounts")
accounts_created = 0
with transaction.atomic():
for account_data in get_accounts_data():
logger.info(f"Creating account: {account_data['code']}")
if create_account(entity, coa, account_data):
accounts_created += 1
logger.info(f"Successfully created {accounts_created} accounts")
return True
except Exception as e:
logger.error(f"Attempt {attempt + 1} failed: {e}")
if attempt < max_retries - 1:
logger.info(f"Retrying in {retry_delay} seconds...")
time.sleep(retry_delay * (attempt + 1)) # Exponential backoff
else:
logger.error(f"All {max_retries} attempts failed for dealer {dealer_id}")
# Schedule a cleanup or notification task
async_task(
"inventory.tasks.handle_account_creation_failure",
dealer_id=dealer_id,
error=str(e)
)
return False
def retry_entity_creation(dealer_id, retry_count=0):
"""
Retry entity creation if initial attempt failed
"""
from .models import Dealer
from yourapp.models import EntityModel
max_retries = 3
if retry_count >= max_retries:
logger.error(f"Max retries reached for dealer {dealer_id}")
return
try:
instance = Dealer.objects.get(id=dealer_id)
if not instance.entity:
# Retry entity creation
entity_name = instance.user.dealer.name
entity = EntityModel.create_entity(
name=entity_name,
admin=instance.user,
use_accrual_method=True,
fy_start_month=1,
)
if entity:
instance.entity = entity
instance.save()
logger.info(f"Successfully created entity on retry {retry_count + 1}")
# Now trigger account creation
async_task(
"inventory.tasks.create_coa_accounts",
dealer_id=dealer_id
)
except Exception as e:
logger.error(f"Retry {retry_count + 1} failed: {e}")
# Schedule another retry
async_task(
"inventory.tasks.retry_entity_creation",
dealer_id=dealer_id,
retry_count=retry_count + 1
)
# def create_coa_accounts(**kwargs):
# logger.info("creating all accounts are created")
# instance = kwargs.get("dealer")
# logger.info(f"Dealer Instance : {instance}")
# entity = instance.entity
# coa = entity.get_default_coa()
# logger.info("Creating default accounts")
# for account_data in get_accounts_data():
# logger.info(f"Creating account: {account_data['code']}")
# create_account(entity, coa, account_data)
# def create_coa_accounts1(pk):

View File

@ -15,6 +15,7 @@ from django.utils import timezone
from django.db import transaction
from django_ledger.io import roles
from django.contrib import messages
from django.db import IntegrityError
from django.shortcuts import redirect
from django_q.tasks import async_task
from django.core.mail import send_mail
@ -2386,7 +2387,19 @@ def get_accounts_data():
def create_account(entity, coa, account_data):
"""
Create account with proper validation and error handling
"""
try:
# Check if account already exists
existing_account = entity.get_all_accounts().filter(
code=account_data["code"]
).first()
if existing_account:
logger.info(f"Account already exists: {account_data['code']}")
return True
account = entity.create_account(
coa_model=coa,
code=account_data["code"],
@ -2395,11 +2408,37 @@ def create_account(entity, coa, account_data):
balance_type=_(account_data["balance_type"]),
active=True,
)
account.role_default = account_data["default"]
account.save()
logger.info(f"Created default account: {account}")
if account:
account.role_default = account_data["default"]
account.save()
logger.info(f"Successfully created account: {account_data['code']}")
return True
except IntegrityError:
logger.warning(f"Account {account_data['code']} already exists (IntegrityError)")
return True
except Exception as e:
logger.error(f"Error creating default account: {account_data['code']}, {e}")
logger.error(f"Error creating account {account_data['code']}: {e}")
return False
return False
# def create_account(entity, coa, account_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,
# )
# logger.info(f"Created account: {account}")
# 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 get_or_generate_car_image(car):

View File

@ -63,7 +63,7 @@ EditorConfig==0.17.1
Faker==37.4.0
fleming==0.7.0
fonttools==4.58.5
fpdf==1.7.2
# fpdf==1.7.2
fpdf2==2.8.3
greenlet==3.2.3
gunicorn==23.0.0

Binary file not shown.

After

Width:  |  Height:  |  Size: 493 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 KiB