django-ledger 0.7.11__py3-none-any.whl → 0.8.0__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/bill.py +0 -4
- django_ledger/forms/closing_entry.py +13 -1
- django_ledger/forms/data_import.py +1 -1
- 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 +8 -26
- 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/models/accounts.py +109 -69
- django_ledger/models/bank_account.py +40 -23
- django_ledger/models/bill.py +79 -63
- django_ledger/models/chart_of_accounts.py +173 -105
- django_ledger/models/closing_entry.py +99 -48
- django_ledger/models/customer.py +60 -39
- django_ledger/models/data_import.py +55 -41
- django_ledger/models/deprecations.py +61 -0
- django_ledger/models/entity.py +18 -16
- 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 +5 -3
- django_ledger/models/purchase_order.py +39 -17
- django_ledger/models/transactions.py +152 -113
- django_ledger/models/unit.py +57 -30
- django_ledger/models/vendor.py +75 -43
- django_ledger/report/core.py +2 -14
- django_ledger/settings.py +56 -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/customer/tags/customer_table.html +5 -5
- 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 +6 -1
- django_ledger/templates/django_ledger/vendor/tags/vendor_table.html +9 -5
- 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 +0 -4
- 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 +13 -17
- django_ledger/views/data_import.py +7 -6
- 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 +25 -19
- django_ledger/views/purchase_order.py +24 -35
- django_ledger/views/unit.py +1 -2
- django_ledger/views/vendor.py +1 -2
- {django_ledger-0.7.11.dist-info → django_ledger-0.8.0.dist-info}/METADATA +43 -75
- {django_ledger-0.7.11.dist-info → django_ledger-0.8.0.dist-info}/RECORD +79 -108
- {django_ledger-0.7.11.dist-info → django_ledger-0.8.0.dist-info}/WHEEL +1 -1
- django_ledger-0.8.0.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.0.dist-info/licenses}/AUTHORS.md +0 -0
- {django_ledger-0.7.11.dist-info → django_ledger-0.8.0.dist-info/licenses}/LICENSE +0 -0
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
{% load django_ledger %}
|
|
2
2
|
{% load i18n %}
|
|
3
3
|
|
|
4
|
+
<style>
|
|
5
|
+
/* Align currency (2nd-to-last column) to the right for financial-statement tables */
|
|
6
|
+
table.financial-statement thead th:nth-last-child(2),
|
|
7
|
+
table.financial-statement tbody td:nth-last-child(2) {
|
|
8
|
+
text-align: right;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
table.financial-statement tfoot td {
|
|
12
|
+
font-weight: 700;
|
|
13
|
+
}
|
|
14
|
+
</style>
|
|
4
15
|
|
|
5
16
|
<div class="table-container">
|
|
6
|
-
<table class="table is-fullwidth is-narrow is-striped">
|
|
17
|
+
<table class="table is-fullwidth is-narrow is-striped is-hoverable is-bordered financial-statement">
|
|
18
|
+
<thead>
|
|
7
19
|
<tr>
|
|
8
20
|
<th>{% trans 'Account Number' %}</th>
|
|
9
21
|
<th>{% trans 'Description' %}</th>
|
|
@@ -11,13 +23,15 @@
|
|
|
11
23
|
<th>{% trans 'Unit' %}</th>
|
|
12
24
|
{% endif %}
|
|
13
25
|
<th>{% trans 'Balance Type' %}</th>
|
|
14
|
-
<th>{% trans 'Balance' %}</th>
|
|
26
|
+
<th class="has-text-right">{% trans 'Balance' %}</th>
|
|
15
27
|
<th>{% trans 'Actions' %}</th>
|
|
16
28
|
</tr>
|
|
29
|
+
</thead>
|
|
30
|
+
<tbody>
|
|
17
31
|
|
|
18
32
|
{# OPERATING INCOME #}
|
|
19
33
|
<tr>
|
|
20
|
-
<td><h2 class="is-size-
|
|
34
|
+
<td><h2 class="is-size-4 has-text-left">{% trans 'Operating Revenues' %}</h2></td>
|
|
21
35
|
<td></td>
|
|
22
36
|
{% if tx_digest.by_unit %}
|
|
23
37
|
<td></td>
|
|
@@ -36,12 +50,20 @@
|
|
|
36
50
|
{% endif %}
|
|
37
51
|
<td class="has-text-centered">
|
|
38
52
|
{% if acc.balance_type == 'debit' %}
|
|
39
|
-
<span class="
|
|
53
|
+
<span class="tag is-info is-light">
|
|
54
|
+
<span class="icon is-small mr-1">{% icon 'bi:arrow-bar-down' 16 %}</span>
|
|
55
|
+
debit
|
|
56
|
+
</span>
|
|
40
57
|
{% elif acc.balance_type == 'credit' %}
|
|
41
|
-
<span class="
|
|
58
|
+
<span class="tag is-success is-light">
|
|
59
|
+
<span class="icon is-small mr-1">{% icon 'bi:arrow-bar-up' 16 %}</span>
|
|
60
|
+
credit
|
|
61
|
+
</span>
|
|
62
|
+
{% else %}
|
|
63
|
+
<span class="tag is-light">{{ acc.balance_type }}</span>
|
|
42
64
|
{% endif %}
|
|
43
65
|
</td>
|
|
44
|
-
<td>{% currency_symbol %}{{ acc.balance | currency_format }}</td>
|
|
66
|
+
<td class="has-text-right">{% currency_symbol %}{{ acc.balance | currency_format }}</td>
|
|
45
67
|
<td>
|
|
46
68
|
<div class="dropdown is-hoverable" id="account-action-{{ account.uuid }}">
|
|
47
69
|
<div class="dropdown-trigger">
|
|
@@ -79,7 +101,7 @@
|
|
|
79
101
|
|
|
80
102
|
{# COGS #}
|
|
81
103
|
<tr>
|
|
82
|
-
<td><h2 class="is-size-
|
|
104
|
+
<td><h2 class="is-size-4 has-text-left">{% trans 'Less: Cost of Goods Sold' %}</h2></td>
|
|
83
105
|
<td></td>
|
|
84
106
|
{% if tx_digest.by_unit %}
|
|
85
107
|
<td></td>
|
|
@@ -97,9 +119,17 @@
|
|
|
97
119
|
{% endif %}
|
|
98
120
|
<td class="has-text-centered">
|
|
99
121
|
{% if acc.balance_type == 'debit' %}
|
|
100
|
-
<span class="
|
|
122
|
+
<span class="tag is-info is-light">
|
|
123
|
+
<span class="icon is-small mr-1">{% icon 'bi:arrow-bar-down' 16 %}</span>
|
|
124
|
+
debit
|
|
125
|
+
</span>
|
|
101
126
|
{% elif acc.balance_type == 'credit' %}
|
|
102
|
-
<span class="
|
|
127
|
+
<span class="tag is-success is-light">
|
|
128
|
+
<span class="icon is-small mr-1">{% icon 'bi:arrow-bar-up' 16 %}</span>
|
|
129
|
+
credit
|
|
130
|
+
</span>
|
|
131
|
+
{% else %}
|
|
132
|
+
<span class="tag is-light">{{ acc.balance_type }}</span>
|
|
103
133
|
{% endif %}
|
|
104
134
|
</td>
|
|
105
135
|
<td>{% currency_symbol %}{{ acc.balance | reverse_sign | currency_format }}</td>
|
|
@@ -153,7 +183,7 @@
|
|
|
153
183
|
|
|
154
184
|
{# OPERATING EXPENSES #}
|
|
155
185
|
<tr>
|
|
156
|
-
<td><h2 class="is-size-
|
|
186
|
+
<td><h2 class="is-size-4 has-text-left">{% trans 'Operating Expenses' %}</h2></td>
|
|
157
187
|
<td></td>
|
|
158
188
|
{% if tx_digest.by_unit %}
|
|
159
189
|
<td></td>
|
|
@@ -171,9 +201,17 @@
|
|
|
171
201
|
{% endif %}
|
|
172
202
|
<td class="has-text-centered">
|
|
173
203
|
{% if acc.balance_type == 'debit' %}
|
|
174
|
-
<span class="
|
|
204
|
+
<span class="tag is-info is-light">
|
|
205
|
+
<span class="icon is-small mr-1">{% icon 'bi:arrow-bar-down' 16 %}</span>
|
|
206
|
+
debit
|
|
207
|
+
</span>
|
|
175
208
|
{% elif acc.balance_type == 'credit' %}
|
|
176
|
-
<span class="
|
|
209
|
+
<span class="tag is-success is-light">
|
|
210
|
+
<span class="icon is-small mr-1">{% icon 'bi:arrow-bar-up' 16 %}</span>
|
|
211
|
+
credit
|
|
212
|
+
</span>
|
|
213
|
+
{% else %}
|
|
214
|
+
<span class="tag is-light">{{ acc.balance_type }}</span>
|
|
177
215
|
{% endif %}
|
|
178
216
|
</td>
|
|
179
217
|
<td>{% currency_symbol %}{{ acc.balance | reverse_sign | currency_format }}</td>
|
|
@@ -227,7 +265,7 @@
|
|
|
227
265
|
|
|
228
266
|
{# OTHER REVENUES #}
|
|
229
267
|
<tr>
|
|
230
|
-
<td><h2 class="is-size-
|
|
268
|
+
<td><h2 class="is-size-4 has-text-left">{% trans 'Other Revenues' %}</h2></td>
|
|
231
269
|
<td></td>
|
|
232
270
|
{% if tx_digest.by_unit %}
|
|
233
271
|
<td></td>
|
|
@@ -245,9 +283,17 @@
|
|
|
245
283
|
{% endif %}
|
|
246
284
|
<td class="has-text-centered">
|
|
247
285
|
{% if acc.balance_type == 'debit' %}
|
|
248
|
-
<span class="
|
|
286
|
+
<span class="tag is-info is-light">
|
|
287
|
+
<span class="icon is-small mr-1">{% icon 'bi:arrow-bar-down' 16 %}</span>
|
|
288
|
+
debit
|
|
289
|
+
</span>
|
|
249
290
|
{% elif acc.balance_type == 'credit' %}
|
|
250
|
-
<span class="
|
|
291
|
+
<span class="tag is-success is-light">
|
|
292
|
+
<span class="icon is-small mr-1">{% icon 'bi:arrow-bar-up' 16 %}</span>
|
|
293
|
+
credit
|
|
294
|
+
</span>
|
|
295
|
+
{% else %}
|
|
296
|
+
<span class="tag is-light">{{ acc.balance_type }}</span>
|
|
251
297
|
{% endif %}
|
|
252
298
|
</td>
|
|
253
299
|
<td class="is-size-5">{% currency_symbol %}{{ acc.balance | currency_format }}</td>
|
|
@@ -288,7 +334,7 @@
|
|
|
288
334
|
|
|
289
335
|
{# OTHER EXPENSES #}
|
|
290
336
|
<tr>
|
|
291
|
-
<td><h2 class="is-size-
|
|
337
|
+
<td><h2 class="is-size-4 has-text-left">{% trans 'Other Expenses' %}</h2></td>
|
|
292
338
|
<td></td>
|
|
293
339
|
{% if tx_digest.by_unit %}
|
|
294
340
|
<td></td>
|
|
@@ -306,9 +352,17 @@
|
|
|
306
352
|
{% endif %}
|
|
307
353
|
<td class="has-text-centered">
|
|
308
354
|
{% if acc.balance_type == 'debit' %}
|
|
309
|
-
<span class="
|
|
355
|
+
<span class="tag is-info is-light">
|
|
356
|
+
<span class="icon is-small mr-1">{% icon 'bi:arrow-bar-down' 16 %}</span>
|
|
357
|
+
debit
|
|
358
|
+
</span>
|
|
310
359
|
{% elif acc.balance_type == 'credit' %}
|
|
311
|
-
<span class="
|
|
360
|
+
<span class="tag is-success is-light">
|
|
361
|
+
<span class="icon is-small mr-1">{% icon 'bi:arrow-bar-up' 16 %}</span>
|
|
362
|
+
credit
|
|
363
|
+
</span>
|
|
364
|
+
{% else %}
|
|
365
|
+
<span class="tag is-light">{{ acc.balance_type }}</span>
|
|
312
366
|
{% endif %}
|
|
313
367
|
</td>
|
|
314
368
|
<td class="is-size-5">{% currency_symbol %}{{ acc.balance | reverse_sign | currency_format }}</td>
|
|
@@ -375,5 +429,6 @@
|
|
|
375
429
|
<td></td>
|
|
376
430
|
</tr>
|
|
377
431
|
|
|
432
|
+
</tbody>
|
|
378
433
|
</table>
|
|
379
434
|
</div>
|
|
@@ -6,51 +6,45 @@
|
|
|
6
6
|
<h4>Solvency:</h4>
|
|
7
7
|
<h5 class="has-text-weight-light">Current
|
|
8
8
|
Ratio: {{ tx_digest.ratios.current_ratio | currency_format }}</h5>
|
|
9
|
-
<progress
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
max="{% fin_ratio_max_value ratio='current_ratio' %}">
|
|
9
|
+
<progress class="progress {{ tx_digest.ratios.current_ratio | fin_ratio_threshold_class:'current_ratio' }}"
|
|
10
|
+
value="{{ tx_digest.ratios.current_ratio }}"
|
|
11
|
+
max="{% fin_ratio_max_value ratio='current_ratio' %}">
|
|
13
12
|
{{ tx_digest.ratios.current_ratio }}</progress>
|
|
14
13
|
<h5 class="has-text-weight-light">Quick
|
|
15
14
|
Ratio: {{ tx_digest.ratios.quick_ratio | currency_format }} </h5>
|
|
16
|
-
<progress
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
max="{% fin_ratio_max_value ratio='quick_ratio' %}">
|
|
15
|
+
<progress class="progress {{ tx_digest.ratios.quick_ratio | fin_ratio_threshold_class:'quick_ratio' }}"
|
|
16
|
+
value="{{ tx_digest.ratios.quick_ratio }}"
|
|
17
|
+
max="{% fin_ratio_max_value ratio='quick_ratio' %}">
|
|
20
18
|
{{ tx_digest.ratios.current_ratio }}</progress>
|
|
21
19
|
|
|
22
20
|
<h4>Leverage:</h4>
|
|
23
21
|
<h5 class="has-text-weight-light">Debt to
|
|
24
22
|
Equity: {{ tx_digest.ratios.debt_to_equity | currency_format }} </h5>
|
|
25
|
-
<progress
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
max="{% fin_ratio_max_value ratio='debt_to_equity' %}">
|
|
23
|
+
<progress class="progress {{ tx_digest.ratios.debt_to_equity | fin_ratio_threshold_class:'debt_to_equity' }}"
|
|
24
|
+
value="{{ tx_digest.ratios.debt_to_equity }}"
|
|
25
|
+
max="{% fin_ratio_max_value ratio='debt_to_equity' %}">
|
|
29
26
|
{{ tx_digest.ratios.debt_to_equity }}</progress>
|
|
30
27
|
|
|
31
28
|
|
|
32
29
|
<h4>Profitability:</h4>
|
|
33
30
|
<h5 class="has-text-weight-light">Return on
|
|
34
31
|
Equity: {{ tx_digest.ratios.return_on_equity | percentage }} </h5>
|
|
35
|
-
<progress
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
max="{% fin_ratio_max_value ratio='return_on_equity' %}">
|
|
32
|
+
<progress class="progress {{ tx_digest.ratios.quick_ratio | fin_ratio_threshold_class:'quick_ratio' }}"
|
|
33
|
+
value="{{ tx_digest.ratios.return_on_equity }}"
|
|
34
|
+
max="{% fin_ratio_max_value ratio='return_on_equity' %}">
|
|
39
35
|
{{ tx_digest.ratios.return_on_equity }}</progress>
|
|
40
36
|
|
|
41
37
|
<h5 class="has-text-weight-light">Return on
|
|
42
38
|
Assets: {{ tx_digest.ratios.return_on_assets | percentage }} </h5>
|
|
43
|
-
<progress
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
max="{% fin_ratio_max_value ratio='return_on_assets' %}">{{ tx_digest.ratios.return_on_assets }}</progress>
|
|
39
|
+
<progress class="progress {{ tx_digest.ratios.return_on_assets | fin_ratio_threshold_class:'return_on_assets' }}"
|
|
40
|
+
value="{{ tx_digest.ratios.return_on_assets }}"
|
|
41
|
+
max="{% fin_ratio_max_value ratio='return_on_assets' %}">{{ tx_digest.ratios.return_on_assets }}</progress>
|
|
47
42
|
|
|
48
43
|
<h5 class="has-text-weight-light">Net Profit
|
|
49
44
|
Margin: {{ tx_digest.ratios.net_profit_margin | percentage }} </h5>
|
|
50
|
-
<progress
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
max="{% fin_ratio_max_value ratio='net_profit_margin' %}">{{ tx_digest.ratios.net_profit_margin }}</progress>
|
|
45
|
+
<progress class="progress {{ tx_digest.ratios.net_profit_margin | fin_ratio_threshold_class:'net_profit_margin' }}"
|
|
46
|
+
value="{{ tx_digest.ratios.net_profit_margin }}"
|
|
47
|
+
max="{% fin_ratio_max_value ratio='net_profit_margin' %}">{{ tx_digest.ratios.net_profit_margin }}</progress>
|
|
54
48
|
|
|
55
49
|
<h5 class="has-text-weight-light">Gross Profit
|
|
56
50
|
Margin: {{ tx_digest.ratios.gross_profit_margin | percentage }} </h5>
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
<p class="subtitle has-text-grey has-text-right">
|
|
9
9
|
<span class="icon">{% icon "bi:cash-stack" 16 %}</span>
|
|
10
10
|
{% trans 'Invoice' %}</p>
|
|
11
|
-
<
|
|
12
|
-
<
|
|
11
|
+
<h2 class="title">{{ invoice.customer.customer_name }}</h2>
|
|
12
|
+
<h3 class="has-text-weight-light">{{ invoice.customer.address_1 }}</h3>
|
|
13
13
|
{% if not invoice.is_past_due %}
|
|
14
14
|
<p class="is-size-5 has-text-info">{% trans 'Due in' %}: {{ invoice.date_due | timeuntil }}</p>
|
|
15
15
|
{% else %}
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
value="{{ invoice.get_progress_percent }}"
|
|
32
32
|
max="100">{{ invoice.get_progress | percentage }}
|
|
33
33
|
</progress>
|
|
34
|
-
{% modal_action invoice 'get' entity_slug %}
|
|
34
|
+
{% modal_action invoice 'get' entity_slug %}
|
|
35
35
|
</div>
|
|
36
36
|
<footer class="card-footer">
|
|
37
37
|
<a href="{% url 'django_ledger:invoice-detail' entity_slug=entity_slug invoice_pk=invoice.uuid %}"
|
|
@@ -8,7 +8,12 @@
|
|
|
8
8
|
<meta charset="utf-8">
|
|
9
9
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
10
10
|
<title>{% block page_title %}{% session_entity_name %} | {{ page_title }}{% endblock %}</title>
|
|
11
|
-
|
|
11
|
+
|
|
12
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.4/css/bulma.min.css">
|
|
13
|
+
<link rel="stylesheet" href="{% static 'django_ledger/css/djl_styles.css' %}">
|
|
14
|
+
<link rel="stylesheet"
|
|
15
|
+
href="https://cdn.rawgit.com/jenil/bulmaswatch/gh-pages/{{ DJANGO_LEDGER_THEME }}/bulmaswatch.min.css">
|
|
16
|
+
|
|
12
17
|
<link rel="shortcut icon" type="image/jpg" href="{% static 'django_ledger/logo/favicon.png' %}">
|
|
13
18
|
|
|
14
19
|
{% block header_extra_js %}{% endblock %}
|
|
@@ -20,15 +20,19 @@
|
|
|
20
20
|
<tr class="has-text-centered">
|
|
21
21
|
<td>{{ vendor.vendor_number }}</td>
|
|
22
22
|
<td>{{ vendor.vendor_name }}</td>
|
|
23
|
-
<td>
|
|
23
|
+
<td>
|
|
24
|
+
{% if vendor.address_1 %}
|
|
25
|
+
{{ vendor.address_1 }}
|
|
26
|
+
{% endif %}
|
|
27
|
+
</td>
|
|
24
28
|
<td>{% if vendor.vendor_code %}{{ vendor.vendor_code }}{% endif %}</td>
|
|
25
29
|
<td>
|
|
26
30
|
{% if vendor.active %}
|
|
27
|
-
<span class="icon has-text-success
|
|
31
|
+
<span class="icon has-text-success">
|
|
28
32
|
{% icon 'ant-design:check-circle-filled' 24 %}
|
|
29
33
|
</span>
|
|
30
34
|
{% elif not vendor.active %}
|
|
31
|
-
<span class="icon has-text-danger
|
|
35
|
+
<span class="icon has-text-danger">
|
|
32
36
|
{% icon 'mdi:dangerous' 24 %}
|
|
33
37
|
</span>
|
|
34
38
|
{% endif %}
|
|
@@ -36,11 +40,11 @@
|
|
|
36
40
|
|
|
37
41
|
<td>
|
|
38
42
|
{% if vendor.hidden %}
|
|
39
|
-
<span class="icon has-text-success
|
|
43
|
+
<span class="icon has-text-success">
|
|
40
44
|
{% icon 'ant-design:check-circle-filled' 24 %}
|
|
41
45
|
</span>
|
|
42
46
|
{% elif not vendor.hidden %}
|
|
43
|
-
<span class="icon has-text-danger
|
|
47
|
+
<span class="icon has-text-danger">
|
|
44
48
|
{% icon 'mdi:dangerous' 24 %}
|
|
45
49
|
</span>
|
|
46
50
|
{% endif %}
|
|
@@ -95,8 +95,7 @@ class AccountModelTests(DjangoLedgerBaseTest):
|
|
|
95
95
|
self.assertEqual(response_create.status_code, 302)
|
|
96
96
|
self.assertTrue(AccountModel.objects.for_entity(
|
|
97
97
|
entity_model=entity_model,
|
|
98
|
-
|
|
99
|
-
coa_slug=entity_model.default_coa_slug,
|
|
98
|
+
coa_model=entity_model.default_coa_slug,
|
|
100
99
|
).with_codes(codes=NEW_ACCOUNT_CODE).exists())
|
|
101
100
|
|
|
102
101
|
# cannot create an account with same code again...
|
django_ledger/tests/test_io.py
CHANGED
|
@@ -195,3 +195,20 @@ class IOTest(DjangoLedgerBaseTest):
|
|
|
195
195
|
# )
|
|
196
196
|
#
|
|
197
197
|
# self.assertEqual(io_digest.get_io_txs_queryset().count(), 0)
|
|
198
|
+
|
|
199
|
+
def test_io_transactions_belong_to_entity(self):
|
|
200
|
+
entity_model = self.get_random_entity_model()
|
|
201
|
+
from_datetime = self.START_DATE
|
|
202
|
+
to_datetime = self.START_DATE + timedelta(days=randint(10, 60))
|
|
203
|
+
|
|
204
|
+
io_digest = entity_model.digest(
|
|
205
|
+
entity_slug=entity_model.slug,
|
|
206
|
+
from_date=from_datetime,
|
|
207
|
+
to_date=to_datetime,
|
|
208
|
+
for_test=True
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
tx_qs = io_digest.get_io_txs_queryset()
|
|
212
|
+
# Every transaction returned by the IO for an entity digest must belong to that entity.
|
|
213
|
+
for tx in tx_qs:
|
|
214
|
+
self.assertEqual(tx['journal_entry__ledger__entity_id'], entity_model.uuid)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from datetime import date, datetime
|
|
2
2
|
from random import choice, randint
|
|
3
|
-
from typing import Union, Optional
|
|
3
|
+
from typing import Union, Optional, List
|
|
4
4
|
from urllib.parse import urlparse
|
|
5
5
|
from uuid import uuid4
|
|
6
6
|
|
|
@@ -31,8 +31,8 @@ class PurchaseOrderModelTests(DjangoLedgerBaseTest):
|
|
|
31
31
|
commit=True)
|
|
32
32
|
return po_model
|
|
33
33
|
|
|
34
|
-
def get_purchase_orders(self, entity_model: EntityModel) ->
|
|
35
|
-
return PurchaseOrderModel.objects.for_entity(entity_model
|
|
34
|
+
def get_purchase_orders(self, entity_model: EntityModel) -> List[PurchaseOrderModel]:
|
|
35
|
+
return PurchaseOrderModel.objects.for_entity(entity_model=entity_model)
|
|
36
36
|
|
|
37
37
|
def test_protected_views(self):
|
|
38
38
|
"""
|
|
@@ -96,8 +96,7 @@ class TransactionModelFormSetTest(DjangoLedgerBaseTest):
|
|
|
96
96
|
|
|
97
97
|
# get a journal entry that has transactions...
|
|
98
98
|
je_model = JournalEntryModel.objects.for_entity(
|
|
99
|
-
|
|
100
|
-
user_model=self.user_model
|
|
99
|
+
entity_model=entity_model
|
|
101
100
|
).annotate(
|
|
102
101
|
txs_count=Count('transactionmodel')).filter(
|
|
103
102
|
txs_count__gt=0).order_by('-timestamp').first()
|
django_ledger/urls/__init__.py
CHANGED
|
@@ -5,11 +5,9 @@ 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
|
-
|
|
9
8
|
from django.urls import path, include
|
|
10
9
|
|
|
11
10
|
from django_ledger import views
|
|
12
|
-
from django_ledger.settings import DJANGO_LEDGER_GRAPHQL_SUPPORT_ENABLED
|
|
13
11
|
|
|
14
12
|
app_name = 'django_ledger'
|
|
15
13
|
|
|
@@ -41,5 +39,3 @@ urlpatterns = [
|
|
|
41
39
|
path('', views.RootUrlView.as_view(), name='root'),
|
|
42
40
|
]
|
|
43
41
|
|
|
44
|
-
if DJANGO_LEDGER_GRAPHQL_SUPPORT_ENABLED:
|
|
45
|
-
pass
|
django_ledger/views/bill.py
CHANGED
|
@@ -64,8 +64,7 @@ class BillModelCreateView(BillModelModelBaseView, CreateView):
|
|
|
64
64
|
|
|
65
65
|
if self.for_estimate and 'ce_pk' in self.kwargs:
|
|
66
66
|
estimate_qs = EstimateModel.objects.for_entity(
|
|
67
|
-
|
|
68
|
-
user_model=self.request.user
|
|
67
|
+
entity_model=self.AUTHORIZED_ENTITY_MODEL,
|
|
69
68
|
)
|
|
70
69
|
estimate_model: EstimateModel = get_object_or_404(estimate_qs, uuid__exact=self.kwargs['ce_pk'])
|
|
71
70
|
if not estimate_model.can_bind():
|
|
@@ -87,8 +86,7 @@ class BillModelCreateView(BillModelModelBaseView, CreateView):
|
|
|
87
86
|
return HttpResponseBadRequest()
|
|
88
87
|
|
|
89
88
|
po_qs = PurchaseOrderModel.objects.for_entity(
|
|
90
|
-
|
|
91
|
-
user_model=self.request.user
|
|
89
|
+
entity_model=self.kwargs['entity_slug'],
|
|
92
90
|
).prefetch_related('itemtransactionmodel_set')
|
|
93
91
|
po_model: PurchaseOrderModel = get_object_or_404(po_qs, uuid__exact=po_pk)
|
|
94
92
|
po_itemtxs_qs = po_model.itemtransactionmodel_set.filter(
|
|
@@ -104,8 +102,7 @@ class BillModelCreateView(BillModelModelBaseView, CreateView):
|
|
|
104
102
|
}) + f'?item_uuids={po_item_uuids_qry_param}'
|
|
105
103
|
elif self.for_estimate:
|
|
106
104
|
estimate_qs = EstimateModel.objects.for_entity(
|
|
107
|
-
|
|
108
|
-
user_model=self.request.user
|
|
105
|
+
entity_model=self.AUTHORIZED_ENTITY_MODEL
|
|
109
106
|
)
|
|
110
107
|
estimate_uuid = self.kwargs['ce_pk']
|
|
111
108
|
estimate_model: EstimateModel = get_object_or_404(estimate_qs, uuid__exact=estimate_uuid)
|
|
@@ -137,15 +134,16 @@ class BillModelCreateView(BillModelModelBaseView, CreateView):
|
|
|
137
134
|
def form_valid(self, form):
|
|
138
135
|
bill_model: BillModel = form.save(commit=False)
|
|
139
136
|
ledger_model, bill_model = bill_model.configure(
|
|
140
|
-
entity_slug=self.AUTHORIZED_ENTITY_MODEL,
|
|
137
|
+
entity_slug=self.AUTHORIZED_ENTITY_MODEL.slug,
|
|
138
|
+
user_model=self.request.user,
|
|
141
139
|
commit_ledger=True
|
|
142
140
|
)
|
|
143
141
|
|
|
144
142
|
if self.for_estimate:
|
|
145
143
|
ce_pk = self.kwargs['ce_pk']
|
|
146
144
|
estimate_model_qs = EstimateModel.objects.for_entity(
|
|
147
|
-
|
|
148
|
-
|
|
145
|
+
entity_model=self.kwargs['entity_slug']
|
|
146
|
+
)
|
|
149
147
|
|
|
150
148
|
estimate_model = get_object_or_404(estimate_model_qs, uuid__exact=ce_pk)
|
|
151
149
|
bill_model.bind_estimate(estimate_model=estimate_model, commit=False)
|
|
@@ -157,8 +155,7 @@ class BillModelCreateView(BillModelModelBaseView, CreateView):
|
|
|
157
155
|
return HttpResponseBadRequest()
|
|
158
156
|
item_uuids = item_uuids.split(',')
|
|
159
157
|
po_qs = PurchaseOrderModel.objects.for_entity(
|
|
160
|
-
|
|
161
|
-
user_model=self.request.user
|
|
158
|
+
entity_model=self.kwargs['entity_slug'],
|
|
162
159
|
)
|
|
163
160
|
po_model: PurchaseOrderModel = get_object_or_404(po_qs, uuid__exact=po_pk)
|
|
164
161
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
|
|
3
3
|
Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
|
|
4
4
|
"""
|
|
5
|
+
from typing import Optional
|
|
5
6
|
|
|
6
7
|
from django.contrib import messages
|
|
7
8
|
from django.core.exceptions import ImproperlyConfigured, ValidationError
|
|
@@ -10,18 +11,19 @@ from django.views.generic import UpdateView, ListView, RedirectView, CreateView
|
|
|
10
11
|
from django.views.generic.detail import SingleObjectMixin
|
|
11
12
|
|
|
12
13
|
from django_ledger.forms.chart_of_accounts import ChartOfAccountsModelUpdateForm, ChartOfAccountsModelCreateForm
|
|
14
|
+
from django_ledger.models import EntityModel, ChartOfAccountModelQuerySet
|
|
13
15
|
from django_ledger.models.chart_of_accounts import ChartOfAccountModel
|
|
14
16
|
from django_ledger.views.mixins import DjangoLedgerSecurityMixIn
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
class ChartOfAccountModelModelBaseViewMixIn(DjangoLedgerSecurityMixIn):
|
|
18
|
-
queryset = None
|
|
20
|
+
queryset: Optional[ChartOfAccountModelQuerySet] = None
|
|
19
21
|
|
|
20
22
|
def get_queryset(self):
|
|
21
23
|
if self.queryset is None:
|
|
22
|
-
entity_model = self.get_authorized_entity_instance()
|
|
23
|
-
self.queryset = entity_model.chartofaccountmodel_set.all().order_by('-updated')
|
|
24
|
-
return
|
|
24
|
+
entity_model: EntityModel = self.get_authorized_entity_instance()
|
|
25
|
+
self.queryset: ChartOfAccountModelQuerySet = entity_model.chartofaccountmodel_set.all().order_by('-updated')
|
|
26
|
+
return self.queryset
|
|
25
27
|
|
|
26
28
|
|
|
27
29
|
class ChartOfAccountModelListView(ChartOfAccountModelModelBaseViewMixIn, ListView):
|
|
@@ -5,6 +5,7 @@ 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
|
+
from typing import Optional
|
|
8
9
|
|
|
9
10
|
from django.contrib import messages
|
|
10
11
|
from django.core.exceptions import ValidationError, ImproperlyConfigured
|
|
@@ -21,13 +22,14 @@ from django.views.generic.detail import SingleObjectMixin
|
|
|
21
22
|
|
|
22
23
|
from django_ledger.forms.closing_entry import ClosingEntryCreateForm, ClosingEntryUpdateForm
|
|
23
24
|
from django_ledger.io.io_core import get_localdate
|
|
25
|
+
from django_ledger.models import EstimateModel, ClosingEntryModelQuerySet
|
|
24
26
|
from django_ledger.models.closing_entry import ClosingEntryModel
|
|
25
27
|
from django_ledger.models.entity import EntityModel
|
|
26
28
|
from django_ledger.views import DjangoLedgerSecurityMixIn
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
class ClosingEntryModelBaseView(DjangoLedgerSecurityMixIn):
|
|
30
|
-
queryset = None
|
|
32
|
+
queryset: Optional[ClosingEntryModelQuerySet] = None
|
|
31
33
|
|
|
32
34
|
def get_queryset(self):
|
|
33
35
|
if self.queryset is None:
|
|
@@ -75,16 +77,18 @@ class ClosingEntryModelCreateView(ClosingEntryModelBaseView, CreateView):
|
|
|
75
77
|
'header_title': PAGE_TITLE,
|
|
76
78
|
'header_subtitle_icon': 'file-icons:finaldraft'
|
|
77
79
|
}
|
|
80
|
+
form_class = ClosingEntryCreateForm
|
|
78
81
|
|
|
79
82
|
def get_initial(self):
|
|
80
83
|
return {
|
|
81
|
-
'closing_date': get_localdate()
|
|
84
|
+
'closing_date': get_localdate(),
|
|
85
|
+
'entity_model': self.AUTHORIZED_ENTITY_MODEL
|
|
82
86
|
}
|
|
83
87
|
|
|
84
|
-
def
|
|
85
|
-
return
|
|
86
|
-
|
|
87
|
-
|
|
88
|
+
def get_form_kwargs(self, **kwargs):
|
|
89
|
+
return super().get_form_kwargs(**kwargs) | {
|
|
90
|
+
'entity_model': self.AUTHORIZED_ENTITY_MODEL
|
|
91
|
+
}
|
|
88
92
|
|
|
89
93
|
def get_context_data(self, **kwargs):
|
|
90
94
|
ctx = super().get_context_data(**kwargs)
|
|
@@ -100,7 +104,7 @@ class ClosingEntryModelCreateView(ClosingEntryModelBaseView, CreateView):
|
|
|
100
104
|
force_update=True,
|
|
101
105
|
post_closing_entry=False
|
|
102
106
|
)
|
|
103
|
-
self.ce_model = ce_model
|
|
107
|
+
self.ce_model: EstimateModel = ce_model
|
|
104
108
|
return HttpResponseRedirect(self.get_success_url())
|
|
105
109
|
|
|
106
110
|
def get_success_url(self):
|
django_ledger/views/customer.py
CHANGED
|
@@ -5,32 +5,30 @@ 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
|
+
from typing import Optional
|
|
8
9
|
|
|
9
10
|
from django.urls import reverse
|
|
10
11
|
from django.utils.translation import gettext_lazy as _
|
|
11
|
-
from django.views.generic import ListView, CreateView, UpdateView
|
|
12
|
+
from django.views.generic import ListView, CreateView, UpdateView, DeleteView
|
|
12
13
|
|
|
13
14
|
from django_ledger.forms.customer import CustomerModelForm
|
|
14
|
-
from django_ledger.models.customer import CustomerModel
|
|
15
|
+
from django_ledger.models.customer import CustomerModel, CustomerModelQueryset
|
|
15
16
|
from django_ledger.models.entity import EntityModel
|
|
16
17
|
from django_ledger.views.mixins import DjangoLedgerSecurityMixIn
|
|
17
18
|
|
|
18
19
|
|
|
19
|
-
class CustomerModelModelViewQuerySetMixIn:
|
|
20
|
-
queryset = None
|
|
20
|
+
class CustomerModelModelViewQuerySetMixIn(DjangoLedgerSecurityMixIn):
|
|
21
|
+
queryset: Optional[CustomerModelQueryset] = None
|
|
21
22
|
|
|
22
23
|
def get_queryset(self):
|
|
23
24
|
if self.queryset is None:
|
|
24
25
|
self.queryset = CustomerModel.objects.for_entity(
|
|
25
|
-
|
|
26
|
-
user_model=self.request.user
|
|
26
|
+
entity_model=self.kwargs['entity_slug'],
|
|
27
27
|
).order_by('-updated')
|
|
28
|
-
return
|
|
28
|
+
return self.queryset
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
class CustomerModelListView(
|
|
32
|
-
CustomerModelModelViewQuerySetMixIn,
|
|
33
|
-
ListView):
|
|
31
|
+
class CustomerModelListView(CustomerModelModelViewQuerySetMixIn, ListView):
|
|
34
32
|
template_name = 'django_ledger/customer/customer_list.html'
|
|
35
33
|
PAGE_TITLE = _('Customer List')
|
|
36
34
|
extra_context = {
|
|
@@ -41,9 +39,7 @@ class CustomerModelListView(DjangoLedgerSecurityMixIn,
|
|
|
41
39
|
context_object_name = 'customers'
|
|
42
40
|
|
|
43
41
|
|
|
44
|
-
class CustomerModelCreateView(
|
|
45
|
-
CustomerModelModelViewQuerySetMixIn,
|
|
46
|
-
CreateView):
|
|
42
|
+
class CustomerModelCreateView(CustomerModelModelViewQuerySetMixIn, CreateView):
|
|
47
43
|
template_name = 'django_ledger/customer/customer_create.html'
|
|
48
44
|
PAGE_TITLE = _('Create New Customer')
|
|
49
45
|
form_class = CustomerModelForm
|
|
@@ -70,9 +66,7 @@ class CustomerModelCreateView(DjangoLedgerSecurityMixIn,
|
|
|
70
66
|
return super().form_valid(form)
|
|
71
67
|
|
|
72
68
|
|
|
73
|
-
class CustomerModelUpdateView(
|
|
74
|
-
CustomerModelModelViewQuerySetMixIn,
|
|
75
|
-
UpdateView):
|
|
69
|
+
class CustomerModelUpdateView(CustomerModelModelViewQuerySetMixIn, UpdateView):
|
|
76
70
|
template_name = 'django_ledger/customer/customer_update.html'
|
|
77
71
|
PAGE_TITLE = _('Customer Update')
|
|
78
72
|
form_class = CustomerModelForm
|
|
@@ -99,4 +93,6 @@ class CustomerModelUpdateView(DjangoLedgerSecurityMixIn,
|
|
|
99
93
|
form.save()
|
|
100
94
|
return super().form_valid(form)
|
|
101
95
|
|
|
102
|
-
|
|
96
|
+
|
|
97
|
+
class CustomerModelDeleteView(CustomerModelModelViewQuerySetMixIn, DeleteView):
|
|
98
|
+
pass
|