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.
- django_ledger/__init__.py +1 -1
- django_ledger/contrib/django_ledger_graphene/journal_entry/schema.py +2 -3
- django_ledger/contrib/django_ledger_graphene/transaction/schema.py +9 -7
- django_ledger/forms/account.py +4 -1
- django_ledger/forms/journal_entry.py +19 -12
- django_ledger/forms/transactions.py +8 -12
- django_ledger/io/io_core.py +17 -12
- django_ledger/io/io_library.py +3 -3
- django_ledger/migrations/0001_initial.py +1 -1
- django_ledger/migrations/0019_alter_transactionmodel_amount_and_more.py +33 -0
- django_ledger/models/bill.py +17 -2
- django_ledger/models/chart_of_accounts.py +4 -0
- django_ledger/models/closing_entry.py +8 -6
- django_ledger/models/data_import.py +1 -0
- django_ledger/models/invoice.py +12 -4
- django_ledger/models/journal_entry.py +843 -481
- django_ledger/models/ledger.py +45 -4
- django_ledger/models/mixins.py +5 -5
- django_ledger/models/transactions.py +303 -305
- django_ledger/models/unit.py +42 -22
- django_ledger/static/django_ledger/bundle/djetler.bundle.js +1 -1
- django_ledger/static/django_ledger/bundle/styles.bundle.js +1 -1
- django_ledger/templates/django_ledger/account/tags/account_txs_table.html +1 -1
- django_ledger/templates/django_ledger/account/tags/accounts_table.html +1 -1
- django_ledger/templates/django_ledger/bills/bill_detail.html +1 -1
- django_ledger/templates/django_ledger/bills/bill_update.html +1 -1
- django_ledger/templates/django_ledger/components/icon.html +1 -1
- django_ledger/templates/django_ledger/data_import/tags/data_import_job_txs_imported.html +8 -1
- django_ledger/templates/django_ledger/financial_statements/balance_sheet.html +1 -0
- django_ledger/templates/django_ledger/financial_statements/tags/balance_sheet_statement.html +4 -4
- django_ledger/templates/django_ledger/financial_statements/tags/income_statement.html +118 -0
- django_ledger/templates/django_ledger/includes/nav.html +9 -5
- django_ledger/templates/django_ledger/invoice/includes/card_invoice.html +69 -69
- django_ledger/templates/django_ledger/invoice/invoice_detail.html +1 -1
- django_ledger/templates/django_ledger/journal_entry/je_create.html +2 -3
- django_ledger/templates/django_ledger/journal_entry/je_delete.html +2 -3
- django_ledger/templates/django_ledger/journal_entry/je_detail.html +1 -1
- django_ledger/templates/django_ledger/journal_entry/je_detail_txs.html +8 -8
- django_ledger/templates/django_ledger/journal_entry/je_list.html +16 -13
- django_ledger/templates/django_ledger/journal_entry/je_update.html +2 -3
- django_ledger/templates/django_ledger/journal_entry/tags/je_table.html +24 -24
- django_ledger/templates/django_ledger/journal_entry/tags/je_txs_table.html +17 -14
- django_ledger/templates/django_ledger/ledger/tags/ledgers_table.html +38 -37
- django_ledger/templates/django_ledger/transactions/tags/txs_table.html +69 -0
- django_ledger/templatetags/django_ledger.py +35 -48
- django_ledger/urls/account.py +4 -4
- django_ledger/views/account.py +8 -8
- django_ledger/views/journal_entry.py +84 -101
- django_ledger/views/ledger.py +16 -21
- django_ledger/views/mixins.py +17 -28
- {django_ledger-0.7.2.dist-info → django_ledger-0.7.4.dist-info}/METADATA +8 -3
- {django_ledger-0.7.2.dist-info → django_ledger-0.7.4.dist-info}/RECORD +56 -104
- assets/node_modules/node-gyp/gyp/gyp_main.py +0 -45
- assets/node_modules/node-gyp/gyp/pylib/gyp/MSVSNew.py +0 -367
- assets/node_modules/node-gyp/gyp/pylib/gyp/MSVSProject.py +0 -206
- assets/node_modules/node-gyp/gyp/pylib/gyp/MSVSSettings.py +0 -1270
- assets/node_modules/node-gyp/gyp/pylib/gyp/MSVSSettings_test.py +0 -1547
- assets/node_modules/node-gyp/gyp/pylib/gyp/MSVSToolFile.py +0 -59
- assets/node_modules/node-gyp/gyp/pylib/gyp/MSVSUserFile.py +0 -153
- assets/node_modules/node-gyp/gyp/pylib/gyp/MSVSUtil.py +0 -271
- assets/node_modules/node-gyp/gyp/pylib/gyp/MSVSVersion.py +0 -574
- assets/node_modules/node-gyp/gyp/pylib/gyp/__init__.py +0 -666
- assets/node_modules/node-gyp/gyp/pylib/gyp/common.py +0 -654
- assets/node_modules/node-gyp/gyp/pylib/gyp/common_test.py +0 -78
- assets/node_modules/node-gyp/gyp/pylib/gyp/easy_xml.py +0 -165
- assets/node_modules/node-gyp/gyp/pylib/gyp/easy_xml_test.py +0 -109
- assets/node_modules/node-gyp/gyp/pylib/gyp/flock_tool.py +0 -55
- assets/node_modules/node-gyp/gyp/pylib/gyp/generator/__init__.py +0 -0
- assets/node_modules/node-gyp/gyp/pylib/gyp/generator/analyzer.py +0 -808
- assets/node_modules/node-gyp/gyp/pylib/gyp/generator/android.py +0 -1173
- assets/node_modules/node-gyp/gyp/pylib/gyp/generator/cmake.py +0 -1321
- assets/node_modules/node-gyp/gyp/pylib/gyp/generator/compile_commands_json.py +0 -120
- assets/node_modules/node-gyp/gyp/pylib/gyp/generator/dump_dependency_json.py +0 -103
- assets/node_modules/node-gyp/gyp/pylib/gyp/generator/eclipse.py +0 -464
- assets/node_modules/node-gyp/gyp/pylib/gyp/generator/gypd.py +0 -89
- assets/node_modules/node-gyp/gyp/pylib/gyp/generator/gypsh.py +0 -58
- assets/node_modules/node-gyp/gyp/pylib/gyp/generator/make.py +0 -2518
- assets/node_modules/node-gyp/gyp/pylib/gyp/generator/msvs.py +0 -3978
- assets/node_modules/node-gyp/gyp/pylib/gyp/generator/msvs_test.py +0 -44
- assets/node_modules/node-gyp/gyp/pylib/gyp/generator/ninja.py +0 -2936
- assets/node_modules/node-gyp/gyp/pylib/gyp/generator/ninja_test.py +0 -55
- assets/node_modules/node-gyp/gyp/pylib/gyp/generator/xcode.py +0 -1394
- assets/node_modules/node-gyp/gyp/pylib/gyp/generator/xcode_test.py +0 -25
- assets/node_modules/node-gyp/gyp/pylib/gyp/input.py +0 -3137
- assets/node_modules/node-gyp/gyp/pylib/gyp/input_test.py +0 -98
- assets/node_modules/node-gyp/gyp/pylib/gyp/mac_tool.py +0 -771
- assets/node_modules/node-gyp/gyp/pylib/gyp/msvs_emulation.py +0 -1271
- assets/node_modules/node-gyp/gyp/pylib/gyp/ninja_syntax.py +0 -174
- assets/node_modules/node-gyp/gyp/pylib/gyp/simple_copy.py +0 -61
- assets/node_modules/node-gyp/gyp/pylib/gyp/win_tool.py +0 -374
- assets/node_modules/node-gyp/gyp/pylib/gyp/xcode_emulation.py +0 -1939
- assets/node_modules/node-gyp/gyp/pylib/gyp/xcode_ninja.py +0 -302
- assets/node_modules/node-gyp/gyp/pylib/gyp/xcodeproj_file.py +0 -3197
- assets/node_modules/node-gyp/gyp/pylib/gyp/xml_fix.py +0 -65
- assets/node_modules/node-gyp/gyp/setup.py +0 -42
- assets/node_modules/node-gyp/gyp/test_gyp.py +0 -260
- assets/node_modules/node-gyp/gyp/tools/graphviz.py +0 -102
- assets/node_modules/node-gyp/gyp/tools/pretty_gyp.py +0 -156
- assets/node_modules/node-gyp/gyp/tools/pretty_sln.py +0 -181
- assets/node_modules/node-gyp/gyp/tools/pretty_vcproj.py +0 -339
- assets/node_modules/node-gyp/test/fixtures/test-charmap.py +0 -31
- assets/node_modules/node-gyp/update-gyp.py +0 -46
- {django_ledger-0.7.2.dist-info → django_ledger-0.7.4.dist-info}/AUTHORS.md +0 -0
- {django_ledger-0.7.2.dist-info → django_ledger-0.7.4.dist-info}/LICENSE +0 -0
- {django_ledger-0.7.2.dist-info → django_ledger-0.7.4.dist-info}/WHEEL +0 -0
- {django_ledger-0.7.2.dist-info → django_ledger-0.7.4.dist-info}/top_level.txt +0 -0
|
@@ -8,18 +8,20 @@ Miguel Sanda <msanda@arrobalytics.com>
|
|
|
8
8
|
|
|
9
9
|
from calendar import month_abbr
|
|
10
10
|
from random import randint
|
|
11
|
+
from typing import Union
|
|
11
12
|
|
|
12
13
|
from django import template
|
|
13
|
-
from django.db.models import Sum
|
|
14
|
+
from django.db.models import Sum, F
|
|
14
15
|
from django.urls import reverse
|
|
15
16
|
from django.utils.formats import number_format
|
|
17
|
+
from rfc3986.exceptions import ValidationError
|
|
16
18
|
|
|
17
19
|
from django_ledger import __version__
|
|
18
20
|
from django_ledger.forms.app_filters import EntityFilterForm, ActivityFilterForm
|
|
19
21
|
from django_ledger.forms.feedback import BugReportForm, RequestNewFeatureForm
|
|
20
22
|
from django_ledger.io import CREDIT, DEBIT, ROLES_ORDER_ALL
|
|
21
23
|
from django_ledger.io.io_core import validate_activity, get_localdate
|
|
22
|
-
from django_ledger.models import TransactionModel, BillModel, InvoiceModel, EntityUnitModel
|
|
24
|
+
from django_ledger.models import TransactionModel, BillModel, InvoiceModel, EntityUnitModel, JournalEntryModel
|
|
23
25
|
from django_ledger.settings import (
|
|
24
26
|
DJANGO_LEDGER_FINANCIAL_ANALYSIS, DJANGO_LEDGER_CURRENCY_SYMBOL,
|
|
25
27
|
DJANGO_LEDGER_SPACED_CURRENCY_SYMBOL)
|
|
@@ -106,7 +108,9 @@ def balance_sheet_statement(context, io_model, to_date=None):
|
|
|
106
108
|
balance_sheet_statement=True)
|
|
107
109
|
|
|
108
110
|
return {
|
|
109
|
-
'
|
|
111
|
+
'entity_slug': entity_slug,
|
|
112
|
+
'user_model': user_model,
|
|
113
|
+
'tx_digest': io_digest.get_io_data(),
|
|
110
114
|
}
|
|
111
115
|
|
|
112
116
|
|
|
@@ -131,6 +135,8 @@ def cash_flow_statement(context, io_model):
|
|
|
131
135
|
process_groups=True)
|
|
132
136
|
|
|
133
137
|
return {
|
|
138
|
+
'entity_slug': entity_slug,
|
|
139
|
+
'user_model': user_model,
|
|
134
140
|
'tx_digest': io_digest.get_io_data()
|
|
135
141
|
}
|
|
136
142
|
|
|
@@ -162,6 +168,8 @@ def income_statement_table(context, io_model, from_date=None, to_date=None):
|
|
|
162
168
|
)
|
|
163
169
|
|
|
164
170
|
return {
|
|
171
|
+
'entity_slug': entity_slug,
|
|
172
|
+
'user_model': user_model,
|
|
165
173
|
'tx_digest': io_digest.get_io_data()
|
|
166
174
|
}
|
|
167
175
|
|
|
@@ -210,65 +218,42 @@ def jes_table(context, journal_entry_qs, next_url=None):
|
|
|
210
218
|
'ledger_pk': ledger_pk
|
|
211
219
|
})
|
|
212
220
|
return {
|
|
213
|
-
'
|
|
221
|
+
'journal_entry_qs': journal_entry_qs,
|
|
214
222
|
'entity_slug': entity_slug,
|
|
215
223
|
'ledger_pk': ledger_pk,
|
|
216
224
|
'next_url': next_url
|
|
217
225
|
}
|
|
218
226
|
|
|
219
227
|
|
|
220
|
-
@register.inclusion_tag('django_ledger/
|
|
221
|
-
def
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
'
|
|
227
|
-
|
|
228
|
-
'
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
@register.inclusion_tag('django_ledger/journal_entry/tags/je_txs_table.html', takes_context=True)
|
|
234
|
-
def bill_txs_table(context, bill_model: BillModel):
|
|
235
|
-
# todo: move this to bill model...
|
|
236
|
-
txs_queryset = TransactionModel.objects.for_bill(
|
|
237
|
-
bill_model=bill_model.uuid,
|
|
238
|
-
user_model=context['request'].user,
|
|
239
|
-
entity_slug=context['view'].kwargs['entity_slug']
|
|
240
|
-
).select_related('journal_entry', 'journal_entry__entity_unit', 'account').order_by('-journal_entry__timestamp')
|
|
241
|
-
total_credits = sum(tx.amount for tx in txs_queryset if tx.tx_type == CREDIT)
|
|
242
|
-
total_debits = sum(tx.amount for tx in txs_queryset if tx.tx_type == DEBIT)
|
|
243
|
-
return {
|
|
244
|
-
'style': 'detail',
|
|
245
|
-
'txs': txs_queryset,
|
|
246
|
-
'total_debits': total_debits,
|
|
247
|
-
'total_credits': total_credits
|
|
248
|
-
}
|
|
228
|
+
@register.inclusion_tag('django_ledger/transactions/tags/txs_table.html')
|
|
229
|
+
def transactions_table(object_type: Union[JournalEntryModel, BillModel, InvoiceModel], style='detail'):
|
|
230
|
+
if isinstance(object_type, JournalEntryModel):
|
|
231
|
+
transaction_model_qs = object_type.transactionmodel_set.all().with_annotated_details().order_by(
|
|
232
|
+
'-timestamp')
|
|
233
|
+
elif isinstance(object_type, BillModel):
|
|
234
|
+
transaction_model_qs = object_type.get_transaction_queryset(annotated=True).order_by('-timestamp')
|
|
235
|
+
elif isinstance(object_type, InvoiceModel):
|
|
236
|
+
transaction_model_qs = object_type.get_transaction_queryset(annotated=True).order_by('-timestamp')
|
|
237
|
+
else:
|
|
238
|
+
raise ValidationError(
|
|
239
|
+
'Cannot handle object of type {} to get transaction model queryset'.format(type(object_type)))
|
|
249
240
|
|
|
241
|
+
total_credits = sum(tx.amount for tx in transaction_model_qs if tx.is_credit())
|
|
242
|
+
total_debits = sum(tx.amount for tx in transaction_model_qs if tx.is_debit())
|
|
250
243
|
|
|
251
|
-
@register.inclusion_tag('django_ledger/journal_entry/tags/je_txs_table.html', takes_context=True)
|
|
252
|
-
def invoice_txs_table(context, invoice_model: InvoiceModel):
|
|
253
|
-
txs_queryset = TransactionModel.objects.for_invoice(
|
|
254
|
-
invoice_model=invoice_model,
|
|
255
|
-
user_model=context['request'].user,
|
|
256
|
-
entity_slug=context['view'].kwargs['entity_slug']
|
|
257
|
-
).select_related('journal_entry', 'journal_entry__entity_unit', 'account').order_by('-journal_entry__timestamp')
|
|
258
|
-
total_credits = sum(tx.amount for tx in txs_queryset if tx.tx_type == CREDIT)
|
|
259
|
-
total_debits = sum(tx.amount for tx in txs_queryset if tx.tx_type == DEBIT)
|
|
260
244
|
return {
|
|
261
|
-
'style':
|
|
262
|
-
'
|
|
245
|
+
'style': style,
|
|
246
|
+
'transaction_model_qs': transaction_model_qs,
|
|
263
247
|
'total_debits': total_debits,
|
|
264
|
-
'total_credits': total_credits
|
|
248
|
+
'total_credits': total_credits,
|
|
249
|
+
'object': object_type
|
|
265
250
|
}
|
|
266
251
|
|
|
267
252
|
|
|
268
253
|
@register.inclusion_tag('django_ledger/ledger/tags/ledgers_table.html', takes_context=True)
|
|
269
254
|
def ledgers_table(context, ledger_model_qs):
|
|
270
255
|
return {
|
|
271
|
-
'
|
|
256
|
+
'ledger_model_qs': ledger_model_qs,
|
|
272
257
|
'entity_slug': context['view'].kwargs['entity_slug'],
|
|
273
258
|
}
|
|
274
259
|
|
|
@@ -389,10 +374,12 @@ def default_entity(context):
|
|
|
389
374
|
def session_entity_name(context, request=None):
|
|
390
375
|
session_key = get_default_entity_session_key()
|
|
391
376
|
if not request:
|
|
392
|
-
request = context
|
|
393
|
-
session = request.session
|
|
377
|
+
request = context.get('request')
|
|
394
378
|
try:
|
|
379
|
+
session = request.session
|
|
395
380
|
entity_name = session.get(session_key)['entity_name']
|
|
381
|
+
except AttributeError:
|
|
382
|
+
entity_name = 'Django Ledger'
|
|
396
383
|
except KeyError:
|
|
397
384
|
entity_name = 'Django Ledger'
|
|
398
385
|
except TypeError:
|
django_ledger/urls/account.py
CHANGED
|
@@ -36,15 +36,15 @@ urlpatterns = [
|
|
|
36
36
|
|
|
37
37
|
# Account Actions...
|
|
38
38
|
path('<slug:entity_slug>/<slug:coa_slug>/action/<uuid:account_pk>/activate/',
|
|
39
|
-
views.
|
|
39
|
+
views.BaseAccountModelActionView.as_view(action_name='activate'),
|
|
40
40
|
name='account-action-activate'),
|
|
41
41
|
path('<slug:entity_slug>/<slug:coa_slug>/action/<uuid:account_pk>/deactivate/',
|
|
42
|
-
views.
|
|
42
|
+
views.BaseAccountModelActionView.as_view(action_name='deactivate'),
|
|
43
43
|
name='account-action-deactivate'),
|
|
44
44
|
path('<slug:entity_slug>/<slug:coa_slug>/action/<uuid:account_pk>/lock/',
|
|
45
|
-
views.
|
|
45
|
+
views.BaseAccountModelActionView.as_view(action_name='lock'),
|
|
46
46
|
name='account-action-lock'),
|
|
47
47
|
path('<slug:entity_slug>/<slug:coa_slug>/action/<uuid:account_pk>/unlock/',
|
|
48
|
-
views.
|
|
48
|
+
views.BaseAccountModelActionView.as_view(action_name='unlock'),
|
|
49
49
|
name='account-action-unlock')
|
|
50
50
|
]
|
django_ledger/views/account.py
CHANGED
|
@@ -186,7 +186,7 @@ class AccountModelYearDetailView(BaseAccountModelBaseView,
|
|
|
186
186
|
account_model: AccountModel = context['object']
|
|
187
187
|
context['header_title'] = f'Account {account_model.code} - {account_model.name}'
|
|
188
188
|
context['page_title'] = f'Account {account_model.code} - {account_model.name}'
|
|
189
|
-
txs_qs = account_model.transactionmodel_set.all().posted().order_by(
|
|
189
|
+
txs_qs = account_model.transactionmodel_set.all().not_closing_entry().posted().order_by(
|
|
190
190
|
'journal_entry__timestamp'
|
|
191
191
|
).select_related(
|
|
192
192
|
'journal_entry',
|
|
@@ -200,28 +200,28 @@ class AccountModelYearDetailView(BaseAccountModelBaseView,
|
|
|
200
200
|
return context
|
|
201
201
|
|
|
202
202
|
|
|
203
|
-
class AccountModelQuarterDetailView(
|
|
203
|
+
class AccountModelQuarterDetailView(AccountModelYearDetailView, QuarterlyReportMixIn):
|
|
204
204
|
"""
|
|
205
205
|
Account Model Quarter Detail View
|
|
206
206
|
"""
|
|
207
207
|
|
|
208
208
|
|
|
209
|
-
class AccountModelMonthDetailView(
|
|
209
|
+
class AccountModelMonthDetailView(AccountModelYearDetailView, MonthlyReportMixIn):
|
|
210
210
|
"""
|
|
211
211
|
Account Model Month Detail View
|
|
212
212
|
"""
|
|
213
213
|
|
|
214
214
|
|
|
215
|
-
class AccountModelDateDetailView(
|
|
215
|
+
class AccountModelDateDetailView(AccountModelYearDetailView, DateReportMixIn):
|
|
216
216
|
"""
|
|
217
217
|
Account Model Date Detail View
|
|
218
218
|
"""
|
|
219
219
|
|
|
220
220
|
|
|
221
221
|
# ACTIONS...
|
|
222
|
-
class
|
|
223
|
-
|
|
224
|
-
|
|
222
|
+
class BaseAccountModelActionView(BaseAccountModelBaseView,
|
|
223
|
+
RedirectView,
|
|
224
|
+
SingleObjectMixin):
|
|
225
225
|
http_method_names = ['get']
|
|
226
226
|
pk_url_kwarg = 'account_pk'
|
|
227
227
|
action_name = None
|
|
@@ -235,7 +235,7 @@ class AccountModelModelActionView(BaseAccountModelBaseView,
|
|
|
235
235
|
kwargs['user_model'] = self.request.user
|
|
236
236
|
if not self.action_name:
|
|
237
237
|
raise ImproperlyConfigured('View attribute action_name is required.')
|
|
238
|
-
response = super(
|
|
238
|
+
response = super(BaseAccountModelActionView, self).get(request, *args, **kwargs)
|
|
239
239
|
account_model: AccountModel = self.get_object()
|
|
240
240
|
|
|
241
241
|
try:
|
|
@@ -5,75 +5,72 @@ Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
|
|
|
5
5
|
Contributions to this module:
|
|
6
6
|
* Miguel Sanda <msanda@arrobalytics.com>
|
|
7
7
|
"""
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
8
10
|
from django.contrib import messages
|
|
9
11
|
from django.core.exceptions import ImproperlyConfigured, ValidationError
|
|
10
|
-
from django.db.models import Count
|
|
11
12
|
from django.http import HttpResponseForbidden
|
|
12
13
|
from django.urls import reverse
|
|
13
14
|
from django.utils.translation import gettext_lazy as _
|
|
14
|
-
from django.views.generic import (
|
|
15
|
-
|
|
15
|
+
from django.views.generic import (
|
|
16
|
+
YearArchiveView, MonthArchiveView, DetailView, UpdateView, CreateView, RedirectView,
|
|
17
|
+
ArchiveIndexView, DeleteView
|
|
18
|
+
)
|
|
16
19
|
from django.views.generic.detail import SingleObjectMixin
|
|
17
20
|
|
|
18
|
-
from django_ledger.forms.journal_entry import (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
from django_ledger.forms.journal_entry import (
|
|
22
|
+
JournalEntryModelUpdateForm,
|
|
23
|
+
JournalEntryModelCannotEditForm,
|
|
24
|
+
JournalEntryModelCreateForm
|
|
25
|
+
)
|
|
21
26
|
from django_ledger.forms.transactions import get_transactionmodel_formset_class
|
|
22
27
|
from django_ledger.io.io_core import get_localtime
|
|
28
|
+
from django_ledger.models import EntityModel, LedgerModel
|
|
23
29
|
from django_ledger.models.journal_entry import JournalEntryModel
|
|
24
|
-
from django_ledger.models.ledger import LedgerModel
|
|
25
30
|
from django_ledger.views.mixins import DjangoLedgerSecurityMixIn
|
|
26
31
|
|
|
27
32
|
|
|
28
|
-
class
|
|
33
|
+
class JournalEntryModelModelBaseView(DjangoLedgerSecurityMixIn):
|
|
29
34
|
queryset = None
|
|
35
|
+
ledger_model: Optional[LedgerModel] = None
|
|
30
36
|
|
|
31
37
|
def get_queryset(self):
|
|
32
38
|
if self.queryset is None:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
ledger_model: LedgerModel = self.get_ledger_model()
|
|
40
|
+
journal_entry_queryset = ledger_model.journal_entries.select_related('entity_unit', 'ledger', 'ledger__entity').order_by('-timestamp')
|
|
41
|
+
self.queryset = journal_entry_queryset
|
|
42
|
+
return self.queryset
|
|
43
|
+
|
|
44
|
+
def get_ledger_model(self) -> LedgerModel:
|
|
45
|
+
if self.ledger_model is None:
|
|
46
|
+
entity_model: EntityModel = self.get_authorized_entity_instance()
|
|
47
|
+
self.ledger_model = entity_model.get_ledgers().get(uuid__exact=self.kwargs['ledger_pk'])
|
|
48
|
+
return self.ledger_model
|
|
39
49
|
|
|
40
50
|
|
|
41
51
|
# JE Views ---
|
|
42
|
-
class JournalEntryCreateView(
|
|
52
|
+
class JournalEntryCreateView(JournalEntryModelModelBaseView, CreateView):
|
|
43
53
|
template_name = 'django_ledger/journal_entry/je_create.html'
|
|
44
54
|
PAGE_TITLE = _('Create Journal Entry')
|
|
45
55
|
extra_context = {
|
|
46
56
|
'page_title': PAGE_TITLE,
|
|
47
57
|
'header_title': PAGE_TITLE
|
|
48
58
|
}
|
|
49
|
-
|
|
50
|
-
pk_url_kwarg = 'ledger_pk'
|
|
51
|
-
ledger_model = None
|
|
59
|
+
ledger_model: Optional[LedgerModel] = None
|
|
52
60
|
|
|
53
61
|
def get_context_data(self, **kwargs):
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return ctx
|
|
62
|
-
|
|
63
|
-
def get_queryset(self):
|
|
64
|
-
return LedgerModel.objects.for_entity(
|
|
65
|
-
user_model=self.request.user,
|
|
66
|
-
entity_slug=self.kwargs['entity_slug'],
|
|
67
|
-
)
|
|
62
|
+
context = super().get_context_data(**kwargs)
|
|
63
|
+
ledger_model: LedgerModel = self.get_ledger_model()
|
|
64
|
+
context['page_title'] = self.PAGE_TITLE
|
|
65
|
+
context['header_title'] = self.PAGE_TITLE
|
|
66
|
+
context['header_subtitle'] = ledger_model.name
|
|
67
|
+
context['ledger_model'] = ledger_model
|
|
68
|
+
return context
|
|
68
69
|
|
|
69
70
|
def get_form(self, form_class=None):
|
|
70
|
-
if self.ledger_model is None:
|
|
71
|
-
ledger_model = self.get_object()
|
|
72
|
-
self.ledger_model = ledger_model
|
|
73
71
|
return JournalEntryModelCreateForm(
|
|
74
|
-
|
|
75
|
-
ledger_model=self.
|
|
76
|
-
user_model=self.request.user,
|
|
72
|
+
entity_model=self.get_authorized_entity_instance(),
|
|
73
|
+
ledger_model=self.get_ledger_model(),
|
|
77
74
|
**self.get_form_kwargs()
|
|
78
75
|
)
|
|
79
76
|
|
|
@@ -83,42 +80,51 @@ class JournalEntryCreateView(DjangoLedgerSecurityMixIn, CreateView, SingleObject
|
|
|
83
80
|
}
|
|
84
81
|
|
|
85
82
|
def get_success_url(self):
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
'entity_slug': self.kwargs.get('entity_slug'),
|
|
89
|
-
'ledger_pk': self.kwargs.get('ledger_pk')
|
|
90
|
-
})
|
|
83
|
+
ledger_model = self.get_ledger_model()
|
|
84
|
+
return ledger_model.get_journal_entry_list_url()
|
|
91
85
|
|
|
92
86
|
|
|
93
|
-
|
|
94
|
-
|
|
87
|
+
# ARCHIVE VIEWS START....
|
|
88
|
+
class JournalEntryListView(JournalEntryModelModelBaseView, ArchiveIndexView):
|
|
89
|
+
context_object_name = 'journal_entry_qs'
|
|
95
90
|
template_name = 'django_ledger/journal_entry/je_list.html'
|
|
96
91
|
PAGE_TITLE = _('Journal Entries')
|
|
97
|
-
extra_context = {
|
|
98
|
-
'page_title': PAGE_TITLE,
|
|
99
|
-
'header_title': PAGE_TITLE
|
|
100
|
-
}
|
|
101
92
|
http_method_names = ['get']
|
|
102
93
|
date_field = 'timestamp'
|
|
103
94
|
paginate_by = 20
|
|
104
95
|
allow_empty = True
|
|
105
96
|
|
|
106
|
-
def
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
return qs
|
|
97
|
+
def get_context_data(self, **kwargs):
|
|
98
|
+
context = super().get_context_data(**kwargs)
|
|
99
|
+
entity_model: EntityModel = self.get_authorized_entity_instance()
|
|
110
100
|
|
|
101
|
+
ledger_model = self.get_ledger_model()
|
|
102
|
+
context['ledger_model'] = ledger_model
|
|
103
|
+
context['page_title'] = self.PAGE_TITLE
|
|
104
|
+
context['header_title'] = self.PAGE_TITLE
|
|
105
|
+
context['header_subtitle'] = f'{entity_model.name} | Ledger: {ledger_model.name}'
|
|
106
|
+
context['header_subtitle_icon'] = 'bi:journal-check'
|
|
111
107
|
|
|
112
|
-
|
|
108
|
+
if ledger_model.is_locked():
|
|
109
|
+
messages.add_message(self.request,
|
|
110
|
+
message=_('Locked Journal Entry. Must unlock ledger to add new Journal Entries.'),
|
|
111
|
+
level=messages.WARNING,
|
|
112
|
+
extra_tags='is-warning')
|
|
113
|
+
return context
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class JournalEntryYearListView(JournalEntryListView, YearArchiveView):
|
|
113
117
|
make_object_list = True
|
|
114
118
|
|
|
115
119
|
|
|
116
|
-
class JournalEntryMonthListView(
|
|
120
|
+
class JournalEntryMonthListView(JournalEntryListView, MonthArchiveView):
|
|
117
121
|
make_object_list = True
|
|
118
122
|
month_format = '%m'
|
|
119
123
|
|
|
120
124
|
|
|
121
|
-
|
|
125
|
+
# ARCHIVE VIEWS END....
|
|
126
|
+
|
|
127
|
+
class JournalEntryUpdateView(JournalEntryModelModelBaseView, UpdateView):
|
|
122
128
|
context_object_name = 'journal_entry'
|
|
123
129
|
template_name = 'django_ledger/journal_entry/je_update.html'
|
|
124
130
|
pk_url_kwarg = 'je_pk'
|
|
@@ -128,34 +134,18 @@ class JournalEntryUpdateView(DjangoLedgerSecurityMixIn, JournalEntryModelModelVi
|
|
|
128
134
|
'header_title': PAGE_TITLE
|
|
129
135
|
}
|
|
130
136
|
|
|
131
|
-
def
|
|
137
|
+
def get_form_class(self, form_class=None):
|
|
132
138
|
je_model: JournalEntryModel = self.object
|
|
133
139
|
if not je_model.can_edit():
|
|
134
|
-
return JournalEntryModelCannotEditForm
|
|
135
|
-
|
|
136
|
-
ledger_model=je_model.ledger,
|
|
137
|
-
user_model=self.request.user,
|
|
138
|
-
**self.get_form_kwargs()
|
|
139
|
-
)
|
|
140
|
-
return JournalEntryModelUpdateForm(
|
|
141
|
-
entity_slug=self.kwargs['entity_slug'],
|
|
142
|
-
ledger_model=je_model.ledger,
|
|
143
|
-
user_model=self.request.user,
|
|
144
|
-
**self.get_form_kwargs()
|
|
145
|
-
)
|
|
140
|
+
return JournalEntryModelCannotEditForm
|
|
141
|
+
return JournalEntryModelUpdateForm
|
|
146
142
|
|
|
147
143
|
def get_success_url(self):
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
'ledger_pk': self.kwargs['ledger_pk']
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
def get_queryset(self):
|
|
154
|
-
qs = super().get_queryset()
|
|
155
|
-
return qs.prefetch_related('transactionmodel_set', 'transactionmodel_set__account')
|
|
144
|
+
je_model: JournalEntryModel = self.object
|
|
145
|
+
return je_model.get_journal_entry_list_url()
|
|
156
146
|
|
|
157
147
|
|
|
158
|
-
class JournalEntryDetailView(
|
|
148
|
+
class JournalEntryDetailView(JournalEntryModelModelBaseView, DetailView):
|
|
159
149
|
context_object_name = 'journal_entry'
|
|
160
150
|
template_name = 'django_ledger/journal_entry/je_detail.html'
|
|
161
151
|
slug_url_kwarg = 'je_pk'
|
|
@@ -168,28 +158,19 @@ class JournalEntryDetailView(DjangoLedgerSecurityMixIn, JournalEntryModelModelVi
|
|
|
168
158
|
}
|
|
169
159
|
http_method_names = ['get']
|
|
170
160
|
|
|
171
|
-
def get_queryset(self):
|
|
172
|
-
qs = super().get_queryset()
|
|
173
|
-
return qs.prefetch_related('transactionmodel_set', 'transactionmodel_set__account')
|
|
174
|
-
|
|
175
161
|
|
|
176
|
-
class JournalEntryDeleteView(
|
|
162
|
+
class JournalEntryDeleteView(JournalEntryModelModelBaseView, DeleteView):
|
|
177
163
|
template_name = 'django_ledger/journal_entry/je_delete.html'
|
|
178
164
|
context_object_name = 'je_model'
|
|
179
165
|
pk_url_kwarg = 'je_pk'
|
|
180
166
|
|
|
181
167
|
def get_success_url(self) -> str:
|
|
182
168
|
je_model: JournalEntryModel = self.object
|
|
183
|
-
return
|
|
184
|
-
viewname='django_ledger:je-list',
|
|
185
|
-
kwargs={
|
|
186
|
-
'entity_slug': self.AUTHORIZED_ENTITY_MODEL.slug,
|
|
187
|
-
'ledger_pk': je_model.ledger_id
|
|
188
|
-
}
|
|
189
|
-
)
|
|
169
|
+
return je_model.get_journal_entry_list_url()
|
|
190
170
|
|
|
191
171
|
|
|
192
|
-
|
|
172
|
+
# todo:.... move this to transaction list view?.....
|
|
173
|
+
class JournalEntryModelTXSDetailView(JournalEntryModelModelBaseView, DetailView):
|
|
193
174
|
template_name = 'django_ledger/journal_entry/je_detail_txs.html'
|
|
194
175
|
PAGE_TITLE = _('Edit Transactions')
|
|
195
176
|
pk_url_kwarg = 'je_pk'
|
|
@@ -214,11 +195,8 @@ class JournalEntryModelTXSDetailView(DjangoLedgerSecurityMixIn, JournalEntryMode
|
|
|
214
195
|
if not txs_formset:
|
|
215
196
|
TransactionModelFormSet = get_transactionmodel_formset_class(journal_entry_model=je_model)
|
|
216
197
|
context['txs_formset'] = TransactionModelFormSet(
|
|
217
|
-
user_model=self.request.user,
|
|
218
198
|
je_model=je_model,
|
|
219
|
-
|
|
220
|
-
entity_slug=self.kwargs['entity_slug'],
|
|
221
|
-
queryset=je_model.transactionmodel_set.all().order_by('account__code')
|
|
199
|
+
entity_model=self.get_authorized_entity_instance(),
|
|
222
200
|
)
|
|
223
201
|
else:
|
|
224
202
|
context['txs_formset'] = txs_formset
|
|
@@ -234,9 +212,7 @@ class JournalEntryModelTXSDetailView(DjangoLedgerSecurityMixIn, JournalEntryMode
|
|
|
234
212
|
|
|
235
213
|
TransactionModelFormSet = get_transactionmodel_formset_class(journal_entry_model=je_model)
|
|
236
214
|
txs_formset = TransactionModelFormSet(request.POST,
|
|
237
|
-
|
|
238
|
-
ledger_pk=kwargs['ledger_pk'],
|
|
239
|
-
entity_slug=kwargs['entity_slug'],
|
|
215
|
+
entity_model=self.get_authorized_entity_instance(),
|
|
240
216
|
je_model=je_model)
|
|
241
217
|
|
|
242
218
|
if je_model.locked:
|
|
@@ -271,7 +247,11 @@ class JournalEntryModelTXSDetailView(DjangoLedgerSecurityMixIn, JournalEntryMode
|
|
|
271
247
|
|
|
272
248
|
|
|
273
249
|
# ACTION VIEWS...
|
|
274
|
-
class BaseJournalEntryActionView(
|
|
250
|
+
class BaseJournalEntryActionView(
|
|
251
|
+
JournalEntryModelModelBaseView,
|
|
252
|
+
RedirectView,
|
|
253
|
+
SingleObjectMixin
|
|
254
|
+
):
|
|
275
255
|
http_method_names = ['get']
|
|
276
256
|
pk_url_kwarg = 'je_pk'
|
|
277
257
|
action_name = None
|
|
@@ -279,11 +259,14 @@ class BaseJournalEntryActionView(DjangoLedgerSecurityMixIn, RedirectView, Single
|
|
|
279
259
|
|
|
280
260
|
def get_queryset(self):
|
|
281
261
|
return JournalEntryModel.objects.for_entity(
|
|
282
|
-
entity_slug=self.
|
|
262
|
+
entity_slug=self.get_authorized_entity_instance(),
|
|
283
263
|
user_model=self.request.user
|
|
284
|
-
)
|
|
264
|
+
).for_ledger(ledger_pk=self.kwargs['ledger_pk'])
|
|
285
265
|
|
|
286
266
|
def get_redirect_url(self, *args, **kwargs):
|
|
267
|
+
next_url = self.request.GET.get('next')
|
|
268
|
+
if next_url:
|
|
269
|
+
return next_url
|
|
287
270
|
return reverse('django_ledger:je-list',
|
|
288
271
|
kwargs={
|
|
289
272
|
'entity_slug': kwargs['entity_slug'],
|
django_ledger/views/ledger.py
CHANGED
|
@@ -17,6 +17,7 @@ from django.views.generic.detail import SingleObjectMixin
|
|
|
17
17
|
|
|
18
18
|
from django_ledger.forms.ledger import LedgerModelCreateForm, LedgerModelUpdateForm
|
|
19
19
|
from django_ledger.io.io_core import get_localdate
|
|
20
|
+
from django_ledger.models import EntityModel
|
|
20
21
|
from django_ledger.models.ledger import LedgerModel
|
|
21
22
|
from django_ledger.views.mixins import (
|
|
22
23
|
YearlyReportMixIn, QuarterlyReportMixIn,
|
|
@@ -25,19 +26,17 @@ from django_ledger.views.mixins import (
|
|
|
25
26
|
)
|
|
26
27
|
|
|
27
28
|
|
|
28
|
-
class
|
|
29
|
+
class LedgerModelModelBaseView(DjangoLedgerSecurityMixIn):
|
|
29
30
|
queryset = None
|
|
30
31
|
|
|
31
32
|
def get_queryset(self):
|
|
32
33
|
if self.queryset is None:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
user_model=self.request.user
|
|
36
|
-
).select_related('entity')
|
|
34
|
+
entity_model: EntityModel = self.get_authorized_entity_instance()
|
|
35
|
+
self.queryset = entity_model.get_ledgers()
|
|
37
36
|
return self.queryset
|
|
38
37
|
|
|
39
38
|
|
|
40
|
-
class LedgerModelListView(
|
|
39
|
+
class LedgerModelListView(LedgerModelModelBaseView, ArchiveIndexView):
|
|
41
40
|
allow_empty = True
|
|
42
41
|
context_object_name = 'ledger_list'
|
|
43
42
|
template_name = 'django_ledger/ledger/ledger_list.html'
|
|
@@ -91,7 +90,7 @@ class LedgerModelMonthListView(MonthArchiveView, LedgerModelListView):
|
|
|
91
90
|
make_object_list = True
|
|
92
91
|
|
|
93
92
|
|
|
94
|
-
class LedgerModelCreateView(
|
|
93
|
+
class LedgerModelCreateView(LedgerModelModelBaseView, CreateView):
|
|
95
94
|
template_name = 'django_ledger/ledger/ledger_create.html'
|
|
96
95
|
PAGE_TITLE = _('Create Ledger')
|
|
97
96
|
extra_context = {
|
|
@@ -118,7 +117,7 @@ class LedgerModelCreateView(DjangoLedgerSecurityMixIn, LedgerModelModelViewQuery
|
|
|
118
117
|
})
|
|
119
118
|
|
|
120
119
|
|
|
121
|
-
class LedgerModelDetailView(
|
|
120
|
+
class LedgerModelDetailView(LedgerModelModelBaseView, RedirectView):
|
|
122
121
|
|
|
123
122
|
def get_redirect_url(self, *args, **kwargs):
|
|
124
123
|
return reverse('django_ledger:je-list',
|
|
@@ -128,7 +127,7 @@ class LedgerModelDetailView(DjangoLedgerSecurityMixIn, LedgerModelModelViewQuery
|
|
|
128
127
|
})
|
|
129
128
|
|
|
130
129
|
|
|
131
|
-
class LedgerModelUpdateView(
|
|
130
|
+
class LedgerModelUpdateView(LedgerModelModelBaseView, UpdateView):
|
|
132
131
|
context_object_name = 'ledger'
|
|
133
132
|
pk_url_kwarg = 'ledger_pk'
|
|
134
133
|
template_name = 'django_ledger/ledger/ledger_update.html'
|
|
@@ -153,7 +152,7 @@ class LedgerModelUpdateView(DjangoLedgerSecurityMixIn, LedgerModelModelViewQuery
|
|
|
153
152
|
})
|
|
154
153
|
|
|
155
154
|
|
|
156
|
-
class LedgerModelDeleteView(
|
|
155
|
+
class LedgerModelDeleteView(LedgerModelModelBaseView, DeleteView):
|
|
157
156
|
template_name = 'django_ledger/ledger/ledger_delete.html'
|
|
158
157
|
pk_url_kwarg = 'ledger_pk'
|
|
159
158
|
context_object_name = 'ledger_model'
|
|
@@ -167,9 +166,8 @@ class LedgerModelDeleteView(DjangoLedgerSecurityMixIn, LedgerModelModelViewQuery
|
|
|
167
166
|
|
|
168
167
|
# ACTIONS....
|
|
169
168
|
|
|
170
|
-
class LedgerModelModelActionView(
|
|
169
|
+
class LedgerModelModelActionView(LedgerModelModelBaseView,
|
|
171
170
|
RedirectView,
|
|
172
|
-
LedgerModelModelViewQuerySetMixIn,
|
|
173
171
|
SingleObjectMixin):
|
|
174
172
|
http_method_names = ['get']
|
|
175
173
|
pk_url_kwarg = 'ledger_pk'
|
|
@@ -200,7 +198,7 @@ class LedgerModelModelActionView(DjangoLedgerSecurityMixIn,
|
|
|
200
198
|
|
|
201
199
|
|
|
202
200
|
# Ledger Balance Sheet Views...
|
|
203
|
-
class BaseLedgerModelBalanceSheetView(
|
|
201
|
+
class BaseLedgerModelBalanceSheetView(LedgerModelModelBaseView, RedirectView):
|
|
204
202
|
|
|
205
203
|
def get_redirect_url(self, *args, **kwargs):
|
|
206
204
|
year = get_localdate().year
|
|
@@ -211,8 +209,7 @@ class BaseLedgerModelBalanceSheetView(DjangoLedgerSecurityMixIn, RedirectView):
|
|
|
211
209
|
})
|
|
212
210
|
|
|
213
211
|
|
|
214
|
-
class FiscalYearLedgerModelBalanceSheetView(
|
|
215
|
-
LedgerModelModelViewQuerySetMixIn,
|
|
212
|
+
class FiscalYearLedgerModelBalanceSheetView(LedgerModelModelBaseView,
|
|
216
213
|
BaseDateNavigationUrlMixIn,
|
|
217
214
|
EntityUnitMixIn,
|
|
218
215
|
YearlyReportMixIn,
|
|
@@ -249,7 +246,7 @@ class DateLedgerModelBalanceSheetView(FiscalYearLedgerModelBalanceSheetView, Dat
|
|
|
249
246
|
|
|
250
247
|
|
|
251
248
|
# Ledger Income Statement Views...
|
|
252
|
-
class BaseLedgerIncomeStatementView(
|
|
249
|
+
class BaseLedgerIncomeStatementView(LedgerModelModelBaseView, RedirectView):
|
|
253
250
|
|
|
254
251
|
def get_redirect_url(self, *args, **kwargs):
|
|
255
252
|
year = get_localdate().year
|
|
@@ -261,8 +258,7 @@ class BaseLedgerIncomeStatementView(DjangoLedgerSecurityMixIn, RedirectView):
|
|
|
261
258
|
})
|
|
262
259
|
|
|
263
260
|
|
|
264
|
-
class FiscalYearLedgerIncomeStatementView(
|
|
265
|
-
LedgerModelModelViewQuerySetMixIn,
|
|
261
|
+
class FiscalYearLedgerIncomeStatementView(LedgerModelModelBaseView,
|
|
266
262
|
BaseDateNavigationUrlMixIn,
|
|
267
263
|
EntityUnitMixIn,
|
|
268
264
|
YearlyReportMixIn,
|
|
@@ -298,7 +294,7 @@ class DateLedgerIncomeStatementView(FiscalYearLedgerIncomeStatementView, DateRep
|
|
|
298
294
|
|
|
299
295
|
|
|
300
296
|
# CASH FLOW STATEMENT ----
|
|
301
|
-
class BaseLedgerModelCashFlowStatementRedirectView(
|
|
297
|
+
class BaseLedgerModelCashFlowStatementRedirectView(LedgerModelModelBaseView, RedirectView):
|
|
302
298
|
|
|
303
299
|
def get_redirect_url(self, *args, **kwargs):
|
|
304
300
|
year = get_localdate().year
|
|
@@ -310,8 +306,7 @@ class BaseLedgerModelCashFlowStatementRedirectView(DjangoLedgerSecurityMixIn, Re
|
|
|
310
306
|
})
|
|
311
307
|
|
|
312
308
|
|
|
313
|
-
class FiscalYearLedgerModelCashFlowStatementView(
|
|
314
|
-
LedgerModelModelViewQuerySetMixIn,
|
|
309
|
+
class FiscalYearLedgerModelCashFlowStatementView(LedgerModelModelBaseView,
|
|
315
310
|
BaseDateNavigationUrlMixIn,
|
|
316
311
|
EntityUnitMixIn,
|
|
317
312
|
YearlyReportMixIn,
|