Skip to content

Commit 9fa8b58

Browse files
authored
V0.8.3.1 (#299)
* - **Added `is_role_default` and `force_role_default` to account creation logic:** - Updated `EntityModel.create_account` method to support new flags for default role assignment. - Passed `is_role_default` and `force_role_default` to the `ChartOfAccountsModel.create_account` method. - **Implemented default role handling in `ChartOfAccountsModel.create_account`:** - Added validation to prevent multiple default accounts for the same role unless `force_role_default` is enabled. - Updated existing default account behavior to unset the previous default when `force_role_default` is used. - Included transaction atomicity to ensure account creation consistency. - **Refactored account creation logic for clarity and maintainability:** - Reorganized parameter handling and method logic for `AccountModel` creation. - Enhanced validation error messaging for default role assignment conflicts. ### **Summary** Introduced `is_role_default` and `force_role_default` flags for `AccountModel` creation to manage default role accounts more effectively. Implemented validation and transaction safety measures for default account assignments. ### **Backwards Compatibility** Changes are backwards compatible. Default behavior is preserved unless the new flags are explicitly used. * v0.8.3.1 * - **Enhanced `create_account` method in `ChartOfAccountsModel`:** - Improved role default validation logic to utilize more detailed error messaging, replacing queryset code reference with actual `AccountModel` instance code for clarity. - Refactored existing account model handling by replacing variable names (`default_role_account` -> `existing_account_model`) for consistency. - Made line formatting adjustments for improved readability. - **Updated QuickStart Notebook:** - Removed all execution-specific metadata (`ExecuteTime`, `execution_count`) to clean up notebook revisions. - Refined outputs across cells by clearing response data and any previously stored execution results for a fresher state. ### **Summary** Refactored the account creation logic in `ChartOfAccountsModel` for better naming and messaging, enhancing consistency and clarity * Dev Env Dependency Update
1 parent 5a41d81 commit 9fa8b58

File tree

9 files changed

+1190
-3762
lines changed

9 files changed

+1190
-3762
lines changed

django_ledger/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
default_app_config = 'django_ledger.apps.DjangoLedgerConfig'
77

88
"""Django Ledger"""
9-
__version__ = '0.8.3'
9+
__version__ = '0.8.3.1'
1010
__license__ = 'GPLv3 License'
1111

1212
__author__ = 'Miguel Sanda'

django_ledger/models/chart_of_accounts.py

Lines changed: 50 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -43,29 +43,28 @@
4343
import warnings
4444
from random import choices
4545
from string import ascii_lowercase, digits
46-
from typing import Optional, Union, Dict
47-
from uuid import uuid4, UUID
46+
from typing import Dict, Optional, Union
47+
from uuid import UUID, uuid4
4848

4949
from django.contrib.auth import get_user_model
5050
from django.core.exceptions import ValidationError
51-
from django.db import models
52-
from django.db.models import Q, F, Count, Manager, QuerySet, BooleanField, Value
53-
from django.db.models.signals import pre_save, post_save
51+
from django.db import models, transaction
52+
from django.db.models import BooleanField, Count, F, Manager, Q, QuerySet, Value
53+
from django.db.models.signals import post_save, pre_save
5454
from django.dispatch import receiver
5555
from django.urls import reverse
5656
from django.utils.translation import gettext_lazy as _
57-
5857
from django_ledger.io import (
59-
ROOT_COA,
60-
ROOT_GROUP_LEVEL_2,
61-
ROOT_GROUP_META,
6258
ROOT_ASSETS,
63-
ROOT_LIABILITIES,
6459
ROOT_CAPITAL,
65-
ROOT_INCOME,
60+
ROOT_COA,
6661
ROOT_COGS,
6762
ROOT_EXPENSES,
6863
ROOT_GROUP,
64+
ROOT_GROUP_LEVEL_2,
65+
ROOT_GROUP_META,
66+
ROOT_INCOME,
67+
ROOT_LIABILITIES,
6968
)
7069
from django_ledger.models import lazy_loader
7170
from django_ledger.models.accounts import AccountModel, AccountModelQuerySet
@@ -605,6 +604,8 @@ def create_account(
605604
balance_type: str,
606605
active: bool,
607606
root_account_qs: Optional[AccountModelQuerySet] = None,
607+
is_role_default: bool = False,
608+
force_role_default: bool = False,
608609
):
609610
"""
610611
Proper method for inserting a new Account Model into a CoA.
@@ -624,23 +625,51 @@ def create_account(
624625
Specifies whether the account is active or not.
625626
root_account_qs : Optional[AccountModelQuerySet], optional
626627
The query set of root accounts to which the created account should be linked. Defaults to None.
628+
is_role_default : bool
629+
Marks the new account as the default for the account role.
630+
force_role_default: bool
631+
Forces the new account model to be set as default for a specified role. Any pre-existing default account
632+
will be removed as default for the specified role.
627633
628634
Returns
629635
-------
630636
AccountModel
631637
The created account model instance.
632638
"""
633-
account_model = AccountModel(
634-
code=code,
635-
name=name,
636-
role=role,
637-
active=active,
638-
balance_type=balance_type,
639-
coa_model=self,
640-
)
641-
account_model.clean()
642639

643-
account_model = self.insert_account(account_model=account_model, root_account_qs=root_account_qs)
640+
with transaction.atomic():
641+
if is_role_default:
642+
account_model_qs: AccountModelQuerySet = self.get_coa_accounts()
643+
644+
default_role_account_qs: AccountModelQuerySet = account_model_qs.filter(
645+
role__exact=role, role_default=True
646+
)
647+
default_account_exists = default_role_account_qs.exists()
648+
649+
if default_account_exists and not force_role_default:
650+
existing_account_model: AccountModel = default_role_account_qs.get()
651+
raise ChartOfAccountsModelValidationError(
652+
f'The role {role} already has a default account {existing_account_model.code} for CoA {self}'
653+
)
654+
655+
elif default_account_exists and force_role_default:
656+
existing_account_model: AccountModel = default_role_account_qs.get()
657+
existing_account_model.role_default = False
658+
existing_account_model.save(update_fields=['role_default', 'updated'])
659+
660+
account_model = AccountModel(
661+
code=code,
662+
name=name,
663+
role=role,
664+
active=active,
665+
balance_type=balance_type,
666+
coa_model=self,
667+
role_default=is_role_default,
668+
)
669+
670+
account_model.clean()
671+
account_model = self.insert_account(account_model=account_model, root_account_qs=root_account_qs)
672+
644673
return account_model
645674

646675
# ACTIONS -----

django_ledger/models/data_import.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -686,8 +686,7 @@ def get_queryset(self) -> StagedTransactionModelQuerySet:
686686
F('matched_transaction_model__journal_entry__entity_unit__name'),
687687
),
688688
import_account_uuid=F('import_job__bank_account_model__account_model_id'),
689-
children_count=Count(F('split_transaction_set'),
690-
distinct=True),
689+
children_count=Count(F('split_transaction_set'), distinct=True),
691690
children_mapped_count=Count('split_transaction_set__account_model__uuid', distinct=True),
692691
imported_count=Count(
693692
'split_transaction_set',
@@ -1689,7 +1688,12 @@ def can_have_account(self) -> bool:
16891688
bool
16901689
True if the entity can have an account, False otherwise.
16911690
"""
1692-
return not self.is_parent()
1691+
return any(
1692+
[
1693+
all([self.is_parent(), not self.has_children()]),
1694+
self.is_children()
1695+
]
1696+
)
16931697

16941698
def can_have_activity(self) -> bool:
16951699
if any([self.is_transfer(), not self.is_cash_transaction()]):

0 commit comments

Comments
 (0)