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
@@ -10,6 +10,7 @@ Once approved, the user may initiate purchase orders, bills and invoices that wi
10
10
  for tracking purposes. It is however not required to always have an EstimateModel, but recommended in order to be able
11
11
  to produce more specific financial reports associated with a specific scope of work.
12
12
  """
13
+ import warnings
13
14
  from datetime import date
14
15
  from decimal import Decimal
15
16
  from string import ascii_uppercase, digits
@@ -27,7 +28,7 @@ from django.urls import reverse
27
28
  from django.utils.translation import gettext_lazy as _
28
29
 
29
30
  from django_ledger.io.io_core import get_localdate
30
- from django_ledger.models import BillModelQuerySet, InvoiceModelQuerySet
31
+ from django_ledger.models import BillModelQuerySet, InvoiceModelQuerySet, lazy_loader, deprecated_entity_slug_behavior
31
32
  from django_ledger.models.customer import CustomerModel
32
33
  from django_ledger.models.entity import EntityModel, EntityStateModel
33
34
  from django_ledger.models.items import ItemTransactionModelQuerySet, ItemTransactionModel, ItemModelQuerySet, ItemModel
@@ -41,7 +42,8 @@ from django_ledger.models.signals import (
41
42
  estimate_status_completed,
42
43
  estimate_status_in_review
43
44
  )
44
- from django_ledger.settings import DJANGO_LEDGER_DOCUMENT_NUMBER_PADDING, DJANGO_LEDGER_ESTIMATE_NUMBER_PREFIX
45
+ from django_ledger.settings import DJANGO_LEDGER_DOCUMENT_NUMBER_PADDING, DJANGO_LEDGER_ESTIMATE_NUMBER_PREFIX, \
46
+ DJANGO_LEDGER_USE_DEPRECATED_BEHAVIOR
45
47
 
46
48
  ESTIMATE_NUMBER_CHARS = ascii_uppercase + digits
47
49
 
@@ -57,6 +59,14 @@ class EstimateModelQuerySet(models.QuerySet):
57
59
  A custom-defined LedgerModelManager that implements custom QuerySet methods related to the EstimateModel.
58
60
  """
59
61
 
62
+ def for_user(self, user_model):
63
+ if user_model.is_superuser:
64
+ return self
65
+ return self.filter(
66
+ Q(entity__admin=user_model) |
67
+ Q(entity__managers__in=[user_model])
68
+ )
69
+
60
70
  def approved(self):
61
71
  """
62
72
  Approved Estimates or Sales Orders are those that have been approved or completed.
@@ -113,43 +123,62 @@ class EstimateModelQuerySet(models.QuerySet):
113
123
 
114
124
  class EstimateModelManager(models.Manager):
115
125
  """
116
- A custom defined EstimateModelManager that that implements custom QuerySet methods related to the EstimateModel.
126
+ A custom-defined EstimateModelManager that that implements custom QuerySet methods related to the EstimateModel.
117
127
  """
118
128
 
119
- def for_user(self, user_model):
120
- qs = self.get_queryset()
121
- if user_model.is_superuser:
122
- return qs
123
- return qs.filter(
124
- Q(entity__admin=user_model) |
125
- Q(entity__managers__in=[user_model])
126
- )
127
-
128
- def for_entity(self, entity_slug: Union[EntityModel, str], user_model):
129
+ @deprecated_entity_slug_behavior
130
+ def for_entity(self, entity_model: Union[EntityModel, str, UUID] = None, **kwargs) -> EstimateModelQuerySet:
129
131
  """
130
- Fetches a QuerySet of EstimateModels associated with a specific EntityModel & UserModel.
131
- May pass an instance of EntityModel or a String representing the EntityModel slug.
132
+ Filters the queryset based on the given entity model.
133
+
134
+ This method allows filtering the queryset by an `entity_model` parameter, which can
135
+ be of type `EntityModel`, `str`, or `UUID`. It also handles deprecated behavior if
136
+ `user_model` is passed in the `kwargs`.
132
137
 
133
138
  Parameters
134
139
  ----------
135
- entity_slug: str or EntityModel
136
- The entity slug or EntityModel used for filtering the QuerySet.
137
- user_model
138
- Logged in and authenticated django UserModel instance.
140
+ entity_model : EntityModel, str, UUID
141
+ Specifies the entity against which the queryset should be filtered. Accepts an
142
+ `EntityModel` instance, a string (representing the entity slug), or a UUID
143
+ (representing the entity ID).
144
+ **kwargs
145
+ Additional keyword arguments. Supports a deprecated `user_model` argument for
146
+ backward compatibility.
139
147
 
140
148
  Returns
141
149
  -------
142
- EstimateModelQuerySet
143
- Returns a EstimateModelQuerySet with applied filters.
150
+ QuerySet
151
+ A Django QuerySet filtered according to the given entity model.
152
+
153
+ Raises
154
+ ------
155
+ EstimateModelValidationError
156
+ If `entity_model` is of an unsupported type.
144
157
  """
145
- qs = self.for_user(user_model)
146
- if isinstance(entity_slug, EntityModel):
147
- return qs.filter(
148
- Q(entity=entity_slug)
158
+ EntityModel = lazy_loader.get_entity_model()
159
+
160
+ qs = self.get_queryset()
161
+ if 'user_model' in kwargs:
162
+ warnings.warn(
163
+ 'user_model parameter is deprecated and will be removed in a future release. '
164
+ 'Use for_user(user_model).for_entity(entity_model) instead to keep current behavior.',
165
+ DeprecationWarning,
166
+ stacklevel=2
149
167
  )
150
- return qs.filter(
151
- Q(entity__slug__exact=entity_slug)
152
- )
168
+ if DJANGO_LEDGER_USE_DEPRECATED_BEHAVIOR:
169
+ qs = qs.for_user(kwargs['user_model'])
170
+
171
+ if isinstance(entity_model, EntityModel):
172
+ qs = qs.filter(entity=entity_model)
173
+ elif isinstance(entity_model, str):
174
+ qs = qs.filter(entity__slug__exact=entity_model)
175
+ elif isinstance(entity_model, UUID):
176
+ qs = qs.filter(entity_id=entity_model)
177
+ else:
178
+ raise EstimateModelValidationError(
179
+ message='entity_model must be either a string, UUID or an EntityModel'
180
+ )
181
+ return qs
153
182
 
154
183
 
155
184
  class EstimateModelAbstract(CreateUpdateMixIn,
@@ -16,11 +16,11 @@ ________
16
16
  >>> ledger_model, invoice_model = invoice_model.configure(entity_slug=entity_slug, user_model=user_model)
17
17
  >>> invoice_model.save()
18
18
  """
19
-
19
+ import warnings
20
20
  from datetime import date, datetime
21
21
  from decimal import Decimal
22
22
  from typing import Union, Optional, Tuple, Dict
23
- from uuid import uuid4
23
+ from uuid import uuid4, UUID
24
24
 
25
25
  from django.contrib.auth import get_user_model
26
26
  from django.core.exceptions import ValidationError, ObjectDoesNotExist
@@ -37,6 +37,7 @@ from django_ledger.models import (
37
37
  lazy_loader, ItemTransactionModelQuerySet,
38
38
  ItemModelQuerySet, ItemModel, QuerySet, Manager
39
39
  )
40
+ from django_ledger.models.deprecations import deprecated_entity_slug_behavior
40
41
  from django_ledger.models.entity import EntityModel
41
42
  from django_ledger.models.mixins import (
42
43
  CreateUpdateMixIn, AccrualMixIn,
@@ -51,7 +52,8 @@ from django_ledger.models.signals import (
51
52
  invoice_status_canceled,
52
53
  invoice_status_void
53
54
  )
54
- from django_ledger.settings import DJANGO_LEDGER_DOCUMENT_NUMBER_PADDING, DJANGO_LEDGER_INVOICE_NUMBER_PREFIX
55
+ from django_ledger.settings import DJANGO_LEDGER_DOCUMENT_NUMBER_PADDING, DJANGO_LEDGER_INVOICE_NUMBER_PREFIX, \
56
+ DJANGO_LEDGER_USE_DEPRECATED_BEHAVIOR
55
57
 
56
58
  UserModel = get_user_model()
57
59
 
@@ -176,6 +178,14 @@ class InvoiceModelQuerySet(QuerySet):
176
178
  """
177
179
  return self.filter(invoice_status__exact=InvoiceModel.INVOICE_STATUS_APPROVED)
178
180
 
181
+ def for_user(self, user_model):
182
+ if user_model.is_superuser:
183
+ return self
184
+ return self.filter(
185
+ Q(ledger__entity__admin=user_model) |
186
+ Q(ledger__entity__managers__in=[user_model])
187
+ )
188
+
179
189
 
180
190
  class InvoiceModelManager(Manager):
181
191
  """
@@ -183,48 +193,52 @@ class InvoiceModelManager(Manager):
183
193
  The default "get_queryset" has been overridden to refer the custom defined "InvoiceModelQuerySet"
184
194
  """
185
195
 
186
- def get_queryset(self):
187
- qs = super().get_queryset()
196
+ def get_queryset(self) -> InvoiceModelQuerySet:
197
+ qs = InvoiceModelQuerySet(self.model, using=self._db)
188
198
  return qs.select_related(
189
199
  'ledger',
190
200
  'ledger__entity'
191
201
  )
192
202
 
193
- def for_user(self, user_model):
194
- qs = self.get_queryset()
195
- if user_model.is_superuser:
196
- return qs
197
- return qs.filter(
198
- Q(ledger__entity__admin=user_model) |
199
- Q(ledger__entity__managers__in=[user_model])
200
- )
201
-
202
- def for_entity(self, entity_slug, user_model) -> InvoiceModelQuerySet:
203
+ @deprecated_entity_slug_behavior
204
+ def for_entity(self, entity_model: EntityModel | str | UUID = None, **kwargs) -> InvoiceModelQuerySet:
203
205
  """
204
206
  Returns a QuerySet of InvoiceModels associated with a specific EntityModel & UserModel.
205
207
  May pass an instance of EntityModel or a String representing the EntityModel slug.
206
208
 
207
209
  Parameters
208
210
  ----------
209
- entity_slug: str or EntityModel
211
+ entity_model: str or EntityModel
210
212
  The entity slug or EntityModel used for filtering the QuerySet.
211
- user_model
212
- The request UserModel to check for privileges.
213
213
 
214
214
  Returns
215
215
  -------
216
216
  InvoiceModelQuerySet
217
217
  A Filtered InvoiceModelQuerySet.
218
218
  """
219
- qs = self.for_user(user_model)
220
- if isinstance(entity_slug, EntityModel):
221
- return qs.filter(ledger__entity=entity_slug)
222
- elif isinstance(entity_slug, str):
223
- return qs.filter(ledger__entity__slug__exact=entity_slug)
219
+ qs = self.get_queryset()
224
220
 
225
- def for_entity_unpaid(self, entity_slug, user_model):
226
- qs = self.for_entity(entity_slug=entity_slug, user_model=user_model)
227
- return qs.approved()
221
+ if 'user_model' in kwargs:
222
+ warnings.warn(
223
+ 'user_model parameter is deprecated and will be removed in a future release. '
224
+ 'Use for_user(user_model).for_entity(entity_model) instead to keep current behavior.',
225
+ DeprecationWarning,
226
+ stacklevel=2
227
+ )
228
+ if DJANGO_LEDGER_USE_DEPRECATED_BEHAVIOR:
229
+ qs = qs.for_user(kwargs['user_model'])
230
+
231
+ if isinstance(entity_model, EntityModel):
232
+ qs = qs.filter(ledger__entity=entity_model)
233
+ elif isinstance(entity_model, UUID):
234
+ qs = qs.filter(ledger__entity_id=entity_model)
235
+ elif isinstance(entity_model, str):
236
+ qs = qs.filter(ledger__entity__slug__exact=entity_model)
237
+ else:
238
+ raise InvoiceModelValidationError(
239
+ message='Must provide either a string, UUID or an EntityModel',
240
+ )
241
+ return qs
228
242
 
229
243
 
230
244
  class InvoiceModelAbstract(
@@ -325,14 +339,20 @@ class InvoiceModelAbstract(
325
339
 
326
340
  cash_account = models.ForeignKey('django_ledger.AccountModel',
327
341
  on_delete=models.RESTRICT,
342
+ null=True,
343
+ blank=True,
328
344
  verbose_name=_('Cash Account'),
329
345
  related_name=f'{REL_NAME_PREFIX}_cash_account')
330
346
  prepaid_account = models.ForeignKey('django_ledger.AccountModel',
331
347
  on_delete=models.RESTRICT,
348
+ null=True,
349
+ blank=True,
332
350
  verbose_name=_('Prepaid Account'),
333
351
  related_name=f'{REL_NAME_PREFIX}_prepaid_account')
334
352
  unearned_account = models.ForeignKey('django_ledger.AccountModel',
335
353
  on_delete=models.RESTRICT,
354
+ null=True,
355
+ blank=True,
336
356
  verbose_name=_('Unearned Account'),
337
357
  related_name=f'{REL_NAME_PREFIX}_unearned_account')
338
358