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.

Files changed (139) hide show
  1. django_ledger/__init__.py +1 -1
  2. django_ledger/context.py +12 -0
  3. django_ledger/forms/account.py +45 -46
  4. django_ledger/forms/bill.py +0 -4
  5. django_ledger/forms/closing_entry.py +13 -1
  6. django_ledger/forms/data_import.py +182 -63
  7. django_ledger/forms/estimate.py +3 -6
  8. django_ledger/forms/invoice.py +3 -7
  9. django_ledger/forms/item.py +10 -18
  10. django_ledger/forms/purchase_order.py +2 -4
  11. django_ledger/io/io_core.py +515 -400
  12. django_ledger/io/io_generator.py +7 -6
  13. django_ledger/io/io_library.py +1 -2
  14. django_ledger/migrations/0025_alter_billmodel_cash_account_and_more.py +70 -0
  15. django_ledger/migrations/0026_stagedtransactionmodel_customer_model_and_more.py +56 -0
  16. django_ledger/models/__init__.py +2 -1
  17. django_ledger/models/accounts.py +109 -69
  18. django_ledger/models/bank_account.py +40 -23
  19. django_ledger/models/bill.py +386 -333
  20. django_ledger/models/chart_of_accounts.py +173 -105
  21. django_ledger/models/closing_entry.py +99 -48
  22. django_ledger/models/customer.py +100 -66
  23. django_ledger/models/data_import.py +818 -323
  24. django_ledger/models/deprecations.py +61 -0
  25. django_ledger/models/entity.py +891 -644
  26. django_ledger/models/estimate.py +57 -28
  27. django_ledger/models/invoice.py +46 -26
  28. django_ledger/models/items.py +503 -142
  29. django_ledger/models/journal_entry.py +61 -47
  30. django_ledger/models/ledger.py +106 -42
  31. django_ledger/models/mixins.py +424 -281
  32. django_ledger/models/purchase_order.py +39 -17
  33. django_ledger/models/receipt.py +1083 -0
  34. django_ledger/models/transactions.py +242 -139
  35. django_ledger/models/unit.py +93 -54
  36. django_ledger/models/utils.py +12 -2
  37. django_ledger/models/vendor.py +121 -70
  38. django_ledger/report/core.py +2 -14
  39. django_ledger/settings.py +57 -71
  40. django_ledger/static/django_ledger/bundle/djetler.bundle.js +1 -1
  41. django_ledger/static/django_ledger/bundle/djetler.bundle.js.LICENSE.txt +25 -0
  42. django_ledger/static/django_ledger/bundle/styles.bundle.js +1 -1
  43. django_ledger/static/django_ledger/css/djl_styles.css +273 -0
  44. django_ledger/templates/django_ledger/bills/includes/card_bill.html +2 -2
  45. django_ledger/templates/django_ledger/components/menu.html +41 -26
  46. django_ledger/templates/django_ledger/components/period_navigator.html +5 -3
  47. django_ledger/templates/django_ledger/customer/customer_detail.html +87 -0
  48. django_ledger/templates/django_ledger/customer/customer_list.html +0 -1
  49. django_ledger/templates/django_ledger/customer/tags/customer_table.html +8 -6
  50. django_ledger/templates/django_ledger/data_import/tags/data_import_job_txs_imported.html +24 -3
  51. django_ledger/templates/django_ledger/data_import/tags/data_import_job_txs_table.html +26 -10
  52. django_ledger/templates/django_ledger/entity/entity_dashboard.html +2 -2
  53. django_ledger/templates/django_ledger/entity/includes/card_entity.html +12 -6
  54. django_ledger/templates/django_ledger/financial_statements/balance_sheet.html +1 -1
  55. django_ledger/templates/django_ledger/financial_statements/cash_flow.html +4 -1
  56. django_ledger/templates/django_ledger/financial_statements/income_statement.html +4 -1
  57. django_ledger/templates/django_ledger/financial_statements/tags/balance_sheet_statement.html +27 -3
  58. django_ledger/templates/django_ledger/financial_statements/tags/cash_flow_statement.html +16 -4
  59. django_ledger/templates/django_ledger/financial_statements/tags/income_statement.html +73 -18
  60. django_ledger/templates/django_ledger/includes/widget_ratios.html +18 -24
  61. django_ledger/templates/django_ledger/invoice/includes/card_invoice.html +3 -3
  62. django_ledger/templates/django_ledger/layouts/base.html +7 -2
  63. django_ledger/templates/django_ledger/layouts/content_layout_1.html +1 -1
  64. django_ledger/templates/django_ledger/receipt/customer_receipt_report.html +115 -0
  65. django_ledger/templates/django_ledger/receipt/receipt_delete.html +30 -0
  66. django_ledger/templates/django_ledger/receipt/receipt_detail.html +89 -0
  67. django_ledger/templates/django_ledger/receipt/receipt_list.html +134 -0
  68. django_ledger/templates/django_ledger/receipt/vendor_receipt_report.html +115 -0
  69. django_ledger/templates/django_ledger/vendor/tags/vendor_table.html +12 -7
  70. django_ledger/templates/django_ledger/vendor/vendor_detail.html +86 -0
  71. django_ledger/templatetags/django_ledger.py +338 -191
  72. django_ledger/tests/test_accounts.py +1 -2
  73. django_ledger/tests/test_io.py +17 -0
  74. django_ledger/tests/test_purchase_order.py +3 -3
  75. django_ledger/tests/test_transactions.py +1 -2
  76. django_ledger/urls/__init__.py +1 -4
  77. django_ledger/urls/customer.py +3 -0
  78. django_ledger/urls/data_import.py +3 -0
  79. django_ledger/urls/receipt.py +102 -0
  80. django_ledger/urls/vendor.py +1 -0
  81. django_ledger/views/__init__.py +1 -0
  82. django_ledger/views/bill.py +8 -11
  83. django_ledger/views/chart_of_accounts.py +6 -4
  84. django_ledger/views/closing_entry.py +11 -7
  85. django_ledger/views/customer.py +68 -30
  86. django_ledger/views/data_import.py +120 -66
  87. django_ledger/views/djl_api.py +3 -5
  88. django_ledger/views/entity.py +2 -4
  89. django_ledger/views/estimate.py +3 -7
  90. django_ledger/views/inventory.py +3 -5
  91. django_ledger/views/invoice.py +4 -6
  92. django_ledger/views/item.py +7 -11
  93. django_ledger/views/journal_entry.py +1 -2
  94. django_ledger/views/mixins.py +125 -93
  95. django_ledger/views/purchase_order.py +24 -35
  96. django_ledger/views/receipt.py +294 -0
  97. django_ledger/views/unit.py +1 -2
  98. django_ledger/views/vendor.py +54 -16
  99. {django_ledger-0.7.11.dist-info → django_ledger-0.8.1.dist-info}/METADATA +43 -75
  100. {django_ledger-0.7.11.dist-info → django_ledger-0.8.1.dist-info}/RECORD +104 -122
  101. {django_ledger-0.7.11.dist-info → django_ledger-0.8.1.dist-info}/WHEEL +1 -1
  102. django_ledger-0.8.1.dist-info/top_level.txt +1 -0
  103. django_ledger/contrib/django_ledger_graphene/__init__.py +0 -0
  104. django_ledger/contrib/django_ledger_graphene/accounts/schema.py +0 -33
  105. django_ledger/contrib/django_ledger_graphene/api.py +0 -42
  106. django_ledger/contrib/django_ledger_graphene/apps.py +0 -6
  107. django_ledger/contrib/django_ledger_graphene/auth/mutations.py +0 -49
  108. django_ledger/contrib/django_ledger_graphene/auth/schema.py +0 -6
  109. django_ledger/contrib/django_ledger_graphene/bank_account/mutations.py +0 -61
  110. django_ledger/contrib/django_ledger_graphene/bank_account/schema.py +0 -34
  111. django_ledger/contrib/django_ledger_graphene/bill/mutations.py +0 -0
  112. django_ledger/contrib/django_ledger_graphene/bill/schema.py +0 -34
  113. django_ledger/contrib/django_ledger_graphene/coa/mutations.py +0 -0
  114. django_ledger/contrib/django_ledger_graphene/coa/schema.py +0 -30
  115. django_ledger/contrib/django_ledger_graphene/customers/__init__.py +0 -0
  116. django_ledger/contrib/django_ledger_graphene/customers/mutations.py +0 -71
  117. django_ledger/contrib/django_ledger_graphene/customers/schema.py +0 -43
  118. django_ledger/contrib/django_ledger_graphene/data_import/mutations.py +0 -0
  119. django_ledger/contrib/django_ledger_graphene/data_import/schema.py +0 -0
  120. django_ledger/contrib/django_ledger_graphene/entity/mutations.py +0 -0
  121. django_ledger/contrib/django_ledger_graphene/entity/schema.py +0 -94
  122. django_ledger/contrib/django_ledger_graphene/item/mutations.py +0 -0
  123. django_ledger/contrib/django_ledger_graphene/item/schema.py +0 -31
  124. django_ledger/contrib/django_ledger_graphene/journal_entry/mutations.py +0 -0
  125. django_ledger/contrib/django_ledger_graphene/journal_entry/schema.py +0 -35
  126. django_ledger/contrib/django_ledger_graphene/ledger/mutations.py +0 -0
  127. django_ledger/contrib/django_ledger_graphene/ledger/schema.py +0 -32
  128. django_ledger/contrib/django_ledger_graphene/purchase_order/mutations.py +0 -0
  129. django_ledger/contrib/django_ledger_graphene/purchase_order/schema.py +0 -31
  130. django_ledger/contrib/django_ledger_graphene/transaction/mutations.py +0 -0
  131. django_ledger/contrib/django_ledger_graphene/transaction/schema.py +0 -36
  132. django_ledger/contrib/django_ledger_graphene/unit/mutations.py +0 -0
  133. django_ledger/contrib/django_ledger_graphene/unit/schema.py +0 -27
  134. django_ledger/contrib/django_ledger_graphene/vendor/mutations.py +0 -0
  135. django_ledger/contrib/django_ledger_graphene/vendor/schema.py +0 -37
  136. django_ledger/contrib/django_ledger_graphene/views.py +0 -12
  137. django_ledger-0.7.11.dist-info/top_level.txt +0 -4
  138. {django_ledger-0.7.11.dist-info → django_ledger-0.8.1.dist-info/licenses}/AUTHORS.md +0 -0
  139. {django_ledger-0.7.11.dist-info → django_ledger-0.8.1.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,294 @@
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 typing import Optional
10
+ from calendar import month_name
11
+
12
+ from django.http import HttpResponseForbidden
13
+ from django.shortcuts import get_object_or_404
14
+ from django.utils.translation import gettext_lazy as _
15
+ from django.views.generic import (
16
+ ArchiveIndexView,
17
+ DeleteView,
18
+ DetailView,
19
+ MonthArchiveView,
20
+ YearArchiveView,
21
+ )
22
+
23
+ from django_ledger.models import CustomerModel, EntityModel, LedgerModel, VendorModel
24
+ from django_ledger.models.receipt import ReceiptModel, ReceiptModelQuerySet
25
+ from django_ledger.models.transactions import TransactionModel
26
+ from django_ledger.views.mixins import (
27
+ DjangoLedgerSecurityMixIn,
28
+ QuarterlyReportMixIn,
29
+ )
30
+
31
+
32
+ class BaseReceiptModelViewMixIn(DjangoLedgerSecurityMixIn):
33
+ queryset: Optional[ReceiptModelQuerySet] = None
34
+
35
+ def get_queryset(self):
36
+ if self.queryset is None:
37
+ entity_model: EntityModel = self.AUTHORIZED_ENTITY_MODEL
38
+ qs = entity_model.get_receipts()
39
+ qs = qs.select_related(
40
+ 'ledger_model', 'customer_model', 'vendor_model'
41
+ ).order_by('-receipt_date', '-created')
42
+
43
+ receipt_type = self.kwargs.get('receipt_type')
44
+ if receipt_type:
45
+ qs = qs.filter(receipt_type__exact=receipt_type)
46
+ if receipt_type in [
47
+ ReceiptModel.SALES_RECEIPT,
48
+ ReceiptModel.SALES_REFUND,
49
+ ]:
50
+ qs = qs.filter(
51
+ customer_model__isnull=False, vendor_model__isnull=True
52
+ )
53
+
54
+ elif receipt_type in [
55
+ ReceiptModel.EXPENSE_RECEIPT,
56
+ ReceiptModel.EXPENSE_REFUND,
57
+ ]:
58
+ qs = qs.filter(
59
+ vendor_model__isnull=False, customer_model__isnull=True
60
+ )
61
+
62
+ vendor_pk = self.kwargs.get('vendor_pk')
63
+ if vendor_pk:
64
+ qs = qs.for_vendor(vendor_model=vendor_pk)
65
+
66
+ customer_pk = self.kwargs.get('customer_pk')
67
+ if customer_pk:
68
+ qs = qs.for_customer(customer_model=customer_pk)
69
+
70
+ self.queryset = qs
71
+
72
+ return self.queryset
73
+
74
+
75
+ class ReceiptModelListView(BaseReceiptModelViewMixIn, ArchiveIndexView):
76
+ template_name = 'django_ledger/receipt/receipt_list.html'
77
+ context_object_name = 'receipt_list'
78
+ PAGE_TITLE = _('Receipts List')
79
+ date_field = 'receipt_date'
80
+ paginate_by = 20
81
+ paginate_orphans = 2
82
+ allow_empty = True
83
+ extra_context = {
84
+ 'title': PAGE_TITLE,
85
+ 'page_title': PAGE_TITLE,
86
+ 'header_title': PAGE_TITLE,
87
+ 'header_subtitle_icon': 'mdi:receipt',
88
+ }
89
+
90
+ def get_context_data(self, **kwargs):
91
+ context = super().get_context_data(**kwargs)
92
+ subtitle = None
93
+
94
+ receipt_type = self.kwargs.get('receipt_type')
95
+
96
+ if receipt_type:
97
+ context['title'] = ReceiptModel.RECEIPT_TYPES_MAP[receipt_type]
98
+
99
+ vendor_pk = self.kwargs.get('vendor_pk')
100
+ if vendor_pk:
101
+ vendor = VendorModel.objects.for_entity(
102
+ entity_model=self.AUTHORIZED_ENTITY_MODEL
103
+ ).get(uuid__exact=vendor_pk)
104
+ subtitle = vendor.vendor_name
105
+ customer_pk = self.kwargs.get('customer_pk')
106
+
107
+ if customer_pk:
108
+ customer = CustomerModel.objects.for_entity(
109
+ entity_model=self.AUTHORIZED_ENTITY_MODEL
110
+ ).get(uuid__exact=customer_pk)
111
+ subtitle = customer.customer_name
112
+
113
+ if subtitle:
114
+ context['header_subtitle'] = subtitle
115
+ return context
116
+
117
+
118
+ class ReceiptModelYearListView(ReceiptModelListView, YearArchiveView):
119
+ def get_context_data(self, **kwargs):
120
+ context = super().get_context_data(**kwargs)
121
+ context['year'] = self.get_year()
122
+ context['page_title'] = _(f'Receipts List {self.get_year()}')
123
+ context['header_title'] = _(f'Receipts List {self.get_year()}')
124
+ context['header_subtitle'] = self.AUTHORIZED_ENTITY_MODEL.name
125
+ return context
126
+
127
+
128
+ class ReceiptModelQuarterListView(ReceiptModelYearListView, QuarterlyReportMixIn):
129
+ def get_queryset(self):
130
+ qs = super().get_queryset()
131
+ return qs.for_dates(from_date=self.get_from_date(), to_date=self.get_to_date())
132
+
133
+ def get_context_data(self, **kwargs):
134
+ context = super().get_context_data(**kwargs)
135
+ context['page_title'] = _(f'Receipts List Q{self.get_quarter()}')
136
+ context['header_title'] = _(f'Receipts List Q{self.get_quarter()}')
137
+ context['header_subtitle'] = self.AUTHORIZED_ENTITY_MODEL.name
138
+ return context
139
+
140
+
141
+ class ReceiptModelMonthListView(ReceiptModelYearListView, MonthArchiveView):
142
+ def get_context_data(self, **kwargs):
143
+ context = super().get_context_data(**kwargs)
144
+ year = self.get_year()
145
+ month_num = int(self.get_month())
146
+ month_label = month_name[month_num]
147
+ context['page_title'] = _(f'Receipts List {month_label} {year}')
148
+ context['header_title'] = _(f'Receipts List {month_label}, {year}')
149
+ context['header_subtitle'] = self.AUTHORIZED_ENTITY_MODEL.name
150
+ return context
151
+
152
+
153
+ class ReceiptModelDetailView(BaseReceiptModelViewMixIn, DetailView):
154
+ template_name = 'django_ledger/receipt/receipt_detail.html'
155
+ context_object_name = 'receipt'
156
+ slug_field = 'uuid'
157
+ slug_url_kwarg = 'receipt_pk'
158
+
159
+ def get_context_data(self, **kwargs):
160
+ context = super().get_context_data(**kwargs)
161
+ receipt_model: ReceiptModel = self.object
162
+ ledger_model: LedgerModel = receipt_model.ledger_model
163
+ title = _(f'Receipt {receipt_model.receipt_number}')
164
+ context['page_title'] = title
165
+ context['header_title'] = title
166
+ context['header_subtitle'] = receipt_model.receipt_date
167
+ context['header_subtitle_icon'] = 'mdi:receipt'
168
+
169
+ tx_list = (
170
+ TransactionModel.objects.for_entity(
171
+ entity_model=self.AUTHORIZED_ENTITY_MODEL
172
+ )
173
+ .for_ledger(ledger_model=ledger_model)
174
+ .posted()
175
+ .not_closing_entry()
176
+ .select_related(
177
+ 'account',
178
+ 'journal_entry',
179
+ 'journal_entry__entity_unit',
180
+ )
181
+ .order_by('journal_entry__timestamp', 'account__code')
182
+ )
183
+
184
+ context['tx_list'] = tx_list
185
+ context['staged_tx'] = receipt_model.staged_transaction_model
186
+ if receipt_model.staged_transaction_model_id:
187
+ context['import_job'] = receipt_model.staged_transaction_model.import_job
188
+ return context
189
+
190
+
191
+ # VENDOR VIEWS......
192
+ class VendorReceiptReportListView(ReceiptModelListView):
193
+ template_name = 'django_ledger/receipt/vendor_receipt_report.html'
194
+
195
+ def get_context_data(self, **kwargs):
196
+ context = super().get_context_data(**kwargs)
197
+ vendor_pk = self.kwargs['vendor_pk']
198
+ vendor_model_qs = VendorModel.objects.for_entity(
199
+ entity_model=self.AUTHORIZED_ENTITY_MODEL
200
+ )
201
+ vendor_model: VendorModel = get_object_or_404(
202
+ vendor_model_qs, uuid__exact=vendor_pk
203
+ )
204
+ context['vendor_model'] = vendor_model
205
+ context['page_title'] = _(f'Vendor Receipts {vendor_model.vendor_name}')
206
+ context['header_title'] = _('Vendor Receipts')
207
+ context['header_subtitle'] = vendor_model.vendor_name
208
+ context['header_subtitle_icon'] = 'mdi:receipt'
209
+ return context
210
+
211
+
212
+ class VendorReceiptReportYearListView(ReceiptModelYearListView):
213
+ template_name = 'django_ledger/receipt/vendor_receipt_report.html'
214
+
215
+
216
+ class VendorReceiptReportQuarterListView(ReceiptModelQuarterListView):
217
+ template_name = 'django_ledger/receipt/vendor_receipt_report.html'
218
+
219
+
220
+ class VendorReceiptReportMonthListView(ReceiptModelMonthListView):
221
+ template_name = 'django_ledger/receipt/vendor_receipt_report.html'
222
+
223
+
224
+ # CUSTOMERS VIEWS......
225
+ class CustomerReceiptReportListView(ReceiptModelListView):
226
+ template_name = 'django_ledger/receipt/customer_receipt_report.html'
227
+ allow_empty = True
228
+
229
+ def get_context_data(self, **kwargs):
230
+ context = super().get_context_data(**kwargs)
231
+ customer_model_qs = CustomerModel.objects.for_entity(
232
+ entity_model=self.AUTHORIZED_ENTITY_MODEL
233
+ )
234
+ customer_pk = self.kwargs['customer_pk']
235
+ customer_model = get_object_or_404(
236
+ customer_model_qs,
237
+ uuid__exact=customer_pk,
238
+ )
239
+ context['vendor_model'] = customer_model
240
+ context['page_title'] = _(f'Customer Receipts {customer_model.name}')
241
+ context['header_title'] = _('Customer Receipts')
242
+ context['header_subtitle'] = customer_model.vendor_name
243
+ context['header_subtitle_icon'] = 'mdi:receipt'
244
+ return context
245
+
246
+
247
+ class CustomerReceiptReportYearListView(CustomerReceiptReportListView):
248
+ template_name = 'django_ledger/receipt/customer_receipt_report.html'
249
+ make_object_list = True
250
+
251
+
252
+ class CustomerReceiptReportQuarterListView(ReceiptModelQuarterListView):
253
+ template_name = 'django_ledger/receipt/customer_receipt_report.html'
254
+ make_object_list = True
255
+
256
+
257
+ class CustomerReceiptReportMonthListView(ReceiptModelMonthListView):
258
+ template_name = 'django_ledger/receipt/customer_receipt_report.html'
259
+ month_format = '%m'
260
+
261
+
262
+ class ReceiptModelDeleteView(BaseReceiptModelViewMixIn, DeleteView):
263
+ template_name = 'django_ledger/receipt/receipt_delete.html'
264
+ context_object_name = 'receipt'
265
+ slug_field = 'uuid'
266
+ slug_url_kwarg = 'receipt_pk'
267
+
268
+ def get_context_data(self, **kwargs):
269
+ context = super().get_context_data(**kwargs)
270
+ receipt: ReceiptModel = self.object
271
+ title = _(f'Delete Receipt {receipt.receipt_number}')
272
+ context['page_title'] = title
273
+ context['header_title'] = title
274
+ context['header_subtitle_icon'] = 'mdi:receipt'
275
+ return context
276
+
277
+ def can_delete(self, receipt_model: ReceiptModel) -> bool:
278
+ entity_model: EntityModel = self.AUTHORIZED_ENTITY_MODEL
279
+ ce_date = entity_model.get_closing_entry_for_date(
280
+ io_date=receipt_model.receipt_date, inclusive=True
281
+ )
282
+ return ce_date is None
283
+
284
+ def delete(self, request, *args, **kwargs):
285
+ receipt_model: ReceiptModel = self.object
286
+ if not receipt_model.can_delete():
287
+ return HttpResponseForbidden(
288
+ 'Receipt cannot be deleted because it falls within a closed period.'
289
+ )
290
+ return super().delete(request, *args, **kwargs)
291
+
292
+ def get_success_url(self):
293
+ receipt_model: ReceiptModel = self.object
294
+ return receipt_model.get_list_url()
@@ -28,8 +28,7 @@ class EntityUnitModelModelBaseView(DjangoLedgerSecurityMixIn):
28
28
  def get_queryset(self):
29
29
  if self.queryset is None:
30
30
  self.queryset = EntityUnitModel.objects.for_entity(
31
- entity_slug=self.kwargs['entity_slug'],
32
- user_model=self.request.user
31
+ entity_model=self.get_authorized_entity_instance()
33
32
  ).select_related('entity')
34
33
  return super().get_queryset()
35
34
 
@@ -5,13 +5,16 @@ 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
+
8
9
  from django.shortcuts import get_object_or_404
9
10
  from django.urls import reverse
10
11
  from django.utils.translation import gettext_lazy as _
11
- from django.views.generic import ListView, CreateView, UpdateView
12
+ from django.views.generic import CreateView, DetailView, ListView, UpdateView
12
13
 
13
14
  from django_ledger.forms.vendor import VendorModelForm
15
+ from django_ledger.models.bill import BillModel
14
16
  from django_ledger.models.entity import EntityModel
17
+ from django_ledger.models.receipt import ReceiptModel
15
18
  from django_ledger.models.vendor import VendorModel
16
19
  from django_ledger.views.mixins import DjangoLedgerSecurityMixIn
17
20
 
@@ -22,8 +25,7 @@ class VendorModelModelBaseView(DjangoLedgerSecurityMixIn):
22
25
  def get_queryset(self):
23
26
  if self.queryset is None:
24
27
  self.queryset = VendorModel.objects.for_entity(
25
- entity_slug=self.kwargs['entity_slug'],
26
- user_model=self.request.user
28
+ entity_model=self.kwargs['entity_slug']
27
29
  ).order_by('-updated')
28
30
  return super().get_queryset()
29
31
 
@@ -35,7 +37,7 @@ class VendorModelListView(VendorModelModelBaseView, ListView):
35
37
  extra_context = {
36
38
  'page_title': PAGE_TITLE,
37
39
  'header_title': PAGE_TITLE,
38
- 'header_subtitle_icon': 'bi:person-lines-fill'
40
+ 'header_subtitle_icon': 'bi:person-lines-fill',
39
41
  }
40
42
 
41
43
  def get_context_data(self, **kwargs):
@@ -53,21 +55,21 @@ class VendorModelCreateView(VendorModelModelBaseView, CreateView):
53
55
  extra_context = {
54
56
  'page_title': PAGE_TITLE,
55
57
  'header_title': PAGE_TITLE,
56
- 'header_subtitle_icon': 'bi:person-lines-fill'
58
+ 'header_subtitle_icon': 'bi:person-lines-fill',
57
59
  }
58
60
 
59
61
  def get_success_url(self):
60
- return reverse('django_ledger:vendor-list',
61
- kwargs={
62
- 'entity_slug': self.kwargs['entity_slug']
63
- })
62
+ return reverse(
63
+ 'django_ledger:vendor-list',
64
+ kwargs={'entity_slug': self.kwargs['entity_slug']},
65
+ )
64
66
 
65
67
  def form_valid(self, form):
66
68
  vendor_model: VendorModel = form.save(commit=False)
67
- entity_model_qs = EntityModel.objects.for_user(
68
- user_model=self.request.user
69
+ entity_model_qs = EntityModel.objects.for_user(user_model=self.request.user)
70
+ entity_model = get_object_or_404(
71
+ klass=entity_model_qs, slug__exact=self.kwargs['entity_slug']
69
72
  )
70
- entity_model = get_object_or_404(klass=entity_model_qs, slug__exact=self.kwargs['entity_slug'])
71
73
  vendor_model.entity_model = entity_model
72
74
  return super().form_valid(form)
73
75
 
@@ -91,7 +93,43 @@ class VendorModelUpdateView(VendorModelModelBaseView, UpdateView):
91
93
  return context
92
94
 
93
95
  def get_success_url(self):
94
- return reverse('django_ledger:vendor-list',
95
- kwargs={
96
- 'entity_slug': self.kwargs['entity_slug']
97
- })
96
+ return reverse(
97
+ 'django_ledger:vendor-list',
98
+ kwargs={'entity_slug': self.kwargs['entity_slug']},
99
+ )
100
+
101
+
102
+ class VendorModelDetailView(VendorModelModelBaseView, DetailView):
103
+ template_name = 'django_ledger/vendor/vendor_detail.html'
104
+ context_object_name = 'vendor'
105
+ PAGE_TITLE = _('Vendor Details')
106
+ slug_url_kwarg = 'vendor_pk'
107
+ slug_field = 'uuid'
108
+
109
+ def get_context_data(self, **kwargs):
110
+ context = super().get_context_data(**kwargs)
111
+
112
+ vendor_model: VendorModel = self.object
113
+ receipts_qs = (
114
+ ReceiptModel.objects.for_entity(entity_model=self.AUTHORIZED_ENTITY_MODEL)
115
+ .for_vendor(vendor_model=vendor_model)
116
+ .order_by('-updated')
117
+ )
118
+
119
+ bills_qs = (
120
+ BillModel.objects.for_entity(entity_model=self.AUTHORIZED_ENTITY_MODEL)
121
+ .filter(vendor=vendor_model)
122
+ .order_by('-updated')
123
+ )
124
+
125
+ context.update(
126
+ {
127
+ 'page_title': self.PAGE_TITLE,
128
+ 'header_title': self.PAGE_TITLE,
129
+ 'header_subtitle': f'{vendor_model.vendor_name} · {vendor_model.vendor_number}',
130
+ 'header_subtitle_icon': 'bi:person-lines-fill',
131
+ 'receipts': receipts_qs,
132
+ 'bills': bills_qs,
133
+ }
134
+ )
135
+ return context
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: django-ledger
3
- Version: 0.7.11
3
+ Version: 0.8.1
4
4
  Summary: Double entry accounting system built on the Django Web Framework.
5
5
  Author-email: Miguel Sanda <msanda@arrobalytics.com>
6
6
  Maintainer-email: Miguel Sanda <msanda@arrobalytics.com>
@@ -18,26 +18,18 @@ Classifier: Framework :: Django :: 5.0
18
18
  Classifier: Intended Audience :: Financial and Insurance Industry
19
19
  Classifier: Intended Audience :: End Users/Desktop
20
20
  Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
21
- Requires-Python: >=3.10
21
+ Requires-Python: >=3.11
22
22
  Description-Content-Type: text/markdown
23
23
  License-File: LICENSE
24
24
  License-File: AUTHORS.md
25
- Requires-Dist: asgiref>=3.5.2; python_version >= "3.7"
26
- Requires-Dist: django>=2.2
27
- Requires-Dist: django-treebeard>=4.5.1
28
- Requires-Dist: faker>=15.3.3
29
- Requires-Dist: markdown>=3.4.1
25
+ Requires-Dist: django>=4.2
26
+ Requires-Dist: django-treebeard>=4.7.1
27
+ Requires-Dist: faker>=37.6.0
28
+ Requires-Dist: fpdf2>=2.8.4
29
+ Requires-Dist: markdown>=3.9
30
30
  Requires-Dist: ofxtools>=0.9.5
31
- Requires-Dist: pillow>=9.3.0
32
- Requires-Dist: python-dateutil>=2.8.2; python_version >= "2.7" and python_version not in "3.0, 3.1, 3.2, 3.3"
33
- Requires-Dist: six>=1.16.0; python_version >= "2.7" and python_version not in "3.0, 3.1, 3.2, 3.3"
34
- Requires-Dist: sqlparse>=0.4.3; python_version >= "3.5"
35
- Provides-Extra: dev
36
- Requires-Dist: sphinx~=4.5.0; extra == "dev"
37
- Requires-Dist: behave~=1.2.6; extra == "dev"
38
- Requires-Dist: pipenv-setup; extra == "dev"
39
- Requires-Dist: pylint; extra == "dev"
40
- Requires-Dist: furo; extra == "dev"
31
+ Requires-Dist: pillow>=11.3.0
32
+ Dynamic: license-file
41
33
 
42
34
  ![django ledger logo](https://us-east-1.linodeobjects.com/django-ledger/logo/django-ledger-logo@2x.png)
43
35
 
@@ -72,8 +64,11 @@ Created and developed by [Miguel Sanda](https://www.miguelsanda.com).
72
64
 
73
65
  ## Getting Involved
74
66
 
75
- All pull requests are welcome, as long as they address bugfixes, enhancements, new ideas, or add value to the project in any shape or form.
76
- Please refrain from submitting pull requests that focus solely on code linting, refactoring, or similar cosmetic changes.
67
+ All pull requests are welcome, as long as they address bugfixes, enhancements, new ideas, or add value to the project in
68
+ any shape or form.
69
+
70
+ Please refrain from submitting pull requests that focus solely on code linting, auto-generated code,
71
+ refactoring, or similar cosmetic non-value add changes.
77
72
 
78
73
  - **Feature Requests/Bug Reports**: Open an issue in the repository
79
74
  - **For software customization, advanced features and consulting services**:
@@ -100,56 +95,12 @@ is [here](https://docs.djangoproject.com/en/4.2/intro/tutorial01/#creating-a-pro
100
95
  Make sure you refer to the django version you are using.
101
96
 
102
97
  The easiest way to start is to use the zero-config Django Ledger starter template. See
103
- details [here](https://github.com/arrobalytics/django-ledger-starter). Otherwise, you may create your
104
- project from scratch.
105
-
106
- To create a new Django Ledger project:
107
-
108
- * Make sure you have the latest version of python [here](https://www.python.org/) (recommended).
109
-
110
- * Install Django:
111
-
112
- ```shell
113
- pip install django
114
- ```
115
-
116
- * Install Python [Pipenv](https://pipenv.pypa.io/en/latest/) (python package manager):
117
-
118
- ```shell script
119
- pip install pipenv
120
- ```
98
+ details [here](https://github.com/arrobalytics/django-ledger-starter).
99
+ Otherwise, you may create your project from scratch.
121
100
 
122
- * Go to your desired development folder and create a new django project:
101
+ ## Adding Django Ledger to an existing project.
123
102
 
124
- ```shell
125
- django-admin startproject django_ledger_project && cd django_ledger_project
126
- ```
127
-
128
- * Install Django on you virtual environment.
129
-
130
- ```shell
131
- pipenv install django
132
- ```
133
-
134
- * Install Django Ledger
135
-
136
- ```shell script
137
- pipenv install "django-ledger[graphql,pdf]"
138
- ```
139
-
140
- Alternatively, you can use:
141
-
142
- ```shell script
143
- pipenv install django-ledger\[graphql,pdf\]
144
- ```
145
-
146
- * Activate your new virtual environment:
147
-
148
- ```shell
149
- pipenv shell
150
- ```
151
-
152
- * Add django_ledger to INSTALLED_APPS in you new Django Project.
103
+ ### Add django_ledger to INSTALLED_APPS in you new Django Project.
153
104
 
154
105
  ```python
155
106
  INSTALLED_APPS = [
@@ -159,16 +110,25 @@ INSTALLED_APPS = [
159
110
  ]
160
111
  ```
161
112
 
162
- * Perform database migrations:
113
+ ### Add Django Ledger Context Preprocessor
163
114
 
164
- ```shell
165
- python manage.py migrate
115
+ ```python
116
+ TEMPLATES = [
117
+ {
118
+ 'OPTIONS': {
119
+ 'context_processors': [
120
+ '...',
121
+ 'django_ledger.context.django_ledger_context' # Add this line to a context_processors list..
122
+ ],
123
+ },
124
+ },
125
+ ]
166
126
  ```
167
127
 
168
- * Add Django SuperUser and follow the prompts.
128
+ ### Perform database migrations:
169
129
 
170
130
  ```shell
171
- python manage.py createsuperuser
131
+ python manage.py migrate
172
132
  ```
173
133
 
174
134
  * Add URLs to your project's __urls.py__:
@@ -183,7 +143,7 @@ urlpatterns = [
183
143
  ]
184
144
  ```
185
145
 
186
- * Run your project:
146
+ ### Run your project:
187
147
 
188
148
  ```shell
189
149
  python manage.py runserver
@@ -194,7 +154,15 @@ python manage.py runserver
194
154
  if you followed this installation guide).
195
155
  * Use your superuser credentials to login.
196
156
 
197
- # How To Set Up Django Ledger for Development
157
+ ## Deprecated behavior setting (v0.8.0+)
158
+
159
+ Starting with version v0.8.0, Django Ledger introduces the DJANGO_LEDGER_USE_DEPRECATED_BEHAVIOR setting to control
160
+ access to deprecated features and legacy behaviors.
161
+
162
+ - Default: False (deprecated features are disabled by default)
163
+ - To temporarily keep using deprecated features while you transition, set this to True in your Django settings.
164
+
165
+ ## Setting Up Django Ledger for Development
198
166
 
199
167
  Django Ledger comes with a basic development environment already configured under __dev_env/__ folder not to be used
200
168
  for production environments. If you want to contribute to the project perform the following steps: