Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v0.7.4 #243

Open
wants to merge 10 commits into
base: develop
Choose a base branch
from
172 changes: 84 additions & 88 deletions Pipfile.lock

Large diffs are not rendered by default.

9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

Django Ledger is a powerful financial management system built on the Django Web Framework. It offers a simplified API for handling complex accounting tasks in financially driven applications.

[Join our Discord](https://discord.gg/c7PZcbYgrc) | [Documentation](https://django-ledger.readthedocs.io/en/latest/) | [QuickStart Notebook](https://github.com/arrobalytics/django-ledger/blob/develop/notebooks/QuickStart%20Notebook.ipynb)
[FREE Get Started Guide](https://www.djangoledger.com/get-started) | [Join our Discord](https://discord.gg/c7PZcbYgrc) | [Documentation](https://django-ledger.readthedocs.io/en/latest/) | [QuickStart Notebook](https://github.com/arrobalytics/django-ledger/blob/develop/notebooks/QuickStart%20Notebook.ipynb)

## Key Features

Expand Down Expand Up @@ -85,7 +85,12 @@ pipenv install django
* Install Django Ledger

```shell script
pipenv install django-ledger[graphql,pdf]
pipenv install "django-ledger[graphql,pdf]"
```

Alternatively, you can use:
```shell script
pipenv install django-ledger\[graphql,pdf\]
```

* Activate your new virtual environment:
Expand Down
2 changes: 1 addition & 1 deletion django_ledger/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
default_app_config = 'django_ledger.apps.DjangoLedgerConfig'

"""Django Ledger"""
__version__ = '0.7.3'
__version__ = '0.7.4'
__license__ = 'GPLv3 License'

__author__ = 'Miguel Sanda'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,9 @@ def resolve_all_journal_entry(self, info, slug_name, pk_ledger, **kwargs):
sort = info.context.GET.get('sort')
if not sort:
sort = '-updated'
return JournalEntryModel.objects.for_ledger(
ledger_pk=pk_ledger,
return JournalEntryModel.objects.for_entity(
entity_slug=slug_name,
user_model=info.context.user
).order_by(sort)
).for_ledger(ledger_pk=pk_ledger).order_by(sort)
else:
return JournalEntryModel.objects.none()
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,20 @@ class Meta:
'description': ['exact', 'icontains', 'istartswith'],
}
interfaces = (relay.Node,)


class TransactionsQuery(graphene.ObjectType):
all_transactions = DjangoFilterConnectionField(TransactionNode, slug_name=graphene.String(required=True),
pk_je=graphene.UUID(), pk_ledger=graphene.UUID())
all_transactions = DjangoFilterConnectionField(
TransactionNode,
slug_name=graphene.String(required=True),
pk_je=graphene.UUID(),
pk_ledger=graphene.UUID())

def resolve_all_transactions(self, info, slug_name, pk_je, pk_ledger, **kwargs):
if info.context.user.is_authenticated:
return TransactionModel.objects.for_journal_entry(
return TransactionModel.objects.for_entity(
entity_slug=slug_name,
user_model=info.context.user,
je_model=pk_je,
ledger_model=pk_ledger
).order_by('account__code')
).for_journal_entry(je_model=pk_je).order_by('account__code')
else:
return TransactionModel.objects.none()

21 changes: 16 additions & 5 deletions django_ledger/forms/journal_entry.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
from typing import Union
from uuid import UUID

from django.forms import ModelForm, Textarea, Select, DateTimeInput, ValidationError
from django.utils.translation import gettext_lazy as _

from django_ledger.models import EntityModel
from django_ledger.models.journal_entry import JournalEntryModel
from django_ledger.models.ledger import LedgerModel
from django_ledger.models.unit import EntityUnitModel
Expand All @@ -9,19 +13,26 @@

class JournalEntryModelCreateForm(ModelForm):
def __init__(self,
entity_slug: str,
ledger_model: LedgerModel,
entity_model: EntityModel,
ledger_model: Union[str, UUID, LedgerModel],
user_model, *args, **kwargs):
super().__init__(*args, **kwargs)
self.USER_MODEL = user_model
self.ENTITY_SLUG = entity_slug
self.ENTITY_MODEL: EntityModel = entity_model

# processes the provided ledger model UUID is valid....
if isinstance(ledger_model, (str, UUID)):
ledger_model = self.ENTITY_MODEL.get_ledgers().get(uuid__exact=ledger_model)
elif isinstance(ledger_model, LedgerModel):
self.ENTITY_MODEL.validate_ledger_model_for_entity(ledger_model)

self.LEDGER_MODEL: LedgerModel = ledger_model
self.USER_MODEL = user_model

if 'timestamp' in self.fields:
self.fields['timestamp'].required = False
if 'entity_unit' in self.fields:
self.fields['entity_unit'].queryset = EntityUnitModel.objects.for_entity(
entity_slug=self.ENTITY_SLUG,
entity_slug=self.ENTITY_MODEL,
user_model=self.USER_MODEL
)

Expand Down
25 changes: 14 additions & 11 deletions django_ledger/io/io_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,11 +359,12 @@ def database_digest(self,
raise IOValidationError('Inconsistent entity_slug. '
f'Provided {entity_slug} does not match actual {self.slug}')
if unit_slug:
txs_queryset_init = TransactionModel.objects.for_unit(

txs_queryset_init = TransactionModel.objects.for_entity(
user_model=user_model,
entity_slug=entity_slug or self.slug,
unit_slug=unit_slug
)
entity_slug=entity_slug or self.slug
).for_unit(unit_slug=unit_slug)

else:
txs_queryset_init = TransactionModel.objects.for_entity(
user_model=user_model,
Expand All @@ -373,20 +374,22 @@ def database_digest(self,
if not entity_slug:
raise IOValidationError(
'Calling digest from Entity Unit requires entity_slug explicitly for safety')
txs_queryset_init = TransactionModel.objects.for_unit(

txs_queryset_init = TransactionModel.objects.for_entity(
user_model=user_model,
entity_slug=entity_slug,
unit_slug=unit_slug or self
)
).for_unit(unit_slug=unit_slug or self)

elif self.is_ledger_model():
if not entity_slug:
raise IOValidationError(
'Calling digest from Ledger Model requires entity_slug explicitly for safety')
txs_queryset_init = TransactionModel.objects.for_ledger(
user_model=user_model,

txs_queryset_init = TransactionModel.objects.for_entity(
entity_slug=entity_slug,
ledger_model=self
)
user_model=user_model,
).for_ledger(ledger_model=self)

else:
raise IOValidationError(
message=f'Cannot call digest from {self.__class__.__name__}'
Expand Down
19 changes: 17 additions & 2 deletions django_ledger/models/bill.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,13 @@
from django_ledger.io.io_core import get_localtime, get_localdate
from django_ledger.models.entity import EntityModel
from django_ledger.models.items import ItemTransactionModelQuerySet, ItemTransactionModel, ItemModel, ItemModelQuerySet
from django_ledger.models.mixins import (CreateUpdateMixIn, AccrualMixIn, MarkdownNotesMixIn,
PaymentTermsMixIn, ItemizeMixIn)
from django_ledger.models.mixins import (
CreateUpdateMixIn,
AccrualMixIn,
MarkdownNotesMixIn,
PaymentTermsMixIn,
ItemizeMixIn
)
from django_ledger.models.signals import (
bill_status_draft,
bill_status_in_review,
Expand Down Expand Up @@ -575,6 +580,16 @@ def get_itemtxs_data(self,

# ### ItemizeMixIn implementation END...

def get_transaction_queryset(self, annotated: bool = False):
"""
Fetches the TransactionModelQuerySet associated with the BillModel instance.
"""
TransactionModel = lazy_loader.get_txs_model()
transaction_model_qs = TransactionModel.objects.all().for_ledger(ledger_model=self.ledger_id)
if annotated:
return transaction_model_qs.with_annotated_details()
return transaction_model_qs

# State..
def get_migrate_state_desc(self) -> str:
"""
Expand Down
4 changes: 4 additions & 0 deletions django_ledger/models/chart_of_accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,10 @@ def get_account_root_node(self,
return qs
return qs.get()

raise ChartOfAccountsModelValidationError(
message='Adding Root account to Chart of Accounts is not allowed.'
)

def get_non_root_coa_accounts_qs(self) -> AccountModelQuerySet:
"""
Returns a query set of non-root accounts in the chart of accounts.
Expand Down
14 changes: 8 additions & 6 deletions django_ledger/models/closing_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,10 +232,11 @@ def mark_as_unposted(self, commit: bool = False, update_entity_meta: bool = Fals
message=_(f'Closing Entry {self.closing_date} is not posted.')
)
self.posted = False
TransactionModel.objects.for_ledger(
ledger_model=self.ledger_model,

TransactionModel.objects.for_entity(
entity_slug=self.entity_model_id
).delete()
).for_ledger(ledger_model=self.ledger_model).delete()

self.ledger_model.journal_entries.all().delete()
if commit:
self.save(
Expand Down Expand Up @@ -303,10 +304,11 @@ def delete(self, **kwargs):
raise ClosingEntryValidationError(
message=_('Cannot delete a posted Closing Entry')
)
TransactionModel.objects.for_ledger(
ledger_model=self.ledger_model,

TransactionModel.objects.for_entity(
entity_slug=self.entity_model_id
).delete()
).for_ledger(ledger_model=self.ledger_model).delete()

return self.ledger_model.delete()

def get_delete_html_id(self) -> str:
Expand Down
16 changes: 12 additions & 4 deletions django_ledger/models/invoice.py
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,16 @@ def get_itemtxs_data(self,

# ### ItemizeMixIn implementation END...

def get_transaction_queryset(self, annotated: bool = False):
"""
Fetches the TransactionModelQuerySet associated with the InvoiceModel instance.
"""
TransactionModel = lazy_loader.get_txs_model()
transaction_model_qs = TransactionModel.objects.all().for_ledger(ledger_model=self.ledger_id)
if annotated:
return transaction_model_qs.with_annotated_details()
return transaction_model_qs

def get_migrate_state_desc(self):
"""
Description used when migrating transactions into the LedgerModel.
Expand All @@ -557,8 +567,7 @@ def get_migrate_state_desc(self):
"""
return f'Invoice {self.invoice_number} account adjustment.'

def get_migration_data(self,
queryset: Optional[ItemTransactionModelQuerySet] = None) -> ItemTransactionModelQuerySet:
def get_migration_data(self, queryset: Optional[ItemTransactionModelQuerySet] = None) -> ItemTransactionModelQuerySet:

"""
Fetches necessary item transaction data to perform a migration into the LedgerModel.
Expand Down Expand Up @@ -591,8 +600,7 @@ def get_migration_data(self,
'total_amount').annotate(
account_unit_total=Sum('total_amount'))

def update_amount_due(self,
itemtxs_qs: Optional[ItemTransactionModelQuerySet] = None) -> ItemTransactionModelQuerySet:
def update_amount_due(self, itemtxs_qs: Optional[ItemTransactionModelQuerySet] = None) -> ItemTransactionModelQuerySet:
"""
Updates the InvoiceModel amount due.

Expand Down
Loading