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/customer.py
CHANGED
|
@@ -8,21 +8,25 @@ created before it can be assigned to the InvoiceModel. Only customers who are ac
|
|
|
8
8
|
|
|
9
9
|
import os
|
|
10
10
|
import warnings
|
|
11
|
-
from uuid import
|
|
11
|
+
from uuid import UUID, uuid4
|
|
12
12
|
|
|
13
13
|
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
|
14
|
-
from django.db import models, transaction
|
|
15
|
-
from django.db.models import
|
|
14
|
+
from django.db import IntegrityError, models, transaction
|
|
15
|
+
from django.db.models import F, Manager, Q, QuerySet
|
|
16
16
|
from django.utils.text import slugify
|
|
17
17
|
from django.utils.translation import gettext_lazy as _
|
|
18
18
|
|
|
19
19
|
from django_ledger.models.deprecations import deprecated_entity_slug_behavior
|
|
20
|
-
from django_ledger.models.mixins import
|
|
20
|
+
from django_ledger.models.mixins import (
|
|
21
|
+
ContactInfoMixIn,
|
|
22
|
+
CreateUpdateMixIn,
|
|
23
|
+
TaxCollectionMixIn,
|
|
24
|
+
)
|
|
21
25
|
from django_ledger.models.utils import lazy_loader
|
|
22
26
|
from django_ledger.settings import (
|
|
23
|
-
DJANGO_LEDGER_DOCUMENT_NUMBER_PADDING,
|
|
24
27
|
DJANGO_LEDGER_CUSTOMER_NUMBER_PREFIX,
|
|
25
|
-
|
|
28
|
+
DJANGO_LEDGER_DOCUMENT_NUMBER_PADDING,
|
|
29
|
+
DJANGO_LEDGER_USE_DEPRECATED_BEHAVIOR,
|
|
26
30
|
)
|
|
27
31
|
|
|
28
32
|
|
|
@@ -67,8 +71,8 @@ class CustomerModelQueryset(QuerySet):
|
|
|
67
71
|
if user_model.is_superuser:
|
|
68
72
|
return self
|
|
69
73
|
return self.filter(
|
|
70
|
-
Q(entity_model__admin=user_model)
|
|
71
|
-
Q(entity_model__managers__in=[user_model])
|
|
74
|
+
Q(entity_model__admin=user_model)
|
|
75
|
+
| Q(entity_model__managers__in=[user_model])
|
|
72
76
|
)
|
|
73
77
|
|
|
74
78
|
def active(self) -> QuerySet:
|
|
@@ -117,9 +121,7 @@ class CustomerModelQueryset(QuerySet):
|
|
|
117
121
|
CustomerModelQueryset
|
|
118
122
|
A QuerySet of visible Customers.
|
|
119
123
|
"""
|
|
120
|
-
return self.filter(
|
|
121
|
-
Q(hidden=False) & Q(active=True)
|
|
122
|
-
)
|
|
124
|
+
return self.filter(Q(hidden=False) & Q(active=True))
|
|
123
125
|
|
|
124
126
|
|
|
125
127
|
class CustomerModelManager(Manager):
|
|
@@ -129,7 +131,9 @@ class CustomerModelManager(Manager):
|
|
|
129
131
|
"""
|
|
130
132
|
|
|
131
133
|
@deprecated_entity_slug_behavior
|
|
132
|
-
def for_entity(
|
|
134
|
+
def for_entity(
|
|
135
|
+
self, entity_model: 'EntityModel | str | UUID' = None, **kwargs
|
|
136
|
+
) -> CustomerModelQueryset:
|
|
133
137
|
"""
|
|
134
138
|
Fetches a QuerySet of CustomerModel associated with a specific EntityModel & UserModel.
|
|
135
139
|
May pass an instance of EntityModel or a String representing the EntityModel slug.
|
|
@@ -151,7 +155,7 @@ class CustomerModelManager(Manager):
|
|
|
151
155
|
'user_model parameter is deprecated and will be removed in a future release. '
|
|
152
156
|
'Use for_user(user_model).for_entity(entity_model) instead to keep current behavior.',
|
|
153
157
|
DeprecationWarning,
|
|
154
|
-
stacklevel=2
|
|
158
|
+
stacklevel=2,
|
|
155
159
|
)
|
|
156
160
|
if DJANGO_LEDGER_USE_DEPRECATED_BEHAVIOR:
|
|
157
161
|
qs = qs.for_user(kwargs['user_model'])
|
|
@@ -176,7 +180,7 @@ class CustomerModelAbstract(ContactInfoMixIn, TaxCollectionMixIn, CreateUpdateMi
|
|
|
176
180
|
|
|
177
181
|
1. :func:`ContactInfoMixIn <django_ledger.models.mixins.ContactInfoMixIn>`
|
|
178
182
|
2. :func:`CreateUpdateMixIn <django_ledger.models.mixins.CreateUpdateMixIn>`
|
|
179
|
-
|
|
183
|
+
|
|
180
184
|
Attributes
|
|
181
185
|
__________
|
|
182
186
|
uuid : UUID
|
|
@@ -210,19 +214,27 @@ class CustomerModelAbstract(ContactInfoMixIn, TaxCollectionMixIn, CreateUpdateMi
|
|
|
210
214
|
unique=True,
|
|
211
215
|
null=True,
|
|
212
216
|
blank=True,
|
|
213
|
-
verbose_name='User defined customer code'
|
|
217
|
+
verbose_name='User defined customer code',
|
|
214
218
|
)
|
|
215
219
|
customer_name = models.CharField(max_length=100)
|
|
216
|
-
customer_number = models.CharField(
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
220
|
+
customer_number = models.CharField(
|
|
221
|
+
max_length=30,
|
|
222
|
+
editable=False,
|
|
223
|
+
verbose_name=_('Customer Number'),
|
|
224
|
+
help_text='System generated customer number.',
|
|
225
|
+
)
|
|
226
|
+
entity_model = models.ForeignKey(
|
|
227
|
+
'django_ledger.EntityModel',
|
|
228
|
+
editable=False,
|
|
229
|
+
on_delete=models.CASCADE,
|
|
230
|
+
verbose_name=_('Customer Entity'),
|
|
231
|
+
)
|
|
222
232
|
description = models.TextField()
|
|
223
233
|
active = models.BooleanField(default=True)
|
|
224
234
|
hidden = models.BooleanField(default=False)
|
|
225
|
-
picture = models.ImageField(
|
|
235
|
+
picture = models.ImageField(
|
|
236
|
+
upload_to=customer_picture_upload_to, null=True, blank=True
|
|
237
|
+
)
|
|
226
238
|
|
|
227
239
|
additional_info = models.JSONField(null=True, blank=True, default=dict)
|
|
228
240
|
|
|
@@ -238,9 +250,7 @@ class CustomerModelAbstract(ContactInfoMixIn, TaxCollectionMixIn, CreateUpdateMi
|
|
|
238
250
|
models.Index(fields=['active']),
|
|
239
251
|
models.Index(fields=['hidden']),
|
|
240
252
|
]
|
|
241
|
-
unique_together = [
|
|
242
|
-
('entity_model', 'customer_number')
|
|
243
|
-
]
|
|
253
|
+
unique_together = [('entity_model', 'customer_number')]
|
|
244
254
|
|
|
245
255
|
def __str__(self):
|
|
246
256
|
if not self.customer_number:
|
|
@@ -257,10 +267,7 @@ class CustomerModelAbstract(ContactInfoMixIn, TaxCollectionMixIn, CreateUpdateMi
|
|
|
257
267
|
bool
|
|
258
268
|
True if customer model can be generated, else False.
|
|
259
269
|
"""
|
|
260
|
-
return all([
|
|
261
|
-
self.entity_model_id,
|
|
262
|
-
not self.customer_number
|
|
263
|
-
])
|
|
270
|
+
return all([self.entity_model_id, not self.customer_number])
|
|
264
271
|
|
|
265
272
|
def _get_next_state_model(self, raise_exception: bool = True):
|
|
266
273
|
"""
|
|
@@ -282,10 +289,12 @@ class CustomerModelAbstract(ContactInfoMixIn, TaxCollectionMixIn, CreateUpdateMi
|
|
|
282
289
|
try:
|
|
283
290
|
LOOKUP = {
|
|
284
291
|
'entity_model_id__exact': self.entity_model_id,
|
|
285
|
-
'key__exact': EntityStateModel.KEY_CUSTOMER
|
|
292
|
+
'key__exact': EntityStateModel.KEY_CUSTOMER,
|
|
286
293
|
}
|
|
287
294
|
|
|
288
|
-
state_model_qs = EntityStateModel.objects.filter(
|
|
295
|
+
state_model_qs = EntityStateModel.objects.filter(
|
|
296
|
+
**LOOKUP
|
|
297
|
+
).select_for_update()
|
|
289
298
|
state_model = state_model_qs.get()
|
|
290
299
|
state_model.sequence = F('sequence') + 1
|
|
291
300
|
state_model.save()
|
|
@@ -293,13 +302,12 @@ class CustomerModelAbstract(ContactInfoMixIn, TaxCollectionMixIn, CreateUpdateMi
|
|
|
293
302
|
|
|
294
303
|
return state_model
|
|
295
304
|
except ObjectDoesNotExist:
|
|
296
|
-
|
|
297
305
|
LOOKUP = {
|
|
298
306
|
'entity_model_id': self.entity_model_id,
|
|
299
307
|
'entity_unit_id': None,
|
|
300
308
|
'fiscal_year': None,
|
|
301
309
|
'key': EntityStateModel.KEY_CUSTOMER,
|
|
302
|
-
'sequence': 1
|
|
310
|
+
'sequence': 1,
|
|
303
311
|
}
|
|
304
312
|
state_model = EntityStateModel.objects.create(**LOOKUP)
|
|
305
313
|
return state_model
|
|
@@ -324,7 +332,6 @@ class CustomerModelAbstract(ContactInfoMixIn, TaxCollectionMixIn, CreateUpdateMi
|
|
|
324
332
|
"""
|
|
325
333
|
if self.can_generate_customer_number():
|
|
326
334
|
with transaction.atomic(durable=True):
|
|
327
|
-
|
|
328
335
|
state_model = None
|
|
329
336
|
while not state_model:
|
|
330
337
|
state_model = self._get_next_state_model(raise_exception=False)
|
|
@@ -337,6 +344,12 @@ class CustomerModelAbstract(ContactInfoMixIn, TaxCollectionMixIn, CreateUpdateMi
|
|
|
337
344
|
|
|
338
345
|
return self.customer_number
|
|
339
346
|
|
|
347
|
+
def validate_for_entity(self, entity_model: 'EntityModel'):
|
|
348
|
+
if entity_model.uuid != self.entity_model_id:
|
|
349
|
+
raise CustomerModelValidationError(
|
|
350
|
+
'EntityModel does not belong to this Vendor'
|
|
351
|
+
)
|
|
352
|
+
|
|
340
353
|
def clean(self):
|
|
341
354
|
"""
|
|
342
355
|
Custom defined clean method that fetches the next customer number if not yet fetched.
|