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.

Files changed (56) hide show
  1. django_ledger/__init__.py +1 -1
  2. django_ledger/forms/account.py +45 -46
  3. django_ledger/forms/data_import.py +182 -64
  4. django_ledger/io/io_core.py +507 -374
  5. django_ledger/migrations/0026_stagedtransactionmodel_customer_model_and_more.py +56 -0
  6. django_ledger/models/__init__.py +2 -1
  7. django_ledger/models/bill.py +337 -300
  8. django_ledger/models/customer.py +47 -34
  9. django_ledger/models/data_import.py +770 -289
  10. django_ledger/models/entity.py +882 -637
  11. django_ledger/models/mixins.py +421 -282
  12. django_ledger/models/receipt.py +1083 -0
  13. django_ledger/models/transactions.py +105 -41
  14. django_ledger/models/unit.py +42 -30
  15. django_ledger/models/utils.py +12 -2
  16. django_ledger/models/vendor.py +85 -66
  17. django_ledger/settings.py +1 -0
  18. django_ledger/static/django_ledger/bundle/djetler.bundle.js +1 -1
  19. django_ledger/static/django_ledger/bundle/djetler.bundle.js.LICENSE.txt +1 -13
  20. django_ledger/templates/django_ledger/bills/bill_update.html +1 -1
  21. django_ledger/templates/django_ledger/components/period_navigator.html +5 -3
  22. django_ledger/templates/django_ledger/customer/customer_detail.html +87 -0
  23. django_ledger/templates/django_ledger/customer/customer_list.html +0 -1
  24. django_ledger/templates/django_ledger/customer/tags/customer_table.html +3 -1
  25. django_ledger/templates/django_ledger/data_import/tags/data_import_job_txs_imported.html +24 -3
  26. django_ledger/templates/django_ledger/data_import/tags/data_import_job_txs_table.html +26 -10
  27. django_ledger/templates/django_ledger/entity/entity_dashboard.html +2 -2
  28. django_ledger/templates/django_ledger/invoice/invoice_update.html +1 -1
  29. django_ledger/templates/django_ledger/layouts/base.html +3 -1
  30. django_ledger/templates/django_ledger/layouts/content_layout_1.html +1 -1
  31. django_ledger/templates/django_ledger/receipt/customer_receipt_report.html +115 -0
  32. django_ledger/templates/django_ledger/receipt/receipt_delete.html +30 -0
  33. django_ledger/templates/django_ledger/receipt/receipt_detail.html +89 -0
  34. django_ledger/templates/django_ledger/receipt/receipt_list.html +134 -0
  35. django_ledger/templates/django_ledger/receipt/vendor_receipt_report.html +115 -0
  36. django_ledger/templates/django_ledger/vendor/tags/vendor_table.html +3 -2
  37. django_ledger/templates/django_ledger/vendor/vendor_detail.html +86 -0
  38. django_ledger/templatetags/django_ledger.py +338 -191
  39. django_ledger/urls/__init__.py +1 -0
  40. django_ledger/urls/customer.py +3 -0
  41. django_ledger/urls/data_import.py +3 -0
  42. django_ledger/urls/receipt.py +102 -0
  43. django_ledger/urls/vendor.py +1 -0
  44. django_ledger/views/__init__.py +1 -0
  45. django_ledger/views/customer.py +56 -14
  46. django_ledger/views/data_import.py +119 -66
  47. django_ledger/views/mixins.py +112 -86
  48. django_ledger/views/receipt.py +294 -0
  49. django_ledger/views/vendor.py +53 -14
  50. {django_ledger-0.8.0.dist-info → django_ledger-0.8.2.dist-info}/METADATA +1 -1
  51. {django_ledger-0.8.0.dist-info → django_ledger-0.8.2.dist-info}/RECORD +55 -45
  52. django_ledger/static/django_ledger/bundle/styles.bundle.js +0 -1
  53. {django_ledger-0.8.0.dist-info → django_ledger-0.8.2.dist-info}/WHEEL +0 -0
  54. {django_ledger-0.8.0.dist-info → django_ledger-0.8.2.dist-info}/licenses/AUTHORS.md +0 -0
  55. {django_ledger-0.8.0.dist-info → django_ledger-0.8.2.dist-info}/licenses/LICENSE +0 -0
  56. {django_ledger-0.8.0.dist-info → django_ledger-0.8.2.dist-info}/top_level.txt +0 -0
@@ -7,6 +7,7 @@ the Vendor states the amount owed by the recipient for the purposes of supplying
7
7
  In addition to tracking the bill amount, it tracks the paid and due amount.
8
8
 
9
9
  """
10
+
10
11
  import warnings
11
12
  from datetime import date, datetime
12
13
  from decimal import Decimal
@@ -26,13 +27,18 @@ from django_ledger.io import ASSET_CA_CASH, ASSET_CA_PREPAID, LIABILITY_CL_ACC_P
26
27
  from django_ledger.io.io_core import get_localtime, get_localdate
27
28
  from django_ledger.models.deprecations import deprecated_entity_slug_behavior
28
29
  from django_ledger.models.entity import EntityModel
29
- from django_ledger.models.items import ItemTransactionModelQuerySet, ItemTransactionModel, ItemModel, ItemModelQuerySet
30
+ from django_ledger.models.items import (
31
+ ItemTransactionModelQuerySet,
32
+ ItemTransactionModel,
33
+ ItemModel,
34
+ ItemModelQuerySet,
35
+ )
30
36
  from django_ledger.models.mixins import (
31
37
  CreateUpdateMixIn,
32
38
  AccrualMixIn,
33
39
  MarkdownNotesMixIn,
34
40
  PaymentTermsMixIn,
35
- ItemizeMixIn
41
+ ItemizeMixIn,
36
42
  )
37
43
  from django_ledger.models.signals import (
38
44
  bill_status_draft,
@@ -43,8 +49,11 @@ from django_ledger.models.signals import (
43
49
  bill_status_void,
44
50
  )
45
51
  from django_ledger.models.utils import lazy_loader
46
- from django_ledger.settings import (DJANGO_LEDGER_DOCUMENT_NUMBER_PADDING, DJANGO_LEDGER_BILL_NUMBER_PREFIX,
47
- DJANGO_LEDGER_USE_DEPRECATED_BEHAVIOR)
52
+ from django_ledger.settings import (
53
+ DJANGO_LEDGER_DOCUMENT_NUMBER_PADDING,
54
+ DJANGO_LEDGER_BILL_NUMBER_PREFIX,
55
+ DJANGO_LEDGER_USE_DEPRECATED_BEHAVIOR,
56
+ )
48
57
 
49
58
  UserModel = get_user_model()
50
59
 
@@ -83,8 +92,8 @@ class BillModelQuerySet(QuerySet):
83
92
  if user_model.is_superuser:
84
93
  return self
85
94
  return self.filter(
86
- Q(ledger__entity__admin=user_model) |
87
- Q(ledger__entity__managers__in=[user_model])
95
+ Q(ledger__entity__admin=user_model)
96
+ | Q(ledger__entity__managers__in=[user_model])
88
97
  )
89
98
 
90
99
  def draft(self):
@@ -168,8 +177,8 @@ class BillModelQuerySet(QuerySet):
168
177
  Returns a QuerySet of active bills only.
169
178
  """
170
179
  return self.filter(
171
- Q(bill_status__exact=BillModel.BILL_STATUS_APPROVED) |
172
- Q(bill_status__exact=BillModel.BILL_STATUS_PAID)
180
+ Q(bill_status__exact=BillModel.BILL_STATUS_APPROVED)
181
+ | Q(bill_status__exact=BillModel.BILL_STATUS_PAID)
173
182
  )
174
183
 
175
184
  def overdue(self):
@@ -205,13 +214,12 @@ class BillModelManager(Manager):
205
214
 
206
215
  def get_queryset(self) -> BillModelQuerySet:
207
216
  qs = BillModelQuerySet(self.model, using=self._db)
208
- return qs.select_related(
209
- 'ledger',
210
- 'ledger__entity'
211
- )
217
+ return qs.select_related('ledger', 'ledger__entity')
212
218
 
213
219
  @deprecated_entity_slug_behavior
214
- def for_entity(self, entity_model: EntityModel | str | UUID = None, **kwargs) -> BillModelQuerySet:
220
+ def for_entity(
221
+ self, entity_model: EntityModel | str | UUID = None, **kwargs
222
+ ) -> BillModelQuerySet:
215
223
  """
216
224
  Fetches a QuerySet of BillModels associated with a specific EntityModel & UserModel.
217
225
  May pass an instance of EntityModel or a String representing the EntityModel slug.
@@ -232,7 +240,7 @@ class BillModelManager(Manager):
232
240
  'user_model parameter is deprecated and will be removed in a future release. '
233
241
  'Use for_user(user_model).for_entity(entity_model) instead to keep current behavior.',
234
242
  DeprecationWarning,
235
- stacklevel=2
243
+ stacklevel=2,
236
244
  )
237
245
  if DJANGO_LEDGER_USE_DEPRECATED_BEHAVIOR:
238
246
  qs = qs.for_user(kwargs['user_model'])
@@ -240,22 +248,16 @@ class BillModelManager(Manager):
240
248
  if isinstance(entity_model, EntityModel):
241
249
  qs = qs.filter(ledger__entity=entity_model)
242
250
  elif isinstance(entity_model, str):
243
- qs = qs.filter(ledger__entity__slug__exact=entity_model)
251
+ qs = qs.filter(ledger__entity__slug__exact=entity_model)
244
252
  elif isinstance(entity_model, UUID):
245
253
  qs = qs.filter(ledger__entity_id=entity_model)
246
254
  else:
247
- raise BillModelValidationError(
248
- 'Must pass EntityModel, slug or UUID'
249
- )
255
+ raise BillModelValidationError('Must pass EntityModel, slug or UUID')
250
256
  return qs
251
257
 
252
258
 
253
259
  class BillModelAbstract(
254
- AccrualMixIn,
255
- ItemizeMixIn,
256
- PaymentTermsMixIn,
257
- MarkdownNotesMixIn,
258
- CreateUpdateMixIn
260
+ AccrualMixIn, ItemizeMixIn, PaymentTermsMixIn, MarkdownNotesMixIn, CreateUpdateMixIn
259
261
  ):
260
262
  """
261
263
  This is the main abstract class which the BillModel database will inherit from.
@@ -323,6 +325,7 @@ class BillModelAbstract(
323
325
  The canceled date represents the date when the BillModel was canceled, if applicable.
324
326
  Will be null unless BillModel is canceled. Defaults to :func:`localdate <django.utils.timezone.localdate>`.
325
327
  """
328
+
326
329
  REL_NAME_PREFIX = 'bill'
327
330
  IS_DEBIT_BALANCE = False
328
331
  ALLOW_MIGRATE = True
@@ -340,7 +343,7 @@ class BillModelAbstract(
340
343
  (BILL_STATUS_APPROVED, _('Approved')),
341
344
  (BILL_STATUS_PAID, _('Paid')),
342
345
  (BILL_STATUS_CANCELED, _('Canceled')),
343
- (BILL_STATUS_VOID, _('Void'))
346
+ (BILL_STATUS_VOID, _('Void')),
344
347
  ]
345
348
  """
346
349
  The different bill status options and their representation in the Database.
@@ -353,58 +356,79 @@ class BillModelAbstract(
353
356
  on_delete=models.CASCADE,
354
357
  null=True,
355
358
  blank=True,
356
- editable=False
359
+ editable=False,
360
+ )
361
+ bill_number = models.SlugField(
362
+ max_length=20, verbose_name=_('Bill Number'), editable=False
363
+ )
364
+ bill_status = models.CharField(
365
+ max_length=10,
366
+ choices=BILL_STATUS,
367
+ default=BILL_STATUS[0][0],
368
+ verbose_name=_('Bill Status'),
369
+ )
370
+ xref = models.SlugField(
371
+ null=True, blank=True, verbose_name=_('External Reference Number')
372
+ )
373
+ vendor = models.ForeignKey(
374
+ 'django_ledger.VendorModel', on_delete=models.CASCADE, verbose_name=_('Vendor')
375
+ )
376
+
377
+ cash_account = models.ForeignKey(
378
+ 'django_ledger.AccountModel',
379
+ on_delete=models.RESTRICT,
380
+ null=True,
381
+ blank=True,
382
+ verbose_name=_('Cash Account'),
383
+ related_name=f'{REL_NAME_PREFIX}_cash_account',
384
+ )
385
+ prepaid_account = models.ForeignKey(
386
+ 'django_ledger.AccountModel',
387
+ on_delete=models.RESTRICT,
388
+ null=True,
389
+ blank=True,
390
+ verbose_name=_('Prepaid Account'),
391
+ related_name=f'{REL_NAME_PREFIX}_prepaid_account',
392
+ )
393
+ unearned_account = models.ForeignKey(
394
+ 'django_ledger.AccountModel',
395
+ on_delete=models.RESTRICT,
396
+ null=True,
397
+ blank=True,
398
+ verbose_name=_('Unearned Account'),
399
+ related_name=f'{REL_NAME_PREFIX}_unearned_account',
400
+ )
401
+
402
+ additional_info = models.JSONField(
403
+ blank=True, null=True, default=dict, verbose_name=_('Bill Additional Info')
404
+ )
405
+ bill_items = models.ManyToManyField(
406
+ 'django_ledger.ItemModel',
407
+ through='django_ledger.ItemTransactionModel',
408
+ through_fields=('bill_model', 'item_model'),
409
+ verbose_name=_('Bill Items'),
410
+ )
411
+
412
+ ce_model = models.ForeignKey(
413
+ 'django_ledger.EstimateModel',
414
+ on_delete=models.RESTRICT,
415
+ null=True,
416
+ blank=True,
417
+ verbose_name=_('Associated Customer Job/Estimate'),
357
418
  )
358
- bill_number = models.SlugField(max_length=20, verbose_name=_('Bill Number'), editable=False)
359
- bill_status = models.CharField(max_length=10,
360
- choices=BILL_STATUS,
361
- default=BILL_STATUS[0][0],
362
- verbose_name=_('Bill Status'))
363
- xref = models.SlugField(null=True, blank=True, verbose_name=_('External Reference Number'))
364
- vendor = models.ForeignKey('django_ledger.VendorModel',
365
- on_delete=models.CASCADE,
366
- verbose_name=_('Vendor'))
367
-
368
- cash_account = models.ForeignKey('django_ledger.AccountModel',
369
- on_delete=models.RESTRICT,
370
- null=True,
371
- blank=True,
372
- verbose_name=_('Cash Account'),
373
- related_name=f'{REL_NAME_PREFIX}_cash_account')
374
- prepaid_account = models.ForeignKey('django_ledger.AccountModel',
375
- on_delete=models.RESTRICT,
376
- null=True,
377
- blank=True,
378
- verbose_name=_('Prepaid Account'),
379
- related_name=f'{REL_NAME_PREFIX}_prepaid_account')
380
- unearned_account = models.ForeignKey('django_ledger.AccountModel',
381
- on_delete=models.RESTRICT,
382
- null=True,
383
- blank=True,
384
- verbose_name=_('Unearned Account'),
385
- related_name=f'{REL_NAME_PREFIX}_unearned_account')
386
-
387
- additional_info = models.JSONField(blank=True,
388
- null=True,
389
- default=dict,
390
- verbose_name=_('Bill Additional Info'))
391
- bill_items = models.ManyToManyField('django_ledger.ItemModel',
392
- through='django_ledger.ItemTransactionModel',
393
- through_fields=('bill_model', 'item_model'),
394
- verbose_name=_('Bill Items'))
395
-
396
- ce_model = models.ForeignKey('django_ledger.EstimateModel',
397
- on_delete=models.RESTRICT,
398
- null=True,
399
- blank=True,
400
- verbose_name=_('Associated Customer Job/Estimate'))
401
419
 
402
420
  date_draft = models.DateField(null=True, blank=True, verbose_name=_('Draft Date'))
403
- date_in_review = models.DateField(null=True, blank=True, verbose_name=_('In Review Date'))
404
- date_approved = models.DateField(null=True, blank=True, verbose_name=_('Approved Date'))
421
+ date_in_review = models.DateField(
422
+ null=True, blank=True, verbose_name=_('In Review Date')
423
+ )
424
+ date_approved = models.DateField(
425
+ null=True, blank=True, verbose_name=_('Approved Date')
426
+ )
405
427
  date_paid = models.DateField(null=True, blank=True, verbose_name=_('Paid Date'))
406
428
  date_void = models.DateField(null=True, blank=True, verbose_name=_('Void Date'))
407
- date_canceled = models.DateField(null=True, blank=True, verbose_name=_('Canceled Date'))
429
+ date_canceled = models.DateField(
430
+ null=True, blank=True, verbose_name=_('Canceled Date')
431
+ )
408
432
 
409
433
  objects = BillModelManager.from_queryset(queryset_class=BillModelQuerySet)()
410
434
 
@@ -416,11 +440,9 @@ class BillModelAbstract(
416
440
  indexes = [
417
441
  models.Index(fields=['bill_status']),
418
442
  models.Index(fields=['terms']),
419
-
420
443
  models.Index(fields=['cash_account']),
421
444
  models.Index(fields=['prepaid_account']),
422
445
  models.Index(fields=['unearned_account']),
423
-
424
446
  models.Index(fields=['date_due']),
425
447
  models.Index(fields=['date_draft']),
426
448
  models.Index(fields=['date_in_review']),
@@ -428,7 +450,6 @@ class BillModelAbstract(
428
450
  models.Index(fields=['date_paid']),
429
451
  models.Index(fields=['date_canceled']),
430
452
  models.Index(fields=['date_void']),
431
-
432
453
  models.Index(fields=['vendor']),
433
454
  models.Index(fields=['bill_number']),
434
455
  ]
@@ -437,20 +458,19 @@ class BillModelAbstract(
437
458
  return f'Bill: {self.bill_number} | {self.get_bill_status_display()}'
438
459
 
439
460
  def is_configured(self) -> bool:
440
- return all([
441
- super().is_configured(),
442
- self.bill_status
443
- ])
461
+ return all([super().is_configured(), self.bill_status])
444
462
 
445
463
  # Configuration...
446
- def configure(self,
447
- entity_slug: Union[str, EntityModel],
448
- user_model: Optional[UserModel] = None,
449
- date_draft: Optional[Union[date, datetime]] = None,
450
- ledger_posted: bool = False,
451
- ledger_name: str = None,
452
- commit: bool = False,
453
- commit_ledger: bool = False):
464
+ def configure(
465
+ self,
466
+ entity_slug: Union[str, EntityModel],
467
+ user_model: Optional[UserModel] = None,
468
+ date_draft: Optional[Union[date, datetime]] = None,
469
+ ledger_posted: bool = False,
470
+ ledger_name: str = None,
471
+ commit: bool = False,
472
+ commit_ledger: bool = False,
473
+ ):
454
474
  """
455
475
  A configuration hook which executes all initial BillModel setup on to the LedgerModel and all initial
456
476
  values of the BillModel. Can only call this method once in the lifetime of a BillModel.
@@ -483,13 +503,19 @@ class BillModelAbstract(
483
503
  if not self.is_configured():
484
504
  if isinstance(entity_slug, str):
485
505
  if not user_model:
486
- raise BillModelValidationError(_('Must pass user_model when using entity_slug.'))
506
+ raise BillModelValidationError(
507
+ _('Must pass user_model when using entity_slug.')
508
+ )
487
509
  entity_qs = EntityModel.objects.for_user(user_model=user_model)
488
- entity_model: EntityModel = get_object_or_404(entity_qs, slug__exact=entity_slug)
510
+ entity_model: EntityModel = get_object_or_404(
511
+ entity_qs, slug__exact=entity_slug
512
+ )
489
513
  elif isinstance(entity_slug, EntityModel):
490
514
  entity_model = entity_slug
491
515
  else:
492
- raise BillModelValidationError('entity_slug must be an instance of str or EntityModel')
516
+ raise BillModelValidationError(
517
+ 'entity_slug must be an instance of str or EntityModel'
518
+ )
493
519
 
494
520
  if entity_model.is_accrual_method():
495
521
  self.accrue = True
@@ -504,7 +530,9 @@ class BillModelAbstract(
504
530
  self.date_draft = get_localdate() if not date_draft else date_draft
505
531
 
506
532
  LedgerModel = lazy_loader.get_ledger_model()
507
- ledger_model: LedgerModel = LedgerModel(entity=entity_model, posted=ledger_posted)
533
+ ledger_model: LedgerModel = LedgerModel(
534
+ entity=entity_model, posted=ledger_posted
535
+ )
508
536
  ledger_name = f'Bill {self.uuid}' if not ledger_name else ledger_name
509
537
  ledger_model.name = ledger_name
510
538
  ledger_model.configure_for_wrapper_model(model_instance=self)
@@ -532,24 +560,30 @@ class BillModelAbstract(
532
560
  return self.is_draft()
533
561
 
534
562
  def migrate_itemtxs(self, itemtxs: Dict, operation: str, commit: bool = False):
535
- itemtxs_batch = super().migrate_itemtxs(itemtxs=itemtxs, commit=commit, operation=operation)
563
+ itemtxs_batch = super().migrate_itemtxs(
564
+ itemtxs=itemtxs, commit=commit, operation=operation
565
+ )
536
566
  self.update_amount_due(itemtxs_qs=itemtxs_batch)
537
567
  self.get_state(commit=True)
538
568
 
539
569
  if commit:
540
- self.save(update_fields=['amount_due',
541
- 'amount_receivable',
542
- 'amount_unearned',
543
- 'amount_earned',
544
- 'updated'])
570
+ self.save(
571
+ update_fields=[
572
+ 'amount_due',
573
+ 'amount_receivable',
574
+ 'amount_unearned',
575
+ 'amount_earned',
576
+ 'updated',
577
+ ]
578
+ )
545
579
  return itemtxs_batch
546
580
 
547
581
  def get_item_model_qs(self) -> ItemModelQuerySet:
548
- return ItemModel.objects.filter(
549
- entity_id__exact=self.ledger.entity_id
550
- ).bills()
582
+ return ItemModel.objects.filter(entity_id__exact=self.ledger.entity_id).bills()
551
583
 
552
- def validate_itemtxs_qs(self, queryset: Union[ItemTransactionModelQuerySet, List[ItemTransactionModel]]):
584
+ def validate_itemtxs_qs(
585
+ self, queryset: Union[ItemTransactionModelQuerySet, List[ItemTransactionModel]]
586
+ ):
553
587
  """
554
588
  Validates that the entire ItemTransactionModelQuerySet is bound to the BillModel.
555
589
 
@@ -558,16 +592,18 @@ class BillModelAbstract(
558
592
  queryset: ItemTransactionModelQuerySet or list of ItemTransactionModel.
559
593
  ItemTransactionModelQuerySet to validate.
560
594
  """
561
- valid = all([
562
- i.bill_model_id == self.uuid for i in queryset
563
- ])
595
+ valid = all([i.bill_model_id == self.uuid for i in queryset])
564
596
  if not valid:
565
- raise BillModelValidationError(f'Invalid queryset. All items must be assigned to Bill {self.uuid}')
597
+ raise BillModelValidationError(
598
+ f'Invalid queryset. All items must be assigned to Bill {self.uuid}'
599
+ )
566
600
 
567
- def get_itemtxs_data(self,
568
- queryset: Optional[ItemTransactionModelQuerySet] = None,
569
- aggregate_on_db: bool = False,
570
- lazy_agg: bool = False) -> Tuple[ItemTransactionModelQuerySet, Dict]:
601
+ def get_itemtxs_data(
602
+ self,
603
+ queryset: Optional[ItemTransactionModelQuerySet] = None,
604
+ aggregate_on_db: bool = False,
605
+ lazy_agg: bool = False,
606
+ ) -> Tuple[ItemTransactionModelQuerySet, Dict]:
571
607
  """
572
608
  Fetches the BillModel Items and aggregates the QuerySet.
573
609
 
@@ -583,21 +619,18 @@ class BillModelAbstract(
583
619
  """
584
620
  if not queryset:
585
621
  queryset = self.itemtransactionmodel_set.all().select_related(
586
- 'item_model',
587
- 'entity_unit',
588
- 'po_model',
589
- 'bill_model')
622
+ 'item_model', 'entity_unit', 'po_model', 'bill_model'
623
+ )
590
624
  else:
591
625
  self.validate_itemtxs_qs(queryset)
592
626
 
593
627
  if aggregate_on_db and isinstance(queryset, ItemTransactionModelQuerySet):
594
628
  return queryset, queryset.aggregate(
595
- total_amount__sum=Sum('total_amount'),
596
- total_items=Count('uuid')
629
+ total_amount__sum=Sum('total_amount'), total_items=Count('uuid')
597
630
  )
598
631
  return queryset, {
599
632
  'total_amount__sum': sum(i.total_amount for i in queryset),
600
- 'total_items': len(queryset)
633
+ 'total_items': len(queryset),
601
634
  } if not lazy_agg else None
602
635
 
603
636
  # ### ItemizeMixIn implementation END...
@@ -607,7 +640,9 @@ class BillModelAbstract(
607
640
  Fetches the TransactionModelQuerySet associated with the BillModel instance.
608
641
  """
609
642
  TransactionModel = lazy_loader.get_txs_model()
610
- transaction_model_qs = TransactionModel.objects.all().for_ledger(ledger_model=self.ledger_id)
643
+ transaction_model_qs = TransactionModel.objects.all().for_ledger(
644
+ ledger_model=self.ledger_id
645
+ )
611
646
  if annotated:
612
647
  return transaction_model_qs.with_annotated_details()
613
648
  return transaction_model_qs
@@ -624,8 +659,9 @@ class BillModelAbstract(
624
659
  """
625
660
  return f'Bill {self.bill_number} account adjustment.'
626
661
 
627
- def get_migration_data(self,
628
- queryset: Optional[ItemTransactionModelQuerySet] = None) -> ItemTransactionModelQuerySet:
662
+ def get_migration_data(
663
+ self, queryset: Optional[ItemTransactionModelQuerySet] = None
664
+ ) -> ItemTransactionModelQuerySet:
629
665
  """
630
666
  Fetches necessary item transaction data to perform a migration into the LedgerModel.
631
667
 
@@ -640,21 +676,30 @@ class BillModelAbstract(
640
676
  else:
641
677
  self.validate_itemtxs_qs(queryset)
642
678
 
643
- return queryset.order_by('item_model__expense_account__uuid',
644
- 'entity_unit__uuid',
645
- 'item_model__expense_account__balance_type').values(
646
- 'item_model__expense_account__uuid',
647
- 'item_model__inventory_account__uuid',
648
- 'item_model__expense_account__balance_type',
649
- 'item_model__inventory_account__balance_type',
650
- 'entity_unit__slug',
651
- 'entity_unit__uuid',
652
- 'total_amount').annotate(
653
- account_unit_total=Sum('total_amount')
679
+ return (
680
+ queryset.order_by(
681
+ 'item_model__expense_account__uuid',
682
+ 'entity_unit__uuid',
683
+ 'item_model__expense_account__balance_type',
684
+ )
685
+ .values(
686
+ 'item_model__expense_account__uuid',
687
+ 'item_model__inventory_account__uuid',
688
+ 'item_model__expense_account__balance_type',
689
+ 'item_model__inventory_account__balance_type',
690
+ 'entity_unit__slug',
691
+ 'entity_unit__uuid',
692
+ 'total_amount',
693
+ )
694
+ .annotate(account_unit_total=Sum('total_amount'))
654
695
  )
655
696
 
656
- def update_amount_due(self, itemtxs_qs: Optional[
657
- Union[ItemTransactionModelQuerySet, List[ItemTransactionModel]]] = None) -> ItemTransactionModelQuerySet:
697
+ def update_amount_due(
698
+ self,
699
+ itemtxs_qs: Optional[
700
+ Union[ItemTransactionModelQuerySet, List[ItemTransactionModel]]
701
+ ] = None,
702
+ ) -> ItemTransactionModelQuerySet:
658
703
  """
659
704
  Updates the BillModel amount due.
660
705
 
@@ -737,11 +782,7 @@ class BillModelAbstract(
737
782
  bool
738
783
  True if BillModel is Active, else False.
739
784
  """
740
- return any([
741
- self.is_paid(),
742
- self.is_approved(),
743
- self.is_void()
744
- ])
785
+ return any([self.is_paid(), self.is_approved(), self.is_void()])
745
786
 
746
787
  def is_void(self) -> bool:
747
788
  """
@@ -788,10 +829,7 @@ class BillModelAbstract(
788
829
  bool
789
830
  True if BillModel can be marked as in review, else False.
790
831
  """
791
- return all([
792
- self.is_configured(),
793
- self.is_draft()
794
- ])
832
+ return all([self.is_configured(), self.is_draft()])
795
833
 
796
834
  def can_approve(self) -> bool:
797
835
  """
@@ -827,11 +865,7 @@ class BillModelAbstract(
827
865
  bool
828
866
  True if BillModel can be deleted, else False.
829
867
  """
830
- return any([
831
- self.is_review(),
832
- self.is_draft(),
833
- not self.ledger.is_locked()
834
- ])
868
+ return any([self.is_review(), self.is_draft(), not self.ledger.is_locked()])
835
869
 
836
870
  def can_void(self) -> bool:
837
871
  """
@@ -842,10 +876,7 @@ class BillModelAbstract(
842
876
  bool
843
877
  True if BillModel can be marked as void, else False.
844
878
  """
845
- return all([
846
- self.is_approved(),
847
- float(self.amount_paid) == 0.00
848
- ])
879
+ return all([self.is_approved(), float(self.amount_paid) == 0.00])
849
880
 
850
881
  def can_cancel(self) -> bool:
851
882
  """
@@ -856,10 +887,7 @@ class BillModelAbstract(
856
887
  bool
857
888
  True if BillModel can be marked as canceled, else False.
858
889
  """
859
- return any([
860
- self.is_draft(),
861
- self.is_review()
862
- ])
890
+ return any([self.is_draft(), self.is_review()])
863
891
 
864
892
  def can_edit_items(self) -> bool:
865
893
  """
@@ -907,16 +935,16 @@ class BillModelAbstract(
907
935
  """
908
936
  if self.ce_model_id:
909
937
  if raise_exception:
910
- raise BillModelValidationError(f'Bill {self.bill_number} already bound to '
911
- f'Estimate {self.ce_model.estimate_number}')
938
+ raise BillModelValidationError(
939
+ f'Bill {self.bill_number} already bound to '
940
+ f'Estimate {self.ce_model.estimate_number}'
941
+ )
912
942
  return False
913
943
 
914
944
  is_approved = estimate_model.is_approved()
915
945
  if not is_approved and raise_exception:
916
- raise BillModelValidationError(f'Cannot bind estimate that is not approved.')
917
- return all([
918
- is_approved
919
- ])
946
+ raise BillModelValidationError('Cannot bind estimate that is not approved.')
947
+ return all([is_approved])
920
948
 
921
949
  def can_bind_po(self, po_model, raise_exception: bool = False) -> bool:
922
950
  """
@@ -939,12 +967,14 @@ class BillModelAbstract(
939
967
  """
940
968
  if not po_model.is_approved():
941
969
  if raise_exception:
942
- raise BillModelValidationError(f'Cannot bind an unapproved PO.')
970
+ raise BillModelValidationError('Cannot bind an unapproved PO.')
943
971
  return False
944
972
 
945
973
  if po_model.date_approved > self.date_draft:
946
974
  if raise_exception:
947
- raise BillModelValidationError(f'Approved PO date cannot be greater than Bill draft date.')
975
+ raise BillModelValidationError(
976
+ 'Approved PO date cannot be greater than Bill draft date.'
977
+ )
948
978
  return False
949
979
 
950
980
  return True
@@ -959,11 +989,7 @@ class BillModelAbstract(
959
989
  bool
960
990
  True if BillModel can generate its bill_number, else False.
961
991
  """
962
- return all([
963
- not self.bill_number,
964
- self.is_draft(),
965
- self.is_configured()
966
- ])
992
+ return all([not self.bill_number, self.is_draft(), self.is_configured()])
967
993
 
968
994
  # ACTIONS ---
969
995
 
@@ -980,11 +1006,13 @@ class BillModelAbstract(
980
1006
  """
981
1007
  return self.is_approved()
982
1008
 
983
- def make_payment(self,
984
- payment_amount: Union[Decimal, float, int],
985
- payment_date: Optional[Union[datetime, date]] = None,
986
- commit: bool = False,
987
- raise_exception: bool = True):
1009
+ def make_payment(
1010
+ self,
1011
+ payment_amount: Union[Decimal, float, int],
1012
+ payment_date: Optional[Union[datetime, date]] = None,
1013
+ commit: bool = False,
1014
+ raise_exception: bool = True,
1015
+ ):
988
1016
  """
989
1017
  Makes a payment to the BillModel.
990
1018
 
@@ -1035,7 +1063,7 @@ class BillModelAbstract(
1035
1063
  user_model=None,
1036
1064
  entity_slug=self.ledger.entity.slug,
1037
1065
  je_timestamp=payment_date,
1038
- raise_exception=True
1066
+ raise_exception=True,
1039
1067
  )
1040
1068
  self.save(
1041
1069
  update_fields=[
@@ -1043,10 +1071,13 @@ class BillModelAbstract(
1043
1071
  'amount_earned',
1044
1072
  'amount_unearned',
1045
1073
  'amount_receivable',
1046
- 'updated'
1047
- ])
1074
+ 'updated',
1075
+ ]
1076
+ )
1048
1077
 
1049
- def bind_estimate(self, estimate_model, commit: bool = False, raise_exception: bool = True):
1078
+ def bind_estimate(
1079
+ self, estimate_model, commit: bool = False, raise_exception: bool = True
1080
+ ):
1050
1081
  """
1051
1082
  Binds BillModel to a given EstimateModel. Raises ValueError if EstimateModel cannot be bound.
1052
1083
 
@@ -1070,12 +1101,11 @@ class BillModelAbstract(
1070
1101
  self.ce_model = estimate_model
1071
1102
  self.clean()
1072
1103
  if commit:
1073
- self.save(update_fields=[
1074
- 'ce_model',
1075
- 'updated'
1076
- ])
1104
+ self.save(update_fields=['ce_model', 'updated'])
1077
1105
 
1078
- def mark_as_draft(self, date_draft: Optional[date] = None, commit: bool = False, **kwargs):
1106
+ def mark_as_draft(
1107
+ self, date_draft: Optional[date] = None, commit: bool = False, **kwargs
1108
+ ):
1079
1109
  """
1080
1110
  Marks BillModel as Draft.
1081
1111
 
@@ -1104,16 +1134,10 @@ class BillModelAbstract(
1104
1134
 
1105
1135
  self.clean()
1106
1136
  if commit:
1107
- self.save(
1108
- update_fields=[
1109
- 'bill_status',
1110
- 'date_draft',
1111
- 'updated'
1112
- ]
1113
- )
1114
- bill_status_draft.send_robust(sender=self.__class__,
1115
- instance=self,
1116
- commited=commit, **kwargs)
1137
+ self.save(update_fields=['bill_status', 'date_draft', 'updated'])
1138
+ bill_status_draft.send_robust(
1139
+ sender=self.__class__, instance=self, commited=commit, **kwargs
1140
+ )
1117
1141
 
1118
1142
  def get_mark_as_draft_html_id(self) -> str:
1119
1143
  """
@@ -1143,11 +1167,10 @@ class BillModelAbstract(
1143
1167
  """
1144
1168
  if not entity_slug:
1145
1169
  entity_slug = self.ledger.entity.slug
1146
- return reverse('django_ledger:bill-action-mark-as-draft',
1147
- kwargs={
1148
- 'entity_slug': entity_slug,
1149
- 'bill_pk': self.uuid
1150
- })
1170
+ return reverse(
1171
+ 'django_ledger:bill-action-mark-as-draft',
1172
+ kwargs={'entity_slug': entity_slug, 'bill_pk': self.uuid},
1173
+ )
1151
1174
 
1152
1175
  def get_mark_as_draft_message(self) -> str:
1153
1176
  """
@@ -1161,12 +1184,14 @@ class BillModelAbstract(
1161
1184
  return _('Do you want to mark Bill %s as Draft?') % self.bill_number
1162
1185
 
1163
1186
  # IN REVIEW ACTIONS....
1164
- def mark_as_review(self,
1165
- commit: bool = False,
1166
- itemtxs_qs: ItemTransactionModelQuerySet = None,
1167
- date_in_review: Optional[date] = None,
1168
- raise_exception: bool = True,
1169
- **kwargs):
1187
+ def mark_as_review(
1188
+ self,
1189
+ commit: bool = False,
1190
+ itemtxs_qs: ItemTransactionModelQuerySet = None,
1191
+ date_in_review: Optional[date] = None,
1192
+ raise_exception: bool = True,
1193
+ **kwargs,
1194
+ ):
1170
1195
  """
1171
1196
  Marks BillModel as In Review.
1172
1197
 
@@ -1194,7 +1219,9 @@ class BillModelAbstract(
1194
1219
  self.validate_itemtxs_qs(queryset=itemtxs_qs)
1195
1220
 
1196
1221
  if not itemtxs_qs.count():
1197
- raise BillModelValidationError(message=f'Cannot review a {self.__class__.__name__} without items...')
1222
+ raise BillModelValidationError(
1223
+ message=f'Cannot review a {self.__class__.__name__} without items...'
1224
+ )
1198
1225
 
1199
1226
  if not self.amount_due:
1200
1227
  raise BillModelValidationError(
@@ -1213,16 +1240,10 @@ class BillModelAbstract(
1213
1240
 
1214
1241
  self.clean()
1215
1242
  if commit:
1216
- self.save(
1217
- update_fields=[
1218
- 'date_in_review',
1219
- 'bill_status',
1220
- 'updated'
1221
- ]
1222
- )
1223
- bill_status_in_review.send_robust(sender=self.__class__,
1224
- instance=self,
1225
- commited=commit, **kwargs)
1243
+ self.save(update_fields=['date_in_review', 'bill_status', 'updated'])
1244
+ bill_status_in_review.send_robust(
1245
+ sender=self.__class__, instance=self, commited=commit, **kwargs
1246
+ )
1226
1247
 
1227
1248
  def get_mark_as_review_html_id(self) -> str:
1228
1249
  """
@@ -1252,11 +1273,10 @@ class BillModelAbstract(
1252
1273
  """
1253
1274
  if not entity_slug:
1254
1275
  entity_slug = self.ledger.entity.slug
1255
- return reverse('django_ledger:bill-action-mark-as-review',
1256
- kwargs={
1257
- 'entity_slug': entity_slug,
1258
- 'bill_pk': self.uuid
1259
- })
1276
+ return reverse(
1277
+ 'django_ledger:bill-action-mark-as-review',
1278
+ kwargs={'entity_slug': entity_slug, 'bill_pk': self.uuid},
1279
+ )
1260
1280
 
1261
1281
  def get_mark_as_review_message(self) -> str:
1262
1282
  """
@@ -1270,14 +1290,16 @@ class BillModelAbstract(
1270
1290
  return _('Do you want to mark Bill %s as In Review?') % self.bill_number
1271
1291
 
1272
1292
  # APPROVED ACTIONS....
1273
- def mark_as_approved(self,
1274
- user_model,
1275
- entity_slug: Optional[str] = None,
1276
- date_approved: Optional[Union[date, datetime]] = None,
1277
- commit: bool = False,
1278
- force_migrate: bool = False,
1279
- raise_exception: bool = True,
1280
- **kwargs):
1293
+ def mark_as_approved(
1294
+ self,
1295
+ user_model,
1296
+ entity_slug: Optional[str] = None,
1297
+ date_approved: Optional[Union[date, datetime]] = None,
1298
+ commit: bool = False,
1299
+ force_migrate: bool = False,
1300
+ raise_exception: bool = True,
1301
+ **kwargs,
1302
+ ):
1281
1303
  """
1282
1304
  Marks BillModel as Approved.
1283
1305
 
@@ -1327,12 +1349,12 @@ class BillModelAbstract(
1327
1349
  entity_slug=entity_slug,
1328
1350
  user_model=user_model,
1329
1351
  je_timestamp=date_approved,
1330
- force_migrate=self.accrue
1352
+ force_migrate=self.accrue,
1331
1353
  )
1332
1354
  self.ledger.post(commit=commit, raise_exception=raise_exception)
1333
- bill_status_approved.send_robust(sender=self.__class__,
1334
- instance=self,
1335
- commited=commit, **kwargs)
1355
+ bill_status_approved.send_robust(
1356
+ sender=self.__class__, instance=self, commited=commit, **kwargs
1357
+ )
1336
1358
 
1337
1359
  def get_mark_as_approved_html_id(self) -> str:
1338
1360
  """
@@ -1362,11 +1384,10 @@ class BillModelAbstract(
1362
1384
  """
1363
1385
  if not entity_slug:
1364
1386
  entity_slug = self.ledger.entity.slug
1365
- return reverse('django_ledger:bill-action-mark-as-approved',
1366
- kwargs={
1367
- 'entity_slug': entity_slug,
1368
- 'bill_pk': self.uuid
1369
- })
1387
+ return reverse(
1388
+ 'django_ledger:bill-action-mark-as-approved',
1389
+ kwargs={'entity_slug': entity_slug, 'bill_pk': self.uuid},
1390
+ )
1370
1391
 
1371
1392
  def get_mark_as_approved_message(self) -> str:
1372
1393
  """
@@ -1380,14 +1401,15 @@ class BillModelAbstract(
1380
1401
  return _('Do you want to mark Bill %s as Approved?') % self.bill_number
1381
1402
 
1382
1403
  # PAY ACTIONS....
1383
- def mark_as_paid(self,
1384
- user_model,
1385
- entity_slug: Optional[str] = None,
1386
- date_paid: Optional[Union[date, datetime]] = None,
1387
- itemtxs_qs: Optional[ItemTransactionModelQuerySet] = None,
1388
- commit: bool = False,
1389
- **kwargs):
1390
-
1404
+ def mark_as_paid(
1405
+ self,
1406
+ user_model,
1407
+ entity_slug: Optional[str] = None,
1408
+ date_paid: Optional[Union[date, datetime]] = None,
1409
+ itemtxs_qs: Optional[ItemTransactionModelQuerySet] = None,
1410
+ commit: bool = False,
1411
+ **kwargs,
1412
+ ):
1391
1413
  """
1392
1414
  Marks BillModel as Paid.
1393
1415
 
@@ -1410,7 +1432,9 @@ class BillModelAbstract(
1410
1432
  Commits transaction into the Database. Defaults to False.
1411
1433
  """
1412
1434
  if not self.can_pay():
1413
- raise BillModelValidationError(f'Cannot mark Bill {self.bill_number} as paid...')
1435
+ raise BillModelValidationError(
1436
+ f'Cannot mark Bill {self.bill_number} as paid...'
1437
+ )
1414
1438
 
1415
1439
  if date_paid:
1416
1440
  if isinstance(date_paid, datetime):
@@ -1424,10 +1448,13 @@ class BillModelAbstract(
1424
1448
  self.amount_paid = self.amount_due
1425
1449
 
1426
1450
  if self.date_paid > get_localdate():
1427
- raise BillModelValidationError(f'Cannot pay {self.__class__.__name__} in the future.')
1451
+ raise BillModelValidationError(
1452
+ f'Cannot pay {self.__class__.__name__} in the future.'
1453
+ )
1428
1454
  if self.date_paid < self.date_approved:
1429
1455
  raise BillModelValidationError(
1430
- f'Cannot pay {self.__class__.__name__} before approved date {self.date_approved}.')
1456
+ f'Cannot pay {self.__class__.__name__} before approved date {self.date_approved}.'
1457
+ )
1431
1458
 
1432
1459
  self.bill_status = self.BILL_STATUS_PAID
1433
1460
  self.get_state(commit=True)
@@ -1441,9 +1468,9 @@ class BillModelAbstract(
1441
1468
  if commit:
1442
1469
  self.save()
1443
1470
  ItemTransactionModel = lazy_loader.get_item_transaction_model()
1444
- itemtxs_qs.filter(
1445
- po_model_id__isnull=False
1446
- ).update(po_item_status=ItemTransactionModel.STATUS_ORDERED)
1471
+ itemtxs_qs.filter(po_model_id__isnull=False).update(
1472
+ po_item_status=ItemTransactionModel.STATUS_ORDERED
1473
+ )
1447
1474
 
1448
1475
  if not entity_slug:
1449
1476
  entity_slug = self.ledger.entity.slug
@@ -1453,12 +1480,12 @@ class BillModelAbstract(
1453
1480
  entity_slug=entity_slug,
1454
1481
  itemtxs_qs=itemtxs_qs,
1455
1482
  je_timestamp=date_paid,
1456
- force_migrate=True
1483
+ force_migrate=True,
1457
1484
  )
1458
1485
  self.lock_ledger(commit=True)
1459
- bill_status_paid.send_robust(sender=self.__class__,
1460
- instance=self,
1461
- commited=commit, **kwargs)
1486
+ bill_status_paid.send_robust(
1487
+ sender=self.__class__, instance=self, commited=commit, **kwargs
1488
+ )
1462
1489
 
1463
1490
  def get_mark_as_paid_html_id(self) -> str:
1464
1491
  """
@@ -1488,11 +1515,10 @@ class BillModelAbstract(
1488
1515
  """
1489
1516
  if not entity_slug:
1490
1517
  entity_slug = self.ledger.entity.slug
1491
- return reverse('django_ledger:bill-action-mark-as-paid',
1492
- kwargs={
1493
- 'entity_slug': entity_slug,
1494
- 'bill_pk': self.uuid
1495
- })
1518
+ return reverse(
1519
+ 'django_ledger:bill-action-mark-as-paid',
1520
+ kwargs={'entity_slug': entity_slug, 'bill_pk': self.uuid},
1521
+ )
1496
1522
 
1497
1523
  def get_mark_as_paid_message(self) -> str:
1498
1524
  """
@@ -1506,12 +1532,14 @@ class BillModelAbstract(
1506
1532
  return _('Do you want to mark Bill %s as Paid?') % self.bill_number
1507
1533
 
1508
1534
  # VOID Actions...
1509
- def mark_as_void(self,
1510
- user_model,
1511
- entity_slug: Optional[str] = None,
1512
- date_void: Optional[date] = None,
1513
- commit: bool = False,
1514
- **kwargs):
1535
+ def mark_as_void(
1536
+ self,
1537
+ user_model,
1538
+ entity_slug: Optional[str] = None,
1539
+ date_void: Optional[date] = None,
1540
+ commit: bool = False,
1541
+ **kwargs,
1542
+ ):
1515
1543
  """
1516
1544
  Marks BillModel as Void.
1517
1545
  When mark as void, all transactions associated with BillModel are reversed as of the void date.
@@ -1532,7 +1560,9 @@ class BillModelAbstract(
1532
1560
  Commits transaction into DB. Defaults to False.
1533
1561
  """
1534
1562
  if not self.can_void():
1535
- raise BillModelValidationError(f'Bill {self.bill_number} cannot be voided. Must be approved.')
1563
+ raise BillModelValidationError(
1564
+ f'Bill {self.bill_number} cannot be voided. Must be approved.'
1565
+ )
1536
1566
 
1537
1567
  if date_void:
1538
1568
  if isinstance(date_void, datetime):
@@ -1557,12 +1587,13 @@ class BillModelAbstract(
1557
1587
  void=True,
1558
1588
  void_date=self.date_void,
1559
1589
  raise_exception=False,
1560
- force_migrate=True)
1590
+ force_migrate=True,
1591
+ )
1561
1592
  self.save()
1562
1593
  self.lock_ledger(commit=False, raise_exception=False)
1563
- bill_status_void.send_robust(sender=self.__class__,
1564
- instance=self,
1565
- commited=commit, **kwargs)
1594
+ bill_status_void.send_robust(
1595
+ sender=self.__class__, instance=self, commited=commit, **kwargs
1596
+ )
1566
1597
 
1567
1598
  def get_mark_as_void_html_id(self) -> str:
1568
1599
  """
@@ -1591,11 +1622,10 @@ class BillModelAbstract(
1591
1622
  """
1592
1623
  if not entity_slug:
1593
1624
  entity_slug = self.ledger.entity.slug
1594
- return reverse('django_ledger:bill-action-mark-as-void',
1595
- kwargs={
1596
- 'entity_slug': entity_slug,
1597
- 'bill_pk': self.uuid
1598
- })
1625
+ return reverse(
1626
+ 'django_ledger:bill-action-mark-as-void',
1627
+ kwargs={'entity_slug': entity_slug, 'bill_pk': self.uuid},
1628
+ )
1599
1629
 
1600
1630
  def get_mark_as_void_message(self) -> str:
1601
1631
  """
@@ -1609,7 +1639,9 @@ class BillModelAbstract(
1609
1639
  return _('Do you want to void Bill %s?') % self.bill_number
1610
1640
 
1611
1641
  # Cancel Actions...
1612
- def mark_as_canceled(self, date_canceled: Optional[date] = None, commit: bool = False, **kwargs):
1642
+ def mark_as_canceled(
1643
+ self, date_canceled: Optional[date] = None, commit: bool = False, **kwargs
1644
+ ):
1613
1645
  """
1614
1646
  Mark BillModel as Canceled.
1615
1647
 
@@ -1623,16 +1655,18 @@ class BillModelAbstract(
1623
1655
  Commits transaction into the Database. Defaults to False.
1624
1656
  """
1625
1657
  if not self.can_cancel():
1626
- raise BillModelValidationError(f'Bill {self.bill_number} cannot be canceled. Must be draft or in review.')
1658
+ raise BillModelValidationError(
1659
+ f'Bill {self.bill_number} cannot be canceled. Must be draft or in review.'
1660
+ )
1627
1661
 
1628
1662
  self.date_canceled = get_localdate() if not date_canceled else date_canceled
1629
1663
  self.bill_status = self.BILL_STATUS_CANCELED
1630
1664
  self.clean()
1631
1665
  if commit:
1632
1666
  self.save()
1633
- bill_status_canceled.send_robust(sender=self.__class__,
1634
- instance=self,
1635
- commited=commit, **kwargs)
1667
+ bill_status_canceled.send_robust(
1668
+ sender=self.__class__, instance=self, commited=commit, **kwargs
1669
+ )
1636
1670
 
1637
1671
  def get_mark_as_canceled_html_id(self) -> str:
1638
1672
  """
@@ -1664,11 +1698,10 @@ class BillModelAbstract(
1664
1698
  if not entity_slug:
1665
1699
  entity_slug = self.ledger.entity.slug
1666
1700
 
1667
- return reverse('django_ledger:bill-action-mark-as-canceled',
1668
- kwargs={
1669
- 'entity_slug': entity_slug,
1670
- 'bill_pk': self.uuid
1671
- })
1701
+ return reverse(
1702
+ 'django_ledger:bill-action-mark-as-canceled',
1703
+ kwargs={'entity_slug': entity_slug, 'bill_pk': self.uuid},
1704
+ )
1672
1705
 
1673
1706
  def get_mark_as_canceled_message(self) -> str:
1674
1707
  """
@@ -1846,28 +1879,28 @@ class BillModelAbstract(
1846
1879
  'entity_model_id__exact': self.ledger.entity_id,
1847
1880
  'entity_unit_id__exact': None,
1848
1881
  'fiscal_year': fy_key,
1849
- 'key__exact': EntityStateModel.KEY_BILL
1882
+ 'key__exact': EntityStateModel.KEY_BILL,
1850
1883
  }
1851
1884
 
1852
- state_model_qs = EntityStateModel.objects.filter(**LOOKUP).select_related(
1853
- 'entity_model').select_for_update()
1885
+ state_model_qs = (
1886
+ EntityStateModel.objects.filter(**LOOKUP)
1887
+ .select_related('entity_model')
1888
+ .select_for_update()
1889
+ )
1854
1890
  state_model = state_model_qs.get()
1855
1891
  state_model.sequence = F('sequence') + 1
1856
1892
  state_model.save(update_fields=['sequence'])
1857
1893
  state_model.refresh_from_db()
1858
1894
  return state_model
1859
1895
  except ObjectDoesNotExist:
1860
- EntityModel = lazy_loader.get_entity_model()
1861
- entity_model = EntityModel.objects.get(uuid__exact=self.ledger.entity_id)
1862
- fy_key = entity_model.get_fy_for_date(dt=self.date_draft)
1863
-
1864
1896
  LOOKUP = {
1865
1897
  'entity_model_id': entity_model.uuid,
1866
1898
  'entity_unit_id': None,
1867
1899
  'fiscal_year': fy_key,
1868
1900
  'key': EntityStateModel.KEY_BILL,
1869
- 'sequence': 1
1901
+ 'sequence': 1,
1870
1902
  }
1903
+
1871
1904
  state_model = EntityStateModel.objects.create(**LOOKUP)
1872
1905
  return state_model
1873
1906
  except IntegrityError as e:
@@ -1892,12 +1925,13 @@ class BillModelAbstract(
1892
1925
  """
1893
1926
  if self.can_generate_bill_number():
1894
1927
  with transaction.atomic(durable=True):
1895
-
1896
1928
  state_model = None
1897
1929
  while not state_model:
1898
1930
  state_model = self._get_next_state_model(raise_exception=False)
1899
1931
 
1900
- seq = str(state_model.sequence).zfill(DJANGO_LEDGER_DOCUMENT_NUMBER_PADDING)
1932
+ seq = str(state_model.sequence).zfill(
1933
+ DJANGO_LEDGER_DOCUMENT_NUMBER_PADDING
1934
+ )
1901
1935
  self.bill_number = f'{DJANGO_LEDGER_BILL_NUMBER_PREFIX}-{state_model.fiscal_year}-{seq}'
1902
1936
 
1903
1937
  if commit:
@@ -1910,11 +1944,10 @@ class BillModelAbstract(
1910
1944
 
1911
1945
  # --> URLs <---
1912
1946
  def get_absolute_url(self):
1913
- return reverse('django_ledger:bill-detail',
1914
- kwargs={
1915
- 'entity_slug': self.ledger.entity.slug,
1916
- 'bill_pk': self.uuid
1917
- })
1947
+ return reverse(
1948
+ 'django_ledger:bill-detail',
1949
+ kwargs={'entity_slug': self.ledger.entity.slug, 'bill_pk': self.uuid},
1950
+ )
1918
1951
 
1919
1952
  def clean(self, commit: bool = True):
1920
1953
  """
@@ -1932,9 +1965,13 @@ class BillModelAbstract(
1932
1965
  if self.cash_account.role != ASSET_CA_CASH:
1933
1966
  raise ValidationError(f'Cash account must be of role {ASSET_CA_CASH}.')
1934
1967
  if self.prepaid_account.role != ASSET_CA_PREPAID:
1935
- raise ValidationError(f'Prepaid account must be of role {ASSET_CA_PREPAID}.')
1968
+ raise ValidationError(
1969
+ f'Prepaid account must be of role {ASSET_CA_PREPAID}.'
1970
+ )
1936
1971
  if self.unearned_account.role != LIABILITY_CL_ACC_PAYABLE:
1937
- raise ValidationError(f'Unearned account must be of role {LIABILITY_CL_ACC_PAYABLE}.')
1972
+ raise ValidationError(
1973
+ f'Unearned account must be of role {LIABILITY_CL_ACC_PAYABLE}.'
1974
+ )
1938
1975
 
1939
1976
 
1940
1977
  class BillModel(BillModelAbstract):