fix the coa issue

This commit is contained in:
ismail 2025-09-10 14:49:18 +03:00
parent ad35058720
commit f2cd3f6969
5 changed files with 122 additions and 135 deletions

View File

@ -9,6 +9,8 @@ def check_create_coa_accounts(task):
"""
Hook to verify account creation and handle failures
"""
from .models import Dealer
if task.success:
logger.info("Account creation task completed successfully")
return
@ -16,14 +18,15 @@ def check_create_coa_accounts(task):
logger.warning("Account creation task failed, checking status...")
try:
dealer_id = task.kwargs.get('dealer_id')
dealer_id = task.kwargs.get('dealer_id',None)
coa_slug = task.kwargs.get('coa_slug', None)
logger.info(f"Checking accounts for dealer {dealer_id}")
logger.info(f"COA slug: {coa_slug}")
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)
instance = Dealer.objects.get(id=dealer_id)
entity = instance.entity
if not entity:
@ -34,11 +37,11 @@ def check_create_coa_accounts(task):
try:
coa = entity.get_coa_model_qs().get(slug=coa_slug)
except Exception as e:
logger.error(f"COA with slug {coa_slug} not found for entity {entity.id}: {e}")
logger.error(f"COA with slug {coa_slug} not found for entity {entity.pk}: {e}")
else:
coa = entity.get_default_coa()
if not coa:
logger.error(f"No COA for entity {entity.id}")
logger.error(f"No COA for entity {entity.pk}")
return
# Check which accounts are missing and create them
@ -46,7 +49,7 @@ def check_create_coa_accounts(task):
missing_accounts = []
for account_data in get_accounts_data():
if not entity.get_all_accounts().filter(code=account_data["code"]).exists():
if not entity.get_all_accounts().filter(coa_model=coa,code=account_data["code"]).exists():
missing_accounts.append(account_data)
logger.info(f"Missing account: {account_data['code']}")

View File

@ -658,16 +658,12 @@ class Car(Base):
CarMake,
models.DO_NOTHING,
db_column="id_car_make",
null=True,
blank=True,
verbose_name=_("Make"),
)
id_car_model = models.ForeignKey(
CarModel,
models.DO_NOTHING,
db_column="id_car_model",
null=True,
blank=True,
verbose_name=_("Model"),
)
year = models.IntegerField(verbose_name=_("Year"))

View File

@ -138,50 +138,36 @@ def create_car_location(sender, instance, created, **kwargs):
@receiver(post_save, sender=models.Dealer)
def create_ledger_entity(sender, instance, created, **kwargs):
if created:
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 not created:
return
if entity:
instance.entity = entity
instance.save(update_fields=['entity'])
try:
with transaction.atomic():
# Create entity
entity = models.EntityModel.create_entity(
name=instance.user.dealer.name,
admin=instance.user,
use_accrual_method=True,
fy_start_month=1,
)
if not entity:
raise Exception("Entity creation failed")
# Create COA synchronously first
coa = entity.create_chart_of_accounts(
assign_as_default=True, commit=True,
coa_name=_(f"{entity_name}-COA")
)
instance.entity = entity
instance.save(update_fields=['entity'])
if coa:
# Create essential UOMs synchronously
for u in models.UnitOfMeasure.choices:
entity.create_uom(name=u[1], unit_abbr=u[0])
# Schedule async task after successful synchronous operations
async_task(
func="inventory.tasks.create_coa_accounts",
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
# Create default COA
entity.create_chart_of_accounts(
assign_as_default=True,
commit=True,
coa_name=f"{entity.name}-COA"
)
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
)
logger.info(f"✅ Setup complete for dealer {instance.id}: entity & COA ready.")
except Exception as e:
logger.error(f"💥 Failed setup for dealer {instance.id}: {e}")
# Optional: schedule retry or alert
# Create Entity
# @receiver(post_save, sender=models.Dealer)
# def create_ledger_entity(sender, instance, created, **kwargs):
@ -1406,6 +1392,7 @@ def handle_user_registration(sender, instance, created, **kwargs):
if instance.is_created:
logger.info(f"User account created: {instance.email}, sending email")
# instance.create_account()
send_email(
settings.DEFAULT_FROM_EMAIL,
instance.email,
@ -1430,15 +1417,31 @@ def handle_user_registration(sender, instance, created, **kwargs):
@receiver(post_save, sender=ChartOfAccountModel)
def handle_chart_of_account(sender, instance, created, **kwargs):
if created:
entity = instance.entity
dealer = instance.entity.admin.dealer
# Create UOMs (minimal, no logging per item)
if not entity.get_uom_all():
for code, name in models.UnitOfMeasure.choices:
entity.create_uom(name=name, unit_abbr=code)
try:
dealer = instance.entity.dealers.first()
async_task(
func="inventory.tasks.create_coa_accounts",
dealer_id=dealer.pk, # Pass ID instead of object
coa_slug=instance.slug,
hook="inventory.hooks.check_create_coa_accounts",
ack_failure=True, # Ensure task failures are acknowledged
sync=False # Explicitly set to async
# Schedule async account creation AFTER commit
transaction.on_commit(
lambda: async_task(
"inventory.tasks.create_coa_accounts",
dealer_id=dealer.pk,
coa_slug=instance.slug,
hook="inventory.hooks.check_create_coa_accounts",
ack_failure=True,
)
)
# async_task(
# func="inventory.tasks.create_coa_accounts",
# dealer_id=dealer.pk, # Pass ID instead of object
# coa_slug=instance.slug,
# 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"Error handling chart of account: {e}")

View File

@ -18,7 +18,7 @@ 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 .utils import get_accounts_data, create_account
# from .utils import get_accounts_data, create_account
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
@ -63,70 +63,64 @@ def create_settings(pk):
)
def create_coa_accounts(dealer_id, **kwargs):
def create_coa_accounts(dealer_id,**kwargs):
"""
Create COA accounts with retry logic and proper error handling
Idempotent: Creates only missing default accounts.
Safe to retry. Returns True if all done.
"""
from .models import Dealer
from .utils import create_account, get_accounts_data
from .utils import get_accounts_data, create_account
try:
dealer = Dealer.objects.get(pk=dealer_id)
entity = dealer.entity
coa_slug = kwargs.get('coa_slug', None)
if not entity:
logger.error(f"❌ No entity for dealer {dealer_id}")
return False
max_retries = 3
retry_delay = 2 # seconds
coa_slug = kwargs.get('coa_slug', None)
logger.info(f"chart of account model slug {coa_slug}")
logger.info(f"Attempting to create accounts for dealer {dealer_id}")
for attempt in range(max_retries):
try:
logger.info(f"Attempt {attempt + 1} to create accounts for dealer {dealer_id}")
instance = Dealer.objects.get(pk=dealer_id)
entity = instance.entity
if not entity:
logger.error(f"No entity found for dealer {dealer_id}")
if coa_slug:
try:
coa = entity.get_coa_model_qs().get(slug=coa_slug)
except Exception as e:
logger.error(f"COA with slug {coa_slug} not found for entity {entity.pk}: {e}")
return False
else:
coa = entity.get_default_coa()
if coa_slug:
try:
coa = entity.get_coa_model_qs().get(slug=coa_slug)
logger.info(f"COA with slug {coa_slug} found for entity {entity.pk}")
except Exception as e:
logger.error(f"COA with slug {coa_slug} not found for entity {entity.pk}: {e}")
else:
coa = entity.get_default_coa()
logger.info(f"Default COA found for entity {entity.pk}")
if not coa:
logger.error(f"❌ No default COA for entity {entity.pk}")
return False
if not coa:
logger.error(f"No COA found for entity {entity.pk}")
return False
# Get missing accounts
existing_codes = set(entity.get_all_accounts().filter(coa_model=coa).values_list('code', flat=True))
accounts_to_create = [
acc for acc in get_accounts_data()
if acc["code"] not in existing_codes
]
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")
if not accounts_to_create:
logger.info("✅ All default accounts already exist.")
return True
except Exception as e:
logger.error(f"Attempt {attempt + 1} failed: {e}")
# Create missing ones
logger.info(f"🔧 Creating {len(accounts_to_create)} missing accounts...")
success = True
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
for acc in accounts_to_create:
if not create_account(entity, coa, acc):
logger.warning(f"⚠️ Failed to create account: {acc['code']}")
success = False # don't fail task, just log
if success:
logger.info("✅ All missing accounts created successfully.")
else:
logger.warning("⚠️ Some accounts failed to create — check logs.")
return success # Django-Q will mark as failed if False
except Exception as e:
logger.error(f"💥 Task failed for dealer {dealer_id}: {e}")
raise # Let Django-Q handle retry if configured
def retry_entity_creation(dealer_id, retry_count=0):
"""
@ -161,7 +155,7 @@ def retry_entity_creation(dealer_id, retry_count=0):
# Now trigger account creation
async_task(
"inventory.tasks.create_coa_accounts",
dealer_id=dealer_id
dealer_id=dealer_id,
)
except Exception as e:

View File

@ -2388,45 +2388,36 @@ def get_accounts_data():
def create_account(entity, coa, account_data):
"""
Create account with proper validation and error handling
"""
logger.info(f"Creating account: {account_data['code']}")
logger.info(f"COA: {coa}")
try:
# existing_account = AccountModel.objects.filter(coa_model=coa,code=account_data["code"])
existing_account = entity.get_all_accounts().filter(
coa_model=coa,
code=account_data["code"]
)
if existing_account:
# Skip if exists
if coa.get_coa_accounts().filter(code=account_data["code"]).exists():
logger.info(f"Account already exists: {account_data['code']}")
return True
logger.info(f"Creating account: {account_data['code']}")
account = entity.create_account(
coa_model=coa,
logger.info(f"Account does not exist: {account_data['code']},creating...")
account = coa.create_account(
code=account_data["code"],
name=account_data["name"],
role=account_data["role"],
balance_type=_(account_data["balance_type"]),
balance_type=account_data["balance_type"],
active=True,
)
logger.info(f"Successfully created account: {account_data['code']}")
logger.info(f"Created account: {account}")
if account:
account.role_default = account_data["default"]
account.save()
logger.info(f"Successfully created account: {account_data['code']}")
account.role_default = account_data.get("default", False)
account.save(update_fields=['role_default'])
return True
except IntegrityError:
logger.warning(f"Account {account_data['code']} already exists (IntegrityError)")
return True
return True # Already created by race condition
except Exception as e:
logger.error(f"Error creating account {account_data['code']}: {e}")
return False
logger.error(f"❌ Error creating {account_data['code']}: {e}")
return False
# def create_account(entity, coa, account_data):
# try:
# account = entity.create_account(