django-ledger 0.6.4__py3-none-any.whl → 0.7.1__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 (93) hide show
  1. django_ledger/__init__.py +1 -4
  2. django_ledger/admin/__init__.py +1 -1
  3. django_ledger/admin/{coa.py → chart_of_accounts.py} +1 -1
  4. django_ledger/admin/entity.py +1 -1
  5. django_ledger/contrib/django_ledger_graphene/accounts/schema.py +1 -1
  6. django_ledger/forms/account.py +43 -38
  7. django_ledger/forms/bank_account.py +5 -2
  8. django_ledger/forms/bill.py +24 -36
  9. django_ledger/forms/chart_of_accounts.py +82 -0
  10. django_ledger/forms/customer.py +1 -1
  11. django_ledger/forms/data_import.py +3 -3
  12. django_ledger/forms/estimate.py +1 -1
  13. django_ledger/forms/invoice.py +5 -7
  14. django_ledger/forms/item.py +24 -15
  15. django_ledger/forms/transactions.py +3 -3
  16. django_ledger/io/io_core.py +4 -2
  17. django_ledger/io/io_library.py +1 -1
  18. django_ledger/io/io_middleware.py +5 -0
  19. django_ledger/migrations/0017_alter_accountmodel_unique_together_and_more.py +31 -0
  20. django_ledger/migrations/0018_transactionmodel_cleared_transactionmodel_reconciled_and_more.py +37 -0
  21. django_ledger/models/__init__.py +1 -1
  22. django_ledger/models/accounts.py +229 -265
  23. django_ledger/models/bank_account.py +6 -6
  24. django_ledger/models/bill.py +7 -6
  25. django_ledger/models/{coa.py → chart_of_accounts.py} +187 -72
  26. django_ledger/models/closing_entry.py +5 -10
  27. django_ledger/models/coa_default.py +10 -9
  28. django_ledger/models/customer.py +6 -6
  29. django_ledger/models/data_import.py +12 -8
  30. django_ledger/models/entity.py +96 -39
  31. django_ledger/models/estimate.py +6 -10
  32. django_ledger/models/invoice.py +14 -11
  33. django_ledger/models/items.py +23 -14
  34. django_ledger/models/journal_entry.py +73 -30
  35. django_ledger/models/ledger.py +8 -8
  36. django_ledger/models/mixins.py +0 -3
  37. django_ledger/models/purchase_order.py +9 -9
  38. django_ledger/models/signals.py +0 -3
  39. django_ledger/models/transactions.py +24 -7
  40. django_ledger/models/unit.py +4 -3
  41. django_ledger/models/utils.py +0 -3
  42. django_ledger/models/vendor.py +4 -3
  43. django_ledger/settings.py +28 -3
  44. django_ledger/templates/django_ledger/account/account_create.html +2 -2
  45. django_ledger/templates/django_ledger/account/account_update.html +1 -1
  46. django_ledger/templates/django_ledger/account/tags/account_txs_table.html +1 -0
  47. django_ledger/templates/django_ledger/account/tags/accounts_table.html +29 -19
  48. django_ledger/templates/django_ledger/bills/bill_detail.html +3 -3
  49. django_ledger/templates/django_ledger/chart_of_accounts/coa_create.html +25 -0
  50. django_ledger/templates/django_ledger/chart_of_accounts/coa_list.html +25 -6
  51. django_ledger/templates/django_ledger/chart_of_accounts/coa_update.html +2 -2
  52. django_ledger/templates/django_ledger/chart_of_accounts/includes/coa_card.html +10 -4
  53. django_ledger/templates/django_ledger/expense/tags/expense_item_table.html +7 -0
  54. django_ledger/templates/django_ledger/financial_statements/tags/balance_sheet_statement.html +2 -2
  55. django_ledger/templates/django_ledger/includes/footer.html +2 -2
  56. django_ledger/templates/django_ledger/invoice/invoice_detail.html +3 -3
  57. django_ledger/templatetags/django_ledger.py +7 -1
  58. django_ledger/tests/base.py +23 -7
  59. django_ledger/tests/test_accounts.py +145 -9
  60. django_ledger/urls/account.py +17 -24
  61. django_ledger/urls/chart_of_accounts.py +6 -0
  62. django_ledger/utils.py +9 -36
  63. django_ledger/views/__init__.py +2 -2
  64. django_ledger/views/account.py +91 -116
  65. django_ledger/views/auth.py +1 -1
  66. django_ledger/views/bank_account.py +9 -11
  67. django_ledger/views/bill.py +91 -80
  68. django_ledger/views/{coa.py → chart_of_accounts.py} +49 -44
  69. django_ledger/views/closing_entry.py +8 -0
  70. django_ledger/views/customer.py +1 -1
  71. django_ledger/views/data_import.py +1 -1
  72. django_ledger/views/entity.py +1 -1
  73. django_ledger/views/estimate.py +13 -8
  74. django_ledger/views/feedback.py +1 -1
  75. django_ledger/views/financial_statement.py +1 -1
  76. django_ledger/views/home.py +1 -1
  77. django_ledger/views/inventory.py +9 -0
  78. django_ledger/views/invoice.py +5 -2
  79. django_ledger/views/item.py +58 -68
  80. django_ledger/views/journal_entry.py +1 -1
  81. django_ledger/views/ledger.py +3 -1
  82. django_ledger/views/mixins.py +25 -13
  83. django_ledger/views/purchase_order.py +1 -1
  84. django_ledger/views/transactions.py +1 -1
  85. django_ledger/views/unit.py +9 -0
  86. django_ledger/views/vendor.py +1 -1
  87. {django_ledger-0.6.4.dist-info → django_ledger-0.7.1.dist-info}/AUTHORS.md +8 -2
  88. {django_ledger-0.6.4.dist-info → django_ledger-0.7.1.dist-info}/METADATA +33 -44
  89. {django_ledger-0.6.4.dist-info → django_ledger-0.7.1.dist-info}/RECORD +92 -89
  90. {django_ledger-0.6.4.dist-info → django_ledger-0.7.1.dist-info}/WHEEL +1 -1
  91. django_ledger/forms/coa.py +0 -47
  92. {django_ledger-0.6.4.dist-info → django_ledger-0.7.1.dist-info}/LICENSE +0 -0
  93. {django_ledger-0.6.4.dist-info → django_ledger-0.7.1.dist-info}/top_level.txt +0 -0
@@ -2,16 +2,13 @@
2
2
  Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
3
3
  Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
4
4
 
5
- Contributions to this module:
6
- * Miguel Sanda <msanda@arrobalytics.com>
7
- * Pranav P Tulshyan ptulshyan77@gmail.com<>
8
-
9
5
  The EntityModel represents the Company, Corporation, Legal Entity, Enterprise or Person that engage and operate as a
10
6
  business. EntityModels can be created as part of a parent/child model structure to accommodate complex corporate
11
7
  structures where certain entities may be owned by other entities and may also generate consolidated financial statements.
8
+
12
9
  Another use case of parent/child model structures is the coordination and authorization of inter-company transactions
13
- across multiple related entities. The EntityModel encapsulates all LedgerModel, JournalEntryModel and TransactionModel which is the core structure of
14
- Django Ledger in order to track and produce all financials.
10
+ across multiple related entities. The EntityModel encapsulates all LedgerModel, JournalEntryModel and TransactionModel
11
+ which is the core structure of Django Ledger in order to track and produce all financials.
15
12
 
16
13
  The EntityModel must be assigned an Administrator at creation, and may have optional Managers that will have the ability
17
14
  to operate on such EntityModel.
@@ -36,7 +33,7 @@ from django.core.cache import caches
36
33
  from django.core.exceptions import ValidationError, ObjectDoesNotExist
37
34
  from django.core.validators import MinValueValidator
38
35
  from django.db import models
39
- from django.db.models import Q
36
+ from django.db.models import Q, F, Model
40
37
  from django.db.models.signals import pre_save
41
38
  from django.urls import reverse
42
39
  from django.utils.text import slugify
@@ -47,7 +44,7 @@ from django_ledger.io import roles as roles_module, validate_roles, IODigestCont
47
44
  from django_ledger.io.io_core import IOMixIn, get_localtime, get_localdate
48
45
  from django_ledger.models.accounts import AccountModel, AccountModelQuerySet, DEBIT, CREDIT
49
46
  from django_ledger.models.bank_account import BankAccountModelQuerySet, BankAccountModel
50
- from django_ledger.models.coa import ChartOfAccountModel, ChartOfAccountModelQuerySet
47
+ from django_ledger.models.chart_of_accounts import ChartOfAccountModel, ChartOfAccountModelQuerySet
51
48
  from django_ledger.models.coa_default import CHART_OF_ACCOUNTS_ROOT_MAP
52
49
  from django_ledger.models.customer import CustomerModelQueryset, CustomerModel
53
50
  from django_ledger.models.items import (ItemModelQuerySet, ItemTransactionModelQuerySet,
@@ -113,8 +110,14 @@ class EntityModelManager(MP_NodeManager):
113
110
 
114
111
  def get_queryset(self):
115
112
  """Sets the custom queryset as the default."""
116
- qs = EntityModelQuerySet(self.model, using=self._db).order_by('path')
117
- return qs.order_by('path').select_related('admin', 'default_coa')
113
+ qs = EntityModelQuerySet(
114
+ self.model,
115
+ using=self._db).order_by('path')
116
+ return qs.order_by('path').select_related(
117
+ 'admin',
118
+ 'default_coa').annotate(
119
+ _default_coa_slug=F('default_coa__slug'),
120
+ )
118
121
 
119
122
  def for_user(self, user_model, authorized_superuser: bool = False):
120
123
  """
@@ -421,6 +424,9 @@ class EntityModelFiscalPeriodMixIn:
421
424
 
422
425
 
423
426
  class EntityModelClosingEntryMixIn:
427
+ """
428
+ Closing Entries provide
429
+ """
424
430
 
425
431
  def validate_closing_entry_model(self, closing_entry_model, closing_date: Optional[date] = None):
426
432
  if isinstance(self, EntityModel):
@@ -519,7 +525,6 @@ class EntityModelClosingEntryMixIn:
519
525
  return self.get_closing_entry_queryset_for_date(closing_date=closing_date)
520
526
 
521
527
  # ----> Create Closing Entries <----
522
-
523
528
  def create_closing_entry_for_date(self,
524
529
  closing_date: date,
525
530
  closing_entry_model=None,
@@ -578,7 +583,6 @@ class EntityModelClosingEntryMixIn:
578
583
  return f'closing_entry_{end_dt_str}_{self.uuid}'
579
584
 
580
585
  # ----> Closing Entry Caching Month < -----
581
-
582
586
  def get_closing_entry_cache_for_date(self,
583
587
  closing_date: date,
584
588
  cache_name: str = 'default',
@@ -801,6 +805,13 @@ class EntityModelAbstract(MP_Node,
801
805
  super().__init__(*args, **kwargs)
802
806
  self._CLOSING_ENTRY_DATES: Optional[List[date]] = None
803
807
 
808
+ @property
809
+ def default_coa_slug(self):
810
+ try:
811
+ return getattr(self, '_default_coa_slug')
812
+ except AttributeError:
813
+ return self.default_coa.slug
814
+
804
815
  # ## Logging ###
805
816
  def get_logger_name(self):
806
817
  return f'EntityModel {self.uuid}'
@@ -1272,7 +1283,10 @@ class EntityModelAbstract(MP_Node,
1272
1283
  def get_coa_accounts(self,
1273
1284
  coa_model: Optional[Union[ChartOfAccountModel, UUID, str]] = None,
1274
1285
  active: bool = True,
1275
- order_by: Optional[Tuple] = ('code',)) -> AccountModelQuerySet:
1286
+ locked: bool = False,
1287
+ order_by: Optional[Tuple] = ('code',),
1288
+ return_coa_model: bool = False,
1289
+ ) -> Union[AccountModelQuerySet, Tuple[ChartOfAccountModel, AccountModelQuerySet]]:
1276
1290
  """
1277
1291
  Fetches the AccountModelQuerySet for a specific ChartOfAccountModel.
1278
1292
 
@@ -1282,6 +1296,8 @@ class EntityModelAbstract(MP_Node,
1282
1296
  The ChartOfAccountsModel UUID, model instance or slug to pull accounts from. If None, will use default CoA.
1283
1297
  active: bool
1284
1298
  Selects only active accounts.
1299
+ locked: bool
1300
+ Selects only locked accounts.
1285
1301
  order_by: list of strings.
1286
1302
  Optional list of fields passed to the order_by QuerySet method.
1287
1303
 
@@ -1294,9 +1310,9 @@ class EntityModelAbstract(MP_Node,
1294
1310
  if not coa_model:
1295
1311
  coa_model = self.default_coa
1296
1312
  elif isinstance(coa_model, UUID):
1297
- coa_model = self.chartofaccountmodel_set.get(uuid__exact=coa_model)
1313
+ coa_model = self.chartofaccountmodel_set.select_related('entity').get(uuid__exact=coa_model)
1298
1314
  elif isinstance(coa_model, str):
1299
- coa_model = self.chartofaccountmodel_set.get(slug__exact=coa_model)
1315
+ coa_model = self.chartofaccountmodel_set.select_related('entity').get(slug__exact=coa_model)
1300
1316
  elif isinstance(coa_model, ChartOfAccountModel):
1301
1317
  self.validate_chart_of_accounts_for_entity(coa_model=coa_model)
1302
1318
  else:
@@ -1309,9 +1325,14 @@ class EntityModelAbstract(MP_Node,
1309
1325
  if active:
1310
1326
  account_model_qs = account_model_qs.active()
1311
1327
 
1328
+ if locked:
1329
+ account_model_qs = account_model_qs.locked()
1330
+
1312
1331
  if order_by:
1313
1332
  account_model_qs = account_model_qs.order_by(*order_by)
1314
1333
 
1334
+ if return_coa_model:
1335
+ return coa_model, account_model_qs
1315
1336
  return account_model_qs
1316
1337
 
1317
1338
  def get_default_coa_accounts(self,
@@ -1694,11 +1715,12 @@ class EntityModelAbstract(MP_Node,
1694
1715
 
1695
1716
  account_model_qs = self.get_coa_accounts(coa_model=coa_model, active=True)
1696
1717
 
1697
- account_model_qs = account_model_qs.with_roles(roles=[
1698
- roles_module.ASSET_CA_CASH,
1699
- roles_module.ASSET_CA_PREPAID,
1700
- roles_module.LIABILITY_CL_ACC_PAYABLE
1701
- ]).is_role_default()
1718
+ account_model_qs = account_model_qs.with_roles(
1719
+ roles=[
1720
+ roles_module.ASSET_CA_CASH,
1721
+ roles_module.ASSET_CA_PREPAID,
1722
+ roles_module.LIABILITY_CL_ACC_PAYABLE
1723
+ ]).is_role_default()
1702
1724
 
1703
1725
  # evaluates the queryset...
1704
1726
  len(account_model_qs)
@@ -1803,11 +1825,12 @@ class EntityModelAbstract(MP_Node,
1803
1825
  raise EntityModelValidationError('CustomerModel must be an instance of CustomerModel, UUID or str.')
1804
1826
 
1805
1827
  account_model_qs = self.get_coa_accounts(coa_model=coa_model, active=True)
1806
- account_model_qs = account_model_qs.with_roles(roles=[
1807
- roles_module.ASSET_CA_CASH,
1808
- roles_module.ASSET_CA_RECEIVABLES,
1809
- roles_module.LIABILITY_CL_DEFERRED_REVENUE
1810
- ]).is_role_default()
1828
+ account_model_qs = account_model_qs.with_roles(
1829
+ roles=[
1830
+ roles_module.ASSET_CA_CASH,
1831
+ roles_module.ASSET_CA_RECEIVABLES,
1832
+ roles_module.LIABILITY_CL_DEFERRED_REVENUE
1833
+ ]).is_role_default()
1811
1834
 
1812
1835
  # evaluates the queryset...
1813
1836
  len(account_model_qs)
@@ -1998,7 +2021,9 @@ class EntityModelAbstract(MP_Node,
1998
2021
  raise EntityModelValidationError(
1999
2022
  _(f'Invalid Account Type: choices are {BankAccountModel.VALID_ACCOUNT_TYPES}'))
2000
2023
  account_model_qs = self.get_coa_accounts(coa_model=coa_model, active=True)
2001
- account_model_qs = account_model_qs.with_roles(roles=roles_module.ASSET_CA_CASH).is_role_default()
2024
+ account_model_qs = account_model_qs.with_roles(
2025
+ roles=roles_module.ASSET_CA_CASH
2026
+ ).is_role_default()
2002
2027
  bank_account_model = BankAccountModel(
2003
2028
  name=name,
2004
2029
  entity_model=self,
@@ -2154,11 +2179,12 @@ class EntityModelAbstract(MP_Node,
2154
2179
  raise EntityModelValidationError(f'Invalid UnitOfMeasureModel for entity {self.slug}...')
2155
2180
 
2156
2181
  account_model_qs = self.get_coa_accounts(coa_model=coa_model, active=True)
2157
- account_model_qs = account_model_qs.with_roles(roles=[
2158
- roles_module.ASSET_CA_INVENTORY,
2159
- roles_module.COGS,
2160
- roles_module.INCOME_OPERATIONAL
2161
- ]).is_role_default()
2182
+ account_model_qs = account_model_qs.with_roles(
2183
+ roles=[
2184
+ roles_module.ASSET_CA_INVENTORY,
2185
+ roles_module.COGS,
2186
+ roles_module.INCOME_OPERATIONAL
2187
+ ]).is_role_default()
2162
2188
 
2163
2189
  # evaluates the queryset...
2164
2190
  len(account_model_qs)
@@ -2229,10 +2255,11 @@ class EntityModelAbstract(MP_Node,
2229
2255
  raise EntityModelValidationError(f'Invalid UnitOfMeasureModel for entity {self.slug}...')
2230
2256
 
2231
2257
  account_model_qs = self.get_coa_accounts(coa_model=coa_model, active=True)
2232
- account_model_qs = account_model_qs.with_roles(roles=[
2233
- roles_module.COGS,
2234
- roles_module.INCOME_OPERATIONAL
2235
- ]).is_role_default()
2258
+ account_model_qs = account_model_qs.with_roles(
2259
+ roles=[
2260
+ roles_module.COGS,
2261
+ roles_module.INCOME_OPERATIONAL
2262
+ ]).is_role_default()
2236
2263
 
2237
2264
  # evaluates the queryset...
2238
2265
  len(account_model_qs)
@@ -2308,7 +2335,9 @@ class EntityModelAbstract(MP_Node,
2308
2335
  raise EntityModelValidationError(f'Invalid UnitOfMeasureModel for entity {self.slug}...')
2309
2336
 
2310
2337
  account_model_qs = self.get_coa_accounts(coa_model=coa_model, active=True)
2311
- account_model_qs = account_model_qs.with_roles(roles=roles_module.EXPENSE_OPERATIONAL)
2338
+ account_model_qs = account_model_qs.with_roles(
2339
+ roles=roles_module.EXPENSE_OPERATIONAL
2340
+ )
2312
2341
  if not expense_account:
2313
2342
  expense_account = account_model_qs.is_role_default().get()
2314
2343
  elif isinstance(expense_account, UUID):
@@ -2407,7 +2436,9 @@ class EntityModelAbstract(MP_Node,
2407
2436
  raise EntityModelValidationError(f'Invalid UnitOfMeasureModel for entity {self.slug}...')
2408
2437
 
2409
2438
  account_model_qs = self.get_coa_accounts(coa_model=coa_model, active=True)
2410
- account_model_qs = account_model_qs.with_roles(roles=roles_module.ASSET_CA_INVENTORY)
2439
+ account_model_qs = account_model_qs.with_roles(
2440
+ roles=roles_module.ASSET_CA_INVENTORY
2441
+ )
2411
2442
  if not inventory_account:
2412
2443
  inventory_account = account_model_qs.is_role_default().get()
2413
2444
  elif isinstance(inventory_account, UUID):
@@ -2627,7 +2658,9 @@ class EntityModelAbstract(MP_Node,
2627
2658
  ROLES_NEEDED.append(roles_module.EQUITY_CAPITAL)
2628
2659
 
2629
2660
  account_model_qs = self.get_coa_accounts(coa_model=coa_model)
2630
- account_model_qs = account_model_qs.with_roles(roles=ROLES_NEEDED).is_role_default()
2661
+ account_model_qs = account_model_qs.with_roles(
2662
+ roles=ROLES_NEEDED
2663
+ ).is_role_default()
2631
2664
 
2632
2665
  if not cash_account or not capital_account:
2633
2666
  if cash_account or capital_account:
@@ -3005,6 +3038,22 @@ class EntityModelAbstract(MP_Node,
3005
3038
  }
3006
3039
  )
3007
3040
 
3041
+ def get_coa_list_inactive_url(self) -> str:
3042
+ return reverse(
3043
+ viewname='django_ledger:coa-list-inactive',
3044
+ kwargs={
3045
+ 'entity_slug': self.slug
3046
+ }
3047
+ )
3048
+
3049
+ def get_coa_create_url(self) -> str:
3050
+ return reverse(
3051
+ viewname='django_ledger:coa-create',
3052
+ kwargs={
3053
+ 'entity_slug': self.slug
3054
+ }
3055
+ )
3056
+
3008
3057
  def get_accounts_url(self) -> str:
3009
3058
  """
3010
3059
  The EntityModel Code of Accounts llist import URL.
@@ -3071,10 +3120,14 @@ class EntityModel(EntityModelAbstract):
3071
3120
  """
3072
3121
  Entity Model Base Class From Abstract
3073
3122
  """
3123
+ class Meta(EntityModelAbstract.Meta):
3124
+ swappable = 'DJANGO_LEDGER_ENTITY_MODEL'
3125
+ abstract = False
3126
+
3074
3127
 
3075
3128
 
3076
3129
  # ## ENTITY STATE....
3077
- class EntityStateModelAbstract(models.Model):
3130
+ class EntityStateModelAbstract(Model):
3078
3131
  KEY_JOURNAL_ENTRY = 'je'
3079
3132
  KEY_PURCHASE_ORDER = 'po'
3080
3133
  KEY_BILL = 'bill'
@@ -3135,6 +3188,10 @@ class EntityStateModel(EntityStateModelAbstract):
3135
3188
  Entity State Model Base Class from Abstract.
3136
3189
  """
3137
3190
 
3191
+ class Meta(EntityStateModelAbstract.Meta):
3192
+ swappable = 'DJANGO_LEDGER_ENTITY_STATE_MODEL'
3193
+ abstract = False
3194
+
3138
3195
 
3139
3196
  # ## ENTITY MANAGEMENT.....
3140
3197
  class EntityManagementModelAbstract(CreateUpdateMixIn):
@@ -2,9 +2,6 @@
2
2
  Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
3
3
  Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
4
4
 
5
- Contributions to this module:
6
- * Miguel Sanda <msanda@arrobalytics.com>
7
-
8
5
  The EstimateModel provides the means to estimate customer requests, jobs or quotes that may ultimately be considered
9
6
  contracts, if approved. The EstimateModels will estimate revenues and costs associated with a specific scope of work
10
7
  which is documented using ItemTransactionModels.
@@ -140,12 +137,6 @@ class EstimateModelManager(models.Manager):
140
137
  user_model
141
138
  Logged in and authenticated django UserModel instance.
142
139
 
143
- Examples
144
- --------
145
- >>> request_user = request.user
146
- >>> slug = kwargs['entity_slug'] # may come from request kwargs
147
- >>> bill_model_qs = EstimateModel.objects.for_entity(user_model=request_user, entity_slug=slug)
148
-
149
140
  Returns
150
141
  -------
151
142
  EstimateModelQuerySet
@@ -1240,7 +1231,8 @@ class EstimateModelAbstract(CreateUpdateMixIn,
1240
1231
  'updated'
1241
1232
  ])
1242
1233
 
1243
- def update_state(self, itemtxs_qs: Optional[Union[ItemTransactionModelQuerySet, List[ItemTransactionModel]]] = None):
1234
+ def update_state(self,
1235
+ itemtxs_qs: Optional[Union[ItemTransactionModelQuerySet, List[ItemTransactionModel]]] = None):
1244
1236
  itemtxs_qs, _ = self.get_itemtxs_data(queryset=itemtxs_qs)
1245
1237
  self.update_cost_estimate(itemtxs_qs)
1246
1238
  self.update_revenue_estimate(itemtxs_qs)
@@ -1620,3 +1612,7 @@ class EstimateModel(EstimateModelAbstract):
1620
1612
  """
1621
1613
  Base EstimateModel Class.
1622
1614
  """
1615
+
1616
+ class Meta(EstimateModelAbstract.Meta):
1617
+ swappable = 'DJANGO_LEDGER_ESTIMATE_MODEL'
1618
+ abstract = False
@@ -2,9 +2,6 @@
2
2
  Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
3
3
  Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
4
4
 
5
- Contributions to this module:
6
- * Miguel Sanda <msanda@arrobalytics.com>
7
-
8
5
  This module implements the InvoiceModel, which represents the Sales Invoice/ Sales Invoice/ Tax Invoice/ Proof of Sale
9
6
  which the :func:`EntityModel <django_ledger.models.entity.EntityModel>` issues to its customers for the supply of
10
7
  goods or services. The model manages all the Sales Invoices which are issued by the :func:`EntityModel
@@ -36,14 +33,16 @@ from django.utils.translation import gettext_lazy as _
36
33
 
37
34
  from django_ledger.io import ASSET_CA_CASH, ASSET_CA_RECEIVABLES, LIABILITY_CL_DEFERRED_REVENUE
38
35
  from django_ledger.io.io_core import get_localtime, get_localdate
39
- from django_ledger.models import lazy_loader, ItemTransactionModelQuerySet, ItemModelQuerySet, ItemModel
36
+ from django_ledger.models import (
37
+ lazy_loader, ItemTransactionModelQuerySet,
38
+ ItemModelQuerySet, ItemModel, QuerySet, Manager
39
+ )
40
40
  from django_ledger.models.entity import EntityModel
41
41
  from django_ledger.models.mixins import (
42
42
  CreateUpdateMixIn, AccrualMixIn,
43
43
  MarkdownNotesMixIn, PaymentTermsMixIn,
44
44
  ItemizeMixIn
45
45
  )
46
-
47
46
  from django_ledger.models.signals import (
48
47
  invoice_status_draft,
49
48
  invoice_status_in_review,
@@ -52,7 +51,6 @@ from django_ledger.models.signals import (
52
51
  invoice_status_canceled,
53
52
  invoice_status_void
54
53
  )
55
-
56
54
  from django_ledger.settings import DJANGO_LEDGER_DOCUMENT_NUMBER_PADDING, DJANGO_LEDGER_INVOICE_NUMBER_PREFIX
57
55
 
58
56
  UserModel = get_user_model()
@@ -62,7 +60,7 @@ class InvoiceModelValidationError(ValidationError):
62
60
  pass
63
61
 
64
62
 
65
- class InvoiceModelQuerySet(models.QuerySet):
63
+ class InvoiceModelQuerySet(QuerySet):
66
64
  """
67
65
  A custom defined QuerySet for the InvoiceModel.
68
66
  This implements multiple methods or queries that we need to run to get a status of Invoices raised by the entity.
@@ -179,7 +177,7 @@ class InvoiceModelQuerySet(models.QuerySet):
179
177
  return self.filter(invoice_status__exact=InvoiceModel.INVOICE_STATUS_APPROVED)
180
178
 
181
179
 
182
- class InvoiceModelManager(models.Manager):
180
+ class InvoiceModelManager(Manager):
183
181
  """
184
182
  A custom defined InvoiceModel Manager that will act as an interface to handling the DB queries to the InvoiceModel.
185
183
  The default "get_queryset" has been overridden to refer the custom defined "InvoiceModelQuerySet"
@@ -575,9 +573,10 @@ class InvoiceModelAbstract(
575
573
  else:
576
574
  self.validate_itemtxs_qs(queryset)
577
575
 
578
- return queryset.select_related('item_model').order_by('item_model__earnings_account__uuid',
579
- 'entity_unit__uuid',
580
- 'item_model__earnings_account__balance_type').values(
576
+ return queryset.select_related('item_model').order_by(
577
+ 'item_model__earnings_account__uuid',
578
+ 'entity_unit__uuid',
579
+ 'item_model__earnings_account__balance_type').values(
581
580
  'item_model__earnings_account__uuid',
582
581
  'item_model__earnings_account__balance_type',
583
582
  'item_model__cogs_account__uuid',
@@ -1820,6 +1819,10 @@ class InvoiceModel(InvoiceModelAbstract):
1820
1819
  Base Invoice Model from Abstract.
1821
1820
  """
1822
1821
 
1822
+ class Meta:
1823
+ swappable = 'DJANGO_LEDGER_INVOICE_MODEL'
1824
+ abstract = False
1825
+
1823
1826
 
1824
1827
  def invoicemodel_presave(instance: InvoiceModel, **kwargs):
1825
1828
  if instance.can_generate_invoice_number():
@@ -2,10 +2,6 @@
2
2
  Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
3
3
  Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
4
4
 
5
- Contributions to this module:
6
- * Miguel Sanda <msanda@arrobalytics.com>
7
- * Pranav P Tulshyan <ptulshyan77@gmail.com>
8
-
9
5
  The Items refer to the additional detail provided to Bills, Invoices, Purchase Orders and Estimates for the purposes of
10
6
  documenting a breakdown of materials, labor, equipment, and other resources used for the purposes of the business
11
7
  operations.
@@ -29,7 +25,7 @@ from uuid import uuid4, UUID
29
25
  from django.core.exceptions import ValidationError, ObjectDoesNotExist
30
26
  from django.core.validators import MinValueValidator
31
27
  from django.db import models, transaction, IntegrityError
32
- from django.db.models import Q, Sum, F, ExpressionWrapper, DecimalField, Value, Case, When, QuerySet
28
+ from django.db.models import Q, Sum, F, ExpressionWrapper, DecimalField, Value, Case, When, QuerySet, Manager
33
29
  from django.db.models.functions import Coalesce
34
30
  from django.utils.translation import gettext_lazy as _
35
31
 
@@ -46,12 +42,12 @@ class ItemModelValidationError(ValidationError):
46
42
  pass
47
43
 
48
44
 
49
- class UnitOfMeasureModelQuerySet(models.QuerySet):
45
+ class UnitOfMeasureModelQuerySet(QuerySet):
50
46
  pass
51
47
 
52
48
 
53
49
  # UNIT OF MEASURES MODEL....
54
- class UnitOfMeasureModelManager(models.Manager):
50
+ class UnitOfMeasureModelManager(Manager):
55
51
  """
56
52
  A custom defined QuerySet Manager for the UnitOfMeasureModel.
57
53
  """
@@ -153,7 +149,7 @@ class UnitOfMeasureModelAbstract(CreateUpdateMixIn):
153
149
 
154
150
 
155
151
  # ITEM MODEL....
156
- class ItemModelQuerySet(models.QuerySet):
152
+ class ItemModelQuerySet(QuerySet):
157
153
  """
158
154
  A custom-defined ItemModelQuerySet that implements custom QuerySet methods related to the ItemModel.
159
155
  """
@@ -291,7 +287,7 @@ class ItemModelQuerySet(models.QuerySet):
291
287
  return self.inventory_all()
292
288
 
293
289
 
294
- class ItemModelManager(models.Manager):
290
+ class ItemModelManager(Manager):
295
291
  """
296
292
  A custom defined ItemModelManager that implement custom QuerySet methods related to the ItemModel
297
293
  """
@@ -389,7 +385,10 @@ class ItemModelManager(models.Manager):
389
385
  ItemModelQuerySet
390
386
  A Filtered ItemModelQuerySet.
391
387
  """
392
- qs = self.for_entity_active(entity_slug=entity_slug, user_model=user_model)
388
+ qs = self.for_entity_active(
389
+ entity_slug=entity_slug,
390
+ user_model=user_model
391
+ )
393
392
  return qs.filter(
394
393
  (
395
394
  Q(is_product_or_service=False) &
@@ -527,7 +526,6 @@ class ItemModelAbstract(CreateUpdateMixIn):
527
526
  uuid = models.UUIDField(default=uuid4, editable=False, primary_key=True)
528
527
  name = models.CharField(max_length=100, verbose_name=_('Item Name'))
529
528
 
530
- # todo: rename this and remove 'id' from it.
531
529
  item_id = models.CharField(max_length=50, blank=True, null=True, verbose_name=_('Internal ID'))
532
530
  item_number = models.CharField(max_length=30, editable=False, verbose_name=_('Item Number'))
533
531
  item_role = models.CharField(max_length=10, choices=ITEM_ROLE_CHOICES, null=True, blank=True)
@@ -851,7 +849,7 @@ class ItemModelAbstract(CreateUpdateMixIn):
851
849
 
852
850
 
853
851
  # ITEM TRANSACTION MODELS...
854
- class ItemTransactionModelQuerySet(models.QuerySet):
852
+ class ItemTransactionModelQuerySet(QuerySet):
855
853
 
856
854
  def is_received(self):
857
855
  return self.filter(po_item_status=ItemTransactionModel.STATUS_RECEIVED)
@@ -877,7 +875,7 @@ class ItemTransactionModelQuerySet(models.QuerySet):
877
875
  }
878
876
 
879
877
 
880
- class ItemTransactionModelManager(models.Manager):
878
+ class ItemTransactionModelManager(Manager):
881
879
 
882
880
  def for_user(self, user_model):
883
881
  qs = self.get_queryset()
@@ -1406,20 +1404,31 @@ class ItemTransactionModelAbstract(CreateUpdateMixIn):
1406
1404
 
1407
1405
 
1408
1406
  # FINAL MODEL CLASSES....
1409
-
1410
1407
  class UnitOfMeasureModel(UnitOfMeasureModelAbstract):
1411
1408
  """
1412
1409
  Base UnitOfMeasureModel from Abstract.
1413
1410
  """
1414
1411
 
1412
+ class Meta(UnitOfMeasureModelAbstract.Meta):
1413
+ abstract = False
1414
+ swappable = 'DJANGO_LEDGER_UNIT_OF_MEASURE_MODEL'
1415
+
1415
1416
 
1416
1417
  class ItemTransactionModel(ItemTransactionModelAbstract):
1417
1418
  """
1418
1419
  Base ItemTransactionModel from Abstract.
1419
1420
  """
1420
1421
 
1422
+ class Meta(ItemTransactionModelAbstract.Meta):
1423
+ abstract = False
1424
+ swappable = 'DJANGO_LEDGER_ITEM_TRANSACTION_MODEL'
1425
+
1421
1426
 
1422
1427
  class ItemModel(ItemModelAbstract):
1423
1428
  """
1424
1429
  Base ItemModel from Abstract.
1425
1430
  """
1431
+
1432
+ class Meta(ItemModelAbstract.Meta):
1433
+ abstract = False
1434
+ swappable = 'DJANGO_LEDGER_ITEM_MODEL'