django-ledger 0.6.2__py3-none-any.whl → 0.6.4__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 (65) hide show
  1. django_ledger/__init__.py +1 -1
  2. django_ledger/admin/coa.py +1 -2
  3. django_ledger/admin/ledger.py +1 -0
  4. django_ledger/forms/account.py +22 -11
  5. django_ledger/forms/data_import.py +2 -1
  6. django_ledger/io/ofx.py +3 -2
  7. django_ledger/io/roles.py +6 -0
  8. django_ledger/models/accounts.py +523 -196
  9. django_ledger/models/bill.py +28 -14
  10. django_ledger/models/closing_entry.py +8 -0
  11. django_ledger/models/entity.py +12 -5
  12. django_ledger/models/estimate.py +93 -21
  13. django_ledger/models/invoice.py +44 -14
  14. django_ledger/models/journal_entry.py +112 -49
  15. django_ledger/models/ledger.py +32 -0
  16. django_ledger/models/purchase_order.py +34 -3
  17. django_ledger/models/signals.py +58 -0
  18. django_ledger/report/cash_flow_statement.py +1 -1
  19. django_ledger/report/core.py +1 -1
  20. django_ledger/static/.DS_Store +0 -0
  21. django_ledger/static/django_ledger/.DS_Store +0 -0
  22. django_ledger/static/django_ledger/logo_2/.DS_Store +0 -0
  23. django_ledger/static/django_ledger/logo_2/django_ledger_logo_dark.png +0 -0
  24. django_ledger/static/django_ledger/logo_2/django_ledger_logo_dark@0.5x.png +0 -0
  25. django_ledger/static/django_ledger/logo_2/django_ledger_logo_dark@2x.png +0 -0
  26. django_ledger/static/django_ledger/logo_2/django_ledger_logo_dark@3x.png +0 -0
  27. django_ledger/static/django_ledger/logo_2/djl-full-vert.png +0 -0
  28. django_ledger/static/django_ledger/logo_2/djl-full-vert@0.5x.png +0 -0
  29. django_ledger/static/django_ledger/logo_2/djl-full-vert@2x.png +0 -0
  30. django_ledger/static/django_ledger/logo_2/djl-full-vert@3x.png +0 -0
  31. django_ledger/static/django_ledger/logo_2/djl-logo-full-horiz.png +0 -0
  32. django_ledger/static/django_ledger/logo_2/djl-logo-full-horiz@0.5x.png +0 -0
  33. django_ledger/static/django_ledger/logo_2/djl-logo-full-horiz@2x.png +0 -0
  34. django_ledger/static/django_ledger/logo_2/djl-logo-full-horiz@3x.png +0 -0
  35. django_ledger/static/django_ledger/logo_2/djl-logo-full-vert.png +0 -0
  36. django_ledger/static/django_ledger/logo_2/djl-logo-full-vert@0.5x.png +0 -0
  37. django_ledger/static/django_ledger/logo_2/djl-logo-full-vert@2x.png +0 -0
  38. django_ledger/static/django_ledger/logo_2/djl-logo-full-vert@3x.png +0 -0
  39. django_ledger/static/django_ledger/logo_2/djl-logo.png +0 -0
  40. django_ledger/static/django_ledger/logo_2/djl-logo@0.5x.png +0 -0
  41. django_ledger/static/django_ledger/logo_2/djl-logo@2x.png +0 -0
  42. django_ledger/static/django_ledger/logo_2/djl-logo@3x.png +0 -0
  43. django_ledger/static/django_ledger/logo_2/djl-txt-full-horiz.png +0 -0
  44. django_ledger/static/django_ledger/logo_2/djl-txt-full-horiz@0.5x.png +0 -0
  45. django_ledger/static/django_ledger/logo_2/djl-txt-full-horiz@2x.png +0 -0
  46. django_ledger/static/django_ledger/logo_2/djl-txt-full-horiz@3x.png +0 -0
  47. django_ledger/static/django_ledger/logo_2/djl-txt-full-vert.png +0 -0
  48. django_ledger/static/django_ledger/logo_2/djl-txt-full-vert@0.5x.png +0 -0
  49. django_ledger/static/django_ledger/logo_2/djl-txt-full-vert@2x.png +0 -0
  50. django_ledger/static/django_ledger/logo_2/djl-txt-full-vert@3x.png +0 -0
  51. django_ledger/static/django_ledger/logo_2/djl-txt-horiz.png +0 -0
  52. django_ledger/static/django_ledger/logo_2/djl-txt-horiz@0.5x.png +0 -0
  53. django_ledger/static/django_ledger/logo_2/djl-txt-horiz@2x.png +0 -0
  54. django_ledger/static/django_ledger/logo_2/djl-txt-horiz@3x.png +0 -0
  55. django_ledger/templates/django_ledger/financial_statements/balance_sheet.html +2 -2
  56. django_ledger/tests/test_io_ofx/__init__.py +0 -0
  57. django_ledger/tests/test_io_ofx/tests.py +52 -0
  58. django_ledger/views/account.py +1 -1
  59. django_ledger/views/bill.py +1 -1
  60. {django_ledger-0.6.2.dist-info → django_ledger-0.6.4.dist-info}/METADATA +1 -1
  61. {django_ledger-0.6.2.dist-info → django_ledger-0.6.4.dist-info}/RECORD +65 -27
  62. {django_ledger-0.6.2.dist-info → django_ledger-0.6.4.dist-info}/top_level.txt +0 -1
  63. {django_ledger-0.6.2.dist-info → django_ledger-0.6.4.dist-info}/AUTHORS.md +0 -0
  64. {django_ledger-0.6.2.dist-info → django_ledger-0.6.4.dist-info}/LICENSE +0 -0
  65. {django_ledger-0.6.2.dist-info → django_ledger-0.6.4.dist-info}/WHEEL +0 -0
@@ -45,17 +45,28 @@ from django.utils.timezone import localtime
45
45
  from django.utils.translation import gettext_lazy as _
46
46
 
47
47
  from django_ledger.io.io_core import get_localtime
48
- from django_ledger.io.roles import (ASSET_CA_CASH, GROUP_CFS_FIN_DIVIDENDS, GROUP_CFS_FIN_ISSUING_EQUITY,
49
- GROUP_CFS_FIN_LT_DEBT_PAYMENTS, GROUP_CFS_FIN_ST_DEBT_PAYMENTS,
50
- GROUP_CFS_INVESTING_AND_FINANCING, GROUP_CFS_INVESTING_PPE,
51
- GROUP_CFS_INVESTING_SECURITIES, validate_roles)
48
+ from django_ledger.io.roles import (
49
+ ASSET_CA_CASH, GROUP_CFS_FIN_DIVIDENDS, GROUP_CFS_FIN_ISSUING_EQUITY,
50
+ GROUP_CFS_FIN_LT_DEBT_PAYMENTS, GROUP_CFS_FIN_ST_DEBT_PAYMENTS,
51
+ GROUP_CFS_INVESTING_AND_FINANCING, GROUP_CFS_INVESTING_PPE,
52
+ GROUP_CFS_INVESTING_SECURITIES, validate_roles
53
+ )
52
54
  from django_ledger.models.accounts import CREDIT, DEBIT
53
55
  from django_ledger.models.entity import EntityStateModel, EntityModel
54
56
  from django_ledger.models.mixins import CreateUpdateMixIn
57
+ from django_ledger.models.signals import (
58
+ journal_entry_unlocked,
59
+ journal_entry_locked,
60
+ journal_entry_posted,
61
+ journal_entry_unposted
62
+ )
55
63
  from django_ledger.models.transactions import TransactionModelQuerySet, TransactionModel
56
64
  from django_ledger.models.utils import lazy_loader
57
- from django_ledger.settings import (DJANGO_LEDGER_JE_NUMBER_PREFIX, DJANGO_LEDGER_DOCUMENT_NUMBER_PADDING,
58
- DJANGO_LEDGER_JE_NUMBER_NO_UNIT_PREFIX)
65
+ from django_ledger.settings import (
66
+ DJANGO_LEDGER_JE_NUMBER_PREFIX,
67
+ DJANGO_LEDGER_DOCUMENT_NUMBER_PADDING,
68
+ DJANGO_LEDGER_JE_NUMBER_NO_UNIT_PREFIX
69
+ )
59
70
 
60
71
 
61
72
  class JournalEntryValidationError(ValidationError):
@@ -588,31 +599,48 @@ class JournalEntryModelAbstract(CreateUpdateMixIn):
588
599
  """
589
600
  if verify and not self.is_verified():
590
601
  txs_qs, verified = self.verify()
591
-
592
602
  if not len(txs_qs):
593
- raise JournalEntryValidationError(
594
- message=_('Cannot post an empty Journal Entry.')
595
- )
603
+ if raise_exception:
604
+ raise JournalEntryValidationError(
605
+ message=_('Cannot post an empty Journal Entry.')
606
+ )
607
+ return
596
608
 
597
609
  if force_lock and not self.is_locked():
598
- self.mark_as_locked(commit=False, raise_exception=True)
610
+ try:
611
+ self.mark_as_locked(commit=False, raise_exception=True)
612
+ except JournalEntryValidationError as e:
613
+ if raise_exception:
614
+ raise e
615
+ return
599
616
 
600
617
  if not self.can_post(ignore_verify=False):
601
618
  if raise_exception:
602
619
  raise JournalEntryValidationError(f'Journal Entry {self.uuid} cannot post.'
603
620
  f' Is verified: {self.is_verified()}')
604
- else:
605
- if not self.is_posted():
606
- self.posted = True
607
- if self.is_posted():
608
- if commit:
609
- self.save(verify=False,
610
- update_fields=[
611
- 'posted',
612
- 'locked',
613
- 'activity',
614
- 'updated'
615
- ])
621
+ return
622
+
623
+ if not self.is_posted():
624
+ self.posted = True
625
+ if self.is_posted():
626
+ if commit:
627
+ self.save(verify=False,
628
+ update_fields=[
629
+ 'posted',
630
+ 'locked',
631
+ 'activity',
632
+ 'updated'
633
+ ])
634
+ journal_entry_posted.send_robust(sender=self.__class__,
635
+ instance=self,
636
+ commited=commit,
637
+ **kwargs)
638
+
639
+ def post(self, **kwargs):
640
+ """
641
+ Proxy function for `mark_as_posted` method.
642
+ """
643
+ return self.mark_as_posted(**kwargs)
616
644
 
617
645
  def mark_as_unposted(self, commit: bool = False, raise_exception: bool = False, **kwargs):
618
646
  """
@@ -632,18 +660,30 @@ class JournalEntryModelAbstract(CreateUpdateMixIn):
632
660
  if not self.can_unpost():
633
661
  if raise_exception:
634
662
  raise JournalEntryValidationError(f'Journal Entry {self.uuid} cannot unpost.')
635
- else:
636
- if self.is_posted():
637
- self.posted = False
638
- self.activity = None
639
- if not self.is_posted():
640
- if commit:
641
- self.save(verify=False,
642
- update_fields=[
643
- 'posted',
644
- 'activity',
645
- 'updated'
646
- ])
663
+ return
664
+ if self.is_posted():
665
+ self.posted = False
666
+ self.activity = None
667
+ if not self.is_posted():
668
+ if commit:
669
+ self.save(
670
+ verify=False,
671
+ update_fields=[
672
+ 'posted',
673
+ 'activity',
674
+ 'updated'
675
+ ]
676
+ )
677
+ journal_entry_unposted.send_robust(sender=self.__class__,
678
+ instance=self,
679
+ commited=commit,
680
+ **kwargs)
681
+
682
+ def unpost(self, **kwargs):
683
+ """
684
+ Proxy function for `mark_as_unposted` method.
685
+ """
686
+ return self.mark_as_unposted(**kwargs)
647
687
 
648
688
  def mark_as_locked(self, commit: bool = False, raise_exception: bool = False, **kwargs):
649
689
  """
@@ -662,14 +702,26 @@ class JournalEntryModelAbstract(CreateUpdateMixIn):
662
702
  """
663
703
  if not self.can_lock():
664
704
  if raise_exception:
665
- raise JournalEntryValidationError(f'Journal Entry {self.uuid} is already locked.')
666
- else:
667
- if not self.is_locked():
668
- self.generate_activity(force_update=True)
669
- self.locked = True
670
- if self.is_locked():
671
- if commit:
672
- self.save(verify=False)
705
+ if raise_exception:
706
+ raise JournalEntryValidationError(f'Journal Entry {self.uuid} is already locked.')
707
+ return
708
+
709
+ if not self.is_locked():
710
+ self.generate_activity(force_update=True)
711
+ self.locked = True
712
+ if self.is_locked():
713
+ if commit:
714
+ self.save(verify=False)
715
+ journal_entry_locked.send_robust(sender=self.__class__,
716
+ instance=self,
717
+ commited=commit,
718
+ **kwargs)
719
+
720
+ def lock(self, **kwargs):
721
+ """
722
+ Proxy function for `mark_as_locked` method.
723
+ """
724
+ return self.mark_as_locked(**kwargs)
673
725
 
674
726
  def mark_as_unlocked(self, commit: bool = False, raise_exception: bool = False, **kwargs):
675
727
  """
@@ -687,12 +739,23 @@ class JournalEntryModelAbstract(CreateUpdateMixIn):
687
739
  if not self.can_unlock():
688
740
  if raise_exception:
689
741
  raise JournalEntryValidationError(f'Journal Entry {self.uuid} is already unlocked.')
690
- else:
691
- if self.is_locked():
692
- self.locked = False
693
- if not self.is_locked():
694
- if commit:
695
- self.save(verify=False)
742
+ return
743
+
744
+ if self.is_locked():
745
+ self.locked = False
746
+ if not self.is_locked():
747
+ if commit:
748
+ self.save(verify=False)
749
+ journal_entry_unlocked.send_robust(sender=self.__class__,
750
+ instance=self,
751
+ commited=commit,
752
+ **kwargs)
753
+
754
+ def unlock(self, **kwargs):
755
+ """
756
+ Proxy function for `mark_as_unlocked` method.
757
+ """
758
+ return self.mark_as_unlocked(**kwargs)
696
759
 
697
760
  def get_transaction_queryset(self, select_accounts: bool = True) -> TransactionModelQuerySet:
698
761
  """
@@ -37,6 +37,14 @@ from django.db import models
37
37
  from django.db.models import Q, Min, F, Count
38
38
  from django.urls import reverse
39
39
  from django.utils.translation import gettext_lazy as _
40
+ from django_ledger.models.signals import (
41
+ ledger_posted,
42
+ ledger_unposted,
43
+ ledger_locked,
44
+ ledger_unlocked,
45
+ ledger_hidden,
46
+ ledger_unhidden
47
+ )
40
48
 
41
49
  from django_ledger.io.io_core import IOMixIn
42
50
  from django_ledger.models import lazy_loader
@@ -456,6 +464,10 @@ class LedgerModelAbstract(CreateUpdateMixIn, IOMixIn):
456
464
  'posted',
457
465
  'updated'
458
466
  ])
467
+ ledger_posted.send_robust(sender=self.__class__,
468
+ instance=self,
469
+ commited=commit,
470
+ **kwargs)
459
471
 
460
472
  def post_journal_entries(self, commit: bool = True, **kwargs):
461
473
  je_model_qs = self.journal_entries.unposted()
@@ -488,6 +500,10 @@ class LedgerModelAbstract(CreateUpdateMixIn, IOMixIn):
488
500
  'posted',
489
501
  'updated'
490
502
  ])
503
+ ledger_unposted.send_robust(sender=self.__class__,
504
+ instance=self,
505
+ commited=commit,
506
+ **kwargs)
491
507
 
492
508
  def lock(self, commit: bool = False, raise_exception: bool = True, **kwargs):
493
509
  """
@@ -513,6 +529,10 @@ class LedgerModelAbstract(CreateUpdateMixIn, IOMixIn):
513
529
  'locked',
514
530
  'updated'
515
531
  ])
532
+ ledger_locked.send_robust(sender=self.__class__,
533
+ instance=self,
534
+ commited=commit,
535
+ **kwargs)
516
536
 
517
537
  def lock_journal_entries(self, commit: bool = True, **kwargs):
518
538
  je_model_qs = self.journal_entries.unlocked()
@@ -544,6 +564,10 @@ class LedgerModelAbstract(CreateUpdateMixIn, IOMixIn):
544
564
  'locked',
545
565
  'updated'
546
566
  ])
567
+ ledger_unlocked.send_robust(sender=self.__class__,
568
+ instance=self,
569
+ commited=commit,
570
+ **kwargs)
547
571
 
548
572
  def hide(self, commit: bool = False, raise_exception: bool = True, **kwargs):
549
573
  if not self.can_hide():
@@ -558,6 +582,10 @@ class LedgerModelAbstract(CreateUpdateMixIn, IOMixIn):
558
582
  'hidden',
559
583
  'updated'
560
584
  ])
585
+ ledger_hidden.send_robust(sender=self.__class__,
586
+ instance=self,
587
+ commited=commit,
588
+ **kwargs)
561
589
 
562
590
  def unhide(self, commit: bool = False, raise_exception: bool = True, **kwargs):
563
591
  if not self.can_unhide():
@@ -572,6 +600,10 @@ class LedgerModelAbstract(CreateUpdateMixIn, IOMixIn):
572
600
  'hidden',
573
601
  'updated'
574
602
  ])
603
+ ledger_unhidden.send_robust(sender=self.__class__,
604
+ instance=self,
605
+ commited=commit,
606
+ **kwargs)
575
607
 
576
608
  def delete(self, **kwargs):
577
609
  if not self.can_delete():
@@ -39,6 +39,14 @@ from django_ledger.models.items import ItemTransactionModel, ItemTransactionMode
39
39
  from django_ledger.models.mixins import CreateUpdateMixIn, MarkdownNotesMixIn, ItemizeMixIn
40
40
  from django_ledger.models.utils import lazy_loader
41
41
  from django_ledger.settings import DJANGO_LEDGER_DOCUMENT_NUMBER_PADDING, DJANGO_LEDGER_PO_NUMBER_PREFIX
42
+ from django_ledger.models.signals import (
43
+ po_status_draft,
44
+ po_status_void,
45
+ po_status_fulfilled,
46
+ po_status_approved,
47
+ po_status_canceled,
48
+ po_status_in_review
49
+ )
42
50
 
43
51
  PO_NUMBER_CHARS = ascii_uppercase + digits
44
52
 
@@ -403,9 +411,8 @@ class PurchaseOrderModelAbstract(CreateUpdateMixIn,
403
411
  } if not lazy_agg else None
404
412
 
405
413
  # ### ItemizeMixIn implementation END...
406
- def update_state(self,
407
- itemtxs_qs: Optional[Union[ItemTransactionModelQuerySet, List[ItemTransactionModel]]] = None
408
- ) -> Tuple:
414
+ def update_state(self, itemtxs_qs: Optional[
415
+ Union[ItemTransactionModelQuerySet, List[ItemTransactionModel]]] = None) -> Tuple:
409
416
 
410
417
  """
411
418
  Updates the state of the PurchaseOrderModel.
@@ -696,6 +703,10 @@ class PurchaseOrderModelAbstract(CreateUpdateMixIn,
696
703
  'po_status',
697
704
  'updated'
698
705
  ])
706
+ po_status_draft.send_robust(sender=self.__class__,
707
+ instance=self,
708
+ commited=commit,
709
+ **kwargs)
699
710
 
700
711
  def get_mark_as_draft_html_id(self):
701
712
  """
@@ -765,6 +776,10 @@ class PurchaseOrderModelAbstract(CreateUpdateMixIn,
765
776
  'date_in_review',
766
777
  'updated'
767
778
  ])
779
+ po_status_in_review.send_robust(sender=self.__class__,
780
+ instance=self,
781
+ commited=commit,
782
+ **kwargs)
768
783
 
769
784
  def get_mark_as_review_html_id(self):
770
785
  """
@@ -828,6 +843,10 @@ class PurchaseOrderModelAbstract(CreateUpdateMixIn,
828
843
  'po_status',
829
844
  'updated'
830
845
  ])
846
+ po_status_approved.send_robust(sender=self.__class__,
847
+ instance=self,
848
+ commited=commit,
849
+ **kwargs)
831
850
 
832
851
  def get_mark_as_approved_html_id(self):
833
852
  """
@@ -890,6 +909,10 @@ class PurchaseOrderModelAbstract(CreateUpdateMixIn,
890
909
  'date_canceled',
891
910
  'updated'
892
911
  ])
912
+ po_status_canceled.send_robust(sender=self.__class__,
913
+ instance=self,
914
+ commited=commit,
915
+ **kwargs)
893
916
 
894
917
  def get_mark_as_canceled_html_id(self):
895
918
  """
@@ -983,6 +1006,10 @@ class PurchaseOrderModelAbstract(CreateUpdateMixIn,
983
1006
  'po_status',
984
1007
  'updated'
985
1008
  ])
1009
+ po_status_fulfilled.send_robust(sender=self.__class__,
1010
+ instance=self,
1011
+ commited=commit,
1012
+ **kwargs)
986
1013
 
987
1014
  def get_mark_as_fulfilled_html_id(self):
988
1015
  """
@@ -1057,6 +1084,10 @@ class PurchaseOrderModelAbstract(CreateUpdateMixIn,
1057
1084
  'po_status',
1058
1085
  'updated'
1059
1086
  ])
1087
+ po_status_void.send_robust(sender=self.__class__,
1088
+ instance=self,
1089
+ commited=commit,
1090
+ **kwargs)
1060
1091
 
1061
1092
  def get_mark_as_void_html_id(self):
1062
1093
  """
@@ -0,0 +1,58 @@
1
+ """
2
+ Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
3
+ Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
4
+
5
+ Contributions to this module:
6
+ * Miguel Sanda <msanda@arrobalytics.com>
7
+
8
+ The signals module provide the means to notify listeners about important events or states in the models,
9
+ such as a ledger model being posted or a bill status changing.
10
+ """
11
+
12
+ from django.dispatch import Signal
13
+
14
+ # Ledger Model Signals...
15
+ ledger_posted = Signal()
16
+ ledger_unposted = Signal()
17
+ ledger_locked = Signal()
18
+ ledger_unlocked = Signal()
19
+ ledger_hidden = Signal()
20
+ ledger_unhidden = Signal()
21
+
22
+ # Journal Entry Model Signals...
23
+ journal_entry_posted = Signal()
24
+ journal_entry_unposted = Signal()
25
+ journal_entry_locked = Signal()
26
+ journal_entry_unlocked = Signal()
27
+
28
+ # Bill Model Signals...
29
+ bill_status_draft = Signal()
30
+ bill_status_in_review = Signal()
31
+ bill_status_approved = Signal()
32
+ bill_status_paid = Signal()
33
+ bill_status_canceled = Signal()
34
+ bill_status_void = Signal()
35
+
36
+ # Invoice Model Signals...
37
+ invoice_status_draft = Signal()
38
+ invoice_status_in_review = Signal()
39
+ invoice_status_approved = Signal()
40
+ invoice_status_paid = Signal()
41
+ invoice_status_canceled = Signal()
42
+ invoice_status_void = Signal()
43
+
44
+ # PO Model Signals...
45
+ po_status_draft = Signal()
46
+ po_status_in_review = Signal()
47
+ po_status_approved = Signal()
48
+ po_status_fulfilled = Signal()
49
+ po_status_canceled = Signal()
50
+ po_status_void = Signal()
51
+
52
+ # Estimate Model Signals...
53
+ estimate_status_draft = Signal()
54
+ estimate_status_in_review = Signal()
55
+ estimate_status_approved = Signal()
56
+ estimate_status_completed = Signal()
57
+ estimate_status_canceled = Signal()
58
+ estimate_status_void = Signal()
@@ -1,7 +1,7 @@
1
1
  from datetime import datetime, date
2
2
  from typing import Optional, Dict, Union
3
3
 
4
- from django_ledger.io.io_digest import IODigestContextManager
4
+ from django_ledger.io.io_context import IODigestContextManager
5
5
  from django_ledger.report.core import BaseReportSupport, PDFReportValidationError
6
6
  from django_ledger.settings import DJANGO_LEDGER_CURRENCY_SYMBOL
7
7
  from django_ledger.templatetags.django_ledger import currency_format
@@ -3,7 +3,7 @@ from typing import Optional, Dict
3
3
  from django.contrib.staticfiles import finders
4
4
  from django.core.exceptions import ValidationError
5
5
 
6
- from django_ledger.io.io_digest import IODigestContextManager
6
+ from django_ledger.io.io_context import IODigestContextManager
7
7
  from django_ledger.models.ledger import LedgerModel
8
8
  from django_ledger.models.unit import EntityUnitModel
9
9
  from django_ledger.settings import DJANGO_LEDGER_PDF_SUPPORT_ENABLED
Binary file
@@ -44,10 +44,10 @@
44
44
  {% balance_sheet_statement io_model=object %}
45
45
  {% if entity %}
46
46
  <a class="button is-fullwidth is-dark mb-1"
47
- href="{% url 'django_ledger:ledger-list' entity_slug=view.kwargs.entity_slug %}">{% trans 'Go Back' %}</a>
47
+ href="{% url 'django_ledger:entity-dashboard' entity_slug=view.kwargs.entity_slug %}">{% trans 'Go Back' %}</a>
48
48
  {% elif ledger %}
49
49
  <a class="button is-fullwidth is-dark my-2"
50
- href="{% url 'django_ledger:entity-dashboard' entity_slug=view.kwargs.entity_slug %}">{% trans 'Go Back' %}</a>
50
+ href="{% url 'django_ledger:ledger-list' entity_slug=view.kwargs.entity_slug %}">{% trans 'Go Back' %}</a>
51
51
  {% endif %}
52
52
  <a class="button is-fullwidth is-light my-2"
53
53
  href="?by_unit=1">{% trans 'By Unit' %}</a>
File without changes
@@ -0,0 +1,52 @@
1
+ import os
2
+ from decimal import Decimal
3
+
4
+ from django_ledger.io.ofx import OFXFileManager
5
+ from django_ledger.tests.base import DjangoLedgerBaseTest
6
+
7
+
8
+ class SimpleOFXTest(DjangoLedgerBaseTest):
9
+ BASE_PATH = "django_ledger/tests/test_io_ofx/samples/"
10
+
11
+ def get_sample_ofx(self, ofx_sample_name: str):
12
+ ofx = OFXFileManager(ofx_file_or_path=os.path.join(self.BASE_PATH, ofx_sample_name))
13
+
14
+ return ofx
15
+
16
+ def test_ofx_v1_with_intu_bid_field(self):
17
+ """
18
+ OFX v1 with <INTU.BID> field. These are ofx files that are exported for Quickbooks.
19
+ This field can be used to identify the bank in the absence of the <FI.ORG> fields.
20
+ """
21
+ ofx = self.get_sample_ofx("v1_with_intu_bid.ofx")
22
+ accounts = ofx.get_accounts()
23
+
24
+ # The bank and fid fields are not provided in this ofx file.
25
+ self.assertIsNone(accounts[0]["fid"])
26
+ self.assertIsNone(accounts[0]["bank"])
27
+ # balance observed from the ofx file
28
+ self.assertEqual(ofx.ofx_data.statements[0].balance.balamt, Decimal("123456.49"))
29
+
30
+ def test_ofx_v1_with_open_tags(self):
31
+ """
32
+ OFX v1 with open tags like `<DTSERVER>20211015063225[-5:EST]` instead of `<DTSERVER>20230510120000</DTSERVER>`
33
+ """
34
+ ofx = self.get_sample_ofx("v1_with_open_tags.ofx")
35
+ accounts = ofx.get_accounts()
36
+ account = accounts[0]
37
+
38
+ self.assertIsNone(account["fid"])
39
+ self.assertIsNone(account["bank"])
40
+ self.assertEqual(ofx.ofx_data.statements[0].balance.balamt, Decimal("1868.27"))
41
+
42
+ def test_ofx_v2_good(self):
43
+ """
44
+ ofx v2 uses XML rather than SGML. This is a good ofx v2 file.
45
+ """
46
+ ofx = self.get_sample_ofx("v2_good.ofx")
47
+ accounts = ofx.get_accounts()
48
+ account = accounts[0]
49
+
50
+ self.assertEqual(account["fid"], "123456789")
51
+ self.assertEqual(account["bank"], "BANK NAME")
52
+ self.assertEqual(ofx.ofx_data.statements[0].balance.balamt, Decimal("5000.00"))
@@ -210,7 +210,7 @@ class AccountModelYearDetailView(DjangoLedgerSecurityMixIn,
210
210
  context['header_title'] = f'Account {account.code} - {account.name}'
211
211
  context['page_title'] = f'Account {account.code} - {account.name}'
212
212
  account_model: AccountModel = self.object
213
- txs_qs = account_model.transactionmodel_set.posted().order_by(
213
+ txs_qs = account_model.transactionmodel_set.all().posted().order_by(
214
214
  'journal_entry__timestamp').select_related(
215
215
  'journal_entry', 'journal_entry__entity_unit')
216
216
  txs_qs = txs_qs.from_date(self.get_from_date())
@@ -3,7 +3,7 @@ Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
3
3
  Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
4
4
 
5
5
  Contributions to this module:
6
- Miguel Sanda <msanda@arrobalytics.com>
6
+ * Miguel Sanda <msanda@arrobalytics.com>
7
7
  """
8
8
 
9
9
  from django.contrib import messages
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-ledger
3
- Version: 0.6.2
3
+ Version: 0.6.4
4
4
  Summary: Double entry accounting system built on the Django Web Framework.
5
5
  Author-email: Miguel Sanda <msanda@arrobalytics.com>
6
6
  Maintainer-email: Miguel Sanda <msanda@arrobalytics.com>