django-ledger 0.6.3__py3-none-any.whl → 0.7.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 -4
- django_ledger/admin/coa.py +1 -2
- django_ledger/contrib/django_ledger_graphene/accounts/schema.py +1 -1
- django_ledger/forms/account.py +60 -44
- django_ledger/forms/bank_account.py +3 -2
- django_ledger/forms/bill.py +24 -36
- 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_middleware.py +5 -0
- django_ledger/io/roles.py +6 -0
- django_ledger/migrations/0017_alter_accountmodel_unique_together_and_more.py +31 -0
- django_ledger/models/accounts.py +629 -342
- django_ledger/models/bank_account.py +0 -4
- django_ledger/models/bill.py +0 -3
- django_ledger/models/closing_entry.py +0 -3
- django_ledger/models/coa.py +59 -48
- django_ledger/models/coa_default.py +9 -8
- django_ledger/models/customer.py +0 -4
- django_ledger/models/data_import.py +0 -3
- django_ledger/models/entity.py +80 -40
- django_ledger/models/estimate.py +0 -9
- django_ledger/models/invoice.py +0 -3
- django_ledger/models/items.py +4 -6
- django_ledger/models/journal_entry.py +2 -5
- django_ledger/models/ledger.py +0 -3
- django_ledger/models/mixins.py +0 -3
- django_ledger/models/purchase_order.py +0 -4
- django_ledger/models/signals.py +0 -3
- django_ledger/models/transactions.py +2 -5
- django_ledger/models/unit.py +0 -3
- django_ledger/models/utils.py +0 -3
- django_ledger/models/vendor.py +0 -3
- django_ledger/report/cash_flow_statement.py +1 -1
- django_ledger/static/.DS_Store +0 -0
- django_ledger/static/django_ledger/.DS_Store +0 -0
- django_ledger/static/django_ledger/logo_2/.DS_Store +0 -0
- django_ledger/static/django_ledger/logo_2/django_ledger_logo_dark.png +0 -0
- django_ledger/static/django_ledger/logo_2/django_ledger_logo_dark@0.5x.png +0 -0
- django_ledger/static/django_ledger/logo_2/django_ledger_logo_dark@2x.png +0 -0
- django_ledger/static/django_ledger/logo_2/django_ledger_logo_dark@3x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-full-vert.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-full-vert@0.5x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-full-vert@2x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-full-vert@3x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo-full-horiz.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo-full-horiz@0.5x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo-full-horiz@2x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo-full-horiz@3x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo-full-vert.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo-full-vert@0.5x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo-full-vert@2x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo-full-vert@3x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo@0.5x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo@2x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo@3x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-full-horiz.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-full-horiz@0.5x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-full-horiz@2x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-full-horiz@3x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-full-vert.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-full-vert@0.5x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-full-vert@2x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-full-vert@3x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-horiz.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-horiz@0.5x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-horiz@2x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-horiz@3x.png +0 -0
- 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 +27 -18
- django_ledger/templates/django_ledger/bills/bill_detail.html +3 -3
- django_ledger/templates/django_ledger/expense/tags/expense_item_table.html +7 -0
- 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/utils.py +8 -0
- django_ledger/views/__init__.py +1 -1
- django_ledger/views/account.py +80 -118
- 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/closing_entry.py +8 -0
- django_ledger/views/coa.py +2 -1
- 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 +9 -8
- 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.3.dist-info → django_ledger-0.7.0.dist-info}/AUTHORS.md +8 -2
- {django_ledger-0.6.3.dist-info → django_ledger-0.7.0.dist-info}/METADATA +34 -43
- {django_ledger-0.6.3.dist-info → django_ledger-0.7.0.dist-info}/RECORD +115 -79
- {django_ledger-0.6.3.dist-info → django_ledger-0.7.0.dist-info}/WHEEL +1 -1
- {django_ledger-0.6.3.dist-info → django_ledger-0.7.0.dist-info}/top_level.txt +0 -1
- {django_ledger-0.6.3.dist-info → django_ledger-0.7.0.dist-info}/LICENSE +0 -0
|
@@ -1,16 +1,152 @@
|
|
|
1
|
-
from
|
|
1
|
+
from random import choice
|
|
2
|
+
from urllib.parse import urlparse
|
|
3
|
+
|
|
4
|
+
from django.urls import reverse
|
|
5
|
+
|
|
6
|
+
from django_ledger.forms.account import AccountModelCreateForm
|
|
7
|
+
from django_ledger.io import roles, CREDIT
|
|
8
|
+
from django_ledger.models import EntityModel
|
|
9
|
+
from django_ledger.models.accounts import AccountModel
|
|
2
10
|
from django_ledger.tests.base import DjangoLedgerBaseTest
|
|
11
|
+
from django_ledger.urls.account import urlpatterns as account_urls
|
|
3
12
|
|
|
4
13
|
|
|
5
14
|
class AccountModelTests(DjangoLedgerBaseTest):
|
|
15
|
+
N = 2
|
|
16
|
+
|
|
17
|
+
def setUp(self):
|
|
18
|
+
self.resolve_url_patterns(
|
|
19
|
+
url_patterns=account_urls
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
def test_protected_views(self):
|
|
23
|
+
|
|
24
|
+
self.logout_client()
|
|
25
|
+
entity_model = self.get_random_entity_model()
|
|
26
|
+
account_model: AccountModel = self.get_random_account(entity_model=entity_model)
|
|
27
|
+
|
|
28
|
+
for path, kwargs in self.URL_PATTERNS.items():
|
|
29
|
+
url_kwargs = dict()
|
|
30
|
+
url_kwargs['entity_slug'] = entity_model.slug
|
|
31
|
+
|
|
32
|
+
if 'coa_slug' in kwargs:
|
|
33
|
+
url_kwargs['coa_slug'] = account_model.coa_slug
|
|
34
|
+
if 'account_pk' in kwargs:
|
|
35
|
+
url_kwargs['account_pk'] = account_model.uuid
|
|
36
|
+
if 'year' in kwargs:
|
|
37
|
+
url_kwargs['year'] = self.get_random_date().year
|
|
38
|
+
if 'quarter' in kwargs:
|
|
39
|
+
url_kwargs['quarter'] = choice(range(1, 5))
|
|
40
|
+
if 'month' in kwargs:
|
|
41
|
+
url_kwargs['month'] = self.get_random_date().month
|
|
42
|
+
if 'day' in kwargs:
|
|
43
|
+
url_kwargs['day'] = choice(range(1, 29))
|
|
44
|
+
|
|
45
|
+
url = reverse(f'django_ledger:{path}', kwargs=url_kwargs)
|
|
46
|
+
response = self.CLIENT.get(url, follow=False)
|
|
47
|
+
redirect_url = urlparse(response.url)
|
|
48
|
+
redirect_path = redirect_url.path
|
|
49
|
+
login_path = reverse(viewname='django_ledger:login')
|
|
50
|
+
|
|
51
|
+
self.assertEqual(response.status_code, 302, msg=f'{path} view is not protected.')
|
|
52
|
+
self.assertEqual(redirect_path, login_path, msg=f'{path} view not redirecting to correct auth URL.')
|
|
53
|
+
|
|
54
|
+
def test_account_create(self):
|
|
55
|
+
|
|
56
|
+
entity_model = self.get_random_entity_model()
|
|
57
|
+
account_create_url = reverse(
|
|
58
|
+
viewname='django_ledger:account-create',
|
|
59
|
+
kwargs={
|
|
60
|
+
'entity_slug': entity_model.slug,
|
|
61
|
+
'coa_slug': entity_model.default_coa_slug
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
self.login_client()
|
|
66
|
+
response = self.CLIENT.get(account_create_url)
|
|
67
|
+
|
|
68
|
+
# check if user can access page...
|
|
69
|
+
self.assertEqual(response.status_code, 200, msg="Fail to GET Account Create page.")
|
|
70
|
+
|
|
71
|
+
# check if account create form is rendered...
|
|
72
|
+
account_create_form: AccountModelCreateForm = response.context['form']
|
|
73
|
+
self.assertContains(response, account_create_form.form_id, count=1)
|
|
74
|
+
|
|
75
|
+
# check if all fields are rendered...
|
|
76
|
+
self.assertContains(response, 'name="code"')
|
|
77
|
+
self.assertContains(response, 'name="name"')
|
|
78
|
+
self.assertContains(response, 'name="role"')
|
|
79
|
+
self.assertContains(response, 'name="role_default"')
|
|
80
|
+
self.assertContains(response, 'name="balance_type"')
|
|
81
|
+
self.assertContains(response, 'name="active"')
|
|
82
|
+
self.assertContains(response, 'name="coa_model"')
|
|
83
|
+
|
|
84
|
+
# creates new account...
|
|
85
|
+
NEW_ACCOUNT_CODE = '404000'
|
|
86
|
+
form_data = {
|
|
87
|
+
'code': NEW_ACCOUNT_CODE,
|
|
88
|
+
'name': 'Test Income Account',
|
|
89
|
+
'role': roles.INCOME_OPERATIONAL,
|
|
90
|
+
'role_default': False,
|
|
91
|
+
'balance_type': CREDIT,
|
|
92
|
+
'active': True
|
|
93
|
+
}
|
|
94
|
+
response_create = self.CLIENT.post(account_create_url, data=form_data)
|
|
95
|
+
self.assertEqual(response_create.status_code, 302)
|
|
96
|
+
self.assertTrue(AccountModel.objects.for_entity(
|
|
97
|
+
entity_model=entity_model,
|
|
98
|
+
user_model=self.user_model,
|
|
99
|
+
coa_slug=entity_model.default_coa_slug,
|
|
100
|
+
).with_codes(codes=NEW_ACCOUNT_CODE).exists())
|
|
101
|
+
|
|
102
|
+
# cannot create an account with same code again...
|
|
103
|
+
response_create = self.CLIENT.post(account_create_url, data=form_data)
|
|
104
|
+
self.assertEqual(response_create.status_code, 200)
|
|
105
|
+
self.assertContains(response_create, 'Account with this Chart of Accounts and Account Code already exists')
|
|
106
|
+
|
|
107
|
+
def test_account_activation(self):
|
|
108
|
+
|
|
109
|
+
entity_model: EntityModel = self.get_random_entity_model()
|
|
110
|
+
account_model: AccountModel = self.get_random_account(entity_model=entity_model, active=True)
|
|
111
|
+
|
|
112
|
+
self.assertTrue(account_model.can_deactivate())
|
|
113
|
+
self.assertTrue(account_model.active)
|
|
114
|
+
|
|
115
|
+
account_model.deactivate(commit=True)
|
|
116
|
+
self.assertFalse(account_model.can_deactivate())
|
|
117
|
+
self.assertFalse(account_model.active)
|
|
118
|
+
|
|
119
|
+
account_model.activate(commit=True)
|
|
120
|
+
self.assertTrue(account_model.can_deactivate())
|
|
121
|
+
self.assertTrue(account_model.active)
|
|
122
|
+
|
|
123
|
+
def test_account_lock(self):
|
|
124
|
+
entity_model: EntityModel = self.get_random_entity_model()
|
|
125
|
+
account_model: AccountModel = self.get_random_account(entity_model=entity_model, active=True, locked=False)
|
|
126
|
+
|
|
127
|
+
self.assertTrue(account_model.can_lock())
|
|
128
|
+
self.assertFalse(account_model.can_unlock())
|
|
129
|
+
|
|
130
|
+
account_model.lock(commit=True)
|
|
131
|
+
self.assertFalse(account_model.can_lock())
|
|
132
|
+
self.assertTrue(account_model.can_unlock())
|
|
133
|
+
|
|
134
|
+
account_model.unlock(commit=True)
|
|
135
|
+
self.assertTrue(account_model.can_lock())
|
|
136
|
+
self.assertFalse(account_model.can_unlock())
|
|
6
137
|
|
|
7
|
-
def
|
|
8
|
-
entity_model = self.
|
|
9
|
-
self.
|
|
138
|
+
def test_annotations(self):
|
|
139
|
+
entity_model: EntityModel = self.get_random_entity_model()
|
|
140
|
+
account_model: AccountModel = self.get_random_account(entity_model=entity_model, active=True, locked=False)
|
|
10
141
|
|
|
11
|
-
|
|
12
|
-
|
|
142
|
+
self.assertEqual(account_model.entity_slug, entity_model.slug)
|
|
143
|
+
self.assertEqual(account_model.coa_slug, account_model.coa_model.slug)
|
|
144
|
+
self.assertEqual(account_model.coa_model.active, account_model.is_coa_active())
|
|
13
145
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
146
|
+
def test_can_transact(self):
|
|
147
|
+
entity_model: EntityModel = self.get_random_entity_model()
|
|
148
|
+
account_model: AccountModel = self.get_random_account(entity_model=entity_model, active=True, locked=False)
|
|
149
|
+
self.assertTrue(account_model.can_transact())
|
|
150
|
+
account_model.lock(commit=False)
|
|
151
|
+
self.assertFalse(account_model.can_transact())
|
|
152
|
+
account_model.unlock(commit=False)
|
django_ledger/urls/account.py
CHANGED
|
@@ -4,54 +4,47 @@ from django_ledger import views
|
|
|
4
4
|
|
|
5
5
|
urlpatterns = [
|
|
6
6
|
|
|
7
|
-
# NO COA SLUG USES DEFAULT COA....
|
|
8
|
-
path('<slug:entity_slug>/create/',
|
|
9
|
-
views.AccountModelCreateView.as_view(),
|
|
10
|
-
name='account-create'),
|
|
11
|
-
path('<slug:entity_slug>/list/',
|
|
12
|
-
views.AccountModelListView.as_view(),
|
|
13
|
-
name='account-list'),
|
|
14
|
-
path('<slug:entity_slug>/list/active/',
|
|
15
|
-
views.AccountModelListView.as_view(active_only=True),
|
|
16
|
-
name='account-list-active'),
|
|
17
|
-
|
|
18
|
-
# EXPLICIT COA...
|
|
19
7
|
path('<slug:entity_slug>/<slug:coa_slug>/create/',
|
|
20
8
|
views.AccountModelCreateView.as_view(),
|
|
21
|
-
name='account-create
|
|
9
|
+
name='account-create'),
|
|
22
10
|
path('<slug:entity_slug>/<slug:coa_slug>/list/',
|
|
23
11
|
views.AccountModelListView.as_view(),
|
|
24
|
-
name='account-list
|
|
12
|
+
name='account-list'),
|
|
25
13
|
path('<slug:entity_slug>/<slug:coa_slug>/list/active/',
|
|
26
14
|
views.AccountModelListView.as_view(active_only=True),
|
|
27
|
-
name='account-list-active
|
|
15
|
+
name='account-list-active'),
|
|
28
16
|
|
|
29
17
|
# Account Transaction Detail....
|
|
30
|
-
path('<slug:entity_slug>/update/<uuid:account_pk>/',
|
|
18
|
+
path('<slug:entity_slug>/<slug:coa_slug>/update/<uuid:account_pk>/',
|
|
31
19
|
views.AccountModelUpdateView.as_view(),
|
|
32
20
|
name='account-update'),
|
|
33
|
-
path('<slug:entity_slug>/detail/<uuid:account_pk>/',
|
|
21
|
+
path('<slug:entity_slug>/<slug:coa_slug>/detail/<uuid:account_pk>/',
|
|
34
22
|
views.AccountModelDetailView.as_view(),
|
|
35
23
|
name='account-detail'),
|
|
36
|
-
path('<slug:entity_slug>/detail/<uuid:account_pk>/year/<int:year>/',
|
|
24
|
+
path('<slug:entity_slug>/<slug:coa_slug>/detail/<uuid:account_pk>/year/<int:year>/',
|
|
37
25
|
views.AccountModelYearDetailView.as_view(),
|
|
38
26
|
name='account-detail-year'),
|
|
39
|
-
path('<slug:entity_slug>/detail/<uuid:account_pk>/quarter/<int:year>/<int:quarter>/',
|
|
27
|
+
path('<slug:entity_slug>/<slug:coa_slug>/detail/<uuid:account_pk>/quarter/<int:year>/<int:quarter>/',
|
|
40
28
|
views.AccountModelQuarterDetailView.as_view(),
|
|
41
29
|
name='account-detail-quarter'),
|
|
42
|
-
path('<slug:entity_slug>/detail/<uuid:account_pk>/month/<int:year>/<int:month>/',
|
|
30
|
+
path('<slug:entity_slug>/<slug:coa_slug>/detail/<uuid:account_pk>/month/<int:year>/<int:month>/',
|
|
43
31
|
views.AccountModelMonthDetailView.as_view(),
|
|
44
32
|
name='account-detail-month'),
|
|
45
|
-
path('<slug:entity_slug>/detail/<uuid:account_pk>/date/<int:year>/<int:month>/<int:day>/',
|
|
33
|
+
path('<slug:entity_slug>/<slug:coa_slug>/detail/<uuid:account_pk>/date/<int:year>/<int:month>/<int:day>/',
|
|
46
34
|
views.AccountModelDateDetailView.as_view(),
|
|
47
35
|
name='account-detail-date'),
|
|
48
36
|
|
|
49
37
|
# Account Actions...
|
|
50
|
-
path('<slug:entity_slug>/action/<uuid:account_pk>/activate/',
|
|
38
|
+
path('<slug:entity_slug>/<slug:coa_slug>/action/<uuid:account_pk>/activate/',
|
|
51
39
|
views.AccountModelModelActionView.as_view(action_name='activate'),
|
|
52
40
|
name='account-action-activate'),
|
|
53
|
-
path('<slug:entity_slug>/action/<uuid:account_pk>/deactivate/',
|
|
41
|
+
path('<slug:entity_slug>/<slug:coa_slug>/action/<uuid:account_pk>/deactivate/',
|
|
54
42
|
views.AccountModelModelActionView.as_view(action_name='deactivate'),
|
|
55
43
|
name='account-action-deactivate'),
|
|
56
|
-
|
|
44
|
+
path('<slug:entity_slug>/<slug:coa_slug>/action/<uuid:account_pk>/lock/',
|
|
45
|
+
views.AccountModelModelActionView.as_view(action_name='lock'),
|
|
46
|
+
name='account-action-lock'),
|
|
47
|
+
path('<slug:entity_slug>/<slug:coa_slug>/action/<uuid:account_pk>/unlock/',
|
|
48
|
+
views.AccountModelModelActionView.as_view(action_name='unlock'),
|
|
49
|
+
name='account-action-unlock')
|
|
57
50
|
]
|
django_ledger/utils.py
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
|
|
3
|
+
Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
|
|
4
|
+
|
|
5
|
+
Contributions to this module:
|
|
6
|
+
* Miguel Sanda <msanda@arrobalytics.com>
|
|
7
|
+
"""
|
|
8
|
+
|
|
1
9
|
from datetime import date
|
|
2
10
|
from importlib import import_module
|
|
3
11
|
from itertools import groupby
|
django_ledger/views/__init__.py
CHANGED
|
@@ -3,7 +3,7 @@ Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
|
|
|
3
3
|
Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
|
|
4
4
|
|
|
5
5
|
Contributions to this module:
|
|
6
|
-
Miguel Sanda <msanda@arrobalytics.com>
|
|
6
|
+
* Miguel Sanda <msanda@arrobalytics.com>
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
from django_ledger.views.account import *
|
django_ledger/views/account.py
CHANGED
|
@@ -3,12 +3,11 @@ Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
|
|
|
3
3
|
Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
|
|
4
4
|
|
|
5
5
|
Contributions to this module:
|
|
6
|
-
Miguel Sanda <msanda@arrobalytics.com>
|
|
6
|
+
* Miguel Sanda <msanda@arrobalytics.com>
|
|
7
7
|
"""
|
|
8
8
|
from django.contrib import messages
|
|
9
9
|
from django.core.exceptions import ImproperlyConfigured, ValidationError
|
|
10
10
|
from django.http import HttpResponseRedirect
|
|
11
|
-
from django.shortcuts import get_object_or_404
|
|
12
11
|
from django.urls import reverse
|
|
13
12
|
from django.utils.translation import gettext as _
|
|
14
13
|
from django.views.generic import ListView, UpdateView, CreateView, DetailView
|
|
@@ -17,7 +16,7 @@ from django.views.generic.detail import SingleObjectMixin
|
|
|
17
16
|
|
|
18
17
|
from django_ledger.forms.account import AccountModelUpdateForm, AccountModelCreateForm
|
|
19
18
|
from django_ledger.io.io_core import get_localdate
|
|
20
|
-
from django_ledger.models import ChartOfAccountModel
|
|
19
|
+
from django_ledger.models import EntityModel, ChartOfAccountModel
|
|
21
20
|
from django_ledger.models.accounts import AccountModel
|
|
22
21
|
from django_ledger.views.mixins import (
|
|
23
22
|
YearlyReportMixIn, MonthlyReportMixIn, QuarterlyReportMixIn, DjangoLedgerSecurityMixIn,
|
|
@@ -25,58 +24,51 @@ from django_ledger.views.mixins import (
|
|
|
25
24
|
)
|
|
26
25
|
|
|
27
26
|
|
|
28
|
-
class
|
|
27
|
+
class BaseAccountModelBaseView(DjangoLedgerSecurityMixIn):
|
|
29
28
|
queryset = None
|
|
30
29
|
coa_model = None
|
|
31
30
|
|
|
32
|
-
def
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
self.coa_model = coa_model
|
|
31
|
+
def get_authorized_entity_queryset(self):
|
|
32
|
+
qs = super().get_authorized_entity_queryset()
|
|
33
|
+
return qs.select_related('admin', 'default_coa', 'default_coa__entity')
|
|
34
|
+
|
|
35
|
+
def get_coa_model(self):
|
|
36
|
+
if not self.coa_model:
|
|
37
|
+
entity_model: EntityModel = self.get_authorized_entity_instance()
|
|
38
|
+
self.coa_model = entity_model.chartofaccountmodel_set.get(slug__exact=self.kwargs['coa_slug'])
|
|
41
39
|
return self.coa_model
|
|
42
40
|
|
|
43
41
|
def get_queryset(self):
|
|
44
42
|
if self.queryset is None:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
entity_model: EntityModel = self.get_authorized_entity_instance()
|
|
44
|
+
coa_slug = self.kwargs['coa_slug']
|
|
45
|
+
|
|
46
|
+
coa_model, account_model_qs = entity_model.get_coa_accounts(
|
|
47
|
+
coa_model=entity_model.default_coa if coa_slug == entity_model.default_coa_slug else coa_slug,
|
|
48
|
+
return_coa_model=True,
|
|
49
|
+
active=False
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
account_model_qs = account_model_qs.select_related(
|
|
49
53
|
'coa_model',
|
|
50
54
|
'coa_model__entity'
|
|
51
55
|
).order_by(
|
|
52
|
-
'coa_model', 'role', 'code'
|
|
56
|
+
'coa_model', 'role', 'code'
|
|
57
|
+
).not_coa_root()
|
|
53
58
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if coa_slug:
|
|
58
|
-
qs = qs.filter(coa_model__slug__exact=coa_slug)
|
|
59
|
-
elif account_pk:
|
|
60
|
-
qs = qs.filter(uuid__exact=account_pk)
|
|
61
|
-
else:
|
|
62
|
-
qs = qs.filter(coa_model__slug__exact=self.AUTHORIZED_ENTITY_MODEL.default_coa.slug)
|
|
59
|
+
self.coa_model = coa_model
|
|
60
|
+
self.queryset = account_model_qs
|
|
63
61
|
|
|
64
|
-
self.queryset = qs
|
|
65
62
|
return super().get_queryset()
|
|
66
63
|
|
|
67
|
-
def get_context_data(self,
|
|
68
|
-
context = super().get_context_data(
|
|
69
|
-
|
|
70
|
-
if self.kwargs.get('coa_slug'):
|
|
71
|
-
coa_model_qs = entity_model.chartofaccountmodel_set.all()
|
|
72
|
-
context['coa_model'] = get_object_or_404(coa_model_qs, slug__exact=self.kwargs['coa_slug'])
|
|
73
|
-
else:
|
|
74
|
-
context['coa_model'] = entity_model.default_coa
|
|
64
|
+
def get_context_data(self, **kwargs):
|
|
65
|
+
context = super().get_context_data(**kwargs)
|
|
66
|
+
context['coa_model'] = self.get_coa_model()
|
|
75
67
|
return context
|
|
76
68
|
|
|
77
69
|
|
|
78
70
|
# Account Views ----
|
|
79
|
-
class AccountModelListView(
|
|
71
|
+
class AccountModelListView(BaseAccountModelBaseView, ListView):
|
|
80
72
|
template_name = 'django_ledger/account/account_list.html'
|
|
81
73
|
context_object_name = 'accounts'
|
|
82
74
|
PAGE_TITLE = _('Entity Accounts')
|
|
@@ -93,40 +85,7 @@ class AccountModelListView(DjangoLedgerSecurityMixIn, BaseAccountModelViewQueryS
|
|
|
93
85
|
return qs
|
|
94
86
|
|
|
95
87
|
|
|
96
|
-
class
|
|
97
|
-
context_object_name = 'account'
|
|
98
|
-
template_name = 'django_ledger/account/account_update.html'
|
|
99
|
-
slug_url_kwarg = 'account_pk'
|
|
100
|
-
slug_field = 'uuid'
|
|
101
|
-
|
|
102
|
-
def get_context_data(self, **kwargs):
|
|
103
|
-
context = super().get_context_data(**kwargs)
|
|
104
|
-
context['page_title'] = _('Update Account')
|
|
105
|
-
context['header_title'] = _(f'Update Account: {self.object.code} - {self.object.name}')
|
|
106
|
-
context['header_subtitle_icon'] = 'ic:twotone-account-tree'
|
|
107
|
-
return context
|
|
108
|
-
|
|
109
|
-
def get_form(self, form_class=None):
|
|
110
|
-
account_model = self.object
|
|
111
|
-
|
|
112
|
-
# Set here because user_model is needed to instantiate an instance of MoveNodeForm (AccountModelUpdateForm)
|
|
113
|
-
account_model.USER_MODEL = self.request.user
|
|
114
|
-
return AccountModelUpdateForm(
|
|
115
|
-
entity_model=self.AUTHORIZED_ENTITY_MODEL,
|
|
116
|
-
coa_model=self.get_coa_model(),
|
|
117
|
-
user_model=self.request.user,
|
|
118
|
-
**self.get_form_kwargs()
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
def get_success_url(self):
|
|
122
|
-
entity_slug = self.kwargs['entity_slug']
|
|
123
|
-
return reverse('django_ledger:account-list',
|
|
124
|
-
kwargs={
|
|
125
|
-
'entity_slug': entity_slug,
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
class AccountModelCreateView(DjangoLedgerSecurityMixIn, BaseAccountModelViewQuerySetMixIn, CreateView):
|
|
88
|
+
class AccountModelCreateView(BaseAccountModelBaseView, CreateView):
|
|
130
89
|
template_name = 'django_ledger/account/account_create.html'
|
|
131
90
|
PAGE_TITLE = _('Create Account')
|
|
132
91
|
extra_context = {
|
|
@@ -138,58 +97,63 @@ class AccountModelCreateView(DjangoLedgerSecurityMixIn, BaseAccountModelViewQuer
|
|
|
138
97
|
|
|
139
98
|
def get_form(self, form_class=None):
|
|
140
99
|
return AccountModelCreateForm(
|
|
141
|
-
user_model=self.request.user,
|
|
142
|
-
entity_model=self.AUTHORIZED_ENTITY_MODEL,
|
|
143
100
|
coa_model=self.get_coa_model(),
|
|
144
101
|
**self.get_form_kwargs()
|
|
145
102
|
)
|
|
146
103
|
|
|
147
104
|
def get_context_data(self, *args, **kwargs):
|
|
148
105
|
context = super().get_context_data(*args, **kwargs)
|
|
149
|
-
|
|
106
|
+
coa_model = self.get_coa_model()
|
|
107
|
+
context['coa_model'] = coa_model
|
|
108
|
+
context['header_subtitle'] = f'CoA: {coa_model.name}'
|
|
150
109
|
return context
|
|
151
110
|
|
|
152
|
-
def form_valid(self, form):
|
|
153
|
-
entity_model = self.AUTHORIZED_ENTITY_MODEL
|
|
111
|
+
def form_valid(self, form: AccountModelCreateForm):
|
|
154
112
|
account_model: AccountModel = form.save(commit=False)
|
|
155
|
-
|
|
156
|
-
if not entity_model.has_default_coa():
|
|
157
|
-
entity_model.create_chart_of_accounts(assign_as_default=True, commit=True)
|
|
158
|
-
|
|
159
|
-
coa_model = self.get_coa_model()
|
|
113
|
+
coa_model = account_model.coa_model
|
|
160
114
|
coa_model.insert_account(account_model=account_model)
|
|
161
115
|
return HttpResponseRedirect(self.get_success_url())
|
|
162
116
|
|
|
163
117
|
def get_success_url(self):
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
118
|
+
coa_model: ChartOfAccountModel = self.get_coa_model()
|
|
119
|
+
return coa_model.get_account_list_url()
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class AccountModelUpdateView(BaseAccountModelBaseView, UpdateView):
|
|
123
|
+
context_object_name = 'account'
|
|
124
|
+
template_name = 'django_ledger/account/account_update.html'
|
|
125
|
+
slug_url_kwarg = 'account_pk'
|
|
126
|
+
slug_field = 'uuid'
|
|
127
|
+
form_class = AccountModelUpdateForm
|
|
128
|
+
|
|
129
|
+
def get_context_data(self, **kwargs):
|
|
130
|
+
context = super().get_context_data(**kwargs)
|
|
131
|
+
context['page_title'] = _('Update Account')
|
|
132
|
+
context['header_title'] = _(f'Update Account: {self.object.code} - {self.object.name}')
|
|
133
|
+
context['header_subtitle_icon'] = 'ic:twotone-account-tree'
|
|
134
|
+
return context
|
|
135
|
+
|
|
136
|
+
def get_success_url(self):
|
|
137
|
+
coa_model: ChartOfAccountModel = self.get_coa_model()
|
|
138
|
+
return coa_model.get_account_list_url()
|
|
176
139
|
|
|
177
140
|
|
|
178
|
-
class AccountModelDetailView(
|
|
141
|
+
class AccountModelDetailView(BaseAccountModelBaseView, RedirectView):
|
|
179
142
|
|
|
180
143
|
def get_redirect_url(self, *args, **kwargs):
|
|
181
144
|
loc_date = get_localdate()
|
|
145
|
+
entity_model: EntityModel = self.get_authorized_entity_instance()
|
|
182
146
|
return reverse('django_ledger:account-detail-month',
|
|
183
147
|
kwargs={
|
|
184
|
-
'entity_slug':
|
|
148
|
+
'entity_slug': entity_model.slug,
|
|
185
149
|
'account_pk': self.kwargs['account_pk'],
|
|
150
|
+
'coa_slug': self.kwargs['coa_slug'],
|
|
186
151
|
'year': loc_date.year,
|
|
187
152
|
'month': loc_date.month,
|
|
188
153
|
})
|
|
189
154
|
|
|
190
155
|
|
|
191
|
-
class AccountModelYearDetailView(
|
|
192
|
-
BaseAccountModelViewQuerySetMixIn,
|
|
156
|
+
class AccountModelYearDetailView(BaseAccountModelBaseView,
|
|
193
157
|
BaseDateNavigationUrlMixIn,
|
|
194
158
|
EntityUnitMixIn,
|
|
195
159
|
YearlyReportMixIn,
|
|
@@ -205,23 +169,23 @@ class AccountModelYearDetailView(DjangoLedgerSecurityMixIn,
|
|
|
205
169
|
}
|
|
206
170
|
|
|
207
171
|
def get_context_data(self, **kwargs):
|
|
208
|
-
account = self.object
|
|
209
172
|
context = super().get_context_data(**kwargs)
|
|
210
|
-
context['
|
|
211
|
-
context['
|
|
212
|
-
|
|
173
|
+
account_model: AccountModel = context['object']
|
|
174
|
+
context['header_title'] = f'Account {account_model.code} - {account_model.name}'
|
|
175
|
+
context['page_title'] = f'Account {account_model.code} - {account_model.name}'
|
|
213
176
|
txs_qs = account_model.transactionmodel_set.all().posted().order_by(
|
|
214
|
-
'journal_entry__timestamp'
|
|
215
|
-
|
|
177
|
+
'journal_entry__timestamp'
|
|
178
|
+
).select_related(
|
|
179
|
+
'journal_entry',
|
|
180
|
+
'journal_entry__entity_unit',
|
|
181
|
+
'journal_entry__ledger__billmodel',
|
|
182
|
+
'journal_entry__ledger__invoicemodel',
|
|
183
|
+
)
|
|
216
184
|
txs_qs = txs_qs.from_date(self.get_from_date())
|
|
217
185
|
txs_qs = txs_qs.to_date(self.get_to_date())
|
|
218
186
|
context['transactions'] = txs_qs
|
|
219
187
|
return context
|
|
220
188
|
|
|
221
|
-
def get_queryset(self):
|
|
222
|
-
qs = super().get_queryset()
|
|
223
|
-
return qs.prefetch_related('transactionmodel_set')
|
|
224
|
-
|
|
225
189
|
|
|
226
190
|
class AccountModelQuarterDetailView(QuarterlyReportMixIn, AccountModelYearDetailView):
|
|
227
191
|
"""
|
|
@@ -242,9 +206,8 @@ class AccountModelDateDetailView(DateReportMixIn, AccountModelYearDetailView):
|
|
|
242
206
|
|
|
243
207
|
|
|
244
208
|
# ACTIONS...
|
|
245
|
-
class AccountModelModelActionView(
|
|
209
|
+
class AccountModelModelActionView(BaseAccountModelBaseView,
|
|
246
210
|
RedirectView,
|
|
247
|
-
BaseAccountModelViewQuerySetMixIn,
|
|
248
211
|
SingleObjectMixin):
|
|
249
212
|
http_method_names = ['get']
|
|
250
213
|
pk_url_kwarg = 'account_pk'
|
|
@@ -252,11 +215,8 @@ class AccountModelModelActionView(DjangoLedgerSecurityMixIn,
|
|
|
252
215
|
commit = True
|
|
253
216
|
|
|
254
217
|
def get_redirect_url(self, *args, **kwargs):
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
'entity_slug': kwargs['entity_slug'],
|
|
258
|
-
# 'account_pk': kwargs['account_pk']
|
|
259
|
-
})
|
|
218
|
+
account_model: AccountModel = self.get_object()
|
|
219
|
+
return account_model.get_coa_account_list_url()
|
|
260
220
|
|
|
261
221
|
def get(self, request, *args, **kwargs):
|
|
262
222
|
kwargs['user_model'] = self.request.user
|
|
@@ -268,8 +228,10 @@ class AccountModelModelActionView(DjangoLedgerSecurityMixIn,
|
|
|
268
228
|
try:
|
|
269
229
|
getattr(account_model, self.action_name)(commit=self.commit, **kwargs)
|
|
270
230
|
except ValidationError as e:
|
|
271
|
-
messages.add_message(
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
231
|
+
messages.add_message(
|
|
232
|
+
request,
|
|
233
|
+
message=e.message,
|
|
234
|
+
level=messages.ERROR,
|
|
235
|
+
extra_tags='is-danger'
|
|
236
|
+
)
|
|
275
237
|
return response
|
django_ledger/views/auth.py
CHANGED
|
@@ -3,7 +3,7 @@ Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
|
|
|
3
3
|
Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
|
|
4
4
|
|
|
5
5
|
Contributions to this module:
|
|
6
|
-
Miguel Sanda <msanda@arrobalytics.com>
|
|
6
|
+
* Miguel Sanda <msanda@arrobalytics.com>
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
from django.contrib.auth.views import LoginView, LogoutView
|
|
@@ -3,7 +3,7 @@ Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
|
|
|
3
3
|
Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
|
|
4
4
|
|
|
5
5
|
Contributions to this module:
|
|
6
|
-
Miguel Sanda <msanda@arrobalytics.com>
|
|
6
|
+
* Miguel Sanda <msanda@arrobalytics.com>
|
|
7
7
|
"""
|
|
8
8
|
from django.contrib import messages
|
|
9
9
|
from django.core.exceptions import ImproperlyConfigured, ValidationError
|
|
@@ -13,23 +13,22 @@ from django.views.generic import ListView, CreateView, UpdateView, RedirectView
|
|
|
13
13
|
from django.views.generic.detail import SingleObjectMixin
|
|
14
14
|
|
|
15
15
|
from django_ledger.forms.bank_account import BankAccountCreateForm, BankAccountUpdateForm
|
|
16
|
+
from django_ledger.models import EntityModel
|
|
16
17
|
from django_ledger.models.bank_account import BankAccountModel
|
|
17
18
|
from django_ledger.views.mixins import DjangoLedgerSecurityMixIn
|
|
18
19
|
|
|
19
20
|
|
|
20
|
-
class
|
|
21
|
+
class BankAccountModelModelBaseView(DjangoLedgerSecurityMixIn):
|
|
21
22
|
queryset = None
|
|
22
23
|
|
|
23
24
|
def get_queryset(self):
|
|
24
25
|
if self.queryset is None:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
user_model=self.request.user
|
|
28
|
-
).select_related('cash_account', 'entity_model')
|
|
26
|
+
entity_model: EntityModel = self.get_authorized_entity_instance()
|
|
27
|
+
self.queryset = entity_model.bankaccountmodel_set.select_related('cash_account', 'entity_model')
|
|
29
28
|
return super().get_queryset()
|
|
30
29
|
|
|
31
30
|
|
|
32
|
-
class BankAccountModelListView(
|
|
31
|
+
class BankAccountModelListView(BankAccountModelModelBaseView, ListView):
|
|
33
32
|
template_name = 'django_ledger/bank_account/bank_account_list.html'
|
|
34
33
|
PAGE_TITLE = _('Bank Accounts')
|
|
35
34
|
context_object_name = 'bank_accounts'
|
|
@@ -40,7 +39,7 @@ class BankAccountModelListView(DjangoLedgerSecurityMixIn, BankAccountModelModelV
|
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
|
|
43
|
-
class BankAccountModelCreateView(
|
|
42
|
+
class BankAccountModelCreateView(BankAccountModelModelBaseView, CreateView):
|
|
44
43
|
template_name = 'django_ledger/bank_account/bank_account_create.html'
|
|
45
44
|
PAGE_TITLE = _('Create Bank Account')
|
|
46
45
|
extra_context = {
|
|
@@ -71,7 +70,7 @@ class BankAccountModelCreateView(DjangoLedgerSecurityMixIn, BankAccountModelMode
|
|
|
71
70
|
return super(BankAccountModelCreateView, self).form_valid(form)
|
|
72
71
|
|
|
73
72
|
|
|
74
|
-
class BankAccountModelUpdateView(
|
|
73
|
+
class BankAccountModelUpdateView(BankAccountModelModelBaseView, UpdateView):
|
|
75
74
|
template_name = 'django_ledger/bank_account/bank_account_update.html'
|
|
76
75
|
pk_url_kwarg = 'bank_account_pk'
|
|
77
76
|
PAGE_TITLE = _('Update Bank Account')
|
|
@@ -97,8 +96,7 @@ class BankAccountModelUpdateView(DjangoLedgerSecurityMixIn, BankAccountModelMode
|
|
|
97
96
|
|
|
98
97
|
|
|
99
98
|
# ACTION VIEWS...
|
|
100
|
-
class BaseBankAccountModelActionView(
|
|
101
|
-
BankAccountModelModelViewQuerySetMixIn,
|
|
99
|
+
class BaseBankAccountModelActionView(BankAccountModelModelBaseView,
|
|
102
100
|
RedirectView,
|
|
103
101
|
SingleObjectMixin):
|
|
104
102
|
http_method_names = ['get']
|