django-ledger 0.7.11__py3-none-any.whl → 0.8.0__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 -1
- django_ledger/context.py +12 -0
- django_ledger/forms/bill.py +0 -4
- django_ledger/forms/closing_entry.py +13 -1
- django_ledger/forms/data_import.py +1 -1
- django_ledger/forms/estimate.py +3 -6
- django_ledger/forms/invoice.py +3 -7
- django_ledger/forms/item.py +10 -18
- django_ledger/forms/purchase_order.py +2 -4
- django_ledger/io/io_core.py +8 -26
- django_ledger/io/io_generator.py +7 -6
- django_ledger/io/io_library.py +1 -2
- django_ledger/migrations/0025_alter_billmodel_cash_account_and_more.py +70 -0
- django_ledger/models/accounts.py +109 -69
- django_ledger/models/bank_account.py +40 -23
- django_ledger/models/bill.py +79 -63
- django_ledger/models/chart_of_accounts.py +173 -105
- django_ledger/models/closing_entry.py +99 -48
- django_ledger/models/customer.py +60 -39
- django_ledger/models/data_import.py +55 -41
- django_ledger/models/deprecations.py +61 -0
- django_ledger/models/entity.py +18 -16
- django_ledger/models/estimate.py +57 -28
- django_ledger/models/invoice.py +46 -26
- django_ledger/models/items.py +503 -142
- django_ledger/models/journal_entry.py +61 -47
- django_ledger/models/ledger.py +106 -42
- django_ledger/models/mixins.py +5 -3
- django_ledger/models/purchase_order.py +39 -17
- django_ledger/models/transactions.py +152 -113
- django_ledger/models/unit.py +57 -30
- django_ledger/models/vendor.py +75 -43
- django_ledger/report/core.py +2 -14
- django_ledger/settings.py +56 -71
- django_ledger/static/django_ledger/bundle/djetler.bundle.js +1 -1
- django_ledger/static/django_ledger/bundle/djetler.bundle.js.LICENSE.txt +25 -0
- django_ledger/static/django_ledger/bundle/styles.bundle.js +1 -1
- django_ledger/static/django_ledger/css/djl_styles.css +273 -0
- django_ledger/templates/django_ledger/bills/includes/card_bill.html +2 -2
- django_ledger/templates/django_ledger/components/menu.html +41 -26
- django_ledger/templates/django_ledger/customer/tags/customer_table.html +5 -5
- django_ledger/templates/django_ledger/entity/includes/card_entity.html +12 -6
- django_ledger/templates/django_ledger/financial_statements/balance_sheet.html +1 -1
- django_ledger/templates/django_ledger/financial_statements/cash_flow.html +4 -1
- django_ledger/templates/django_ledger/financial_statements/income_statement.html +4 -1
- django_ledger/templates/django_ledger/financial_statements/tags/balance_sheet_statement.html +27 -3
- django_ledger/templates/django_ledger/financial_statements/tags/cash_flow_statement.html +16 -4
- django_ledger/templates/django_ledger/financial_statements/tags/income_statement.html +73 -18
- django_ledger/templates/django_ledger/includes/widget_ratios.html +18 -24
- django_ledger/templates/django_ledger/invoice/includes/card_invoice.html +3 -3
- django_ledger/templates/django_ledger/layouts/base.html +6 -1
- django_ledger/templates/django_ledger/vendor/tags/vendor_table.html +9 -5
- django_ledger/tests/test_accounts.py +1 -2
- django_ledger/tests/test_io.py +17 -0
- django_ledger/tests/test_purchase_order.py +3 -3
- django_ledger/tests/test_transactions.py +1 -2
- django_ledger/urls/__init__.py +0 -4
- django_ledger/views/bill.py +8 -11
- django_ledger/views/chart_of_accounts.py +6 -4
- django_ledger/views/closing_entry.py +11 -7
- django_ledger/views/customer.py +13 -17
- django_ledger/views/data_import.py +7 -6
- django_ledger/views/djl_api.py +3 -5
- django_ledger/views/entity.py +2 -4
- django_ledger/views/estimate.py +3 -7
- django_ledger/views/inventory.py +3 -5
- django_ledger/views/invoice.py +4 -6
- django_ledger/views/item.py +7 -11
- django_ledger/views/journal_entry.py +1 -2
- django_ledger/views/mixins.py +25 -19
- django_ledger/views/purchase_order.py +24 -35
- django_ledger/views/unit.py +1 -2
- django_ledger/views/vendor.py +1 -2
- {django_ledger-0.7.11.dist-info → django_ledger-0.8.0.dist-info}/METADATA +43 -75
- {django_ledger-0.7.11.dist-info → django_ledger-0.8.0.dist-info}/RECORD +79 -108
- {django_ledger-0.7.11.dist-info → django_ledger-0.8.0.dist-info}/WHEEL +1 -1
- django_ledger-0.8.0.dist-info/top_level.txt +1 -0
- django_ledger/contrib/django_ledger_graphene/__init__.py +0 -0
- django_ledger/contrib/django_ledger_graphene/accounts/schema.py +0 -33
- django_ledger/contrib/django_ledger_graphene/api.py +0 -42
- django_ledger/contrib/django_ledger_graphene/apps.py +0 -6
- django_ledger/contrib/django_ledger_graphene/auth/mutations.py +0 -49
- django_ledger/contrib/django_ledger_graphene/auth/schema.py +0 -6
- django_ledger/contrib/django_ledger_graphene/bank_account/mutations.py +0 -61
- django_ledger/contrib/django_ledger_graphene/bank_account/schema.py +0 -34
- django_ledger/contrib/django_ledger_graphene/bill/mutations.py +0 -0
- django_ledger/contrib/django_ledger_graphene/bill/schema.py +0 -34
- django_ledger/contrib/django_ledger_graphene/coa/mutations.py +0 -0
- django_ledger/contrib/django_ledger_graphene/coa/schema.py +0 -30
- django_ledger/contrib/django_ledger_graphene/customers/__init__.py +0 -0
- django_ledger/contrib/django_ledger_graphene/customers/mutations.py +0 -71
- django_ledger/contrib/django_ledger_graphene/customers/schema.py +0 -43
- django_ledger/contrib/django_ledger_graphene/data_import/mutations.py +0 -0
- django_ledger/contrib/django_ledger_graphene/data_import/schema.py +0 -0
- django_ledger/contrib/django_ledger_graphene/entity/mutations.py +0 -0
- django_ledger/contrib/django_ledger_graphene/entity/schema.py +0 -94
- django_ledger/contrib/django_ledger_graphene/item/mutations.py +0 -0
- django_ledger/contrib/django_ledger_graphene/item/schema.py +0 -31
- django_ledger/contrib/django_ledger_graphene/journal_entry/mutations.py +0 -0
- django_ledger/contrib/django_ledger_graphene/journal_entry/schema.py +0 -35
- django_ledger/contrib/django_ledger_graphene/ledger/mutations.py +0 -0
- django_ledger/contrib/django_ledger_graphene/ledger/schema.py +0 -32
- django_ledger/contrib/django_ledger_graphene/purchase_order/mutations.py +0 -0
- django_ledger/contrib/django_ledger_graphene/purchase_order/schema.py +0 -31
- django_ledger/contrib/django_ledger_graphene/transaction/mutations.py +0 -0
- django_ledger/contrib/django_ledger_graphene/transaction/schema.py +0 -36
- django_ledger/contrib/django_ledger_graphene/unit/mutations.py +0 -0
- django_ledger/contrib/django_ledger_graphene/unit/schema.py +0 -27
- django_ledger/contrib/django_ledger_graphene/vendor/mutations.py +0 -0
- django_ledger/contrib/django_ledger_graphene/vendor/schema.py +0 -37
- django_ledger/contrib/django_ledger_graphene/views.py +0 -12
- django_ledger-0.7.11.dist-info/top_level.txt +0 -4
- {django_ledger-0.7.11.dist-info → django_ledger-0.8.0.dist-info/licenses}/AUTHORS.md +0 -0
- {django_ledger-0.7.11.dist-info → django_ledger-0.8.0.dist-info/licenses}/LICENSE +0 -0
django_ledger/models/accounts.py
CHANGED
|
@@ -47,10 +47,11 @@ Roles serve several purposes:
|
|
|
47
47
|
3. Enable accurate generation of financial statements
|
|
48
48
|
4. Facilitate financial ratio calculations
|
|
49
49
|
"""
|
|
50
|
+
import warnings
|
|
50
51
|
from itertools import groupby
|
|
51
52
|
from random import randint
|
|
52
53
|
from typing import Union, List, Optional
|
|
53
|
-
from uuid import uuid4
|
|
54
|
+
from uuid import uuid4, UUID
|
|
54
55
|
|
|
55
56
|
from django.core.exceptions import ValidationError
|
|
56
57
|
from django.db import models
|
|
@@ -60,6 +61,7 @@ from django.urls import reverse
|
|
|
60
61
|
from django.utils.translation import gettext_lazy as _
|
|
61
62
|
from treebeard.mp_tree import MP_Node, MP_NodeManager, MP_NodeQuerySet
|
|
62
63
|
|
|
64
|
+
from django_ledger.io import DEBIT, CREDIT
|
|
63
65
|
from django_ledger.io.roles import (
|
|
64
66
|
ACCOUNT_ROLE_CHOICES, BS_ROLES, GROUP_INVOICE, GROUP_BILL, validate_roles,
|
|
65
67
|
GROUP_ASSETS, GROUP_LIABILITIES, GROUP_CAPITAL, GROUP_INCOME, GROUP_EXPENSES, GROUP_COGS,
|
|
@@ -67,15 +69,14 @@ from django_ledger.io.roles import (
|
|
|
67
69
|
ROOT_CAPITAL, ROOT_INCOME, ROOT_EXPENSES, ROOT_COA, VALID_PARENTS,
|
|
68
70
|
ROLES_ORDER_ALL, ASSET_CA_CASH
|
|
69
71
|
)
|
|
72
|
+
from django_ledger.models.deprecations import deprecated_entity_slug_behavior
|
|
70
73
|
from django_ledger.models.mixins import CreateUpdateMixIn
|
|
71
74
|
from django_ledger.models.utils import lazy_loader
|
|
72
|
-
from django_ledger.settings import
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
CREDIT = 'credit'
|
|
78
|
-
"""A constant, identifying a CREDIT Account or CREDIT transaction in the respective database fields"""
|
|
75
|
+
from django_ledger.settings import (
|
|
76
|
+
DJANGO_LEDGER_ACCOUNT_CODE_GENERATE,
|
|
77
|
+
DJANGO_LEDGER_ACCOUNT_CODE_USE_PREFIX,
|
|
78
|
+
DJANGO_LEDGER_USE_DEPRECATED_BEHAVIOR
|
|
79
|
+
)
|
|
79
80
|
|
|
80
81
|
|
|
81
82
|
class AccountModelValidationError(ValidationError):
|
|
@@ -87,7 +88,7 @@ class AccountModelQuerySet(MP_NodeQuerySet):
|
|
|
87
88
|
Custom QuerySet for AccountModel inheriting from MP_NodeQuerySet.
|
|
88
89
|
"""
|
|
89
90
|
|
|
90
|
-
def active(self):
|
|
91
|
+
def active(self) -> 'AccountModelQuerySet':
|
|
91
92
|
"""
|
|
92
93
|
Filters the queryset to include only active items.
|
|
93
94
|
|
|
@@ -98,7 +99,7 @@ class AccountModelQuerySet(MP_NodeQuerySet):
|
|
|
98
99
|
"""
|
|
99
100
|
return self.filter(active=True)
|
|
100
101
|
|
|
101
|
-
def inactive(self):
|
|
102
|
+
def inactive(self) -> 'AccountModelQuerySet':
|
|
102
103
|
"""
|
|
103
104
|
Filters and returns queryset entries where the active field is set to False.
|
|
104
105
|
|
|
@@ -109,7 +110,7 @@ class AccountModelQuerySet(MP_NodeQuerySet):
|
|
|
109
110
|
"""
|
|
110
111
|
return self.filter(active=False)
|
|
111
112
|
|
|
112
|
-
def locked(self):
|
|
113
|
+
def locked(self) -> 'AccountModelQuerySet':
|
|
113
114
|
"""
|
|
114
115
|
Filters the queryset to include only locked AccountModels.
|
|
115
116
|
|
|
@@ -120,7 +121,7 @@ class AccountModelQuerySet(MP_NodeQuerySet):
|
|
|
120
121
|
"""
|
|
121
122
|
return self.filter(locked=True)
|
|
122
123
|
|
|
123
|
-
def unlocked(self):
|
|
124
|
+
def unlocked(self) -> 'AccountModelQuerySet':
|
|
124
125
|
"""
|
|
125
126
|
Returns a filtered list of items where the 'locked' attribute is set to False.
|
|
126
127
|
|
|
@@ -131,7 +132,7 @@ class AccountModelQuerySet(MP_NodeQuerySet):
|
|
|
131
132
|
"""
|
|
132
133
|
return self.filter(locked=False)
|
|
133
134
|
|
|
134
|
-
def with_roles(self, roles: Union[List, str]):
|
|
135
|
+
def with_roles(self, roles: Union[List, str]) -> 'AccountModelQuerySet':
|
|
135
136
|
"""
|
|
136
137
|
Filter the accounts based on the specified roles. This method helps to retrieve accounts associated
|
|
137
138
|
with a particular role or a list of roles.
|
|
@@ -156,16 +157,16 @@ class AccountModelQuerySet(MP_NodeQuerySet):
|
|
|
156
157
|
roles = validate_roles(roles)
|
|
157
158
|
return self.filter(role__in=roles)
|
|
158
159
|
|
|
159
|
-
def with_codes(self, codes: Union[List, str]):
|
|
160
|
+
def with_codes(self, codes: Union[List, str]) -> 'AccountModelQuerySet':
|
|
160
161
|
if isinstance(codes, str):
|
|
161
162
|
codes = [codes]
|
|
162
163
|
return self.filter(code__in=codes)
|
|
163
164
|
|
|
164
|
-
def cash(self):
|
|
165
|
+
def cash(self) -> 'AccountModelQuerySet':
|
|
165
166
|
"""Retrieve accounts that are of type ASSET_CA_CASH."""
|
|
166
167
|
return self.filter(role__exact=ASSET_CA_CASH)
|
|
167
168
|
|
|
168
|
-
def expenses(self):
|
|
169
|
+
def expenses(self) -> 'AccountModelQuerySet':
|
|
169
170
|
"""
|
|
170
171
|
Retrieve a queryset containing expenses filtered by specified roles.
|
|
171
172
|
|
|
@@ -180,7 +181,7 @@ class AccountModelQuerySet(MP_NodeQuerySet):
|
|
|
180
181
|
"""
|
|
181
182
|
return self.filter(role__in=GROUP_EXPENSES)
|
|
182
183
|
|
|
183
|
-
def is_coa_root(self):
|
|
184
|
+
def is_coa_root(self) -> 'AccountModelQuerySet':
|
|
184
185
|
"""
|
|
185
186
|
Retrieves the Chart of Accounts (CoA) root node queryset.
|
|
186
187
|
|
|
@@ -194,7 +195,7 @@ class AccountModelQuerySet(MP_NodeQuerySet):
|
|
|
194
195
|
"""
|
|
195
196
|
return self.filter(role__in=ROOT_GROUP)
|
|
196
197
|
|
|
197
|
-
def not_coa_root(self):
|
|
198
|
+
def not_coa_root(self) -> 'AccountModelQuerySet':
|
|
198
199
|
"""
|
|
199
200
|
Exclude AccountModels with ROOT_GROUP role from the QuerySet.
|
|
200
201
|
|
|
@@ -205,7 +206,7 @@ class AccountModelQuerySet(MP_NodeQuerySet):
|
|
|
205
206
|
"""
|
|
206
207
|
return self.exclude(role__in=ROOT_GROUP)
|
|
207
208
|
|
|
208
|
-
def gb_bs_role(self):
|
|
209
|
+
def gb_bs_role(self) -> 'AccountModelQuerySet':
|
|
209
210
|
"""
|
|
210
211
|
Groups accounts by Balance Sheet Bucket and then further groups them by role.
|
|
211
212
|
|
|
@@ -227,7 +228,7 @@ class AccountModelQuerySet(MP_NodeQuerySet):
|
|
|
227
228
|
]) for bsr, gb in accounts_gb
|
|
228
229
|
]
|
|
229
230
|
|
|
230
|
-
def is_role_default(self):
|
|
231
|
+
def is_role_default(self) -> 'AccountModelQuerySet':
|
|
231
232
|
"""
|
|
232
233
|
Filter the queryset to include only entries where `role_default`
|
|
233
234
|
is set to True, excluding entries marked as 'coa_root'.
|
|
@@ -239,7 +240,7 @@ class AccountModelQuerySet(MP_NodeQuerySet):
|
|
|
239
240
|
"""
|
|
240
241
|
return self.not_coa_root().filter(role_default=True)
|
|
241
242
|
|
|
242
|
-
def can_transact(self):
|
|
243
|
+
def can_transact(self) -> 'AccountModelQuerySet':
|
|
243
244
|
"""
|
|
244
245
|
Filter the queryset to include only accounts that can accept new transactions.
|
|
245
246
|
|
|
@@ -254,14 +255,14 @@ class AccountModelQuerySet(MP_NodeQuerySet):
|
|
|
254
255
|
Q(coa_model__active=True)
|
|
255
256
|
)
|
|
256
257
|
|
|
257
|
-
def available(self):
|
|
258
|
+
def available(self) -> 'AccountModelQuerySet':
|
|
258
259
|
return self.filter(
|
|
259
260
|
Q(locked=False) &
|
|
260
261
|
Q(active=True) &
|
|
261
262
|
Q(coa_model__active=True)
|
|
262
263
|
)
|
|
263
264
|
|
|
264
|
-
def for_bill(self):
|
|
265
|
+
def for_bill(self) -> 'AccountModelQuerySet':
|
|
265
266
|
"""
|
|
266
267
|
Retrieves only available and unlocked AccountModels for a specific EntityModel,
|
|
267
268
|
specifically for the creation and management of Bills. Roles within the 'GROUP_BILL'
|
|
@@ -274,7 +275,7 @@ class AccountModelQuerySet(MP_NodeQuerySet):
|
|
|
274
275
|
"""
|
|
275
276
|
return self.available().filter(role__in=GROUP_BILL)
|
|
276
277
|
|
|
277
|
-
def for_invoice(self):
|
|
278
|
+
def for_invoice(self) -> 'AccountModelQuerySet':
|
|
278
279
|
"""
|
|
279
280
|
Retrieves available and unlocked AccountModels for a specific EntityModel, specifically for the creation
|
|
280
281
|
and management of Invoices.
|
|
@@ -290,6 +291,27 @@ class AccountModelQuerySet(MP_NodeQuerySet):
|
|
|
290
291
|
"""
|
|
291
292
|
return self.available().filter(role__in=GROUP_INVOICE)
|
|
292
293
|
|
|
294
|
+
def for_user(self, user_model) -> 'AccountModelQuerySet':
|
|
295
|
+
"""
|
|
296
|
+
Parameters
|
|
297
|
+
----------
|
|
298
|
+
user_model : UserModel
|
|
299
|
+
The user model instance to use for filtering.
|
|
300
|
+
|
|
301
|
+
Returns
|
|
302
|
+
-------
|
|
303
|
+
AccountModelQuerySet
|
|
304
|
+
The filtered queryset based on the user's permissions. Superusers get the complete queryset whereas other
|
|
305
|
+
users get a filtered queryset based on their role as admin or manager in the entity.
|
|
306
|
+
"""
|
|
307
|
+
if user_model.is_superuser:
|
|
308
|
+
return self
|
|
309
|
+
|
|
310
|
+
return self.filter(
|
|
311
|
+
Q(coa_model__entity__admin=user_model) |
|
|
312
|
+
Q(coa_model__entity__managers__in=[user_model])
|
|
313
|
+
)
|
|
314
|
+
|
|
293
315
|
|
|
294
316
|
class AccountModelManager(MP_NodeManager):
|
|
295
317
|
"""
|
|
@@ -299,7 +321,7 @@ class AccountModelManager(MP_NodeManager):
|
|
|
299
321
|
|
|
300
322
|
def get_queryset(self) -> AccountModelQuerySet:
|
|
301
323
|
"""
|
|
302
|
-
Retrieve and return
|
|
324
|
+
Retrieve and return the default AccountModel QuerySet.
|
|
303
325
|
|
|
304
326
|
The query set is ordered by the 'path' field and uses 'select_related' to reduce the number of database queries
|
|
305
327
|
by retrieving the related 'coa_model'.
|
|
@@ -313,80 +335,98 @@ class AccountModelManager(MP_NodeManager):
|
|
|
313
335
|
self.model,
|
|
314
336
|
using=self._db
|
|
315
337
|
).order_by('path').select_related(
|
|
316
|
-
'coa_model'
|
|
338
|
+
'coa_model'
|
|
339
|
+
).annotate(
|
|
317
340
|
_coa_slug=F('coa_model__slug'),
|
|
318
341
|
_coa_active=F('coa_model__active'),
|
|
319
342
|
_entity_slug=F('coa_model__entity__slug'),
|
|
320
343
|
)
|
|
321
344
|
|
|
322
|
-
|
|
323
|
-
"""
|
|
324
|
-
Parameters
|
|
325
|
-
----------
|
|
326
|
-
user_model : UserModel
|
|
327
|
-
The user model instance to use for filtering.
|
|
328
|
-
|
|
329
|
-
Returns
|
|
330
|
-
-------
|
|
331
|
-
AccountModelQuerySet
|
|
332
|
-
The filtered queryset based on the user's permissions. Superusers get the complete queryset whereas other
|
|
333
|
-
users get a filtered queryset based on their role as admin or manager in the entity.
|
|
334
|
-
"""
|
|
335
|
-
qs = self.get_queryset()
|
|
336
|
-
if user_model.is_superuser:
|
|
337
|
-
return qs
|
|
338
|
-
return qs.filter(
|
|
339
|
-
Q(coa_model__entity__admin=user_model) |
|
|
340
|
-
Q(coa_model__entity__managers__in=[user_model])
|
|
341
|
-
)
|
|
342
|
-
|
|
345
|
+
@deprecated_entity_slug_behavior
|
|
343
346
|
def for_entity(
|
|
344
347
|
self,
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
+
entity_model: Union['EntityModel | str | UUID'] = None,
|
|
349
|
+
coa_model: Optional['ChartOfAccountModel | str | UUID'] = None,
|
|
350
|
+
**kwargs
|
|
348
351
|
) -> AccountModelQuerySet:
|
|
349
352
|
"""
|
|
350
|
-
|
|
353
|
+
Filters the queryset for an entity and, optionally, a chart of account (COA) model.
|
|
354
|
+
|
|
355
|
+
The method refines the queryset based on the provided `entity_model` and, optionally,
|
|
356
|
+
the `coa_model`. If a deprecated `user_model` is specified in keyword arguments,
|
|
357
|
+
a warning is issued. The method supports `EntityModel`, `str`, and `UUID` types
|
|
358
|
+
for both `entity_model` and `coa_model`. A validation error is raised for unsupported types.
|
|
351
359
|
|
|
352
360
|
Parameters
|
|
353
361
|
----------
|
|
354
|
-
|
|
355
|
-
The
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
362
|
+
entity_model : Union['EntityModel', str, UUID]
|
|
363
|
+
The entity model used for filtering the queryset. Could be an instance of
|
|
364
|
+
`EntityModel`, a string (slug), or a UUID.
|
|
365
|
+
coa_model : Optional[Union['ChartOfAccountModel', str, UUID]], optional
|
|
366
|
+
The COA model used for filtering the queryset. Can be an instance of
|
|
367
|
+
`ChartOfAccountModel`, a string (slug), or a UUID. If None, default Entity ChartOfAccounts is used.
|
|
368
|
+
Defaults to None.
|
|
369
|
+
**kwargs : dict
|
|
370
|
+
Additional keyword arguments. A deprecated argument `user_model` can be passed
|
|
371
|
+
for backward compatibility.
|
|
361
372
|
|
|
362
373
|
Returns
|
|
363
374
|
-------
|
|
364
375
|
AccountModelQuerySet
|
|
365
|
-
A
|
|
376
|
+
A queryset filtered by the input entity model and, optionally, the chart of
|
|
377
|
+
account model.
|
|
366
378
|
|
|
367
379
|
Raises
|
|
368
380
|
------
|
|
369
381
|
AccountModelValidationError
|
|
370
|
-
If
|
|
382
|
+
If an invalid type is passed for either `entity_model` or `coa_model`.
|
|
383
|
+
|
|
384
|
+
Warns
|
|
385
|
+
-----
|
|
386
|
+
DeprecationWarning
|
|
387
|
+
If the `user_model` parameter is passed in the keyword arguments and the
|
|
388
|
+
application relies on deprecated behavior.
|
|
371
389
|
"""
|
|
372
|
-
qs = self.for_user(user_model)
|
|
373
390
|
EntityModel = lazy_loader.get_entity_model()
|
|
391
|
+
ChartOfAccountModel = lazy_loader.get_coa_model()
|
|
392
|
+
|
|
393
|
+
qs = self.get_queryset()
|
|
394
|
+
|
|
395
|
+
if 'user_model' in kwargs:
|
|
396
|
+
warnings.warn(
|
|
397
|
+
'user_model parameter is deprecated and will be removed in a future release. '
|
|
398
|
+
'Use for_user(user_model).for_entity(entity_model) instead to keep current behavior.',
|
|
399
|
+
DeprecationWarning,
|
|
400
|
+
stacklevel=2
|
|
401
|
+
)
|
|
402
|
+
if DJANGO_LEDGER_USE_DEPRECATED_BEHAVIOR:
|
|
403
|
+
qs = qs.for_user(kwargs['user_model'])
|
|
374
404
|
|
|
375
405
|
if isinstance(entity_model, EntityModel):
|
|
376
|
-
entity_model = entity_model
|
|
377
406
|
qs = qs.filter(coa_model__entity=entity_model)
|
|
378
407
|
elif isinstance(entity_model, str):
|
|
379
408
|
qs = qs.filter(coa_model__entity__slug__exact=entity_model)
|
|
409
|
+
elif isinstance(entity_model, UUID):
|
|
410
|
+
qs = qs.filter(coa_model__entity_id=entity_model)
|
|
380
411
|
else:
|
|
381
412
|
raise AccountModelValidationError(
|
|
382
|
-
message='Must pass an instance of EntityModel or
|
|
413
|
+
message='Must pass an instance of EntityModel, String or UUID for entity_model.'
|
|
383
414
|
)
|
|
384
415
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
416
|
+
if coa_model:
|
|
417
|
+
if isinstance(coa_model, ChartOfAccountModel):
|
|
418
|
+
qs = qs.filter(coa_model=coa_model)
|
|
419
|
+
elif isinstance(coa_model, str):
|
|
420
|
+
qs = qs.filter(coa_model__slug__exact=coa_model)
|
|
421
|
+
elif isinstance(coa_model, UUID):
|
|
422
|
+
qs = qs.filter(coa_model__uuid__exact=coa_model)
|
|
423
|
+
else:
|
|
424
|
+
raise AccountModelValidationError(
|
|
425
|
+
message='Must pass an instance of ChartOfAccountModel, String or UUID for coa_model.'
|
|
426
|
+
)
|
|
427
|
+
return qs
|
|
428
|
+
|
|
429
|
+
return qs.filter(coa_model__slug__exact=F('coa_model__entity__default_coa__slug'))
|
|
390
430
|
|
|
391
431
|
|
|
392
432
|
def account_code_validator(value: str):
|
|
@@ -438,7 +478,7 @@ class AccountModelAbstract(MP_Node, CreateUpdateMixIn):
|
|
|
438
478
|
coa_model = models.ForeignKey('django_ledger.ChartOfAccountModel',
|
|
439
479
|
on_delete=models.CASCADE,
|
|
440
480
|
verbose_name=_('Chart of Accounts'))
|
|
441
|
-
objects = AccountModelManager()
|
|
481
|
+
objects = AccountModelManager.from_queryset(queryset_class=AccountModelQuerySet)()
|
|
442
482
|
|
|
443
483
|
class Meta:
|
|
444
484
|
abstract = True
|
|
@@ -6,8 +6,9 @@ A Bank Account refers to the financial institution which holds financial assets
|
|
|
6
6
|
A bank account usually holds cash, which is a Current Asset. Transactions may be imported using the open financial
|
|
7
7
|
format specification OFX into a staging area for final disposition into the EntityModel ledger.
|
|
8
8
|
"""
|
|
9
|
+
import warnings
|
|
9
10
|
from typing import Optional
|
|
10
|
-
from uuid import uuid4
|
|
11
|
+
from uuid import uuid4, UUID
|
|
11
12
|
|
|
12
13
|
from django.contrib.auth import get_user_model
|
|
13
14
|
from django.core.exceptions import ValidationError
|
|
@@ -17,7 +18,9 @@ from django.shortcuts import get_object_or_404
|
|
|
17
18
|
from django.utils.translation import gettext_lazy as _
|
|
18
19
|
|
|
19
20
|
from django_ledger.models import CreateUpdateMixIn, FinancialAccountInfoMixin
|
|
21
|
+
from django_ledger.models.deprecations import deprecated_entity_slug_behavior
|
|
20
22
|
from django_ledger.models.utils import lazy_loader
|
|
23
|
+
from django_ledger.settings import DJANGO_LEDGER_USE_DEPRECATED_BEHAVIOR
|
|
21
24
|
|
|
22
25
|
UserModel = get_user_model()
|
|
23
26
|
|
|
@@ -28,10 +31,18 @@ class BankAccountValidationError(ValidationError):
|
|
|
28
31
|
|
|
29
32
|
class BankAccountModelQuerySet(QuerySet):
|
|
30
33
|
"""
|
|
31
|
-
A custom
|
|
34
|
+
A custom-defined QuerySet for the BankAccountModel.
|
|
32
35
|
"""
|
|
33
36
|
|
|
34
|
-
def
|
|
37
|
+
def for_user(self, user_model) -> 'BankAccountModelQuerySet':
|
|
38
|
+
if user_model.is_superuser:
|
|
39
|
+
return self
|
|
40
|
+
return self.filter(
|
|
41
|
+
Q(entity_model__admin=user_model) |
|
|
42
|
+
Q(entity_model__managers__in=[user_model])
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
def active(self) -> 'BankAccountModelQuerySet':
|
|
35
46
|
"""
|
|
36
47
|
Active bank accounts which can be used to create new transactions.
|
|
37
48
|
|
|
@@ -42,7 +53,7 @@ class BankAccountModelQuerySet(QuerySet):
|
|
|
42
53
|
"""
|
|
43
54
|
return self.filter(active=True)
|
|
44
55
|
|
|
45
|
-
def hidden(self) ->
|
|
56
|
+
def hidden(self) -> 'BankAccountModelQuerySet':
|
|
46
57
|
"""
|
|
47
58
|
Hidden bank accounts which can be used to create new transactions. but will not show in drop down menus
|
|
48
59
|
in the UI.
|
|
@@ -63,16 +74,8 @@ class BankAccountModelManager(Manager):
|
|
|
63
74
|
def get_queryset(self) -> BankAccountModelQuerySet:
|
|
64
75
|
return BankAccountModelQuerySet(self.model, using=self._db)
|
|
65
76
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if user_model.is_superuser:
|
|
69
|
-
return qs
|
|
70
|
-
return qs.filter(
|
|
71
|
-
Q(entity_model__admin=user_model) |
|
|
72
|
-
Q(entity_model__managers__in=[user_model])
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
def for_entity(self, entity_slug, user_model) -> BankAccountModelQuerySet:
|
|
77
|
+
@deprecated_entity_slug_behavior
|
|
78
|
+
def for_entity(self, entity_model: 'EntityModel | str | UUID' = None, **kwargs) -> BankAccountModelQuerySet:
|
|
76
79
|
"""
|
|
77
80
|
Allows only the authorized user to query the BankAccountModel for a given EntityModel.
|
|
78
81
|
This is the recommended initial QuerySet.
|
|
@@ -81,17 +84,31 @@ class BankAccountModelManager(Manager):
|
|
|
81
84
|
__________
|
|
82
85
|
entity_slug: str or EntityModel
|
|
83
86
|
The entity slug or EntityModel used for filtering the QuerySet.
|
|
84
|
-
user_model
|
|
85
|
-
Logged in and authenticated django UserModel instance.
|
|
86
87
|
"""
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
EntityModel = lazy_loader.get_entity_model()
|
|
89
|
+
|
|
90
|
+
qs = self.get_queryset()
|
|
91
|
+
if 'user_model' in kwargs:
|
|
92
|
+
warnings.warn(
|
|
93
|
+
'user_model parameter is deprecated and will be removed in a future release. '
|
|
94
|
+
'Use for_user(user_model).for_entity(entity_model) instead to keep current behavior.',
|
|
95
|
+
DeprecationWarning,
|
|
96
|
+
stacklevel=2
|
|
91
97
|
)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
98
|
+
if DJANGO_LEDGER_USE_DEPRECATED_BEHAVIOR:
|
|
99
|
+
qs = qs.for_user(kwargs['user_model'])
|
|
100
|
+
|
|
101
|
+
if isinstance(entity_model, EntityModel):
|
|
102
|
+
qs = qs.filter(entity_model=entity_model)
|
|
103
|
+
elif isinstance(entity_model, str):
|
|
104
|
+
qs = qs.filter(entity_model__slug__exact=entity_model)
|
|
105
|
+
elif isinstance(entity_model, UUID):
|
|
106
|
+
qs = qs.filter(entity_model_id=entity_model)
|
|
107
|
+
else:
|
|
108
|
+
raise BankAccountValidationError(
|
|
109
|
+
message=_('Must pass EntityModel slug or EntityModel UUID'),
|
|
110
|
+
)
|
|
111
|
+
return qs
|
|
95
112
|
|
|
96
113
|
|
|
97
114
|
class BankAccountModelAbstract(FinancialAccountInfoMixin, CreateUpdateMixIn):
|