django-ledger 0.7.11__py3-none-any.whl → 0.8.1__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/context.py +12 -0
- django_ledger/forms/account.py +45 -46
- django_ledger/forms/bill.py +0 -4
- django_ledger/forms/closing_entry.py +13 -1
- django_ledger/forms/data_import.py +182 -63
- django_ledger/forms/estimate.py +3 -6
- django_ledger/forms/invoice.py +3 -7
- django_ledger/forms/item.py +10 -18
- django_ledger/forms/purchase_order.py +2 -4
- django_ledger/io/io_core.py +515 -400
- django_ledger/io/io_generator.py +7 -6
- django_ledger/io/io_library.py +1 -2
- django_ledger/migrations/0025_alter_billmodel_cash_account_and_more.py +70 -0
- django_ledger/migrations/0026_stagedtransactionmodel_customer_model_and_more.py +56 -0
- django_ledger/models/__init__.py +2 -1
- django_ledger/models/accounts.py +109 -69
- django_ledger/models/bank_account.py +40 -23
- django_ledger/models/bill.py +386 -333
- django_ledger/models/chart_of_accounts.py +173 -105
- django_ledger/models/closing_entry.py +99 -48
- django_ledger/models/customer.py +100 -66
- django_ledger/models/data_import.py +818 -323
- django_ledger/models/deprecations.py +61 -0
- django_ledger/models/entity.py +891 -644
- django_ledger/models/estimate.py +57 -28
- django_ledger/models/invoice.py +46 -26
- django_ledger/models/items.py +503 -142
- django_ledger/models/journal_entry.py +61 -47
- django_ledger/models/ledger.py +106 -42
- django_ledger/models/mixins.py +424 -281
- django_ledger/models/purchase_order.py +39 -17
- django_ledger/models/receipt.py +1083 -0
- django_ledger/models/transactions.py +242 -139
- django_ledger/models/unit.py +93 -54
- django_ledger/models/utils.py +12 -2
- django_ledger/models/vendor.py +121 -70
- django_ledger/report/core.py +2 -14
- django_ledger/settings.py +57 -71
- django_ledger/static/django_ledger/bundle/djetler.bundle.js +1 -1
- django_ledger/static/django_ledger/bundle/djetler.bundle.js.LICENSE.txt +25 -0
- django_ledger/static/django_ledger/bundle/styles.bundle.js +1 -1
- django_ledger/static/django_ledger/css/djl_styles.css +273 -0
- django_ledger/templates/django_ledger/bills/includes/card_bill.html +2 -2
- django_ledger/templates/django_ledger/components/menu.html +41 -26
- django_ledger/templates/django_ledger/components/period_navigator.html +5 -3
- django_ledger/templates/django_ledger/customer/customer_detail.html +87 -0
- django_ledger/templates/django_ledger/customer/customer_list.html +0 -1
- django_ledger/templates/django_ledger/customer/tags/customer_table.html +8 -6
- django_ledger/templates/django_ledger/data_import/tags/data_import_job_txs_imported.html +24 -3
- django_ledger/templates/django_ledger/data_import/tags/data_import_job_txs_table.html +26 -10
- django_ledger/templates/django_ledger/entity/entity_dashboard.html +2 -2
- django_ledger/templates/django_ledger/entity/includes/card_entity.html +12 -6
- django_ledger/templates/django_ledger/financial_statements/balance_sheet.html +1 -1
- django_ledger/templates/django_ledger/financial_statements/cash_flow.html +4 -1
- django_ledger/templates/django_ledger/financial_statements/income_statement.html +4 -1
- django_ledger/templates/django_ledger/financial_statements/tags/balance_sheet_statement.html +27 -3
- django_ledger/templates/django_ledger/financial_statements/tags/cash_flow_statement.html +16 -4
- django_ledger/templates/django_ledger/financial_statements/tags/income_statement.html +73 -18
- django_ledger/templates/django_ledger/includes/widget_ratios.html +18 -24
- django_ledger/templates/django_ledger/invoice/includes/card_invoice.html +3 -3
- django_ledger/templates/django_ledger/layouts/base.html +7 -2
- django_ledger/templates/django_ledger/layouts/content_layout_1.html +1 -1
- django_ledger/templates/django_ledger/receipt/customer_receipt_report.html +115 -0
- django_ledger/templates/django_ledger/receipt/receipt_delete.html +30 -0
- django_ledger/templates/django_ledger/receipt/receipt_detail.html +89 -0
- django_ledger/templates/django_ledger/receipt/receipt_list.html +134 -0
- django_ledger/templates/django_ledger/receipt/vendor_receipt_report.html +115 -0
- django_ledger/templates/django_ledger/vendor/tags/vendor_table.html +12 -7
- django_ledger/templates/django_ledger/vendor/vendor_detail.html +86 -0
- django_ledger/templatetags/django_ledger.py +338 -191
- django_ledger/tests/test_accounts.py +1 -2
- django_ledger/tests/test_io.py +17 -0
- django_ledger/tests/test_purchase_order.py +3 -3
- django_ledger/tests/test_transactions.py +1 -2
- django_ledger/urls/__init__.py +1 -4
- django_ledger/urls/customer.py +3 -0
- django_ledger/urls/data_import.py +3 -0
- django_ledger/urls/receipt.py +102 -0
- django_ledger/urls/vendor.py +1 -0
- django_ledger/views/__init__.py +1 -0
- django_ledger/views/bill.py +8 -11
- django_ledger/views/chart_of_accounts.py +6 -4
- django_ledger/views/closing_entry.py +11 -7
- django_ledger/views/customer.py +68 -30
- django_ledger/views/data_import.py +120 -66
- django_ledger/views/djl_api.py +3 -5
- django_ledger/views/entity.py +2 -4
- django_ledger/views/estimate.py +3 -7
- django_ledger/views/inventory.py +3 -5
- django_ledger/views/invoice.py +4 -6
- django_ledger/views/item.py +7 -11
- django_ledger/views/journal_entry.py +1 -2
- django_ledger/views/mixins.py +125 -93
- django_ledger/views/purchase_order.py +24 -35
- django_ledger/views/receipt.py +294 -0
- django_ledger/views/unit.py +1 -2
- django_ledger/views/vendor.py +54 -16
- {django_ledger-0.7.11.dist-info → django_ledger-0.8.1.dist-info}/METADATA +43 -75
- {django_ledger-0.7.11.dist-info → django_ledger-0.8.1.dist-info}/RECORD +104 -122
- {django_ledger-0.7.11.dist-info → django_ledger-0.8.1.dist-info}/WHEEL +1 -1
- django_ledger-0.8.1.dist-info/top_level.txt +1 -0
- django_ledger/contrib/django_ledger_graphene/__init__.py +0 -0
- django_ledger/contrib/django_ledger_graphene/accounts/schema.py +0 -33
- django_ledger/contrib/django_ledger_graphene/api.py +0 -42
- django_ledger/contrib/django_ledger_graphene/apps.py +0 -6
- django_ledger/contrib/django_ledger_graphene/auth/mutations.py +0 -49
- django_ledger/contrib/django_ledger_graphene/auth/schema.py +0 -6
- django_ledger/contrib/django_ledger_graphene/bank_account/mutations.py +0 -61
- django_ledger/contrib/django_ledger_graphene/bank_account/schema.py +0 -34
- django_ledger/contrib/django_ledger_graphene/bill/mutations.py +0 -0
- django_ledger/contrib/django_ledger_graphene/bill/schema.py +0 -34
- django_ledger/contrib/django_ledger_graphene/coa/mutations.py +0 -0
- django_ledger/contrib/django_ledger_graphene/coa/schema.py +0 -30
- django_ledger/contrib/django_ledger_graphene/customers/__init__.py +0 -0
- django_ledger/contrib/django_ledger_graphene/customers/mutations.py +0 -71
- django_ledger/contrib/django_ledger_graphene/customers/schema.py +0 -43
- django_ledger/contrib/django_ledger_graphene/data_import/mutations.py +0 -0
- django_ledger/contrib/django_ledger_graphene/data_import/schema.py +0 -0
- django_ledger/contrib/django_ledger_graphene/entity/mutations.py +0 -0
- django_ledger/contrib/django_ledger_graphene/entity/schema.py +0 -94
- django_ledger/contrib/django_ledger_graphene/item/mutations.py +0 -0
- django_ledger/contrib/django_ledger_graphene/item/schema.py +0 -31
- django_ledger/contrib/django_ledger_graphene/journal_entry/mutations.py +0 -0
- django_ledger/contrib/django_ledger_graphene/journal_entry/schema.py +0 -35
- django_ledger/contrib/django_ledger_graphene/ledger/mutations.py +0 -0
- django_ledger/contrib/django_ledger_graphene/ledger/schema.py +0 -32
- django_ledger/contrib/django_ledger_graphene/purchase_order/mutations.py +0 -0
- django_ledger/contrib/django_ledger_graphene/purchase_order/schema.py +0 -31
- django_ledger/contrib/django_ledger_graphene/transaction/mutations.py +0 -0
- django_ledger/contrib/django_ledger_graphene/transaction/schema.py +0 -36
- django_ledger/contrib/django_ledger_graphene/unit/mutations.py +0 -0
- django_ledger/contrib/django_ledger_graphene/unit/schema.py +0 -27
- django_ledger/contrib/django_ledger_graphene/vendor/mutations.py +0 -0
- django_ledger/contrib/django_ledger_graphene/vendor/schema.py +0 -37
- django_ledger/contrib/django_ledger_graphene/views.py +0 -12
- django_ledger-0.7.11.dist-info/top_level.txt +0 -4
- {django_ledger-0.7.11.dist-info → django_ledger-0.8.1.dist-info/licenses}/AUTHORS.md +0 -0
- {django_ledger-0.7.11.dist-info → django_ledger-0.8.1.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,294 @@
|
|
|
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
|
+
|
|
9
|
+
from typing import Optional
|
|
10
|
+
from calendar import month_name
|
|
11
|
+
|
|
12
|
+
from django.http import HttpResponseForbidden
|
|
13
|
+
from django.shortcuts import get_object_or_404
|
|
14
|
+
from django.utils.translation import gettext_lazy as _
|
|
15
|
+
from django.views.generic import (
|
|
16
|
+
ArchiveIndexView,
|
|
17
|
+
DeleteView,
|
|
18
|
+
DetailView,
|
|
19
|
+
MonthArchiveView,
|
|
20
|
+
YearArchiveView,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
from django_ledger.models import CustomerModel, EntityModel, LedgerModel, VendorModel
|
|
24
|
+
from django_ledger.models.receipt import ReceiptModel, ReceiptModelQuerySet
|
|
25
|
+
from django_ledger.models.transactions import TransactionModel
|
|
26
|
+
from django_ledger.views.mixins import (
|
|
27
|
+
DjangoLedgerSecurityMixIn,
|
|
28
|
+
QuarterlyReportMixIn,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class BaseReceiptModelViewMixIn(DjangoLedgerSecurityMixIn):
|
|
33
|
+
queryset: Optional[ReceiptModelQuerySet] = None
|
|
34
|
+
|
|
35
|
+
def get_queryset(self):
|
|
36
|
+
if self.queryset is None:
|
|
37
|
+
entity_model: EntityModel = self.AUTHORIZED_ENTITY_MODEL
|
|
38
|
+
qs = entity_model.get_receipts()
|
|
39
|
+
qs = qs.select_related(
|
|
40
|
+
'ledger_model', 'customer_model', 'vendor_model'
|
|
41
|
+
).order_by('-receipt_date', '-created')
|
|
42
|
+
|
|
43
|
+
receipt_type = self.kwargs.get('receipt_type')
|
|
44
|
+
if receipt_type:
|
|
45
|
+
qs = qs.filter(receipt_type__exact=receipt_type)
|
|
46
|
+
if receipt_type in [
|
|
47
|
+
ReceiptModel.SALES_RECEIPT,
|
|
48
|
+
ReceiptModel.SALES_REFUND,
|
|
49
|
+
]:
|
|
50
|
+
qs = qs.filter(
|
|
51
|
+
customer_model__isnull=False, vendor_model__isnull=True
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
elif receipt_type in [
|
|
55
|
+
ReceiptModel.EXPENSE_RECEIPT,
|
|
56
|
+
ReceiptModel.EXPENSE_REFUND,
|
|
57
|
+
]:
|
|
58
|
+
qs = qs.filter(
|
|
59
|
+
vendor_model__isnull=False, customer_model__isnull=True
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
vendor_pk = self.kwargs.get('vendor_pk')
|
|
63
|
+
if vendor_pk:
|
|
64
|
+
qs = qs.for_vendor(vendor_model=vendor_pk)
|
|
65
|
+
|
|
66
|
+
customer_pk = self.kwargs.get('customer_pk')
|
|
67
|
+
if customer_pk:
|
|
68
|
+
qs = qs.for_customer(customer_model=customer_pk)
|
|
69
|
+
|
|
70
|
+
self.queryset = qs
|
|
71
|
+
|
|
72
|
+
return self.queryset
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class ReceiptModelListView(BaseReceiptModelViewMixIn, ArchiveIndexView):
|
|
76
|
+
template_name = 'django_ledger/receipt/receipt_list.html'
|
|
77
|
+
context_object_name = 'receipt_list'
|
|
78
|
+
PAGE_TITLE = _('Receipts List')
|
|
79
|
+
date_field = 'receipt_date'
|
|
80
|
+
paginate_by = 20
|
|
81
|
+
paginate_orphans = 2
|
|
82
|
+
allow_empty = True
|
|
83
|
+
extra_context = {
|
|
84
|
+
'title': PAGE_TITLE,
|
|
85
|
+
'page_title': PAGE_TITLE,
|
|
86
|
+
'header_title': PAGE_TITLE,
|
|
87
|
+
'header_subtitle_icon': 'mdi:receipt',
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
def get_context_data(self, **kwargs):
|
|
91
|
+
context = super().get_context_data(**kwargs)
|
|
92
|
+
subtitle = None
|
|
93
|
+
|
|
94
|
+
receipt_type = self.kwargs.get('receipt_type')
|
|
95
|
+
|
|
96
|
+
if receipt_type:
|
|
97
|
+
context['title'] = ReceiptModel.RECEIPT_TYPES_MAP[receipt_type]
|
|
98
|
+
|
|
99
|
+
vendor_pk = self.kwargs.get('vendor_pk')
|
|
100
|
+
if vendor_pk:
|
|
101
|
+
vendor = VendorModel.objects.for_entity(
|
|
102
|
+
entity_model=self.AUTHORIZED_ENTITY_MODEL
|
|
103
|
+
).get(uuid__exact=vendor_pk)
|
|
104
|
+
subtitle = vendor.vendor_name
|
|
105
|
+
customer_pk = self.kwargs.get('customer_pk')
|
|
106
|
+
|
|
107
|
+
if customer_pk:
|
|
108
|
+
customer = CustomerModel.objects.for_entity(
|
|
109
|
+
entity_model=self.AUTHORIZED_ENTITY_MODEL
|
|
110
|
+
).get(uuid__exact=customer_pk)
|
|
111
|
+
subtitle = customer.customer_name
|
|
112
|
+
|
|
113
|
+
if subtitle:
|
|
114
|
+
context['header_subtitle'] = subtitle
|
|
115
|
+
return context
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class ReceiptModelYearListView(ReceiptModelListView, YearArchiveView):
|
|
119
|
+
def get_context_data(self, **kwargs):
|
|
120
|
+
context = super().get_context_data(**kwargs)
|
|
121
|
+
context['year'] = self.get_year()
|
|
122
|
+
context['page_title'] = _(f'Receipts List {self.get_year()}')
|
|
123
|
+
context['header_title'] = _(f'Receipts List {self.get_year()}')
|
|
124
|
+
context['header_subtitle'] = self.AUTHORIZED_ENTITY_MODEL.name
|
|
125
|
+
return context
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class ReceiptModelQuarterListView(ReceiptModelYearListView, QuarterlyReportMixIn):
|
|
129
|
+
def get_queryset(self):
|
|
130
|
+
qs = super().get_queryset()
|
|
131
|
+
return qs.for_dates(from_date=self.get_from_date(), to_date=self.get_to_date())
|
|
132
|
+
|
|
133
|
+
def get_context_data(self, **kwargs):
|
|
134
|
+
context = super().get_context_data(**kwargs)
|
|
135
|
+
context['page_title'] = _(f'Receipts List Q{self.get_quarter()}')
|
|
136
|
+
context['header_title'] = _(f'Receipts List Q{self.get_quarter()}')
|
|
137
|
+
context['header_subtitle'] = self.AUTHORIZED_ENTITY_MODEL.name
|
|
138
|
+
return context
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class ReceiptModelMonthListView(ReceiptModelYearListView, MonthArchiveView):
|
|
142
|
+
def get_context_data(self, **kwargs):
|
|
143
|
+
context = super().get_context_data(**kwargs)
|
|
144
|
+
year = self.get_year()
|
|
145
|
+
month_num = int(self.get_month())
|
|
146
|
+
month_label = month_name[month_num]
|
|
147
|
+
context['page_title'] = _(f'Receipts List {month_label} {year}')
|
|
148
|
+
context['header_title'] = _(f'Receipts List {month_label}, {year}')
|
|
149
|
+
context['header_subtitle'] = self.AUTHORIZED_ENTITY_MODEL.name
|
|
150
|
+
return context
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class ReceiptModelDetailView(BaseReceiptModelViewMixIn, DetailView):
|
|
154
|
+
template_name = 'django_ledger/receipt/receipt_detail.html'
|
|
155
|
+
context_object_name = 'receipt'
|
|
156
|
+
slug_field = 'uuid'
|
|
157
|
+
slug_url_kwarg = 'receipt_pk'
|
|
158
|
+
|
|
159
|
+
def get_context_data(self, **kwargs):
|
|
160
|
+
context = super().get_context_data(**kwargs)
|
|
161
|
+
receipt_model: ReceiptModel = self.object
|
|
162
|
+
ledger_model: LedgerModel = receipt_model.ledger_model
|
|
163
|
+
title = _(f'Receipt {receipt_model.receipt_number}')
|
|
164
|
+
context['page_title'] = title
|
|
165
|
+
context['header_title'] = title
|
|
166
|
+
context['header_subtitle'] = receipt_model.receipt_date
|
|
167
|
+
context['header_subtitle_icon'] = 'mdi:receipt'
|
|
168
|
+
|
|
169
|
+
tx_list = (
|
|
170
|
+
TransactionModel.objects.for_entity(
|
|
171
|
+
entity_model=self.AUTHORIZED_ENTITY_MODEL
|
|
172
|
+
)
|
|
173
|
+
.for_ledger(ledger_model=ledger_model)
|
|
174
|
+
.posted()
|
|
175
|
+
.not_closing_entry()
|
|
176
|
+
.select_related(
|
|
177
|
+
'account',
|
|
178
|
+
'journal_entry',
|
|
179
|
+
'journal_entry__entity_unit',
|
|
180
|
+
)
|
|
181
|
+
.order_by('journal_entry__timestamp', 'account__code')
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
context['tx_list'] = tx_list
|
|
185
|
+
context['staged_tx'] = receipt_model.staged_transaction_model
|
|
186
|
+
if receipt_model.staged_transaction_model_id:
|
|
187
|
+
context['import_job'] = receipt_model.staged_transaction_model.import_job
|
|
188
|
+
return context
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
# VENDOR VIEWS......
|
|
192
|
+
class VendorReceiptReportListView(ReceiptModelListView):
|
|
193
|
+
template_name = 'django_ledger/receipt/vendor_receipt_report.html'
|
|
194
|
+
|
|
195
|
+
def get_context_data(self, **kwargs):
|
|
196
|
+
context = super().get_context_data(**kwargs)
|
|
197
|
+
vendor_pk = self.kwargs['vendor_pk']
|
|
198
|
+
vendor_model_qs = VendorModel.objects.for_entity(
|
|
199
|
+
entity_model=self.AUTHORIZED_ENTITY_MODEL
|
|
200
|
+
)
|
|
201
|
+
vendor_model: VendorModel = get_object_or_404(
|
|
202
|
+
vendor_model_qs, uuid__exact=vendor_pk
|
|
203
|
+
)
|
|
204
|
+
context['vendor_model'] = vendor_model
|
|
205
|
+
context['page_title'] = _(f'Vendor Receipts {vendor_model.vendor_name}')
|
|
206
|
+
context['header_title'] = _('Vendor Receipts')
|
|
207
|
+
context['header_subtitle'] = vendor_model.vendor_name
|
|
208
|
+
context['header_subtitle_icon'] = 'mdi:receipt'
|
|
209
|
+
return context
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class VendorReceiptReportYearListView(ReceiptModelYearListView):
|
|
213
|
+
template_name = 'django_ledger/receipt/vendor_receipt_report.html'
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class VendorReceiptReportQuarterListView(ReceiptModelQuarterListView):
|
|
217
|
+
template_name = 'django_ledger/receipt/vendor_receipt_report.html'
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class VendorReceiptReportMonthListView(ReceiptModelMonthListView):
|
|
221
|
+
template_name = 'django_ledger/receipt/vendor_receipt_report.html'
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
# CUSTOMERS VIEWS......
|
|
225
|
+
class CustomerReceiptReportListView(ReceiptModelListView):
|
|
226
|
+
template_name = 'django_ledger/receipt/customer_receipt_report.html'
|
|
227
|
+
allow_empty = True
|
|
228
|
+
|
|
229
|
+
def get_context_data(self, **kwargs):
|
|
230
|
+
context = super().get_context_data(**kwargs)
|
|
231
|
+
customer_model_qs = CustomerModel.objects.for_entity(
|
|
232
|
+
entity_model=self.AUTHORIZED_ENTITY_MODEL
|
|
233
|
+
)
|
|
234
|
+
customer_pk = self.kwargs['customer_pk']
|
|
235
|
+
customer_model = get_object_or_404(
|
|
236
|
+
customer_model_qs,
|
|
237
|
+
uuid__exact=customer_pk,
|
|
238
|
+
)
|
|
239
|
+
context['vendor_model'] = customer_model
|
|
240
|
+
context['page_title'] = _(f'Customer Receipts {customer_model.name}')
|
|
241
|
+
context['header_title'] = _('Customer Receipts')
|
|
242
|
+
context['header_subtitle'] = customer_model.vendor_name
|
|
243
|
+
context['header_subtitle_icon'] = 'mdi:receipt'
|
|
244
|
+
return context
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
class CustomerReceiptReportYearListView(CustomerReceiptReportListView):
|
|
248
|
+
template_name = 'django_ledger/receipt/customer_receipt_report.html'
|
|
249
|
+
make_object_list = True
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class CustomerReceiptReportQuarterListView(ReceiptModelQuarterListView):
|
|
253
|
+
template_name = 'django_ledger/receipt/customer_receipt_report.html'
|
|
254
|
+
make_object_list = True
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
class CustomerReceiptReportMonthListView(ReceiptModelMonthListView):
|
|
258
|
+
template_name = 'django_ledger/receipt/customer_receipt_report.html'
|
|
259
|
+
month_format = '%m'
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class ReceiptModelDeleteView(BaseReceiptModelViewMixIn, DeleteView):
|
|
263
|
+
template_name = 'django_ledger/receipt/receipt_delete.html'
|
|
264
|
+
context_object_name = 'receipt'
|
|
265
|
+
slug_field = 'uuid'
|
|
266
|
+
slug_url_kwarg = 'receipt_pk'
|
|
267
|
+
|
|
268
|
+
def get_context_data(self, **kwargs):
|
|
269
|
+
context = super().get_context_data(**kwargs)
|
|
270
|
+
receipt: ReceiptModel = self.object
|
|
271
|
+
title = _(f'Delete Receipt {receipt.receipt_number}')
|
|
272
|
+
context['page_title'] = title
|
|
273
|
+
context['header_title'] = title
|
|
274
|
+
context['header_subtitle_icon'] = 'mdi:receipt'
|
|
275
|
+
return context
|
|
276
|
+
|
|
277
|
+
def can_delete(self, receipt_model: ReceiptModel) -> bool:
|
|
278
|
+
entity_model: EntityModel = self.AUTHORIZED_ENTITY_MODEL
|
|
279
|
+
ce_date = entity_model.get_closing_entry_for_date(
|
|
280
|
+
io_date=receipt_model.receipt_date, inclusive=True
|
|
281
|
+
)
|
|
282
|
+
return ce_date is None
|
|
283
|
+
|
|
284
|
+
def delete(self, request, *args, **kwargs):
|
|
285
|
+
receipt_model: ReceiptModel = self.object
|
|
286
|
+
if not receipt_model.can_delete():
|
|
287
|
+
return HttpResponseForbidden(
|
|
288
|
+
'Receipt cannot be deleted because it falls within a closed period.'
|
|
289
|
+
)
|
|
290
|
+
return super().delete(request, *args, **kwargs)
|
|
291
|
+
|
|
292
|
+
def get_success_url(self):
|
|
293
|
+
receipt_model: ReceiptModel = self.object
|
|
294
|
+
return receipt_model.get_list_url()
|
django_ledger/views/unit.py
CHANGED
|
@@ -28,8 +28,7 @@ class EntityUnitModelModelBaseView(DjangoLedgerSecurityMixIn):
|
|
|
28
28
|
def get_queryset(self):
|
|
29
29
|
if self.queryset is None:
|
|
30
30
|
self.queryset = EntityUnitModel.objects.for_entity(
|
|
31
|
-
|
|
32
|
-
user_model=self.request.user
|
|
31
|
+
entity_model=self.get_authorized_entity_instance()
|
|
33
32
|
).select_related('entity')
|
|
34
33
|
return super().get_queryset()
|
|
35
34
|
|
django_ledger/views/vendor.py
CHANGED
|
@@ -5,13 +5,16 @@ Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
|
|
|
5
5
|
Contributions to this module:
|
|
6
6
|
* Miguel Sanda <msanda@arrobalytics.com>
|
|
7
7
|
"""
|
|
8
|
+
|
|
8
9
|
from django.shortcuts import get_object_or_404
|
|
9
10
|
from django.urls import reverse
|
|
10
11
|
from django.utils.translation import gettext_lazy as _
|
|
11
|
-
from django.views.generic import
|
|
12
|
+
from django.views.generic import CreateView, DetailView, ListView, UpdateView
|
|
12
13
|
|
|
13
14
|
from django_ledger.forms.vendor import VendorModelForm
|
|
15
|
+
from django_ledger.models.bill import BillModel
|
|
14
16
|
from django_ledger.models.entity import EntityModel
|
|
17
|
+
from django_ledger.models.receipt import ReceiptModel
|
|
15
18
|
from django_ledger.models.vendor import VendorModel
|
|
16
19
|
from django_ledger.views.mixins import DjangoLedgerSecurityMixIn
|
|
17
20
|
|
|
@@ -22,8 +25,7 @@ class VendorModelModelBaseView(DjangoLedgerSecurityMixIn):
|
|
|
22
25
|
def get_queryset(self):
|
|
23
26
|
if self.queryset is None:
|
|
24
27
|
self.queryset = VendorModel.objects.for_entity(
|
|
25
|
-
|
|
26
|
-
user_model=self.request.user
|
|
28
|
+
entity_model=self.kwargs['entity_slug']
|
|
27
29
|
).order_by('-updated')
|
|
28
30
|
return super().get_queryset()
|
|
29
31
|
|
|
@@ -35,7 +37,7 @@ class VendorModelListView(VendorModelModelBaseView, ListView):
|
|
|
35
37
|
extra_context = {
|
|
36
38
|
'page_title': PAGE_TITLE,
|
|
37
39
|
'header_title': PAGE_TITLE,
|
|
38
|
-
'header_subtitle_icon': 'bi:person-lines-fill'
|
|
40
|
+
'header_subtitle_icon': 'bi:person-lines-fill',
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
def get_context_data(self, **kwargs):
|
|
@@ -53,21 +55,21 @@ class VendorModelCreateView(VendorModelModelBaseView, CreateView):
|
|
|
53
55
|
extra_context = {
|
|
54
56
|
'page_title': PAGE_TITLE,
|
|
55
57
|
'header_title': PAGE_TITLE,
|
|
56
|
-
'header_subtitle_icon': 'bi:person-lines-fill'
|
|
58
|
+
'header_subtitle_icon': 'bi:person-lines-fill',
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
def get_success_url(self):
|
|
60
|
-
return reverse(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
return reverse(
|
|
63
|
+
'django_ledger:vendor-list',
|
|
64
|
+
kwargs={'entity_slug': self.kwargs['entity_slug']},
|
|
65
|
+
)
|
|
64
66
|
|
|
65
67
|
def form_valid(self, form):
|
|
66
68
|
vendor_model: VendorModel = form.save(commit=False)
|
|
67
|
-
entity_model_qs = EntityModel.objects.for_user(
|
|
68
|
-
|
|
69
|
+
entity_model_qs = EntityModel.objects.for_user(user_model=self.request.user)
|
|
70
|
+
entity_model = get_object_or_404(
|
|
71
|
+
klass=entity_model_qs, slug__exact=self.kwargs['entity_slug']
|
|
69
72
|
)
|
|
70
|
-
entity_model = get_object_or_404(klass=entity_model_qs, slug__exact=self.kwargs['entity_slug'])
|
|
71
73
|
vendor_model.entity_model = entity_model
|
|
72
74
|
return super().form_valid(form)
|
|
73
75
|
|
|
@@ -91,7 +93,43 @@ class VendorModelUpdateView(VendorModelModelBaseView, UpdateView):
|
|
|
91
93
|
return context
|
|
92
94
|
|
|
93
95
|
def get_success_url(self):
|
|
94
|
-
return reverse(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
return reverse(
|
|
97
|
+
'django_ledger:vendor-list',
|
|
98
|
+
kwargs={'entity_slug': self.kwargs['entity_slug']},
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class VendorModelDetailView(VendorModelModelBaseView, DetailView):
|
|
103
|
+
template_name = 'django_ledger/vendor/vendor_detail.html'
|
|
104
|
+
context_object_name = 'vendor'
|
|
105
|
+
PAGE_TITLE = _('Vendor Details')
|
|
106
|
+
slug_url_kwarg = 'vendor_pk'
|
|
107
|
+
slug_field = 'uuid'
|
|
108
|
+
|
|
109
|
+
def get_context_data(self, **kwargs):
|
|
110
|
+
context = super().get_context_data(**kwargs)
|
|
111
|
+
|
|
112
|
+
vendor_model: VendorModel = self.object
|
|
113
|
+
receipts_qs = (
|
|
114
|
+
ReceiptModel.objects.for_entity(entity_model=self.AUTHORIZED_ENTITY_MODEL)
|
|
115
|
+
.for_vendor(vendor_model=vendor_model)
|
|
116
|
+
.order_by('-updated')
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
bills_qs = (
|
|
120
|
+
BillModel.objects.for_entity(entity_model=self.AUTHORIZED_ENTITY_MODEL)
|
|
121
|
+
.filter(vendor=vendor_model)
|
|
122
|
+
.order_by('-updated')
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
context.update(
|
|
126
|
+
{
|
|
127
|
+
'page_title': self.PAGE_TITLE,
|
|
128
|
+
'header_title': self.PAGE_TITLE,
|
|
129
|
+
'header_subtitle': f'{vendor_model.vendor_name} · {vendor_model.vendor_number}',
|
|
130
|
+
'header_subtitle_icon': 'bi:person-lines-fill',
|
|
131
|
+
'receipts': receipts_qs,
|
|
132
|
+
'bills': bills_qs,
|
|
133
|
+
}
|
|
134
|
+
)
|
|
135
|
+
return context
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: django-ledger
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.1
|
|
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>
|
|
@@ -18,26 +18,18 @@ Classifier: Framework :: Django :: 5.0
|
|
|
18
18
|
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
19
19
|
Classifier: Intended Audience :: End Users/Desktop
|
|
20
20
|
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
21
|
-
Requires-Python: >=3.
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
23
|
License-File: LICENSE
|
|
24
24
|
License-File: AUTHORS.md
|
|
25
|
-
Requires-Dist:
|
|
26
|
-
Requires-Dist: django>=
|
|
27
|
-
Requires-Dist:
|
|
28
|
-
Requires-Dist:
|
|
29
|
-
Requires-Dist: markdown>=3.
|
|
25
|
+
Requires-Dist: django>=4.2
|
|
26
|
+
Requires-Dist: django-treebeard>=4.7.1
|
|
27
|
+
Requires-Dist: faker>=37.6.0
|
|
28
|
+
Requires-Dist: fpdf2>=2.8.4
|
|
29
|
+
Requires-Dist: markdown>=3.9
|
|
30
30
|
Requires-Dist: ofxtools>=0.9.5
|
|
31
|
-
Requires-Dist: pillow>=
|
|
32
|
-
|
|
33
|
-
Requires-Dist: six>=1.16.0; python_version >= "2.7" and python_version not in "3.0, 3.1, 3.2, 3.3"
|
|
34
|
-
Requires-Dist: sqlparse>=0.4.3; python_version >= "3.5"
|
|
35
|
-
Provides-Extra: dev
|
|
36
|
-
Requires-Dist: sphinx~=4.5.0; extra == "dev"
|
|
37
|
-
Requires-Dist: behave~=1.2.6; extra == "dev"
|
|
38
|
-
Requires-Dist: pipenv-setup; extra == "dev"
|
|
39
|
-
Requires-Dist: pylint; extra == "dev"
|
|
40
|
-
Requires-Dist: furo; extra == "dev"
|
|
31
|
+
Requires-Dist: pillow>=11.3.0
|
|
32
|
+
Dynamic: license-file
|
|
41
33
|
|
|
42
34
|

|
|
43
35
|
|
|
@@ -72,8 +64,11 @@ Created and developed by [Miguel Sanda](https://www.miguelsanda.com).
|
|
|
72
64
|
|
|
73
65
|
## Getting Involved
|
|
74
66
|
|
|
75
|
-
All pull requests are welcome, as long as they address bugfixes, enhancements, new ideas, or add value to the project in
|
|
76
|
-
|
|
67
|
+
All pull requests are welcome, as long as they address bugfixes, enhancements, new ideas, or add value to the project in
|
|
68
|
+
any shape or form.
|
|
69
|
+
|
|
70
|
+
Please refrain from submitting pull requests that focus solely on code linting, auto-generated code,
|
|
71
|
+
refactoring, or similar cosmetic non-value add changes.
|
|
77
72
|
|
|
78
73
|
- **Feature Requests/Bug Reports**: Open an issue in the repository
|
|
79
74
|
- **For software customization, advanced features and consulting services**:
|
|
@@ -100,56 +95,12 @@ is [here](https://docs.djangoproject.com/en/4.2/intro/tutorial01/#creating-a-pro
|
|
|
100
95
|
Make sure you refer to the django version you are using.
|
|
101
96
|
|
|
102
97
|
The easiest way to start is to use the zero-config Django Ledger starter template. See
|
|
103
|
-
details [here](https://github.com/arrobalytics/django-ledger-starter).
|
|
104
|
-
project from scratch.
|
|
105
|
-
|
|
106
|
-
To create a new Django Ledger project:
|
|
107
|
-
|
|
108
|
-
* Make sure you have the latest version of python [here](https://www.python.org/) (recommended).
|
|
109
|
-
|
|
110
|
-
* Install Django:
|
|
111
|
-
|
|
112
|
-
```shell
|
|
113
|
-
pip install django
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
* Install Python [Pipenv](https://pipenv.pypa.io/en/latest/) (python package manager):
|
|
117
|
-
|
|
118
|
-
```shell script
|
|
119
|
-
pip install pipenv
|
|
120
|
-
```
|
|
98
|
+
details [here](https://github.com/arrobalytics/django-ledger-starter).
|
|
99
|
+
Otherwise, you may create your project from scratch.
|
|
121
100
|
|
|
122
|
-
|
|
101
|
+
## Adding Django Ledger to an existing project.
|
|
123
102
|
|
|
124
|
-
|
|
125
|
-
django-admin startproject django_ledger_project && cd django_ledger_project
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
* Install Django on you virtual environment.
|
|
129
|
-
|
|
130
|
-
```shell
|
|
131
|
-
pipenv install django
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
* Install Django Ledger
|
|
135
|
-
|
|
136
|
-
```shell script
|
|
137
|
-
pipenv install "django-ledger[graphql,pdf]"
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
Alternatively, you can use:
|
|
141
|
-
|
|
142
|
-
```shell script
|
|
143
|
-
pipenv install django-ledger\[graphql,pdf\]
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
* Activate your new virtual environment:
|
|
147
|
-
|
|
148
|
-
```shell
|
|
149
|
-
pipenv shell
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
* Add django_ledger to INSTALLED_APPS in you new Django Project.
|
|
103
|
+
### Add django_ledger to INSTALLED_APPS in you new Django Project.
|
|
153
104
|
|
|
154
105
|
```python
|
|
155
106
|
INSTALLED_APPS = [
|
|
@@ -159,16 +110,25 @@ INSTALLED_APPS = [
|
|
|
159
110
|
]
|
|
160
111
|
```
|
|
161
112
|
|
|
162
|
-
|
|
113
|
+
### Add Django Ledger Context Preprocessor
|
|
163
114
|
|
|
164
|
-
```
|
|
165
|
-
|
|
115
|
+
```python
|
|
116
|
+
TEMPLATES = [
|
|
117
|
+
{
|
|
118
|
+
'OPTIONS': {
|
|
119
|
+
'context_processors': [
|
|
120
|
+
'...',
|
|
121
|
+
'django_ledger.context.django_ledger_context' # Add this line to a context_processors list..
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
]
|
|
166
126
|
```
|
|
167
127
|
|
|
168
|
-
|
|
128
|
+
### Perform database migrations:
|
|
169
129
|
|
|
170
130
|
```shell
|
|
171
|
-
python manage.py
|
|
131
|
+
python manage.py migrate
|
|
172
132
|
```
|
|
173
133
|
|
|
174
134
|
* Add URLs to your project's __urls.py__:
|
|
@@ -183,7 +143,7 @@ urlpatterns = [
|
|
|
183
143
|
]
|
|
184
144
|
```
|
|
185
145
|
|
|
186
|
-
|
|
146
|
+
### Run your project:
|
|
187
147
|
|
|
188
148
|
```shell
|
|
189
149
|
python manage.py runserver
|
|
@@ -194,7 +154,15 @@ python manage.py runserver
|
|
|
194
154
|
if you followed this installation guide).
|
|
195
155
|
* Use your superuser credentials to login.
|
|
196
156
|
|
|
197
|
-
|
|
157
|
+
## Deprecated behavior setting (v0.8.0+)
|
|
158
|
+
|
|
159
|
+
Starting with version v0.8.0, Django Ledger introduces the DJANGO_LEDGER_USE_DEPRECATED_BEHAVIOR setting to control
|
|
160
|
+
access to deprecated features and legacy behaviors.
|
|
161
|
+
|
|
162
|
+
- Default: False (deprecated features are disabled by default)
|
|
163
|
+
- To temporarily keep using deprecated features while you transition, set this to True in your Django settings.
|
|
164
|
+
|
|
165
|
+
## Setting Up Django Ledger for Development
|
|
198
166
|
|
|
199
167
|
Django Ledger comes with a basic development environment already configured under __dev_env/__ folder not to be used
|
|
200
168
|
for production environments. If you want to contribute to the project perform the following steps:
|