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.
- django_ledger/__init__.py +1 -1
- django_ledger/admin/coa.py +1 -2
- django_ledger/admin/ledger.py +1 -0
- django_ledger/forms/account.py +22 -11
- django_ledger/forms/data_import.py +2 -1
- django_ledger/io/ofx.py +3 -2
- django_ledger/io/roles.py +6 -0
- django_ledger/models/accounts.py +523 -196
- django_ledger/models/bill.py +28 -14
- django_ledger/models/closing_entry.py +8 -0
- django_ledger/models/entity.py +12 -5
- django_ledger/models/estimate.py +93 -21
- django_ledger/models/invoice.py +44 -14
- django_ledger/models/journal_entry.py +112 -49
- django_ledger/models/ledger.py +32 -0
- django_ledger/models/purchase_order.py +34 -3
- django_ledger/models/signals.py +58 -0
- django_ledger/report/cash_flow_statement.py +1 -1
- django_ledger/report/core.py +1 -1
- django_ledger/static/.DS_Store +0 -0
- django_ledger/static/django_ledger/.DS_Store +0 -0
- django_ledger/static/django_ledger/logo_2/.DS_Store +0 -0
- django_ledger/static/django_ledger/logo_2/django_ledger_logo_dark.png +0 -0
- django_ledger/static/django_ledger/logo_2/django_ledger_logo_dark@0.5x.png +0 -0
- django_ledger/static/django_ledger/logo_2/django_ledger_logo_dark@2x.png +0 -0
- django_ledger/static/django_ledger/logo_2/django_ledger_logo_dark@3x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-full-vert.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-full-vert@0.5x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-full-vert@2x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-full-vert@3x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo-full-horiz.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo-full-horiz@0.5x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo-full-horiz@2x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo-full-horiz@3x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo-full-vert.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo-full-vert@0.5x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo-full-vert@2x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo-full-vert@3x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo@0.5x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo@2x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo@3x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-full-horiz.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-full-horiz@0.5x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-full-horiz@2x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-full-horiz@3x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-full-vert.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-full-vert@0.5x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-full-vert@2x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-full-vert@3x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-horiz.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-horiz@0.5x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-horiz@2x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-horiz@3x.png +0 -0
- django_ledger/templates/django_ledger/financial_statements/balance_sheet.html +2 -2
- django_ledger/tests/test_io_ofx/__init__.py +0 -0
- django_ledger/tests/test_io_ofx/tests.py +52 -0
- django_ledger/views/account.py +1 -1
- django_ledger/views/bill.py +1 -1
- {django_ledger-0.6.2.dist-info → django_ledger-0.6.4.dist-info}/METADATA +1 -1
- {django_ledger-0.6.2.dist-info → django_ledger-0.6.4.dist-info}/RECORD +65 -27
- {django_ledger-0.6.2.dist-info → django_ledger-0.6.4.dist-info}/top_level.txt +0 -1
- {django_ledger-0.6.2.dist-info → django_ledger-0.6.4.dist-info}/AUTHORS.md +0 -0
- {django_ledger-0.6.2.dist-info → django_ledger-0.6.4.dist-info}/LICENSE +0 -0
- {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 (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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 (
|
|
58
|
-
|
|
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
|
-
|
|
594
|
-
|
|
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
|
-
|
|
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
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
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
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
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
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
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
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
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
|
"""
|
django_ledger/models/ledger.py
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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
|
django_ledger/report/core.py
CHANGED
|
@@ -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.
|
|
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
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
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:
|
|
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:
|
|
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"))
|
django_ledger/views/account.py
CHANGED
|
@@ -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())
|
django_ledger/views/bill.py
CHANGED
|
@@ -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.
|
|
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>
|