django-ledger 0.8.0__py3-none-any.whl → 0.8.2__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/forms/account.py +45 -46
- django_ledger/forms/data_import.py +182 -64
- django_ledger/io/io_core.py +507 -374
- django_ledger/migrations/0026_stagedtransactionmodel_customer_model_and_more.py +56 -0
- django_ledger/models/__init__.py +2 -1
- django_ledger/models/bill.py +337 -300
- django_ledger/models/customer.py +47 -34
- django_ledger/models/data_import.py +770 -289
- django_ledger/models/entity.py +882 -637
- django_ledger/models/mixins.py +421 -282
- django_ledger/models/receipt.py +1083 -0
- django_ledger/models/transactions.py +105 -41
- django_ledger/models/unit.py +42 -30
- django_ledger/models/utils.py +12 -2
- django_ledger/models/vendor.py +85 -66
- django_ledger/settings.py +1 -0
- django_ledger/static/django_ledger/bundle/djetler.bundle.js +1 -1
- django_ledger/static/django_ledger/bundle/djetler.bundle.js.LICENSE.txt +1 -13
- django_ledger/templates/django_ledger/bills/bill_update.html +1 -1
- 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 +3 -1
- 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/invoice/invoice_update.html +1 -1
- django_ledger/templates/django_ledger/layouts/base.html +3 -1
- 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 +3 -2
- django_ledger/templates/django_ledger/vendor/vendor_detail.html +86 -0
- django_ledger/templatetags/django_ledger.py +338 -191
- django_ledger/urls/__init__.py +1 -0
- 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/customer.py +56 -14
- django_ledger/views/data_import.py +119 -66
- django_ledger/views/mixins.py +112 -86
- django_ledger/views/receipt.py +294 -0
- django_ledger/views/vendor.py +53 -14
- {django_ledger-0.8.0.dist-info → django_ledger-0.8.2.dist-info}/METADATA +1 -1
- {django_ledger-0.8.0.dist-info → django_ledger-0.8.2.dist-info}/RECORD +55 -45
- django_ledger/static/django_ledger/bundle/styles.bundle.js +0 -1
- {django_ledger-0.8.0.dist-info → django_ledger-0.8.2.dist-info}/WHEEL +0 -0
- {django_ledger-0.8.0.dist-info → django_ledger-0.8.2.dist-info}/licenses/AUTHORS.md +0 -0
- {django_ledger-0.8.0.dist-info → django_ledger-0.8.2.dist-info}/licenses/LICENSE +0 -0
- {django_ledger-0.8.0.dist-info → django_ledger-0.8.2.dist-info}/top_level.txt +0 -0
django_ledger/models/vendor.py
CHANGED
|
@@ -10,23 +10,29 @@ Vendors can be flagged as active/inactive or hidden. Vendors who no longer condu
|
|
|
10
10
|
whether temporarily or indefinitely may be flagged as inactive (i.e. active is False). Hidden Vendors will not show up
|
|
11
11
|
as an option in the UI, but can still be used programmatically (via API).
|
|
12
12
|
"""
|
|
13
|
+
|
|
13
14
|
import os
|
|
14
15
|
import warnings
|
|
15
|
-
from uuid import
|
|
16
|
+
from uuid import UUID, uuid4
|
|
16
17
|
|
|
17
18
|
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
|
18
|
-
from django.db import models, transaction
|
|
19
|
-
from django.db.models import
|
|
19
|
+
from django.db import IntegrityError, models, transaction
|
|
20
|
+
from django.db.models import F, Manager, Q, QuerySet
|
|
20
21
|
from django.utils.text import slugify
|
|
21
22
|
from django.utils.translation import gettext_lazy as _
|
|
22
23
|
|
|
23
24
|
from django_ledger.models.deprecations import deprecated_entity_slug_behavior
|
|
24
|
-
from django_ledger.models.mixins import
|
|
25
|
+
from django_ledger.models.mixins import (
|
|
26
|
+
ContactInfoMixIn,
|
|
27
|
+
CreateUpdateMixIn,
|
|
28
|
+
FinancialAccountInfoMixin,
|
|
29
|
+
TaxInfoMixIn,
|
|
30
|
+
)
|
|
25
31
|
from django_ledger.models.utils import lazy_loader
|
|
26
32
|
from django_ledger.settings import (
|
|
27
33
|
DJANGO_LEDGER_DOCUMENT_NUMBER_PADDING,
|
|
34
|
+
DJANGO_LEDGER_USE_DEPRECATED_BEHAVIOR,
|
|
28
35
|
DJANGO_LEDGER_VENDOR_NUMBER_PREFIX,
|
|
29
|
-
DJANGO_LEDGER_USE_DEPRECATED_BEHAVIOR
|
|
30
36
|
)
|
|
31
37
|
|
|
32
38
|
|
|
@@ -52,8 +58,8 @@ class VendorModelQuerySet(QuerySet):
|
|
|
52
58
|
if user_model.is_superuser:
|
|
53
59
|
return self
|
|
54
60
|
return self.filter(
|
|
55
|
-
Q(entity_model__admin=user_model)
|
|
56
|
-
Q(entity_model__managers__in=[user_model])
|
|
61
|
+
Q(entity_model__admin=user_model)
|
|
62
|
+
| Q(entity_model__managers__in=[user_model])
|
|
57
63
|
)
|
|
58
64
|
|
|
59
65
|
def active(self) -> 'VendorModelQuerySet':
|
|
@@ -102,9 +108,7 @@ class VendorModelQuerySet(QuerySet):
|
|
|
102
108
|
VendorModelQuerySet
|
|
103
109
|
A QuerySet of visible Vendors.
|
|
104
110
|
"""
|
|
105
|
-
return self.filter(
|
|
106
|
-
Q(hidden=False) & Q(active=True)
|
|
107
|
-
)
|
|
111
|
+
return self.filter(Q(hidden=False) & Q(active=True))
|
|
108
112
|
|
|
109
113
|
|
|
110
114
|
class VendorModelManager(Manager):
|
|
@@ -116,34 +120,36 @@ class VendorModelManager(Manager):
|
|
|
116
120
|
"""
|
|
117
121
|
|
|
118
122
|
@deprecated_entity_slug_behavior
|
|
119
|
-
def for_entity(
|
|
123
|
+
def for_entity(
|
|
124
|
+
self, entity_model: 'EntityModel | str | UUID' = None, **kwargs
|
|
125
|
+
) -> VendorModelQuerySet:
|
|
120
126
|
"""
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
127
|
+
Filters the queryset for a given entity model.
|
|
128
|
+
|
|
129
|
+
This method modifies the queryset to include only those records
|
|
130
|
+
associated with the specified entity model. The entity model can
|
|
131
|
+
be provided in various formats, such as an instance of `EntityModel`,
|
|
132
|
+
a string representing the entity's slug, or its UUID.
|
|
133
|
+
|
|
134
|
+
If a deprecated parameter `user_model` is provided, it will issue
|
|
135
|
+
a warning and may alter the behavior if the deprecated behavior flag
|
|
136
|
+
`DJANGO_LEDGER_USE_DEPRECATED_BEHAVIOR` is set.
|
|
137
|
+
|
|
138
|
+
Parameters
|
|
139
|
+
----------
|
|
140
|
+
entity_model : EntityModel | str | UUID
|
|
141
|
+
The entity model or its identifier (slug or UUID) to filter the
|
|
142
|
+
queryset by.
|
|
143
|
+
|
|
144
|
+
**kwargs
|
|
145
|
+
Additional parameters for optional functionality. The parameter
|
|
146
|
+
`user_model` is supported for backward compatibility but is
|
|
147
|
+
deprecated and should be avoided.
|
|
148
|
+
|
|
149
|
+
Returns
|
|
150
|
+
-------
|
|
151
|
+
VendorModelQuerySet
|
|
152
|
+
A queryset filtered for the given entity model.
|
|
147
153
|
"""
|
|
148
154
|
EntityModel = lazy_loader.get_entity_model()
|
|
149
155
|
|
|
@@ -153,7 +159,7 @@ class VendorModelManager(Manager):
|
|
|
153
159
|
'user_model parameter is deprecated and will be removed in a future release. '
|
|
154
160
|
'Use for_user(user_model).for_entity(entity_model) instead to keep current behavior.',
|
|
155
161
|
DeprecationWarning,
|
|
156
|
-
stacklevel=2
|
|
162
|
+
stacklevel=2,
|
|
157
163
|
)
|
|
158
164
|
|
|
159
165
|
if DJANGO_LEDGER_USE_DEPRECATED_BEHAVIOR:
|
|
@@ -172,10 +178,9 @@ class VendorModelManager(Manager):
|
|
|
172
178
|
return qs
|
|
173
179
|
|
|
174
180
|
|
|
175
|
-
class VendorModelAbstract(
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
CreateUpdateMixIn):
|
|
181
|
+
class VendorModelAbstract(
|
|
182
|
+
ContactInfoMixIn, FinancialAccountInfoMixin, TaxInfoMixIn, CreateUpdateMixIn
|
|
183
|
+
):
|
|
179
184
|
"""
|
|
180
185
|
This is the main abstract class which the VendorModel database will inherit from.
|
|
181
186
|
The VendorModel inherits functionality from the following MixIns:
|
|
@@ -214,28 +219,33 @@ class VendorModelAbstract(ContactInfoMixIn,
|
|
|
214
219
|
|
|
215
220
|
|
|
216
221
|
"""
|
|
222
|
+
|
|
217
223
|
uuid = models.UUIDField(default=uuid4, editable=False, primary_key=True)
|
|
218
224
|
vendor_code = models.SlugField(
|
|
219
|
-
max_length=50,
|
|
225
|
+
max_length=50, null=True, blank=True, verbose_name='User defined vendor code.'
|
|
226
|
+
)
|
|
227
|
+
vendor_number = models.CharField(
|
|
228
|
+
max_length=30,
|
|
220
229
|
null=True,
|
|
221
230
|
blank=True,
|
|
222
|
-
|
|
231
|
+
editable=False,
|
|
232
|
+
verbose_name=_('Vendor Number'),
|
|
233
|
+
help_text='System generated vendor number.',
|
|
223
234
|
)
|
|
224
|
-
vendor_number = models.CharField(max_length=30,
|
|
225
|
-
null=True,
|
|
226
|
-
blank=True,
|
|
227
|
-
editable=False,
|
|
228
|
-
verbose_name=_('Vendor Number'), help_text='System generated vendor number.')
|
|
229
235
|
vendor_name = models.CharField(max_length=100)
|
|
230
236
|
|
|
231
|
-
entity_model = models.ForeignKey(
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
237
|
+
entity_model = models.ForeignKey(
|
|
238
|
+
'django_ledger.EntityModel',
|
|
239
|
+
on_delete=models.CASCADE,
|
|
240
|
+
verbose_name=_('Vendor Entity'),
|
|
241
|
+
editable=False,
|
|
242
|
+
)
|
|
235
243
|
description = models.TextField()
|
|
236
244
|
active = models.BooleanField(default=True)
|
|
237
245
|
hidden = models.BooleanField(default=False)
|
|
238
|
-
picture = models.ImageField(
|
|
246
|
+
picture = models.ImageField(
|
|
247
|
+
upload_to=vendor_picture_upload_to, null=True, blank=True
|
|
248
|
+
)
|
|
239
249
|
|
|
240
250
|
additional_info = models.JSONField(null=True, blank=True, default=dict)
|
|
241
251
|
|
|
@@ -251,9 +261,7 @@ class VendorModelAbstract(ContactInfoMixIn,
|
|
|
251
261
|
models.Index(fields=['active']),
|
|
252
262
|
models.Index(fields=['hidden']),
|
|
253
263
|
]
|
|
254
|
-
unique_together = [
|
|
255
|
-
('entity_model', 'vendor_number')
|
|
256
|
-
]
|
|
264
|
+
unique_together = [('entity_model', 'vendor_number')]
|
|
257
265
|
abstract = True
|
|
258
266
|
|
|
259
267
|
def __str__(self):
|
|
@@ -261,6 +269,20 @@ class VendorModelAbstract(ContactInfoMixIn,
|
|
|
261
269
|
f'Unknown Vendor: {self.vendor_name}'
|
|
262
270
|
return f'{self.vendor_number}: {self.vendor_name}'
|
|
263
271
|
|
|
272
|
+
def validate_for_entity(self, entity_model: 'EntityModel | str | UUID'):
|
|
273
|
+
EntityModel = lazy_loader.get_entity_model()
|
|
274
|
+
if isinstance(entity_model, str):
|
|
275
|
+
is_valid = entity_model == self.entity_model.slug
|
|
276
|
+
elif isinstance(entity_model, EntityModel):
|
|
277
|
+
is_valid = entity_model == self.entity_model
|
|
278
|
+
elif isinstance(entity_model, UUID):
|
|
279
|
+
is_valid = entity_model == self.entity_model_id
|
|
280
|
+
|
|
281
|
+
if not is_valid:
|
|
282
|
+
raise VendorModelValidationError(
|
|
283
|
+
'EntityModel does not belong to this Vendor'
|
|
284
|
+
)
|
|
285
|
+
|
|
264
286
|
def can_generate_vendor_number(self) -> bool:
|
|
265
287
|
"""
|
|
266
288
|
Determines if the VendorModel can be issued a Vendor Number.
|
|
@@ -271,10 +293,7 @@ class VendorModelAbstract(ContactInfoMixIn,
|
|
|
271
293
|
bool
|
|
272
294
|
True if the vendor number can be generated, else False.
|
|
273
295
|
"""
|
|
274
|
-
return all([
|
|
275
|
-
self.entity_model_id,
|
|
276
|
-
not self.vendor_number
|
|
277
|
-
])
|
|
296
|
+
return all([self.entity_model_id, not self.vendor_number])
|
|
278
297
|
|
|
279
298
|
def _get_next_state_model(self, raise_exception: bool = True):
|
|
280
299
|
"""
|
|
@@ -296,10 +315,12 @@ class VendorModelAbstract(ContactInfoMixIn,
|
|
|
296
315
|
try:
|
|
297
316
|
LOOKUP = {
|
|
298
317
|
'entity_model_id__exact': self.entity_model_id,
|
|
299
|
-
'key__exact': EntityStateModel.KEY_VENDOR
|
|
318
|
+
'key__exact': EntityStateModel.KEY_VENDOR,
|
|
300
319
|
}
|
|
301
320
|
|
|
302
|
-
state_model_qs = EntityStateModel.objects.filter(
|
|
321
|
+
state_model_qs = EntityStateModel.objects.filter(
|
|
322
|
+
**LOOKUP
|
|
323
|
+
).select_for_update()
|
|
303
324
|
state_model = state_model_qs.get()
|
|
304
325
|
state_model.sequence = F('sequence') + 1
|
|
305
326
|
state_model.save()
|
|
@@ -307,13 +328,12 @@ class VendorModelAbstract(ContactInfoMixIn,
|
|
|
307
328
|
|
|
308
329
|
return state_model
|
|
309
330
|
except ObjectDoesNotExist:
|
|
310
|
-
|
|
311
331
|
LOOKUP = {
|
|
312
332
|
'entity_model_id': self.entity_model_id,
|
|
313
333
|
'entity_unit_id': None,
|
|
314
334
|
'fiscal_year': None,
|
|
315
335
|
'key': EntityStateModel.KEY_VENDOR,
|
|
316
|
-
'sequence': 1
|
|
336
|
+
'sequence': 1,
|
|
317
337
|
}
|
|
318
338
|
state_model = EntityStateModel.objects.create(**LOOKUP)
|
|
319
339
|
return state_model
|
|
@@ -338,7 +358,6 @@ class VendorModelAbstract(ContactInfoMixIn,
|
|
|
338
358
|
"""
|
|
339
359
|
if self.can_generate_vendor_number():
|
|
340
360
|
with transaction.atomic(durable=True):
|
|
341
|
-
|
|
342
361
|
state_model = None
|
|
343
362
|
while not state_model:
|
|
344
363
|
state_model = self._get_next_state_model(raise_exception=False)
|
django_ledger/settings.py
CHANGED
|
@@ -34,6 +34,7 @@ DJANGO_LEDGER_PO_NUMBER_PREFIX = getattr(settings, 'DJANGO_LEDGER_PO_NUMBER_PREF
|
|
|
34
34
|
DJANGO_LEDGER_ESTIMATE_NUMBER_PREFIX = getattr(settings, 'DJANGO_LEDGER_ESTIMATE_NUMBER_PREFIX', 'E')
|
|
35
35
|
DJANGO_LEDGER_INVOICE_NUMBER_PREFIX = getattr(settings, 'DJANGO_LEDGER_INVOICE_NUMBER_PREFIX', 'I')
|
|
36
36
|
DJANGO_LEDGER_BILL_NUMBER_PREFIX = getattr(settings, 'DJANGO_LEDGER_BILL_NUMBER_PREFIX', 'B')
|
|
37
|
+
DJANGO_LEDGER_RECEIPT_NUMBER_PREFIX = getattr(settings, 'DJANGO_LEDGER_BILL_NUMBER_PREFIX', 'R')
|
|
37
38
|
DJANGO_LEDGER_VENDOR_NUMBER_PREFIX = getattr(settings, 'DJANGO_LEDGER_VENDOR_NUMBER_PREFIX', 'V')
|
|
38
39
|
DJANGO_LEDGER_CUSTOMER_NUMBER_PREFIX = getattr(settings, 'DJANGO_LEDGER_CUSTOMER_NUMBER_PREFIX', 'C')
|
|
39
40
|
DJANGO_LEDGER_EXPENSE_NUMBER_PREFIX = getattr(settings, 'DJANGO_LEDGER_EXPENSE_NUMBER_PREFIX', 'IEX')
|