fix the coa issue
This commit is contained in:
parent
ad35058720
commit
f2cd3f6969
@ -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']}")
|
||||
|
||||
|
||||
@ -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"))
|
||||
|
||||
@ -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}")
|
||||
@ -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:
|
||||
|
||||
@ -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(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user