update
This commit is contained in:
parent
05b18e9da3
commit
a079ceec6d
@ -6,17 +6,64 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def check_create_coa_accounts(task):
|
def check_create_coa_accounts(task):
|
||||||
logger.info("Checking if all accounts are created")
|
"""
|
||||||
instance = task.kwargs["dealer"]
|
Hook to verify account creation and handle failures
|
||||||
entity = instance.entity
|
"""
|
||||||
coa = entity.get_default_coa()
|
if task.success:
|
||||||
|
logger.info("Account creation task completed successfully")
|
||||||
|
return
|
||||||
|
|
||||||
for account_data in get_accounts_data():
|
logger.warning("Account creation task failed, checking status...")
|
||||||
if entity.get_all_accounts().filter(code=account_data["code"]).exists():
|
|
||||||
logger.info(f"Default account already exists: {account_data['code']}")
|
try:
|
||||||
continue
|
dealer_id = task.kwargs.get('dealer_id')
|
||||||
logger.info(f"Default account does not exist: {account_data['code']}")
|
if not dealer_id:
|
||||||
create_account(entity, coa, account_data)
|
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):
|
def print_results(task):
|
||||||
|
|||||||
@ -685,7 +685,7 @@ class Car(Base):
|
|||||||
)
|
)
|
||||||
#
|
#
|
||||||
additional_services = models.ManyToManyField(
|
additional_services = models.ManyToManyField(
|
||||||
AdditionalServices, related_name="additionals", blank=True, null=True
|
AdditionalServices, related_name="additionals"
|
||||||
)
|
)
|
||||||
cost_price = models.DecimalField(
|
cost_price = models.DecimalField(
|
||||||
max_digits=14,
|
max_digits=14,
|
||||||
|
|||||||
@ -135,55 +135,101 @@ def create_car_location(sender, instance, created, **kwargs):
|
|||||||
print(f"Failed to create CarLocation for car {instance.vin}: {e}")
|
print(f"Failed to create CarLocation for car {instance.vin}: {e}")
|
||||||
|
|
||||||
|
|
||||||
# Create Entity
|
|
||||||
@receiver(post_save, sender=models.Dealer)
|
@receiver(post_save, sender=models.Dealer)
|
||||||
def create_ledger_entity(sender, instance, created, **kwargs):
|
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:
|
if created:
|
||||||
entity_name = instance.user.dealer.name
|
try:
|
||||||
entity = EntityModel.create_entity(
|
# Use transaction to ensure atomicity
|
||||||
name=entity_name,
|
with transaction.atomic():
|
||||||
admin=instance.user,
|
entity_name = instance.user.dealer.name
|
||||||
use_accrual_method=True,
|
entity = EntityModel.create_entity(
|
||||||
fy_start_month=1,
|
name=entity_name,
|
||||||
)
|
admin=instance.user,
|
||||||
|
use_accrual_method=True,
|
||||||
|
fy_start_month=1,
|
||||||
|
)
|
||||||
|
|
||||||
if entity:
|
if entity:
|
||||||
instance.entity = entity
|
instance.entity = entity
|
||||||
instance.save()
|
instance.save(update_fields=['entity'])
|
||||||
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
|
# 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(
|
async_task(
|
||||||
func="inventory.tasks.create_coa_accounts",
|
func="inventory.tasks.create_coa_accounts",
|
||||||
dealer=instance,
|
dealer_id=instance.id, # Pass ID instead of object
|
||||||
hook="inventory.hooks.check_create_coa_accounts",
|
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))
|
# async_task('inventory.tasks.check_create_coa_accounts', instance, schedule_type='O', schedule_time=timedelta(seconds=20))
|
||||||
|
|
||||||
# create_settings(instance.pk)
|
# create_settings(instance.pk)
|
||||||
|
|||||||
@ -62,14 +62,117 @@ def create_settings(pk):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_coa_accounts(**kwargs):
|
def create_coa_accounts(dealer_id, **kwargs):
|
||||||
logger.info("creating all accounts are created")
|
"""
|
||||||
instance = kwargs.get("dealer")
|
Create COA accounts with retry logic and proper error handling
|
||||||
entity = instance.entity
|
"""
|
||||||
coa = entity.get_default_coa()
|
from .models import Dealer
|
||||||
|
from .utils import create_account, get_accounts_data
|
||||||
|
|
||||||
for account_data in get_accounts_data():
|
max_retries = 3
|
||||||
create_account(entity, coa, account_data)
|
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):
|
# def create_coa_accounts1(pk):
|
||||||
|
|||||||
@ -15,6 +15,7 @@ from django.utils import timezone
|
|||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django_ledger.io import roles
|
from django_ledger.io import roles
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
|
from django.db import IntegrityError
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django_q.tasks import async_task
|
from django_q.tasks import async_task
|
||||||
from django.core.mail import send_mail
|
from django.core.mail import send_mail
|
||||||
@ -2386,7 +2387,19 @@ def get_accounts_data():
|
|||||||
|
|
||||||
|
|
||||||
def create_account(entity, coa, account_data):
|
def create_account(entity, coa, account_data):
|
||||||
|
"""
|
||||||
|
Create account with proper validation and error handling
|
||||||
|
"""
|
||||||
try:
|
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(
|
account = entity.create_account(
|
||||||
coa_model=coa,
|
coa_model=coa,
|
||||||
code=account_data["code"],
|
code=account_data["code"],
|
||||||
@ -2395,11 +2408,37 @@ def create_account(entity, coa, account_data):
|
|||||||
balance_type=_(account_data["balance_type"]),
|
balance_type=_(account_data["balance_type"]),
|
||||||
active=True,
|
active=True,
|
||||||
)
|
)
|
||||||
account.role_default = account_data["default"]
|
|
||||||
account.save()
|
if account:
|
||||||
logger.info(f"Created default account: {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:
|
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):
|
def get_or_generate_car_image(car):
|
||||||
|
|||||||
@ -63,7 +63,7 @@ EditorConfig==0.17.1
|
|||||||
Faker==37.4.0
|
Faker==37.4.0
|
||||||
fleming==0.7.0
|
fleming==0.7.0
|
||||||
fonttools==4.58.5
|
fonttools==4.58.5
|
||||||
fpdf==1.7.2
|
# fpdf==1.7.2
|
||||||
fpdf2==2.8.3
|
fpdf2==2.8.3
|
||||||
greenlet==3.2.3
|
greenlet==3.2.3
|
||||||
gunicorn==23.0.0
|
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 |
Loading…
x
Reference in New Issue
Block a user