django-ledger 0.7.2__py3-none-any.whl → 0.7.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of django-ledger might be problematic. Click here for more details.

Files changed (106) hide show
  1. django_ledger/__init__.py +1 -1
  2. django_ledger/contrib/django_ledger_graphene/journal_entry/schema.py +2 -3
  3. django_ledger/contrib/django_ledger_graphene/transaction/schema.py +9 -7
  4. django_ledger/forms/account.py +4 -1
  5. django_ledger/forms/journal_entry.py +19 -12
  6. django_ledger/forms/transactions.py +8 -12
  7. django_ledger/io/io_core.py +17 -12
  8. django_ledger/io/io_library.py +3 -3
  9. django_ledger/migrations/0001_initial.py +1 -1
  10. django_ledger/migrations/0019_alter_transactionmodel_amount_and_more.py +33 -0
  11. django_ledger/models/bill.py +17 -2
  12. django_ledger/models/chart_of_accounts.py +4 -0
  13. django_ledger/models/closing_entry.py +8 -6
  14. django_ledger/models/data_import.py +1 -0
  15. django_ledger/models/invoice.py +12 -4
  16. django_ledger/models/journal_entry.py +843 -481
  17. django_ledger/models/ledger.py +45 -4
  18. django_ledger/models/mixins.py +5 -5
  19. django_ledger/models/transactions.py +303 -305
  20. django_ledger/models/unit.py +42 -22
  21. django_ledger/static/django_ledger/bundle/djetler.bundle.js +1 -1
  22. django_ledger/static/django_ledger/bundle/styles.bundle.js +1 -1
  23. django_ledger/templates/django_ledger/account/tags/account_txs_table.html +1 -1
  24. django_ledger/templates/django_ledger/account/tags/accounts_table.html +1 -1
  25. django_ledger/templates/django_ledger/bills/bill_detail.html +1 -1
  26. django_ledger/templates/django_ledger/bills/bill_update.html +1 -1
  27. django_ledger/templates/django_ledger/components/icon.html +1 -1
  28. django_ledger/templates/django_ledger/data_import/tags/data_import_job_txs_imported.html +8 -1
  29. django_ledger/templates/django_ledger/financial_statements/balance_sheet.html +1 -0
  30. django_ledger/templates/django_ledger/financial_statements/tags/balance_sheet_statement.html +4 -4
  31. django_ledger/templates/django_ledger/financial_statements/tags/income_statement.html +118 -0
  32. django_ledger/templates/django_ledger/includes/nav.html +9 -5
  33. django_ledger/templates/django_ledger/invoice/includes/card_invoice.html +69 -69
  34. django_ledger/templates/django_ledger/invoice/invoice_detail.html +1 -1
  35. django_ledger/templates/django_ledger/journal_entry/je_create.html +2 -3
  36. django_ledger/templates/django_ledger/journal_entry/je_delete.html +2 -3
  37. django_ledger/templates/django_ledger/journal_entry/je_detail.html +1 -1
  38. django_ledger/templates/django_ledger/journal_entry/je_detail_txs.html +8 -8
  39. django_ledger/templates/django_ledger/journal_entry/je_list.html +16 -13
  40. django_ledger/templates/django_ledger/journal_entry/je_update.html +2 -3
  41. django_ledger/templates/django_ledger/journal_entry/tags/je_table.html +24 -24
  42. django_ledger/templates/django_ledger/journal_entry/tags/je_txs_table.html +17 -14
  43. django_ledger/templates/django_ledger/ledger/tags/ledgers_table.html +38 -37
  44. django_ledger/templates/django_ledger/transactions/tags/txs_table.html +69 -0
  45. django_ledger/templatetags/django_ledger.py +35 -48
  46. django_ledger/urls/account.py +4 -4
  47. django_ledger/views/account.py +8 -8
  48. django_ledger/views/journal_entry.py +84 -101
  49. django_ledger/views/ledger.py +16 -21
  50. django_ledger/views/mixins.py +17 -28
  51. {django_ledger-0.7.2.dist-info → django_ledger-0.7.4.dist-info}/METADATA +8 -3
  52. {django_ledger-0.7.2.dist-info → django_ledger-0.7.4.dist-info}/RECORD +56 -104
  53. assets/node_modules/node-gyp/gyp/gyp_main.py +0 -45
  54. assets/node_modules/node-gyp/gyp/pylib/gyp/MSVSNew.py +0 -367
  55. assets/node_modules/node-gyp/gyp/pylib/gyp/MSVSProject.py +0 -206
  56. assets/node_modules/node-gyp/gyp/pylib/gyp/MSVSSettings.py +0 -1270
  57. assets/node_modules/node-gyp/gyp/pylib/gyp/MSVSSettings_test.py +0 -1547
  58. assets/node_modules/node-gyp/gyp/pylib/gyp/MSVSToolFile.py +0 -59
  59. assets/node_modules/node-gyp/gyp/pylib/gyp/MSVSUserFile.py +0 -153
  60. assets/node_modules/node-gyp/gyp/pylib/gyp/MSVSUtil.py +0 -271
  61. assets/node_modules/node-gyp/gyp/pylib/gyp/MSVSVersion.py +0 -574
  62. assets/node_modules/node-gyp/gyp/pylib/gyp/__init__.py +0 -666
  63. assets/node_modules/node-gyp/gyp/pylib/gyp/common.py +0 -654
  64. assets/node_modules/node-gyp/gyp/pylib/gyp/common_test.py +0 -78
  65. assets/node_modules/node-gyp/gyp/pylib/gyp/easy_xml.py +0 -165
  66. assets/node_modules/node-gyp/gyp/pylib/gyp/easy_xml_test.py +0 -109
  67. assets/node_modules/node-gyp/gyp/pylib/gyp/flock_tool.py +0 -55
  68. assets/node_modules/node-gyp/gyp/pylib/gyp/generator/__init__.py +0 -0
  69. assets/node_modules/node-gyp/gyp/pylib/gyp/generator/analyzer.py +0 -808
  70. assets/node_modules/node-gyp/gyp/pylib/gyp/generator/android.py +0 -1173
  71. assets/node_modules/node-gyp/gyp/pylib/gyp/generator/cmake.py +0 -1321
  72. assets/node_modules/node-gyp/gyp/pylib/gyp/generator/compile_commands_json.py +0 -120
  73. assets/node_modules/node-gyp/gyp/pylib/gyp/generator/dump_dependency_json.py +0 -103
  74. assets/node_modules/node-gyp/gyp/pylib/gyp/generator/eclipse.py +0 -464
  75. assets/node_modules/node-gyp/gyp/pylib/gyp/generator/gypd.py +0 -89
  76. assets/node_modules/node-gyp/gyp/pylib/gyp/generator/gypsh.py +0 -58
  77. assets/node_modules/node-gyp/gyp/pylib/gyp/generator/make.py +0 -2518
  78. assets/node_modules/node-gyp/gyp/pylib/gyp/generator/msvs.py +0 -3978
  79. assets/node_modules/node-gyp/gyp/pylib/gyp/generator/msvs_test.py +0 -44
  80. assets/node_modules/node-gyp/gyp/pylib/gyp/generator/ninja.py +0 -2936
  81. assets/node_modules/node-gyp/gyp/pylib/gyp/generator/ninja_test.py +0 -55
  82. assets/node_modules/node-gyp/gyp/pylib/gyp/generator/xcode.py +0 -1394
  83. assets/node_modules/node-gyp/gyp/pylib/gyp/generator/xcode_test.py +0 -25
  84. assets/node_modules/node-gyp/gyp/pylib/gyp/input.py +0 -3137
  85. assets/node_modules/node-gyp/gyp/pylib/gyp/input_test.py +0 -98
  86. assets/node_modules/node-gyp/gyp/pylib/gyp/mac_tool.py +0 -771
  87. assets/node_modules/node-gyp/gyp/pylib/gyp/msvs_emulation.py +0 -1271
  88. assets/node_modules/node-gyp/gyp/pylib/gyp/ninja_syntax.py +0 -174
  89. assets/node_modules/node-gyp/gyp/pylib/gyp/simple_copy.py +0 -61
  90. assets/node_modules/node-gyp/gyp/pylib/gyp/win_tool.py +0 -374
  91. assets/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py +0 -1939
  92. assets/node_modules/node-gyp/gyp/pylib/gyp/xcode_ninja.py +0 -302
  93. assets/node_modules/node-gyp/gyp/pylib/gyp/xcodeproj_file.py +0 -3197
  94. assets/node_modules/node-gyp/gyp/pylib/gyp/xml_fix.py +0 -65
  95. assets/node_modules/node-gyp/gyp/setup.py +0 -42
  96. assets/node_modules/node-gyp/gyp/test_gyp.py +0 -260
  97. assets/node_modules/node-gyp/gyp/tools/graphviz.py +0 -102
  98. assets/node_modules/node-gyp/gyp/tools/pretty_gyp.py +0 -156
  99. assets/node_modules/node-gyp/gyp/tools/pretty_sln.py +0 -181
  100. assets/node_modules/node-gyp/gyp/tools/pretty_vcproj.py +0 -339
  101. assets/node_modules/node-gyp/test/fixtures/test-charmap.py +0 -31
  102. assets/node_modules/node-gyp/update-gyp.py +0 -46
  103. {django_ledger-0.7.2.dist-info → django_ledger-0.7.4.dist-info}/AUTHORS.md +0 -0
  104. {django_ledger-0.7.2.dist-info → django_ledger-0.7.4.dist-info}/LICENSE +0 -0
  105. {django_ledger-0.7.2.dist-info → django_ledger-0.7.4.dist-info}/WHEEL +0 -0
  106. {django_ledger-0.7.2.dist-info → django_ledger-0.7.4.dist-info}/top_level.txt +0 -0
django_ledger/__init__.py CHANGED
@@ -6,7 +6,7 @@ Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
6
6
  default_app_config = 'django_ledger.apps.DjangoLedgerConfig'
7
7
 
8
8
  """Django Ledger"""
9
- __version__ = '0.7.2'
9
+ __version__ = '0.7.4'
10
10
  __license__ = 'GPLv3 License'
11
11
 
12
12
  __author__ = 'Miguel Sanda'
@@ -27,10 +27,9 @@ class JournalEntryQuery(graphene.ObjectType):
27
27
  sort = info.context.GET.get('sort')
28
28
  if not sort:
29
29
  sort = '-updated'
30
- return JournalEntryModel.objects.for_ledger(
31
- ledger_pk=pk_ledger,
30
+ return JournalEntryModel.objects.for_entity(
32
31
  entity_slug=slug_name,
33
32
  user_model=info.context.user
34
- ).order_by(sort)
33
+ ).for_ledger(ledger_pk=pk_ledger).order_by(sort)
35
34
  else:
36
35
  return JournalEntryModel.objects.none()
@@ -17,18 +17,20 @@ class TransactionNode(DjangoObjectType):
17
17
  'description': ['exact', 'icontains', 'istartswith'],
18
18
  }
19
19
  interfaces = (relay.Node,)
20
+
21
+
20
22
  class TransactionsQuery(graphene.ObjectType):
21
- all_transactions = DjangoFilterConnectionField(TransactionNode, slug_name=graphene.String(required=True),
22
- pk_je=graphene.UUID(), pk_ledger=graphene.UUID())
23
+ all_transactions = DjangoFilterConnectionField(
24
+ TransactionNode,
25
+ slug_name=graphene.String(required=True),
26
+ pk_je=graphene.UUID(),
27
+ pk_ledger=graphene.UUID())
23
28
 
24
29
  def resolve_all_transactions(self, info, slug_name, pk_je, pk_ledger, **kwargs):
25
30
  if info.context.user.is_authenticated:
26
- return TransactionModel.objects.for_journal_entry(
31
+ return TransactionModel.objects.for_entity(
27
32
  entity_slug=slug_name,
28
33
  user_model=info.context.user,
29
- je_model=pk_je,
30
- ledger_model=pk_ledger
31
- ).order_by('account__code')
34
+ ).for_journal_entry(je_model=pk_je).order_by('account__code')
32
35
  else:
33
36
  return TransactionModel.objects.none()
34
-
@@ -145,13 +145,16 @@ class AccountModelUpdateForm(MoveNodeForm):
145
145
 
146
146
  class Meta:
147
147
  model = AccountModel
148
- exclude = ('depth', 'numchild', 'path', 'balance_type')
148
+ exclude = ('depth', 'numchild', 'path')
149
149
  widgets = {
150
150
  'role': HiddenInput(),
151
151
  'coa_model': HiddenInput(),
152
152
  'parent': Select(attrs={
153
153
  'class': DJANGO_LEDGER_FORM_INPUT_CLASSES
154
154
  }),
155
+ 'balance_type': Select(attrs={
156
+ 'class': DJANGO_LEDGER_FORM_INPUT_CLASSES
157
+ }),
155
158
  'code': TextInput(attrs={
156
159
  'class': DJANGO_LEDGER_FORM_INPUT_CLASSES
157
160
  }),
@@ -1,32 +1,39 @@
1
+ from typing import Union
2
+ from uuid import UUID
3
+
1
4
  from django.forms import ModelForm, Textarea, Select, DateTimeInput, ValidationError
2
5
  from django.utils.translation import gettext_lazy as _
3
6
 
7
+ from django_ledger.models import EntityModel
4
8
  from django_ledger.models.journal_entry import JournalEntryModel
5
9
  from django_ledger.models.ledger import LedgerModel
6
- from django_ledger.models.unit import EntityUnitModel
7
10
  from django_ledger.settings import DJANGO_LEDGER_FORM_INPUT_CLASSES
8
11
 
9
12
 
10
13
  class JournalEntryModelCreateForm(ModelForm):
11
14
  def __init__(self,
12
- entity_slug: str,
13
- ledger_model: LedgerModel,
14
- user_model, *args, **kwargs):
15
+ entity_model: EntityModel,
16
+ ledger_model: Union[str, UUID, LedgerModel],
17
+ *args, **kwargs
18
+ ):
15
19
  super().__init__(*args, **kwargs)
16
- self.USER_MODEL = user_model
17
- self.ENTITY_SLUG = entity_slug
20
+ self.ENTITY_MODEL: EntityModel = entity_model
21
+
22
+ # processes the provided ledger model UUID is valid....
23
+ if isinstance(ledger_model, (str, UUID)):
24
+ ledger_model = self.ENTITY_MODEL.get_ledgers().get(uuid__exact=ledger_model)
25
+ elif isinstance(ledger_model, LedgerModel):
26
+ self.ENTITY_MODEL.validate_ledger_model_for_entity(ledger_model)
27
+
18
28
  self.LEDGER_MODEL: LedgerModel = ledger_model
19
29
 
20
30
  if 'timestamp' in self.fields:
21
31
  self.fields['timestamp'].required = False
22
32
  if 'entity_unit' in self.fields:
23
- self.fields['entity_unit'].queryset = EntityUnitModel.objects.for_entity(
24
- entity_slug=self.ENTITY_SLUG,
25
- user_model=self.USER_MODEL
26
- )
33
+ self.fields['entity_unit'].queryset = self.ENTITY_MODEL.entityunitmodel_set.all()
27
34
 
28
35
  def clean(self):
29
- if not self.LEDGER_MODEL.can_edit_journal_entries():
36
+ if self.LEDGER_MODEL.is_locked():
30
37
  raise ValidationError(message=_('Cannot create new Journal Entries on a locked Ledger.'))
31
38
  self.instance.ledger = self.LEDGER_MODEL
32
39
  return super().clean()
@@ -57,7 +64,7 @@ class JournalEntryModelCreateForm(ModelForm):
57
64
  }
58
65
 
59
66
 
60
- class JournalEntryModelUpdateForm(JournalEntryModelCreateForm):
67
+ class JournalEntryModelUpdateForm(ModelForm):
61
68
 
62
69
  def clean_timestamp(self):
63
70
  if 'timestamp' in self.changed_data:
@@ -3,15 +3,15 @@ Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
3
3
  Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
4
4
 
5
5
  Contributions to this module:
6
- Miguel Sanda <msanda@arrobalytics.com>
7
- Michael Noel <noel.michael87@gmail.com>
6
+ - Miguel Sanda <msanda@arrobalytics.com>
7
+ - Michael Noel <noel.michael87@gmail.com>
8
8
  """
9
9
 
10
10
  from django.forms import ModelForm, modelformset_factory, BaseModelFormSet, TextInput, Select, ValidationError
11
11
  from django.utils.translation import gettext_lazy as _
12
12
 
13
13
  from django_ledger.io.io_core import check_tx_balance
14
- from django_ledger.models.accounts import AccountModel
14
+ from django_ledger.models import EntityModel
15
15
  from django_ledger.models.journal_entry import JournalEntryModel
16
16
  from django_ledger.models.transactions import TransactionModel
17
17
  from django_ledger.settings import DJANGO_LEDGER_FORM_INPUT_CLASSES
@@ -44,17 +44,13 @@ class TransactionModelForm(ModelForm):
44
44
 
45
45
  class TransactionModelFormSet(BaseModelFormSet):
46
46
 
47
- def __init__(self, *args, entity_slug, user_model, ledger_pk, je_model=None, **kwargs):
47
+ def __init__(self, *args, entity_model: EntityModel, je_model: JournalEntryModel, **kwargs):
48
48
  super().__init__(*args, **kwargs)
49
- self.USER_MODEL = user_model
49
+ je_model.validate_for_entity(entity_model)
50
50
  self.JE_MODEL: JournalEntryModel = je_model
51
- self.LEDGER_PK = ledger_pk
52
- self.ENTITY_SLUG = entity_slug
51
+ self.ENTITY_MODEL = entity_model
53
52
 
54
- account_qs = AccountModel.objects.for_entity(
55
- user_model=self.USER_MODEL,
56
- entity_model=self.ENTITY_SLUG
57
- ).available().order_by('code')
53
+ account_qs = self.ENTITY_MODEL.get_coa_accounts().active().order_by('code')
58
54
 
59
55
  for form in self.forms:
60
56
  form.fields['account'].queryset = account_qs
@@ -64,7 +60,7 @@ class TransactionModelFormSet(BaseModelFormSet):
64
60
  form.fields['amount'].disabled = True
65
61
 
66
62
  def get_queryset(self):
67
- return self.JE_MODEL.transactionmodel_set.all().order_by('account__code')
63
+ return self.JE_MODEL.transactionmodel_set.all()
68
64
 
69
65
  def clean(self):
70
66
  if any(self.errors):
@@ -359,11 +359,12 @@ class IODatabaseMixIn:
359
359
  raise IOValidationError('Inconsistent entity_slug. '
360
360
  f'Provided {entity_slug} does not match actual {self.slug}')
361
361
  if unit_slug:
362
- txs_queryset_init = TransactionModel.objects.for_unit(
362
+
363
+ txs_queryset_init = TransactionModel.objects.for_entity(
363
364
  user_model=user_model,
364
- entity_slug=entity_slug or self.slug,
365
- unit_slug=unit_slug
366
- )
365
+ entity_slug=entity_slug or self.slug
366
+ ).for_unit(unit_slug=unit_slug)
367
+
367
368
  else:
368
369
  txs_queryset_init = TransactionModel.objects.for_entity(
369
370
  user_model=user_model,
@@ -373,20 +374,22 @@ class IODatabaseMixIn:
373
374
  if not entity_slug:
374
375
  raise IOValidationError(
375
376
  'Calling digest from Entity Unit requires entity_slug explicitly for safety')
376
- txs_queryset_init = TransactionModel.objects.for_unit(
377
+
378
+ txs_queryset_init = TransactionModel.objects.for_entity(
377
379
  user_model=user_model,
378
380
  entity_slug=entity_slug,
379
- unit_slug=unit_slug or self
380
- )
381
+ ).for_unit(unit_slug=unit_slug or self)
382
+
381
383
  elif self.is_ledger_model():
382
384
  if not entity_slug:
383
385
  raise IOValidationError(
384
386
  'Calling digest from Ledger Model requires entity_slug explicitly for safety')
385
- txs_queryset_init = TransactionModel.objects.for_ledger(
386
- user_model=user_model,
387
+
388
+ txs_queryset_init = TransactionModel.objects.for_entity(
387
389
  entity_slug=entity_slug,
388
- ledger_model=self
389
- )
390
+ user_model=user_model,
391
+ ).for_ledger(ledger_model=self)
392
+
390
393
  else:
391
394
  raise IOValidationError(
392
395
  message=f'Cannot call digest from {self.__class__.__name__}'
@@ -499,10 +502,11 @@ class IODatabaseMixIn:
499
502
  VALUES = [
500
503
  'account__uuid',
501
504
  'account__balance_type',
502
- 'tx_type',
503
505
  'account__code',
504
506
  'account__name',
505
507
  'account__role',
508
+ 'account__coa_model__slug',
509
+ 'tx_type',
506
510
  ]
507
511
 
508
512
  ANNOTATE = {'balance': Sum('amount')}
@@ -663,6 +667,7 @@ class IODatabaseMixIn:
663
667
  gl = list(g)
664
668
  return {
665
669
  'account_uuid': k[0],
670
+ 'coa_slug': gl[0]['account__coa_model__slug'],
666
671
  'unit_uuid': k[1],
667
672
  'unit_name': gl[0].get('journal_entry__entity_unit__name'),
668
673
  'activity': gl[0].get('journal_entry__activity'),
@@ -433,9 +433,9 @@ class IOBluePrint:
433
433
  return round(amount, self.precision_decimals)
434
434
 
435
435
  def _amount(self, amount: Union[float, Decimal, int]) -> Decimal:
436
- if amount <= 0:
436
+ if amount < 0:
437
437
  raise IOBluePrintValidationError(
438
- message='Amounts must be greater than 0'
438
+ message='Amounts cannot be negative.'
439
439
  )
440
440
 
441
441
  if isinstance(amount, float):
@@ -448,7 +448,7 @@ class IOBluePrint:
448
448
  return Decimal(str(amount))
449
449
 
450
450
  raise IOBluePrintValidationError(
451
- message='Amounts must be float or Decimal'
451
+ message='Amounts must be float, Decimal or int.'
452
452
  )
453
453
 
454
454
  def credit(self, account_code: str, amount: Union[float, Decimal], description: str = None):
@@ -46,7 +46,7 @@ class Migration(migrations.Migration):
46
46
  ('lia_cl_acc_payable', 'Accounts Payable'), ('lia_cl_wages_payable', 'Wages Payable'),
47
47
  ('lia_cl_int_payable', 'Interest Payable'), ('lia_cl_taxes_payable', 'Taxes Payable'),
48
48
  ('lia_cl_st_notes_payable', 'Notes Payable'),
49
- ('lia_cl_ltd_mat', 'Current Maturities of Long Tern Debt'), ('lia_cl_def_rev', 'Deferred Revenue'),
49
+ ('lia_cl_ltd_mat', 'Current Maturities of Long Term Debt'), ('lia_cl_def_rev', 'Deferred Revenue'),
50
50
  ('lia_cl_other', 'Other Liabilities'), ('lia_ltl_notes', 'Notes Payable'),
51
51
  ('lia_ltl_bonds', 'Bonds Payable'), ('lia_ltl_mortgage', 'Mortgage Payable'))), ('Equity', (
52
52
  ('eq_capital', 'Capital'), ('eq_stock_common', 'Common Stock'),
@@ -0,0 +1,33 @@
1
+ # Generated by Django 5.1.5 on 2025-01-17 00:11
2
+
3
+ import django.core.validators
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+ dependencies = [
9
+ ('django_ledger', '0018_transactionmodel_cleared_transactionmodel_reconciled_and_more'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterField(
14
+ model_name='transactionmodel',
15
+ name='amount',
16
+ field=models.DecimalField(decimal_places=2, default=0.0, help_text='Amount of the transaction.',
17
+ max_digits=20, validators=[django.core.validators.MinValueValidator(0)],
18
+ verbose_name='Amount'),
19
+ ),
20
+ migrations.AlterField(
21
+ model_name='transactionmodel',
22
+ name='description',
23
+ field=models.CharField(blank=True,
24
+ help_text='A description to be included with this individual transaction.',
25
+ max_length=100, null=True, verbose_name='Transaction Description'),
26
+ ),
27
+ migrations.AlterField(
28
+ model_name='transactionmodel',
29
+ name='tx_type',
30
+ field=models.CharField(choices=[('credit', 'Credit'), ('debit', 'Debit')], max_length=10,
31
+ verbose_name='Transaction Type'),
32
+ ),
33
+ ]
@@ -33,8 +33,13 @@ from django_ledger.io import ASSET_CA_CASH, ASSET_CA_PREPAID, LIABILITY_CL_ACC_P
33
33
  from django_ledger.io.io_core import get_localtime, get_localdate
34
34
  from django_ledger.models.entity import EntityModel
35
35
  from django_ledger.models.items import ItemTransactionModelQuerySet, ItemTransactionModel, ItemModel, ItemModelQuerySet
36
- from django_ledger.models.mixins import (CreateUpdateMixIn, AccrualMixIn, MarkdownNotesMixIn,
37
- PaymentTermsMixIn, ItemizeMixIn)
36
+ from django_ledger.models.mixins import (
37
+ CreateUpdateMixIn,
38
+ AccrualMixIn,
39
+ MarkdownNotesMixIn,
40
+ PaymentTermsMixIn,
41
+ ItemizeMixIn
42
+ )
38
43
  from django_ledger.models.signals import (
39
44
  bill_status_draft,
40
45
  bill_status_in_review,
@@ -575,6 +580,16 @@ class BillModelAbstract(
575
580
 
576
581
  # ### ItemizeMixIn implementation END...
577
582
 
583
+ def get_transaction_queryset(self, annotated: bool = False):
584
+ """
585
+ Fetches the TransactionModelQuerySet associated with the BillModel instance.
586
+ """
587
+ TransactionModel = lazy_loader.get_txs_model()
588
+ transaction_model_qs = TransactionModel.objects.all().for_ledger(ledger_model=self.ledger_id)
589
+ if annotated:
590
+ return transaction_model_qs.with_annotated_details()
591
+ return transaction_model_qs
592
+
578
593
  # State..
579
594
  def get_migrate_state_desc(self) -> str:
580
595
  """
@@ -286,6 +286,10 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
286
286
  return qs
287
287
  return qs.get()
288
288
 
289
+ raise ChartOfAccountsModelValidationError(
290
+ message='Adding Root account to Chart of Accounts is not allowed.'
291
+ )
292
+
289
293
  def get_non_root_coa_accounts_qs(self) -> AccountModelQuerySet:
290
294
  """
291
295
  Returns a query set of non-root accounts in the chart of accounts.
@@ -232,10 +232,11 @@ class ClosingEntryModelAbstract(CreateUpdateMixIn, MarkdownNotesMixIn):
232
232
  message=_(f'Closing Entry {self.closing_date} is not posted.')
233
233
  )
234
234
  self.posted = False
235
- TransactionModel.objects.for_ledger(
236
- ledger_model=self.ledger_model,
235
+
236
+ TransactionModel.objects.for_entity(
237
237
  entity_slug=self.entity_model_id
238
- ).delete()
238
+ ).for_ledger(ledger_model=self.ledger_model).delete()
239
+
239
240
  self.ledger_model.journal_entries.all().delete()
240
241
  if commit:
241
242
  self.save(
@@ -303,10 +304,11 @@ class ClosingEntryModelAbstract(CreateUpdateMixIn, MarkdownNotesMixIn):
303
304
  raise ClosingEntryValidationError(
304
305
  message=_('Cannot delete a posted Closing Entry')
305
306
  )
306
- TransactionModel.objects.for_ledger(
307
- ledger_model=self.ledger_model,
307
+
308
+ TransactionModel.objects.for_entity(
308
309
  entity_slug=self.entity_model_id
309
- ).delete()
310
+ ).for_ledger(ledger_model=self.ledger_model).delete()
311
+
310
312
  return self.ledger_model.delete()
311
313
 
312
314
  def get_delete_html_id(self) -> str:
@@ -156,6 +156,7 @@ class StagedTransactionModelManager(Manager):
156
156
  When(parent_id__isnull=False, then=F('parent_id'))
157
157
  ),
158
158
  ).annotate(
159
+ entity_unit=F('transaction_model__journal_entry__entity_unit__name'),
159
160
  ready_to_import=Case(
160
161
  # is mapped singleton...
161
162
  When(
@@ -546,6 +546,16 @@ class InvoiceModelAbstract(
546
546
 
547
547
  # ### ItemizeMixIn implementation END...
548
548
 
549
+ def get_transaction_queryset(self, annotated: bool = False):
550
+ """
551
+ Fetches the TransactionModelQuerySet associated with the InvoiceModel instance.
552
+ """
553
+ TransactionModel = lazy_loader.get_txs_model()
554
+ transaction_model_qs = TransactionModel.objects.all().for_ledger(ledger_model=self.ledger_id)
555
+ if annotated:
556
+ return transaction_model_qs.with_annotated_details()
557
+ return transaction_model_qs
558
+
549
559
  def get_migrate_state_desc(self):
550
560
  """
551
561
  Description used when migrating transactions into the LedgerModel.
@@ -557,8 +567,7 @@ class InvoiceModelAbstract(
557
567
  """
558
568
  return f'Invoice {self.invoice_number} account adjustment.'
559
569
 
560
- def get_migration_data(self,
561
- queryset: Optional[ItemTransactionModelQuerySet] = None) -> ItemTransactionModelQuerySet:
570
+ def get_migration_data(self, queryset: Optional[ItemTransactionModelQuerySet] = None) -> ItemTransactionModelQuerySet:
562
571
 
563
572
  """
564
573
  Fetches necessary item transaction data to perform a migration into the LedgerModel.
@@ -591,8 +600,7 @@ class InvoiceModelAbstract(
591
600
  'total_amount').annotate(
592
601
  account_unit_total=Sum('total_amount'))
593
602
 
594
- def update_amount_due(self,
595
- itemtxs_qs: Optional[ItemTransactionModelQuerySet] = None) -> ItemTransactionModelQuerySet:
603
+ def update_amount_due(self, itemtxs_qs: Optional[ItemTransactionModelQuerySet] = None) -> ItemTransactionModelQuerySet:
596
604
  """
597
605
  Updates the InvoiceModel amount due.
598
606