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
django_ledger/__init__.py
CHANGED
django_ledger/context.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from django.conf import settings
|
|
2
|
+
|
|
3
|
+
from django_ledger import __version__
|
|
4
|
+
from django_ledger.settings import DJANGO_LEDGER_THEME
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def django_ledger_context(request):
|
|
8
|
+
return {
|
|
9
|
+
'DEBUG': settings.DEBUG,
|
|
10
|
+
'VERSION': __version__,
|
|
11
|
+
'DJANGO_LEDGER_THEME': DJANGO_LEDGER_THEME,
|
|
12
|
+
}
|
django_ledger/forms/account.py
CHANGED
|
@@ -5,10 +5,19 @@ 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 random import randint
|
|
9
10
|
from typing import Optional
|
|
10
11
|
|
|
11
|
-
from django.forms import
|
|
12
|
+
from django.forms import (
|
|
13
|
+
TextInput,
|
|
14
|
+
Select,
|
|
15
|
+
ModelForm,
|
|
16
|
+
ChoiceField,
|
|
17
|
+
ValidationError,
|
|
18
|
+
CheckboxInput,
|
|
19
|
+
HiddenInput,
|
|
20
|
+
)
|
|
12
21
|
from django.utils.translation import gettext_lazy as _
|
|
13
22
|
from treebeard.forms import MoveNodeForm
|
|
14
23
|
|
|
@@ -62,25 +71,27 @@ class AccountModelCreateForm(ModelForm):
|
|
|
62
71
|
'balance_type',
|
|
63
72
|
'active',
|
|
64
73
|
'active',
|
|
65
|
-
'coa_model'
|
|
74
|
+
'coa_model',
|
|
66
75
|
]
|
|
67
76
|
widgets = {
|
|
68
|
-
'code': TextInput(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
'
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
'code': TextInput(
|
|
78
|
+
attrs={
|
|
79
|
+
'class': DJANGO_LEDGER_FORM_INPUT_CLASSES,
|
|
80
|
+
'placeholder': _(
|
|
81
|
+
'Alpha Numeric (auto generated if not provided)...'
|
|
82
|
+
),
|
|
83
|
+
}
|
|
84
|
+
),
|
|
85
|
+
'name': TextInput(
|
|
86
|
+
attrs={
|
|
87
|
+
'class': DJANGO_LEDGER_FORM_INPUT_CLASSES,
|
|
88
|
+
'placeholder': _('Account Name...'),
|
|
89
|
+
}
|
|
90
|
+
),
|
|
91
|
+
'role': Select(attrs={'class': DJANGO_LEDGER_FORM_INPUT_CLASSES}),
|
|
79
92
|
'role_default': CheckboxInput(),
|
|
80
|
-
'balance_type': Select(attrs={
|
|
81
|
-
|
|
82
|
-
}),
|
|
83
|
-
'coa_model': HiddenInput()
|
|
93
|
+
'balance_type': Select(attrs={'class': DJANGO_LEDGER_FORM_INPUT_CLASSES}),
|
|
94
|
+
'coa_model': HiddenInput(),
|
|
84
95
|
}
|
|
85
96
|
|
|
86
97
|
|
|
@@ -88,7 +99,7 @@ class AccountModelUpdateForm(MoveNodeForm):
|
|
|
88
99
|
"""
|
|
89
100
|
AccountModelUpdateForm
|
|
90
101
|
|
|
91
|
-
A form for updating account model, inheriting from MoveNodeForm.
|
|
102
|
+
A form for updating the account model, inheriting from MoveNodeForm.
|
|
92
103
|
|
|
93
104
|
Attributes
|
|
94
105
|
----------
|
|
@@ -98,36 +109,32 @@ class AccountModelUpdateForm(MoveNodeForm):
|
|
|
98
109
|
An optional choice field for selecting the relative node.
|
|
99
110
|
"""
|
|
100
111
|
|
|
101
|
-
_position = ChoiceField(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
_ref_node_id = ChoiceField(
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
+
_position = ChoiceField(
|
|
113
|
+
required=True,
|
|
114
|
+
label=_('Position'),
|
|
115
|
+
widget=Select(attrs={'class': DJANGO_LEDGER_FORM_INPUT_CLASSES}),
|
|
116
|
+
)
|
|
117
|
+
_ref_node_id = ChoiceField(
|
|
118
|
+
required=False,
|
|
119
|
+
label=_('Relative to'),
|
|
120
|
+
widget=Select(attrs={'class': DJANGO_LEDGER_FORM_INPUT_CLASSES}),
|
|
121
|
+
)
|
|
112
122
|
|
|
113
123
|
def __init__(self, *args, **kwargs):
|
|
114
124
|
super().__init__(*args, **kwargs)
|
|
115
125
|
self.fields['role'].disabled = True
|
|
116
126
|
self.fields['coa_model'].disabled = True
|
|
117
127
|
|
|
118
|
-
|
|
119
128
|
@classmethod
|
|
120
129
|
def mk_dropdown_tree(cls, model, for_node: Optional[AccountModel] = None):
|
|
121
|
-
"""
|
|
130
|
+
"""Creates a tree-like list of choices"""
|
|
122
131
|
|
|
123
132
|
if not for_node:
|
|
124
133
|
raise ValidationError(message='Must provide for_node argument.')
|
|
125
134
|
|
|
126
135
|
qs = for_node.get_account_move_choice_queryset()
|
|
127
136
|
|
|
128
|
-
return [
|
|
129
|
-
(i.uuid, f'{"-" * (i.depth - 1)} {i}') for i in qs
|
|
130
|
-
]
|
|
137
|
+
return [(i.uuid, f'{"-" * (i.depth - 1)} {i}') for i in qs]
|
|
131
138
|
|
|
132
139
|
def clean_role(self):
|
|
133
140
|
return self.instance.role
|
|
@@ -147,17 +154,9 @@ class AccountModelUpdateForm(MoveNodeForm):
|
|
|
147
154
|
widgets = {
|
|
148
155
|
'role': HiddenInput(),
|
|
149
156
|
'coa_model': HiddenInput(),
|
|
150
|
-
'parent': Select(attrs={
|
|
151
|
-
|
|
152
|
-
}),
|
|
153
|
-
'
|
|
154
|
-
'class': DJANGO_LEDGER_FORM_INPUT_CLASSES
|
|
155
|
-
}),
|
|
156
|
-
'code': TextInput(attrs={
|
|
157
|
-
'class': DJANGO_LEDGER_FORM_INPUT_CLASSES
|
|
158
|
-
}),
|
|
159
|
-
'name': TextInput(attrs={
|
|
160
|
-
'class': DJANGO_LEDGER_FORM_INPUT_CLASSES
|
|
161
|
-
}),
|
|
157
|
+
'parent': Select(attrs={'class': DJANGO_LEDGER_FORM_INPUT_CLASSES}),
|
|
158
|
+
'balance_type': Select(attrs={'class': DJANGO_LEDGER_FORM_INPUT_CLASSES}),
|
|
159
|
+
'code': TextInput(attrs={'class': DJANGO_LEDGER_FORM_INPUT_CLASSES}),
|
|
160
|
+
'name': TextInput(attrs={'class': DJANGO_LEDGER_FORM_INPUT_CLASSES}),
|
|
162
161
|
'role_default': CheckboxInput(),
|
|
163
162
|
}
|
django_ledger/forms/bill.py
CHANGED
|
@@ -214,10 +214,6 @@ class BillModelConfigureForm(BaseBillModelUpdateForm):
|
|
|
214
214
|
|
|
215
215
|
class BillItemTransactionForm(ModelForm):
|
|
216
216
|
|
|
217
|
-
# def __init__(self, entity_unit_qs, *args, **kwargs):
|
|
218
|
-
# super().__init__(self, *args, **kwargs)
|
|
219
|
-
# self.fields['entity_unit'].queryset = entity_unit_qs
|
|
220
|
-
|
|
221
217
|
def clean(self):
|
|
222
218
|
cleaned_data = super(BillItemTransactionForm, self).clean()
|
|
223
219
|
itemtxs_model: ItemTransactionModel = self.instance
|
|
@@ -1,15 +1,25 @@
|
|
|
1
|
-
from django.forms import DateInput, ValidationError, ModelForm, Textarea
|
|
1
|
+
from django.forms import DateInput, ValidationError, ModelForm, Textarea, HiddenInput
|
|
2
2
|
from django.utils.translation import gettext_lazy as _
|
|
3
3
|
|
|
4
4
|
from django_ledger.io.io_core import get_localdate
|
|
5
5
|
from django_ledger.models.closing_entry import ClosingEntryModel
|
|
6
|
+
from django_ledger.models.entity import EntityModel
|
|
6
7
|
from django_ledger.settings import DJANGO_LEDGER_FORM_INPUT_CLASSES
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
class ClosingEntryCreateForm(ModelForm):
|
|
10
11
|
|
|
12
|
+
def __init__(self, entity_model: EntityModel, *args, **kwargs):
|
|
13
|
+
super().__init__(*args, **kwargs)
|
|
14
|
+
self.ENTITY_MODEL: EntityModel = entity_model
|
|
15
|
+
self.fields['entity_model'].disabled = True
|
|
16
|
+
|
|
17
|
+
def clean_entity_model(self):
|
|
18
|
+
return self.ENTITY_MODEL
|
|
19
|
+
|
|
11
20
|
def clean_closing_date(self):
|
|
12
21
|
closing_date = self.cleaned_data['closing_date']
|
|
22
|
+
|
|
13
23
|
if closing_date > get_localdate():
|
|
14
24
|
raise ValidationError(
|
|
15
25
|
message=_('Cannot create a closing entry with a future date.'), code='invalid_date'
|
|
@@ -19,10 +29,12 @@ class ClosingEntryCreateForm(ModelForm):
|
|
|
19
29
|
class Meta:
|
|
20
30
|
model = ClosingEntryModel
|
|
21
31
|
fields = [
|
|
32
|
+
'entity_model',
|
|
22
33
|
'closing_date'
|
|
23
34
|
]
|
|
24
35
|
|
|
25
36
|
widgets = {
|
|
37
|
+
'entity_model': HiddenInput(),
|
|
26
38
|
'closing_date': DateInput(attrs={
|
|
27
39
|
'class': DJANGO_LEDGER_FORM_INPUT_CLASSES + ' is-large',
|
|
28
40
|
'placeholder': _('Closing Date (YYYY-MM-DD)...'),
|
|
@@ -1,34 +1,37 @@
|
|
|
1
1
|
from django import forms
|
|
2
2
|
from django.forms import (
|
|
3
|
-
ModelForm,
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
ModelForm,
|
|
4
|
+
BaseModelFormSet,
|
|
5
|
+
modelformset_factory,
|
|
6
|
+
Select,
|
|
7
|
+
NumberInput,
|
|
8
|
+
HiddenInput,
|
|
9
|
+
TextInput,
|
|
10
|
+
ValidationError,
|
|
6
11
|
)
|
|
7
12
|
from django.utils.translation import gettext_lazy as _
|
|
8
13
|
|
|
14
|
+
from django_ledger.io import GROUP_EXPENSES, GROUP_INCOME
|
|
9
15
|
from django_ledger.models import (
|
|
10
16
|
StagedTransactionModel,
|
|
11
17
|
ImportJobModel,
|
|
12
|
-
EntityModel
|
|
18
|
+
EntityModel,
|
|
13
19
|
)
|
|
14
20
|
from django_ledger.settings import DJANGO_LEDGER_FORM_INPUT_CLASSES
|
|
15
21
|
|
|
16
22
|
|
|
17
23
|
class ImportJobModelCreateForm(ModelForm):
|
|
18
|
-
|
|
19
24
|
def __init__(self, entity_model: EntityModel, *args, **kwargs):
|
|
20
25
|
super().__init__(*args, **kwargs)
|
|
21
26
|
self.ENTITY_MODEL: EntityModel = entity_model
|
|
22
|
-
self.fields[
|
|
27
|
+
self.fields[
|
|
28
|
+
'bank_account_model'
|
|
29
|
+
].queryset = self.ENTITY_MODEL.bankaccountmodel_set.all().active()
|
|
23
30
|
|
|
24
31
|
ofx_file = forms.FileField(
|
|
25
32
|
label='Select File...',
|
|
26
33
|
required=True,
|
|
27
|
-
widget=forms.FileInput(
|
|
28
|
-
attrs={
|
|
29
|
-
'class': 'file-input',
|
|
30
|
-
'accept': '.ofx,.qfx'
|
|
31
|
-
})
|
|
34
|
+
widget=forms.FileInput(attrs={'class': 'file-input', 'accept': '.ofx,.qfx'}),
|
|
32
35
|
)
|
|
33
36
|
|
|
34
37
|
class Meta:
|
|
@@ -38,30 +41,31 @@ class ImportJobModelCreateForm(ModelForm):
|
|
|
38
41
|
'description',
|
|
39
42
|
]
|
|
40
43
|
widgets = {
|
|
41
|
-
'description': TextInput(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
'description': TextInput(
|
|
45
|
+
attrs={
|
|
46
|
+
'class': DJANGO_LEDGER_FORM_INPUT_CLASSES + ' is-large',
|
|
47
|
+
'placeholder': _('Name this import...'),
|
|
48
|
+
}
|
|
49
|
+
),
|
|
45
50
|
'bank_account_model': Select(
|
|
46
51
|
attrs={
|
|
47
52
|
'class': DJANGO_LEDGER_FORM_INPUT_CLASSES,
|
|
48
|
-
}
|
|
53
|
+
}
|
|
54
|
+
),
|
|
49
55
|
}
|
|
50
56
|
help_texts = {
|
|
51
|
-
'bank_account_model': _(
|
|
57
|
+
'bank_account_model': _(
|
|
58
|
+
'Select the bank account to import transactions from.'
|
|
59
|
+
),
|
|
52
60
|
}
|
|
53
61
|
|
|
54
62
|
|
|
55
63
|
class ImportJobModelUpdateForm(ModelForm):
|
|
56
64
|
class Meta:
|
|
57
65
|
model = ImportJobModel
|
|
58
|
-
fields = [
|
|
59
|
-
'description'
|
|
60
|
-
]
|
|
66
|
+
fields = ['description']
|
|
61
67
|
widgets = {
|
|
62
|
-
'description': TextInput(attrs={
|
|
63
|
-
'class': DJANGO_LEDGER_FORM_INPUT_CLASSES
|
|
64
|
-
})
|
|
68
|
+
'description': TextInput(attrs={'class': DJANGO_LEDGER_FORM_INPUT_CLASSES})
|
|
65
69
|
}
|
|
66
70
|
|
|
67
71
|
|
|
@@ -71,37 +75,90 @@ class StagedTransactionModelForm(ModelForm):
|
|
|
71
75
|
|
|
72
76
|
def __init__(self, base_formset_instance, *args, **kwargs):
|
|
73
77
|
super().__init__(*args, **kwargs)
|
|
74
|
-
self.BASE_FORMSET_INSTANCE =
|
|
78
|
+
self.BASE_FORMSET_INSTANCE: 'BaseStagedTransactionModelFormSet' = (
|
|
79
|
+
base_formset_instance
|
|
80
|
+
)
|
|
81
|
+
self.VENDOR_CHOICES = self.BASE_FORMSET_INSTANCE.VENDOR_CHOICES
|
|
82
|
+
self.VENDOR_MAP = self.BASE_FORMSET_INSTANCE.VENDOR_MAP
|
|
75
83
|
|
|
76
|
-
|
|
77
|
-
|
|
84
|
+
self.CUSTOMER_CHOICES = self.BASE_FORMSET_INSTANCE.CUSTOMER_CHOICES
|
|
85
|
+
self.CUSTOMER_MAP = self.BASE_FORMSET_INSTANCE.CUSTOMER_MAP
|
|
78
86
|
|
|
79
|
-
|
|
80
|
-
|
|
87
|
+
self.EXPENSE_ACCOUNT_CHOICES = (
|
|
88
|
+
self.BASE_FORMSET_INSTANCE.ACCOUNT_MODEL_EXPENSES_CHOICES
|
|
89
|
+
)
|
|
90
|
+
self.SALES_ACCOUNT_CHOICES = (
|
|
91
|
+
self.BASE_FORMSET_INSTANCE.ACCOUNT_MODEL_SALES_CHOICES
|
|
92
|
+
)
|
|
93
|
+
self.SHOW_VENDOR_FIELD: bool = False
|
|
94
|
+
self.SHOW_CUSTOMER_FIELD: bool = False
|
|
95
|
+
|
|
96
|
+
staged_tx_model: StagedTransactionModel = getattr(self, 'instance', None)
|
|
97
|
+
|
|
98
|
+
if staged_tx_model.can_migrate_receipt():
|
|
99
|
+
if staged_tx_model.is_expense():
|
|
100
|
+
self.fields['customer_model'].widget = forms.HiddenInput()
|
|
101
|
+
self.fields['vendor_model'].choices = self.VENDOR_CHOICES
|
|
102
|
+
self.fields['account_model'].choices = self.EXPENSE_ACCOUNT_CHOICES
|
|
103
|
+
self.SHOW_VENDOR_FIELD = True
|
|
104
|
+
elif staged_tx_model.is_sales():
|
|
105
|
+
self.fields['vendor_model'].widget = forms.HiddenInput()
|
|
106
|
+
self.fields['customer_model'].choices = self.CUSTOMER_CHOICES
|
|
107
|
+
self.fields['account_model'].choices = self.SALES_ACCOUNT_CHOICES
|
|
108
|
+
self.SHOW_CUSTOMER_FIELD = True
|
|
109
|
+
else:
|
|
110
|
+
self.fields['customer_model'].widget = forms.HiddenInput()
|
|
111
|
+
self.fields['vendor_model'].widget = forms.HiddenInput()
|
|
112
|
+
# avoids multiple DB queries rendering the formset...
|
|
113
|
+
self.fields[
|
|
114
|
+
'account_model'
|
|
115
|
+
].choices = self.BASE_FORMSET_INSTANCE.ACCOUNT_MODEL_CHOICES
|
|
81
116
|
|
|
82
117
|
# avoids multiple DB queries rendering the formset...
|
|
83
|
-
self.fields[
|
|
118
|
+
self.fields[
|
|
119
|
+
'unit_model'
|
|
120
|
+
].choices = self.BASE_FORMSET_INSTANCE.UNIT_MODEL_CHOICES
|
|
84
121
|
|
|
85
|
-
if
|
|
86
|
-
if not
|
|
122
|
+
if staged_tx_model:
|
|
123
|
+
if not staged_tx_model.is_children():
|
|
87
124
|
self.fields['amount_split'].widget = HiddenInput()
|
|
88
125
|
self.fields['amount_split'].disabled = True
|
|
89
126
|
else:
|
|
90
127
|
self.fields['bundle_split'].widget = HiddenInput()
|
|
91
128
|
self.fields['bundle_split'].disabled = True
|
|
92
129
|
|
|
93
|
-
if
|
|
130
|
+
if staged_tx_model.has_children():
|
|
131
|
+
self.fields['receipt_type'].widget = HiddenInput()
|
|
132
|
+
self.fields['receipt_type'].disabled = True
|
|
133
|
+
|
|
134
|
+
if not staged_tx_model.can_have_account():
|
|
94
135
|
self.fields['account_model'].widget = HiddenInput()
|
|
95
136
|
self.fields['account_model'].disabled = True
|
|
96
137
|
|
|
97
|
-
if not
|
|
138
|
+
if not staged_tx_model.can_have_unit():
|
|
98
139
|
self.fields['unit_model'].widget = HiddenInput()
|
|
99
140
|
self.fields['unit_model'].disabled = True
|
|
100
141
|
|
|
101
|
-
|
|
142
|
+
# receipt-related field visibility: not allowed for children rows
|
|
143
|
+
if staged_tx_model.is_children():
|
|
144
|
+
for f in [
|
|
145
|
+
'receipt_type',
|
|
146
|
+
'vendor_model',
|
|
147
|
+
'customer_model',
|
|
148
|
+
'bundle_split',
|
|
149
|
+
]:
|
|
150
|
+
self.fields[f].widget = HiddenInput()
|
|
151
|
+
self.fields[f].disabled = True
|
|
152
|
+
|
|
153
|
+
if staged_tx_model.is_single():
|
|
154
|
+
self.fields['bundle_split'].widget = HiddenInput()
|
|
155
|
+
self.fields['bundle_split'].disabled = True
|
|
156
|
+
|
|
157
|
+
if not staged_tx_model.can_migrate():
|
|
102
158
|
self.fields['tx_import'].widget = HiddenInput()
|
|
103
159
|
self.fields['tx_import'].disabled = True
|
|
104
|
-
|
|
160
|
+
|
|
161
|
+
if not staged_tx_model.can_split():
|
|
105
162
|
self.fields['tx_split'].widget = HiddenInput()
|
|
106
163
|
self.fields['tx_split'].disabled = True
|
|
107
164
|
|
|
@@ -114,8 +171,9 @@ class StagedTransactionModelForm(ModelForm):
|
|
|
114
171
|
def clean_unit_model(self):
|
|
115
172
|
staged_txs_model: StagedTransactionModel = self.instance
|
|
116
173
|
if not staged_txs_model.can_have_unit():
|
|
117
|
-
if staged_txs_model.
|
|
174
|
+
if staged_txs_model.is_children():
|
|
118
175
|
return staged_txs_model.parent.unit_model
|
|
176
|
+
return None
|
|
119
177
|
return self.cleaned_data['unit_model']
|
|
120
178
|
|
|
121
179
|
def clean_tx_import(self):
|
|
@@ -125,10 +183,23 @@ class StagedTransactionModelForm(ModelForm):
|
|
|
125
183
|
return self.cleaned_data['tx_import']
|
|
126
184
|
|
|
127
185
|
def clean(self):
|
|
128
|
-
# cannot import and split at the same time
|
|
129
186
|
if self.cleaned_data['tx_import'] and self.cleaned_data['tx_split']:
|
|
130
187
|
raise ValidationError(message=_('Cannot import and split at the same time'))
|
|
131
188
|
|
|
189
|
+
staged_txs_model: StagedTransactionModel = self.instance
|
|
190
|
+
if all(
|
|
191
|
+
[
|
|
192
|
+
staged_txs_model.has_children(),
|
|
193
|
+
staged_txs_model.has_receipt(),
|
|
194
|
+
not staged_txs_model.bundle_split,
|
|
195
|
+
]
|
|
196
|
+
):
|
|
197
|
+
raise ValidationError(
|
|
198
|
+
message=_(
|
|
199
|
+
'Receipt transactions cannot be split into multiple receipts.'
|
|
200
|
+
)
|
|
201
|
+
)
|
|
202
|
+
|
|
132
203
|
class Meta:
|
|
133
204
|
model = StagedTransactionModel
|
|
134
205
|
fields = [
|
|
@@ -136,51 +207,99 @@ class StagedTransactionModelForm(ModelForm):
|
|
|
136
207
|
'bundle_split',
|
|
137
208
|
'amount_split',
|
|
138
209
|
'account_model',
|
|
139
|
-
'unit_model'
|
|
210
|
+
'unit_model',
|
|
211
|
+
'receipt_type',
|
|
212
|
+
'vendor_model',
|
|
213
|
+
'customer_model',
|
|
140
214
|
]
|
|
141
215
|
widgets = {
|
|
142
|
-
'account_model': Select(
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
216
|
+
'account_model': Select(
|
|
217
|
+
attrs={
|
|
218
|
+
'class': DJANGO_LEDGER_FORM_INPUT_CLASSES + ' is-small',
|
|
219
|
+
}
|
|
220
|
+
),
|
|
221
|
+
'unit_model': Select(
|
|
222
|
+
attrs={
|
|
223
|
+
'class': DJANGO_LEDGER_FORM_INPUT_CLASSES + ' is-small',
|
|
224
|
+
}
|
|
225
|
+
),
|
|
226
|
+
'amount_split': NumberInput(
|
|
227
|
+
attrs={'class': DJANGO_LEDGER_FORM_INPUT_CLASSES + ' is-small'}
|
|
228
|
+
),
|
|
229
|
+
'vendor_model': Select(
|
|
230
|
+
attrs={'class': DJANGO_LEDGER_FORM_INPUT_CLASSES + ' is-small'}
|
|
231
|
+
),
|
|
232
|
+
'customer_model': Select(
|
|
233
|
+
attrs={'class': DJANGO_LEDGER_FORM_INPUT_CLASSES + ' is-small'}
|
|
234
|
+
),
|
|
235
|
+
'receipt_type': Select(
|
|
236
|
+
attrs={'class': DJANGO_LEDGER_FORM_INPUT_CLASSES + ' is-small'}
|
|
237
|
+
),
|
|
151
238
|
}
|
|
152
239
|
|
|
153
240
|
|
|
154
241
|
class BaseStagedTransactionModelFormSet(BaseModelFormSet):
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
242
|
+
def __init__(
|
|
243
|
+
self,
|
|
244
|
+
*args,
|
|
245
|
+
entity_model: EntityModel,
|
|
246
|
+
import_job_model: ImportJobModel,
|
|
247
|
+
**kwargs,
|
|
248
|
+
):
|
|
160
249
|
super().__init__(*args, **kwargs)
|
|
161
250
|
|
|
162
251
|
# validates that the job import model belongs to the entity model...
|
|
163
252
|
if import_job_model.entity_uuid != entity_model.uuid:
|
|
164
|
-
raise ValidationError(
|
|
253
|
+
raise ValidationError(
|
|
254
|
+
message=_('Import job does not belong to this entity')
|
|
255
|
+
)
|
|
165
256
|
|
|
166
257
|
self.ENTITY_MODEL = entity_model
|
|
167
258
|
self.IMPORT_JOB_MODEL: ImportJobModel = import_job_model
|
|
168
|
-
self.MAPPED_ACCOUNT_MODEL = self.IMPORT_JOB_MODEL.bank_account_model.account_model
|
|
169
259
|
|
|
170
|
-
self.
|
|
171
|
-
|
|
260
|
+
self.queryset = (
|
|
261
|
+
self.IMPORT_JOB_MODEL.stagedtransactionmodel_set.all().is_pending()
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
self.MAPPED_ACCOUNT_MODEL = (
|
|
265
|
+
self.IMPORT_JOB_MODEL.bank_account_model.account_model
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
self.account_model_qs = (
|
|
269
|
+
entity_model.get_coa_accounts()
|
|
270
|
+
.available()
|
|
271
|
+
.exclude(uuid__exact=self.MAPPED_ACCOUNT_MODEL.uuid)
|
|
172
272
|
)
|
|
173
|
-
self.ACCOUNT_MODEL_CHOICES = [
|
|
174
|
-
(a.uuid, a)
|
|
175
|
-
|
|
273
|
+
self.ACCOUNT_MODEL_CHOICES = [(None, '----')] + [
|
|
274
|
+
(a.uuid, a) for a in self.account_model_qs
|
|
275
|
+
]
|
|
276
|
+
self.ACCOUNT_MODEL_EXPENSES_CHOICES = [(None, '----')] + [
|
|
277
|
+
(a.uuid, a) for a in self.account_model_qs if a.role in GROUP_EXPENSES
|
|
278
|
+
]
|
|
279
|
+
self.ACCOUNT_MODEL_SALES_CHOICES = [(None, '----')] + [
|
|
280
|
+
(a.uuid, a) for a in self.account_model_qs if a.role in GROUP_INCOME
|
|
176
281
|
]
|
|
177
282
|
|
|
178
283
|
self.unit_model_qs = entity_model.entityunitmodel_set.all()
|
|
179
284
|
self.UNIT_MODEL_CHOICES = [
|
|
180
|
-
(u.uuid, u) if i > 0 else (None, '----')
|
|
181
|
-
enumerate(self.unit_model_qs)
|
|
285
|
+
(u.uuid, u) if i > 0 else (None, '----')
|
|
286
|
+
for i, u in enumerate(self.unit_model_qs)
|
|
287
|
+
]
|
|
288
|
+
|
|
289
|
+
self.VENDOR_MODEL_QS = entity_model.vendormodel_set.visible()
|
|
290
|
+
len(self.VENDOR_MODEL_QS)
|
|
291
|
+
self.CUSTOMER_MODEL_QS = entity_model.customermodel_set.visible()
|
|
292
|
+
len(self.CUSTOMER_MODEL_QS)
|
|
293
|
+
|
|
294
|
+
self.VENDOR_CHOICES = [(None, '-----')] + [
|
|
295
|
+
(str(v.uuid), v) for v in self.VENDOR_MODEL_QS
|
|
182
296
|
]
|
|
183
|
-
self.
|
|
297
|
+
self.CUSTOMER_CHOICES = [(None, '-----')] + [
|
|
298
|
+
(str(c.uuid), c) for c in self.CUSTOMER_MODEL_QS
|
|
299
|
+
]
|
|
300
|
+
|
|
301
|
+
self.VENDOR_MAP = dict(self.VENDOR_CHOICES)
|
|
302
|
+
self.CUSTOMER_MAP = dict(self.CUSTOMER_CHOICES)
|
|
184
303
|
|
|
185
304
|
def get_form_kwargs(self, index):
|
|
186
305
|
return {
|
|
@@ -194,5 +313,5 @@ StagedTransactionModelFormSet = modelformset_factory(
|
|
|
194
313
|
formset=BaseStagedTransactionModelFormSet,
|
|
195
314
|
can_delete=True,
|
|
196
315
|
can_order=False,
|
|
197
|
-
extra=0
|
|
316
|
+
extra=0,
|
|
198
317
|
)
|
django_ledger/forms/estimate.py
CHANGED
|
@@ -25,8 +25,7 @@ class EstimateModelCreateForm(forms.ModelForm):
|
|
|
25
25
|
|
|
26
26
|
def get_customer_queryset(self):
|
|
27
27
|
return CustomerModel.objects.for_entity(
|
|
28
|
-
|
|
29
|
-
user_model=self.USER_MODEL
|
|
28
|
+
entity_model=self.ENTITY_SLUG,
|
|
30
29
|
).active()
|
|
31
30
|
|
|
32
31
|
class Meta:
|
|
@@ -116,13 +115,11 @@ class BaseEstimateItemModelFormset(BaseModelFormSet):
|
|
|
116
115
|
self.ENTITY_SLUG = entity_slug
|
|
117
116
|
|
|
118
117
|
items_qs = ItemModel.objects.for_estimate(
|
|
119
|
-
|
|
120
|
-
user_model=self.USER_MODEL
|
|
118
|
+
entity_model=self.ENTITY_SLUG
|
|
121
119
|
)
|
|
122
120
|
|
|
123
121
|
unit_qs = EntityUnitModel.objects.for_entity(
|
|
124
|
-
|
|
125
|
-
user_model=self.USER_MODEL
|
|
122
|
+
entity_model=self.ENTITY_SLUG
|
|
126
123
|
)
|
|
127
124
|
|
|
128
125
|
for form in self.forms:
|
django_ledger/forms/invoice.py
CHANGED
|
@@ -29,8 +29,7 @@ class InvoiceModelCreateForEstimateForm(ModelForm):
|
|
|
29
29
|
def get_customer_queryset(self):
|
|
30
30
|
if 'customer' in self.fields:
|
|
31
31
|
customer_qs = CustomerModel.objects.for_entity(
|
|
32
|
-
|
|
33
|
-
entity_slug=self.ENTITY_SLUG
|
|
32
|
+
entity_model=self.ENTITY_SLUG
|
|
34
33
|
).active()
|
|
35
34
|
self.fields['customer'].queryset = customer_qs
|
|
36
35
|
|
|
@@ -43,7 +42,6 @@ class InvoiceModelCreateForEstimateForm(ModelForm):
|
|
|
43
42
|
]):
|
|
44
43
|
|
|
45
44
|
account_qs = AccountModel.objects.for_entity(
|
|
46
|
-
user_model=self.USER_MODEL,
|
|
47
45
|
entity_model=self.ENTITY_SLUG
|
|
48
46
|
).for_invoice()
|
|
49
47
|
|
|
@@ -237,8 +235,7 @@ class BaseInvoiceItemTransactionFormset(BaseModelFormSet):
|
|
|
237
235
|
self.ENTITY_SLUG = entity_slug
|
|
238
236
|
|
|
239
237
|
items_qs = ItemModel.objects.for_invoice(
|
|
240
|
-
|
|
241
|
-
user_model=self.USER_MODEL
|
|
238
|
+
entity_model=self.ENTITY_SLUG
|
|
242
239
|
)
|
|
243
240
|
|
|
244
241
|
for form in self.forms:
|
|
@@ -252,8 +249,7 @@ class BaseInvoiceItemTransactionFormset(BaseModelFormSet):
|
|
|
252
249
|
def get_queryset(self):
|
|
253
250
|
if not self.queryset:
|
|
254
251
|
self.queryset = ItemTransactionModel.objects.for_invoice(
|
|
255
|
-
|
|
256
|
-
user_model=self.USER_MODEL,
|
|
252
|
+
entity_model=self.ENTITY_SLUG,
|
|
257
253
|
invoice_pk=self.INVOICE_MODEL.uuid
|
|
258
254
|
)
|
|
259
255
|
else:
|