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.
- django_ledger/__init__.py +1 -4
- django_ledger/admin/__init__.py +1 -1
- django_ledger/admin/{coa.py → chart_of_accounts.py} +1 -1
- django_ledger/admin/entity.py +1 -1
- django_ledger/contrib/django_ledger_graphene/accounts/schema.py +1 -1
- django_ledger/forms/account.py +43 -38
- django_ledger/forms/bank_account.py +5 -2
- django_ledger/forms/bill.py +24 -36
- django_ledger/forms/chart_of_accounts.py +82 -0
- django_ledger/forms/customer.py +1 -1
- django_ledger/forms/data_import.py +3 -3
- django_ledger/forms/estimate.py +1 -1
- django_ledger/forms/invoice.py +5 -7
- django_ledger/forms/item.py +24 -15
- django_ledger/forms/transactions.py +3 -3
- django_ledger/io/io_core.py +4 -2
- django_ledger/io/io_library.py +1 -1
- django_ledger/io/io_middleware.py +5 -0
- django_ledger/migrations/0017_alter_accountmodel_unique_together_and_more.py +31 -0
- django_ledger/migrations/0018_transactionmodel_cleared_transactionmodel_reconciled_and_more.py +37 -0
- django_ledger/models/__init__.py +1 -1
- django_ledger/models/accounts.py +229 -265
- django_ledger/models/bank_account.py +6 -6
- django_ledger/models/bill.py +7 -6
- django_ledger/models/{coa.py → chart_of_accounts.py} +187 -72
- django_ledger/models/closing_entry.py +5 -10
- django_ledger/models/coa_default.py +10 -9
- django_ledger/models/customer.py +6 -6
- django_ledger/models/data_import.py +12 -8
- django_ledger/models/entity.py +96 -39
- django_ledger/models/estimate.py +6 -10
- django_ledger/models/invoice.py +14 -11
- django_ledger/models/items.py +23 -14
- django_ledger/models/journal_entry.py +73 -30
- django_ledger/models/ledger.py +8 -8
- django_ledger/models/mixins.py +0 -3
- django_ledger/models/purchase_order.py +9 -9
- django_ledger/models/signals.py +0 -3
- django_ledger/models/transactions.py +24 -7
- django_ledger/models/unit.py +4 -3
- django_ledger/models/utils.py +0 -3
- django_ledger/models/vendor.py +4 -3
- django_ledger/settings.py +28 -3
- django_ledger/templates/django_ledger/account/account_create.html +2 -2
- django_ledger/templates/django_ledger/account/account_update.html +1 -1
- django_ledger/templates/django_ledger/account/tags/account_txs_table.html +1 -0
- django_ledger/templates/django_ledger/account/tags/accounts_table.html +29 -19
- django_ledger/templates/django_ledger/bills/bill_detail.html +3 -3
- django_ledger/templates/django_ledger/chart_of_accounts/coa_create.html +25 -0
- django_ledger/templates/django_ledger/chart_of_accounts/coa_list.html +25 -6
- django_ledger/templates/django_ledger/chart_of_accounts/coa_update.html +2 -2
- django_ledger/templates/django_ledger/chart_of_accounts/includes/coa_card.html +10 -4
- django_ledger/templates/django_ledger/expense/tags/expense_item_table.html +7 -0
- django_ledger/templates/django_ledger/financial_statements/tags/balance_sheet_statement.html +2 -2
- django_ledger/templates/django_ledger/includes/footer.html +2 -2
- django_ledger/templates/django_ledger/invoice/invoice_detail.html +3 -3
- django_ledger/templatetags/django_ledger.py +7 -1
- django_ledger/tests/base.py +23 -7
- django_ledger/tests/test_accounts.py +145 -9
- django_ledger/urls/account.py +17 -24
- django_ledger/urls/chart_of_accounts.py +6 -0
- django_ledger/utils.py +9 -36
- django_ledger/views/__init__.py +2 -2
- django_ledger/views/account.py +91 -116
- django_ledger/views/auth.py +1 -1
- django_ledger/views/bank_account.py +9 -11
- django_ledger/views/bill.py +91 -80
- django_ledger/views/{coa.py → chart_of_accounts.py} +49 -44
- django_ledger/views/closing_entry.py +8 -0
- django_ledger/views/customer.py +1 -1
- django_ledger/views/data_import.py +1 -1
- django_ledger/views/entity.py +1 -1
- django_ledger/views/estimate.py +13 -8
- django_ledger/views/feedback.py +1 -1
- django_ledger/views/financial_statement.py +1 -1
- django_ledger/views/home.py +1 -1
- django_ledger/views/inventory.py +9 -0
- django_ledger/views/invoice.py +5 -2
- django_ledger/views/item.py +58 -68
- django_ledger/views/journal_entry.py +1 -1
- django_ledger/views/ledger.py +3 -1
- django_ledger/views/mixins.py +25 -13
- django_ledger/views/purchase_order.py +1 -1
- django_ledger/views/transactions.py +1 -1
- django_ledger/views/unit.py +9 -0
- django_ledger/views/vendor.py +1 -1
- {django_ledger-0.6.4.dist-info → django_ledger-0.7.1.dist-info}/AUTHORS.md +8 -2
- {django_ledger-0.6.4.dist-info → django_ledger-0.7.1.dist-info}/METADATA +33 -44
- {django_ledger-0.6.4.dist-info → django_ledger-0.7.1.dist-info}/RECORD +92 -89
- {django_ledger-0.6.4.dist-info → django_ledger-0.7.1.dist-info}/WHEEL +1 -1
- django_ledger/forms/coa.py +0 -47
- {django_ledger-0.6.4.dist-info → django_ledger-0.7.1.dist-info}/LICENSE +0 -0
- {django_ledger-0.6.4.dist-info → django_ledger-0.7.1.dist-info}/top_level.txt +0 -0
django_ledger/models/entity.py
CHANGED
|
@@ -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
|
|
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.
|
|
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(
|
|
117
|
-
|
|
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
|
-
|
|
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(
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
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(
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
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(
|
|
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(
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
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(
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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):
|
django_ledger/models/estimate.py
CHANGED
|
@@ -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,
|
|
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
|
django_ledger/models/invoice.py
CHANGED
|
@@ -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
|
|
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(
|
|
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(
|
|
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(
|
|
579
|
-
|
|
580
|
-
|
|
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():
|
django_ledger/models/items.py
CHANGED
|
@@ -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(
|
|
45
|
+
class UnitOfMeasureModelQuerySet(QuerySet):
|
|
50
46
|
pass
|
|
51
47
|
|
|
52
48
|
|
|
53
49
|
# UNIT OF MEASURES MODEL....
|
|
54
|
-
class UnitOfMeasureModelManager(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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'
|