django-ledger 0.7.11__py3-none-any.whl → 0.8.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of django-ledger might be problematic. Click here for more details.
- django_ledger/__init__.py +1 -1
- django_ledger/context.py +12 -0
- django_ledger/forms/account.py +45 -46
- django_ledger/forms/bill.py +0 -4
- django_ledger/forms/closing_entry.py +13 -1
- django_ledger/forms/data_import.py +182 -63
- 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 +515 -400
- 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/migrations/0026_stagedtransactionmodel_customer_model_and_more.py +56 -0
- django_ledger/models/__init__.py +2 -1
- django_ledger/models/accounts.py +109 -69
- django_ledger/models/bank_account.py +40 -23
- django_ledger/models/bill.py +386 -333
- django_ledger/models/chart_of_accounts.py +173 -105
- django_ledger/models/closing_entry.py +99 -48
- django_ledger/models/customer.py +100 -66
- django_ledger/models/data_import.py +818 -323
- django_ledger/models/deprecations.py +61 -0
- django_ledger/models/entity.py +891 -644
- 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 +424 -281
- django_ledger/models/purchase_order.py +39 -17
- django_ledger/models/receipt.py +1083 -0
- django_ledger/models/transactions.py +242 -139
- django_ledger/models/unit.py +93 -54
- django_ledger/models/utils.py +12 -2
- django_ledger/models/vendor.py +121 -70
- django_ledger/report/core.py +2 -14
- django_ledger/settings.py +57 -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/components/period_navigator.html +5 -3
- django_ledger/templates/django_ledger/customer/customer_detail.html +87 -0
- django_ledger/templates/django_ledger/customer/customer_list.html +0 -1
- django_ledger/templates/django_ledger/customer/tags/customer_table.html +8 -6
- django_ledger/templates/django_ledger/data_import/tags/data_import_job_txs_imported.html +24 -3
- django_ledger/templates/django_ledger/data_import/tags/data_import_job_txs_table.html +26 -10
- django_ledger/templates/django_ledger/entity/entity_dashboard.html +2 -2
- 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 +7 -2
- django_ledger/templates/django_ledger/layouts/content_layout_1.html +1 -1
- django_ledger/templates/django_ledger/receipt/customer_receipt_report.html +115 -0
- django_ledger/templates/django_ledger/receipt/receipt_delete.html +30 -0
- django_ledger/templates/django_ledger/receipt/receipt_detail.html +89 -0
- django_ledger/templates/django_ledger/receipt/receipt_list.html +134 -0
- django_ledger/templates/django_ledger/receipt/vendor_receipt_report.html +115 -0
- django_ledger/templates/django_ledger/vendor/tags/vendor_table.html +12 -7
- django_ledger/templates/django_ledger/vendor/vendor_detail.html +86 -0
- django_ledger/templatetags/django_ledger.py +338 -191
- 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 +1 -4
- django_ledger/urls/customer.py +3 -0
- django_ledger/urls/data_import.py +3 -0
- django_ledger/urls/receipt.py +102 -0
- django_ledger/urls/vendor.py +1 -0
- django_ledger/views/__init__.py +1 -0
- 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 +68 -30
- django_ledger/views/data_import.py +120 -66
- 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 +125 -93
- django_ledger/views/purchase_order.py +24 -35
- django_ledger/views/receipt.py +294 -0
- django_ledger/views/unit.py +1 -2
- django_ledger/views/vendor.py +54 -16
- {django_ledger-0.7.11.dist-info → django_ledger-0.8.1.dist-info}/METADATA +43 -75
- {django_ledger-0.7.11.dist-info → django_ledger-0.8.1.dist-info}/RECORD +104 -122
- {django_ledger-0.7.11.dist-info → django_ledger-0.8.1.dist-info}/WHEEL +1 -1
- django_ledger-0.8.1.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.1.dist-info/licenses}/AUTHORS.md +0 -0
- {django_ledger-0.7.11.dist-info → django_ledger-0.8.1.dist-info/licenses}/LICENSE +0 -0
|
@@ -95,8 +95,7 @@ class AccountModelTests(DjangoLedgerBaseTest):
|
|
|
95
95
|
self.assertEqual(response_create.status_code, 302)
|
|
96
96
|
self.assertTrue(AccountModel.objects.for_entity(
|
|
97
97
|
entity_model=entity_model,
|
|
98
|
-
|
|
99
|
-
coa_slug=entity_model.default_coa_slug,
|
|
98
|
+
coa_model=entity_model.default_coa_slug,
|
|
100
99
|
).with_codes(codes=NEW_ACCOUNT_CODE).exists())
|
|
101
100
|
|
|
102
101
|
# cannot create an account with same code again...
|
django_ledger/tests/test_io.py
CHANGED
|
@@ -195,3 +195,20 @@ class IOTest(DjangoLedgerBaseTest):
|
|
|
195
195
|
# )
|
|
196
196
|
#
|
|
197
197
|
# self.assertEqual(io_digest.get_io_txs_queryset().count(), 0)
|
|
198
|
+
|
|
199
|
+
def test_io_transactions_belong_to_entity(self):
|
|
200
|
+
entity_model = self.get_random_entity_model()
|
|
201
|
+
from_datetime = self.START_DATE
|
|
202
|
+
to_datetime = self.START_DATE + timedelta(days=randint(10, 60))
|
|
203
|
+
|
|
204
|
+
io_digest = entity_model.digest(
|
|
205
|
+
entity_slug=entity_model.slug,
|
|
206
|
+
from_date=from_datetime,
|
|
207
|
+
to_date=to_datetime,
|
|
208
|
+
for_test=True
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
tx_qs = io_digest.get_io_txs_queryset()
|
|
212
|
+
# Every transaction returned by the IO for an entity digest must belong to that entity.
|
|
213
|
+
for tx in tx_qs:
|
|
214
|
+
self.assertEqual(tx['journal_entry__ledger__entity_id'], entity_model.uuid)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from datetime import date, datetime
|
|
2
2
|
from random import choice, randint
|
|
3
|
-
from typing import Union, Optional
|
|
3
|
+
from typing import Union, Optional, List
|
|
4
4
|
from urllib.parse import urlparse
|
|
5
5
|
from uuid import uuid4
|
|
6
6
|
|
|
@@ -31,8 +31,8 @@ class PurchaseOrderModelTests(DjangoLedgerBaseTest):
|
|
|
31
31
|
commit=True)
|
|
32
32
|
return po_model
|
|
33
33
|
|
|
34
|
-
def get_purchase_orders(self, entity_model: EntityModel) ->
|
|
35
|
-
return PurchaseOrderModel.objects.for_entity(entity_model
|
|
34
|
+
def get_purchase_orders(self, entity_model: EntityModel) -> List[PurchaseOrderModel]:
|
|
35
|
+
return PurchaseOrderModel.objects.for_entity(entity_model=entity_model)
|
|
36
36
|
|
|
37
37
|
def test_protected_views(self):
|
|
38
38
|
"""
|
|
@@ -96,8 +96,7 @@ class TransactionModelFormSetTest(DjangoLedgerBaseTest):
|
|
|
96
96
|
|
|
97
97
|
# get a journal entry that has transactions...
|
|
98
98
|
je_model = JournalEntryModel.objects.for_entity(
|
|
99
|
-
|
|
100
|
-
user_model=self.user_model
|
|
99
|
+
entity_model=entity_model
|
|
101
100
|
).annotate(
|
|
102
101
|
txs_count=Count('transactionmodel')).filter(
|
|
103
102
|
txs_count__gt=0).order_by('-timestamp').first()
|
django_ledger/urls/__init__.py
CHANGED
|
@@ -5,11 +5,9 @@ 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
|
-
|
|
9
8
|
from django.urls import path, include
|
|
10
9
|
|
|
11
10
|
from django_ledger import views
|
|
12
|
-
from django_ledger.settings import DJANGO_LEDGER_GRAPHQL_SUPPORT_ENABLED
|
|
13
11
|
|
|
14
12
|
app_name = 'django_ledger'
|
|
15
13
|
|
|
@@ -37,9 +35,8 @@ urlpatterns = [
|
|
|
37
35
|
path('feedback/', include('django_ledger.urls.feedback')),
|
|
38
36
|
path('inventory/', include('django_ledger.urls.inventory')),
|
|
39
37
|
path('home/', include('django_ledger.urls.home')),
|
|
38
|
+
path('receipt/', include('django_ledger.urls.receipt')),
|
|
40
39
|
path('api/', include('django_ledger.urls.djl_api')),
|
|
41
40
|
path('', views.RootUrlView.as_view(), name='root'),
|
|
42
41
|
]
|
|
43
42
|
|
|
44
|
-
if DJANGO_LEDGER_GRAPHQL_SUPPORT_ENABLED:
|
|
45
|
-
pass
|
django_ledger/urls/customer.py
CHANGED
|
@@ -7,4 +7,7 @@ urlpatterns = [
|
|
|
7
7
|
path('<slug:entity_slug>/update/<uuid:customer_pk>/',
|
|
8
8
|
views.CustomerModelUpdateView.as_view(),
|
|
9
9
|
name='customer-update'),
|
|
10
|
+
path('<slug:entity_slug>/detail/<uuid:customer_pk>/',
|
|
11
|
+
views.CustomerModelDetailView.as_view(),
|
|
12
|
+
name='customer-detail'),
|
|
10
13
|
]
|
|
@@ -18,4 +18,7 @@ urlpatterns = [
|
|
|
18
18
|
path('<slug:entity_slug>/jobs/<uuid:job_pk>/txs/',
|
|
19
19
|
views.DataImportJobDetailView.as_view(),
|
|
20
20
|
name='data-import-job-txs'),
|
|
21
|
+
path('<slug:entity_slug>/jobs/<uuid:job_pk>/txs/<uuid:staged_tx_pk>/undo/',
|
|
22
|
+
views.StagedTransactionUndoView.as_view(),
|
|
23
|
+
name='data-import-staged-tx-undo'),
|
|
21
24
|
]
|
|
@@ -0,0 +1,102 @@
|
|
|
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
|
+
|
|
9
|
+
from django.urls import path
|
|
10
|
+
|
|
11
|
+
from django_ledger import views
|
|
12
|
+
|
|
13
|
+
urlpatterns = [
|
|
14
|
+
path(
|
|
15
|
+
'<slug:entity_slug>/latest/',
|
|
16
|
+
views.ReceiptModelListView.as_view(),
|
|
17
|
+
name='receipt-list',
|
|
18
|
+
),
|
|
19
|
+
path(
|
|
20
|
+
'<slug:entity_slug>/year/<int:year>/',
|
|
21
|
+
views.ReceiptModelYearListView.as_view(),
|
|
22
|
+
name='receipt-list-year',
|
|
23
|
+
),
|
|
24
|
+
path(
|
|
25
|
+
'<slug:entity_slug>/quarter/<int:year>/q<int:quarter>/',
|
|
26
|
+
views.ReceiptModelQuarterListView.as_view(),
|
|
27
|
+
name='receipt-list-quarter',
|
|
28
|
+
),
|
|
29
|
+
path(
|
|
30
|
+
'<slug:entity_slug>/month/<int:year>/<int:month>/',
|
|
31
|
+
views.ReceiptModelMonthListView.as_view(),
|
|
32
|
+
name='receipt-list-month',
|
|
33
|
+
),
|
|
34
|
+
path(
|
|
35
|
+
'<slug:entity_slug>/detail/<uuid:receipt_pk>/',
|
|
36
|
+
views.ReceiptModelDetailView.as_view(),
|
|
37
|
+
name='receipt-detail',
|
|
38
|
+
),
|
|
39
|
+
path(
|
|
40
|
+
'<slug:entity_slug>/delete/<uuid:receipt_pk>/',
|
|
41
|
+
views.ReceiptModelDeleteView.as_view(),
|
|
42
|
+
name='receipt-delete',
|
|
43
|
+
),
|
|
44
|
+
# Filtered lists
|
|
45
|
+
path(
|
|
46
|
+
'<slug:entity_slug>/type/<str:receipt_type>/',
|
|
47
|
+
views.ReceiptModelListView.as_view(),
|
|
48
|
+
name='receipt-list-type',
|
|
49
|
+
),
|
|
50
|
+
path(
|
|
51
|
+
'<slug:entity_slug>/vendor/<uuid:vendor_pk>/',
|
|
52
|
+
views.ReceiptModelListView.as_view(),
|
|
53
|
+
name='receipt-list-vendor',
|
|
54
|
+
),
|
|
55
|
+
path(
|
|
56
|
+
'<slug:entity_slug>/customer/<uuid:customer_pk>/',
|
|
57
|
+
views.ReceiptModelListView.as_view(),
|
|
58
|
+
name='receipt-list-customer',
|
|
59
|
+
),
|
|
60
|
+
# Reports: Vendor
|
|
61
|
+
path(
|
|
62
|
+
'<slug:entity_slug>/report/vendor/<uuid:vendor_pk>/latest/',
|
|
63
|
+
views.VendorReceiptReportListView.as_view(),
|
|
64
|
+
name='receipt-report-vendor',
|
|
65
|
+
),
|
|
66
|
+
path(
|
|
67
|
+
'<slug:entity_slug>/report/vendor/<uuid:vendor_pk>/year/<int:year>/',
|
|
68
|
+
views.VendorReceiptReportYearListView.as_view(),
|
|
69
|
+
name='receipt-report-vendor-year',
|
|
70
|
+
),
|
|
71
|
+
path(
|
|
72
|
+
'<slug:entity_slug>/report/vendor/<uuid:vendor_pk>/quarter/<int:year>/q<int:quarter>/',
|
|
73
|
+
views.VendorReceiptReportQuarterListView.as_view(),
|
|
74
|
+
name='receipt-report-vendor-quarter',
|
|
75
|
+
),
|
|
76
|
+
path(
|
|
77
|
+
'<slug:entity_slug>/report/vendor/<uuid:vendor_pk>/month/<int:year>/<int:month>/',
|
|
78
|
+
views.VendorReceiptReportMonthListView.as_view(),
|
|
79
|
+
name='receipt-report-vendor-month',
|
|
80
|
+
),
|
|
81
|
+
# Reports: Customer
|
|
82
|
+
path(
|
|
83
|
+
'<slug:entity_slug>/report/customer/<uuid:customer_pk>/latest/',
|
|
84
|
+
views.CustomerReceiptReportListView.as_view(),
|
|
85
|
+
name='receipt-report-customer',
|
|
86
|
+
),
|
|
87
|
+
path(
|
|
88
|
+
'<slug:entity_slug>/report/customer/<uuid:customer_pk>/year/<int:year>/',
|
|
89
|
+
views.CustomerReceiptReportYearListView.as_view(),
|
|
90
|
+
name='receipt-report-customer-year',
|
|
91
|
+
),
|
|
92
|
+
path(
|
|
93
|
+
'<slug:entity_slug>/report/customer/<uuid:customer_pk>/quarter/<int:year>/q<int:quarter>/',
|
|
94
|
+
views.CustomerReceiptReportQuarterListView.as_view(),
|
|
95
|
+
name='receipt-report-customer-quarter',
|
|
96
|
+
),
|
|
97
|
+
path(
|
|
98
|
+
'<slug:entity_slug>/report/customer/<uuid:customer_pk>/month/<int:year>/<int:month>/',
|
|
99
|
+
views.CustomerReceiptReportMonthListView.as_view(),
|
|
100
|
+
name='receipt-report-customer-month',
|
|
101
|
+
),
|
|
102
|
+
]
|
django_ledger/urls/vendor.py
CHANGED
|
@@ -5,4 +5,5 @@ urlpatterns = [
|
|
|
5
5
|
path('<slug:entity_slug>/list/', views.VendorModelListView.as_view(), name='vendor-list'),
|
|
6
6
|
path('<slug:entity_slug>/create/', views.VendorModelCreateView.as_view(), name='vendor-create'),
|
|
7
7
|
path('<slug:entity_slug>/update/<uuid:vendor_pk>/', views.VendorModelUpdateView.as_view(), name='vendor-update'),
|
|
8
|
+
path('<slug:entity_slug>/detail/<uuid:vendor_pk>/', views.VendorModelDetailView.as_view(), name='vendor-detail'),
|
|
8
9
|
]
|
django_ledger/views/__init__.py
CHANGED
django_ledger/views/bill.py
CHANGED
|
@@ -64,8 +64,7 @@ class BillModelCreateView(BillModelModelBaseView, CreateView):
|
|
|
64
64
|
|
|
65
65
|
if self.for_estimate and 'ce_pk' in self.kwargs:
|
|
66
66
|
estimate_qs = EstimateModel.objects.for_entity(
|
|
67
|
-
|
|
68
|
-
user_model=self.request.user
|
|
67
|
+
entity_model=self.AUTHORIZED_ENTITY_MODEL,
|
|
69
68
|
)
|
|
70
69
|
estimate_model: EstimateModel = get_object_or_404(estimate_qs, uuid__exact=self.kwargs['ce_pk'])
|
|
71
70
|
if not estimate_model.can_bind():
|
|
@@ -87,8 +86,7 @@ class BillModelCreateView(BillModelModelBaseView, CreateView):
|
|
|
87
86
|
return HttpResponseBadRequest()
|
|
88
87
|
|
|
89
88
|
po_qs = PurchaseOrderModel.objects.for_entity(
|
|
90
|
-
|
|
91
|
-
user_model=self.request.user
|
|
89
|
+
entity_model=self.kwargs['entity_slug'],
|
|
92
90
|
).prefetch_related('itemtransactionmodel_set')
|
|
93
91
|
po_model: PurchaseOrderModel = get_object_or_404(po_qs, uuid__exact=po_pk)
|
|
94
92
|
po_itemtxs_qs = po_model.itemtransactionmodel_set.filter(
|
|
@@ -104,8 +102,7 @@ class BillModelCreateView(BillModelModelBaseView, CreateView):
|
|
|
104
102
|
}) + f'?item_uuids={po_item_uuids_qry_param}'
|
|
105
103
|
elif self.for_estimate:
|
|
106
104
|
estimate_qs = EstimateModel.objects.for_entity(
|
|
107
|
-
|
|
108
|
-
user_model=self.request.user
|
|
105
|
+
entity_model=self.AUTHORIZED_ENTITY_MODEL
|
|
109
106
|
)
|
|
110
107
|
estimate_uuid = self.kwargs['ce_pk']
|
|
111
108
|
estimate_model: EstimateModel = get_object_or_404(estimate_qs, uuid__exact=estimate_uuid)
|
|
@@ -137,15 +134,16 @@ class BillModelCreateView(BillModelModelBaseView, CreateView):
|
|
|
137
134
|
def form_valid(self, form):
|
|
138
135
|
bill_model: BillModel = form.save(commit=False)
|
|
139
136
|
ledger_model, bill_model = bill_model.configure(
|
|
140
|
-
entity_slug=self.AUTHORIZED_ENTITY_MODEL,
|
|
137
|
+
entity_slug=self.AUTHORIZED_ENTITY_MODEL.slug,
|
|
138
|
+
user_model=self.request.user,
|
|
141
139
|
commit_ledger=True
|
|
142
140
|
)
|
|
143
141
|
|
|
144
142
|
if self.for_estimate:
|
|
145
143
|
ce_pk = self.kwargs['ce_pk']
|
|
146
144
|
estimate_model_qs = EstimateModel.objects.for_entity(
|
|
147
|
-
|
|
148
|
-
|
|
145
|
+
entity_model=self.kwargs['entity_slug']
|
|
146
|
+
)
|
|
149
147
|
|
|
150
148
|
estimate_model = get_object_or_404(estimate_model_qs, uuid__exact=ce_pk)
|
|
151
149
|
bill_model.bind_estimate(estimate_model=estimate_model, commit=False)
|
|
@@ -157,8 +155,7 @@ class BillModelCreateView(BillModelModelBaseView, CreateView):
|
|
|
157
155
|
return HttpResponseBadRequest()
|
|
158
156
|
item_uuids = item_uuids.split(',')
|
|
159
157
|
po_qs = PurchaseOrderModel.objects.for_entity(
|
|
160
|
-
|
|
161
|
-
user_model=self.request.user
|
|
158
|
+
entity_model=self.kwargs['entity_slug'],
|
|
162
159
|
)
|
|
163
160
|
po_model: PurchaseOrderModel = get_object_or_404(po_qs, uuid__exact=po_pk)
|
|
164
161
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
|
|
3
3
|
Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
|
|
4
4
|
"""
|
|
5
|
+
from typing import Optional
|
|
5
6
|
|
|
6
7
|
from django.contrib import messages
|
|
7
8
|
from django.core.exceptions import ImproperlyConfigured, ValidationError
|
|
@@ -10,18 +11,19 @@ from django.views.generic import UpdateView, ListView, RedirectView, CreateView
|
|
|
10
11
|
from django.views.generic.detail import SingleObjectMixin
|
|
11
12
|
|
|
12
13
|
from django_ledger.forms.chart_of_accounts import ChartOfAccountsModelUpdateForm, ChartOfAccountsModelCreateForm
|
|
14
|
+
from django_ledger.models import EntityModel, ChartOfAccountModelQuerySet
|
|
13
15
|
from django_ledger.models.chart_of_accounts import ChartOfAccountModel
|
|
14
16
|
from django_ledger.views.mixins import DjangoLedgerSecurityMixIn
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
class ChartOfAccountModelModelBaseViewMixIn(DjangoLedgerSecurityMixIn):
|
|
18
|
-
queryset = None
|
|
20
|
+
queryset: Optional[ChartOfAccountModelQuerySet] = None
|
|
19
21
|
|
|
20
22
|
def get_queryset(self):
|
|
21
23
|
if self.queryset is None:
|
|
22
|
-
entity_model = self.get_authorized_entity_instance()
|
|
23
|
-
self.queryset = entity_model.chartofaccountmodel_set.all().order_by('-updated')
|
|
24
|
-
return
|
|
24
|
+
entity_model: EntityModel = self.get_authorized_entity_instance()
|
|
25
|
+
self.queryset: ChartOfAccountModelQuerySet = entity_model.chartofaccountmodel_set.all().order_by('-updated')
|
|
26
|
+
return self.queryset
|
|
25
27
|
|
|
26
28
|
|
|
27
29
|
class ChartOfAccountModelListView(ChartOfAccountModelModelBaseViewMixIn, ListView):
|
|
@@ -5,6 +5,7 @@ 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
|
|
8
9
|
|
|
9
10
|
from django.contrib import messages
|
|
10
11
|
from django.core.exceptions import ValidationError, ImproperlyConfigured
|
|
@@ -21,13 +22,14 @@ from django.views.generic.detail import SingleObjectMixin
|
|
|
21
22
|
|
|
22
23
|
from django_ledger.forms.closing_entry import ClosingEntryCreateForm, ClosingEntryUpdateForm
|
|
23
24
|
from django_ledger.io.io_core import get_localdate
|
|
25
|
+
from django_ledger.models import EstimateModel, ClosingEntryModelQuerySet
|
|
24
26
|
from django_ledger.models.closing_entry import ClosingEntryModel
|
|
25
27
|
from django_ledger.models.entity import EntityModel
|
|
26
28
|
from django_ledger.views import DjangoLedgerSecurityMixIn
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
class ClosingEntryModelBaseView(DjangoLedgerSecurityMixIn):
|
|
30
|
-
queryset = None
|
|
32
|
+
queryset: Optional[ClosingEntryModelQuerySet] = None
|
|
31
33
|
|
|
32
34
|
def get_queryset(self):
|
|
33
35
|
if self.queryset is None:
|
|
@@ -75,16 +77,18 @@ class ClosingEntryModelCreateView(ClosingEntryModelBaseView, CreateView):
|
|
|
75
77
|
'header_title': PAGE_TITLE,
|
|
76
78
|
'header_subtitle_icon': 'file-icons:finaldraft'
|
|
77
79
|
}
|
|
80
|
+
form_class = ClosingEntryCreateForm
|
|
78
81
|
|
|
79
82
|
def get_initial(self):
|
|
80
83
|
return {
|
|
81
|
-
'closing_date': get_localdate()
|
|
84
|
+
'closing_date': get_localdate(),
|
|
85
|
+
'entity_model': self.AUTHORIZED_ENTITY_MODEL
|
|
82
86
|
}
|
|
83
87
|
|
|
84
|
-
def
|
|
85
|
-
return
|
|
86
|
-
|
|
87
|
-
|
|
88
|
+
def get_form_kwargs(self, **kwargs):
|
|
89
|
+
return super().get_form_kwargs(**kwargs) | {
|
|
90
|
+
'entity_model': self.AUTHORIZED_ENTITY_MODEL
|
|
91
|
+
}
|
|
88
92
|
|
|
89
93
|
def get_context_data(self, **kwargs):
|
|
90
94
|
ctx = super().get_context_data(**kwargs)
|
|
@@ -100,7 +104,7 @@ class ClosingEntryModelCreateView(ClosingEntryModelBaseView, CreateView):
|
|
|
100
104
|
force_update=True,
|
|
101
105
|
post_closing_entry=False
|
|
102
106
|
)
|
|
103
|
-
self.ce_model = ce_model
|
|
107
|
+
self.ce_model: EstimateModel = ce_model
|
|
104
108
|
return HttpResponseRedirect(self.get_success_url())
|
|
105
109
|
|
|
106
110
|
def get_success_url(self):
|
django_ledger/views/customer.py
CHANGED
|
@@ -6,44 +6,49 @@ Contributions to this module:
|
|
|
6
6
|
* Miguel Sanda <msanda@arrobalytics.com>
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
9
11
|
from django.urls import reverse
|
|
10
12
|
from django.utils.translation import gettext_lazy as _
|
|
11
|
-
from django.views.generic import
|
|
13
|
+
from django.views.generic import (
|
|
14
|
+
CreateView,
|
|
15
|
+
DeleteView,
|
|
16
|
+
DetailView,
|
|
17
|
+
ListView,
|
|
18
|
+
UpdateView,
|
|
19
|
+
)
|
|
12
20
|
|
|
13
21
|
from django_ledger.forms.customer import CustomerModelForm
|
|
14
|
-
from django_ledger.models.customer import CustomerModel
|
|
22
|
+
from django_ledger.models.customer import CustomerModel, CustomerModelQueryset
|
|
15
23
|
from django_ledger.models.entity import EntityModel
|
|
24
|
+
from django_ledger.models.invoice import InvoiceModel
|
|
25
|
+
from django_ledger.models.receipt import ReceiptModel
|
|
16
26
|
from django_ledger.views.mixins import DjangoLedgerSecurityMixIn
|
|
17
27
|
|
|
18
28
|
|
|
19
|
-
class CustomerModelModelViewQuerySetMixIn:
|
|
20
|
-
queryset = None
|
|
29
|
+
class CustomerModelModelViewQuerySetMixIn(DjangoLedgerSecurityMixIn):
|
|
30
|
+
queryset: Optional[CustomerModelQueryset] = None
|
|
21
31
|
|
|
22
32
|
def get_queryset(self):
|
|
23
33
|
if self.queryset is None:
|
|
24
34
|
self.queryset = CustomerModel.objects.for_entity(
|
|
25
|
-
|
|
26
|
-
user_model=self.request.user
|
|
35
|
+
entity_model=self.kwargs['entity_slug'],
|
|
27
36
|
).order_by('-updated')
|
|
28
|
-
return
|
|
37
|
+
return self.queryset
|
|
29
38
|
|
|
30
39
|
|
|
31
|
-
class CustomerModelListView(
|
|
32
|
-
CustomerModelModelViewQuerySetMixIn,
|
|
33
|
-
ListView):
|
|
40
|
+
class CustomerModelListView(CustomerModelModelViewQuerySetMixIn, ListView):
|
|
34
41
|
template_name = 'django_ledger/customer/customer_list.html'
|
|
35
42
|
PAGE_TITLE = _('Customer List')
|
|
36
43
|
extra_context = {
|
|
37
44
|
'page_title': PAGE_TITLE,
|
|
38
45
|
'header_title': PAGE_TITLE,
|
|
39
|
-
'header_subtitle_icon': 'dashicons:businesswoman'
|
|
46
|
+
'header_subtitle_icon': 'dashicons:businesswoman',
|
|
40
47
|
}
|
|
41
48
|
context_object_name = 'customers'
|
|
42
49
|
|
|
43
50
|
|
|
44
|
-
class CustomerModelCreateView(
|
|
45
|
-
CustomerModelModelViewQuerySetMixIn,
|
|
46
|
-
CreateView):
|
|
51
|
+
class CustomerModelCreateView(CustomerModelModelViewQuerySetMixIn, CreateView):
|
|
47
52
|
template_name = 'django_ledger/customer/customer_create.html'
|
|
48
53
|
PAGE_TITLE = _('Create New Customer')
|
|
49
54
|
form_class = CustomerModelForm
|
|
@@ -51,28 +56,26 @@ class CustomerModelCreateView(DjangoLedgerSecurityMixIn,
|
|
|
51
56
|
extra_context = {
|
|
52
57
|
'page_title': PAGE_TITLE,
|
|
53
58
|
'header_title': PAGE_TITLE,
|
|
54
|
-
'header_subtitle_icon': 'dashicons:businesswoman'
|
|
59
|
+
'header_subtitle_icon': 'dashicons:businesswoman',
|
|
55
60
|
}
|
|
56
61
|
|
|
57
62
|
def get_success_url(self):
|
|
58
|
-
return reverse(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
63
|
+
return reverse(
|
|
64
|
+
'django_ledger:customer-list',
|
|
65
|
+
kwargs={'entity_slug': self.kwargs['entity_slug']},
|
|
66
|
+
)
|
|
62
67
|
|
|
63
68
|
def form_valid(self, form):
|
|
64
69
|
customer_model: CustomerModel = form.save(commit=False)
|
|
65
|
-
entity_model = EntityModel.objects.for_user(
|
|
66
|
-
|
|
67
|
-
)
|
|
70
|
+
entity_model = EntityModel.objects.for_user(user_model=self.request.user).get(
|
|
71
|
+
slug__exact=self.kwargs['entity_slug']
|
|
72
|
+
)
|
|
68
73
|
customer_model.entity_model = entity_model
|
|
69
74
|
customer_model.save()
|
|
70
75
|
return super().form_valid(form)
|
|
71
76
|
|
|
72
77
|
|
|
73
|
-
class CustomerModelUpdateView(
|
|
74
|
-
CustomerModelModelViewQuerySetMixIn,
|
|
75
|
-
UpdateView):
|
|
78
|
+
class CustomerModelUpdateView(CustomerModelModelViewQuerySetMixIn, UpdateView):
|
|
76
79
|
template_name = 'django_ledger/customer/customer_update.html'
|
|
77
80
|
PAGE_TITLE = _('Customer Update')
|
|
78
81
|
form_class = CustomerModelForm
|
|
@@ -90,13 +93,48 @@ class CustomerModelUpdateView(DjangoLedgerSecurityMixIn,
|
|
|
90
93
|
return context
|
|
91
94
|
|
|
92
95
|
def get_success_url(self):
|
|
93
|
-
return reverse(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
return reverse(
|
|
97
|
+
'django_ledger:customer-list',
|
|
98
|
+
kwargs={'entity_slug': self.kwargs['entity_slug']},
|
|
99
|
+
)
|
|
97
100
|
|
|
98
101
|
def form_valid(self, form):
|
|
99
102
|
form.save()
|
|
100
103
|
return super().form_valid(form)
|
|
101
104
|
|
|
102
|
-
|
|
105
|
+
|
|
106
|
+
class CustomerModelDeleteView(CustomerModelModelViewQuerySetMixIn, DeleteView):
|
|
107
|
+
pass
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class CustomerModelDetailView(CustomerModelModelViewQuerySetMixIn, DetailView):
|
|
111
|
+
template_name = 'django_ledger/customer/customer_detail.html'
|
|
112
|
+
context_object_name = 'customer'
|
|
113
|
+
PAGE_TITLE = _('Customer Details')
|
|
114
|
+
slug_url_kwarg = 'customer_pk'
|
|
115
|
+
slug_field = 'uuid'
|
|
116
|
+
|
|
117
|
+
def get_context_data(self, **kwargs):
|
|
118
|
+
context = super().get_context_data(**kwargs)
|
|
119
|
+
customer: CustomerModel = self.object
|
|
120
|
+
receipts_qs = (
|
|
121
|
+
ReceiptModel.objects.for_entity(entity_model=self.AUTHORIZED_ENTITY_MODEL)
|
|
122
|
+
.for_customer(customer_model=customer)
|
|
123
|
+
.order_by('-updated')
|
|
124
|
+
)
|
|
125
|
+
invoices_qs = (
|
|
126
|
+
InvoiceModel.objects.for_entity(entity_model=self.AUTHORIZED_ENTITY_MODEL)
|
|
127
|
+
.filter(customer=customer)
|
|
128
|
+
.order_by('-updated')
|
|
129
|
+
)
|
|
130
|
+
context.update(
|
|
131
|
+
{
|
|
132
|
+
'page_title': self.PAGE_TITLE,
|
|
133
|
+
'header_title': self.PAGE_TITLE,
|
|
134
|
+
'header_subtitle': f'{customer.customer_name} · {customer.customer_number}',
|
|
135
|
+
'header_subtitle_icon': 'dashicons:businesswoman',
|
|
136
|
+
'receipts': receipts_qs,
|
|
137
|
+
'invoices': invoices_qs,
|
|
138
|
+
}
|
|
139
|
+
)
|
|
140
|
+
return context
|