django-ledger 0.7.10__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.

Files changed (115) hide show
  1. django_ledger/__init__.py +1 -1
  2. django_ledger/context.py +12 -0
  3. django_ledger/forms/bill.py +0 -4
  4. django_ledger/forms/closing_entry.py +13 -1
  5. django_ledger/forms/data_import.py +1 -1
  6. django_ledger/forms/estimate.py +3 -6
  7. django_ledger/forms/invoice.py +3 -7
  8. django_ledger/forms/item.py +10 -18
  9. django_ledger/forms/purchase_order.py +2 -4
  10. django_ledger/io/io_core.py +25 -32
  11. django_ledger/io/io_generator.py +7 -6
  12. django_ledger/io/io_library.py +1 -2
  13. django_ledger/migrations/0024_billmodel_entity_model_invoicemodel_entity_model.py +24 -0
  14. django_ledger/migrations/0025_alter_billmodel_cash_account_and_more.py +70 -0
  15. django_ledger/models/accounts.py +109 -69
  16. django_ledger/models/bank_account.py +40 -23
  17. django_ledger/models/bill.py +89 -63
  18. django_ledger/models/chart_of_accounts.py +173 -105
  19. django_ledger/models/closing_entry.py +99 -48
  20. django_ledger/models/customer.py +60 -39
  21. django_ledger/models/data_import.py +55 -41
  22. django_ledger/models/deprecations.py +61 -0
  23. django_ledger/models/entity.py +18 -16
  24. django_ledger/models/estimate.py +57 -28
  25. django_ledger/models/invoice.py +58 -28
  26. django_ledger/models/items.py +503 -142
  27. django_ledger/models/journal_entry.py +61 -47
  28. django_ledger/models/ledger.py +106 -42
  29. django_ledger/models/mixins.py +16 -10
  30. django_ledger/models/purchase_order.py +39 -17
  31. django_ledger/models/transactions.py +152 -113
  32. django_ledger/models/unit.py +57 -30
  33. django_ledger/models/vendor.py +75 -43
  34. django_ledger/report/core.py +2 -14
  35. django_ledger/settings.py +56 -71
  36. django_ledger/static/django_ledger/bundle/djetler.bundle.js +1 -1
  37. django_ledger/static/django_ledger/bundle/djetler.bundle.js.LICENSE.txt +25 -0
  38. django_ledger/static/django_ledger/bundle/styles.bundle.js +1 -1
  39. django_ledger/static/django_ledger/css/djl_styles.css +273 -0
  40. django_ledger/templates/django_ledger/bills/includes/card_bill.html +2 -2
  41. django_ledger/templates/django_ledger/components/menu.html +41 -26
  42. django_ledger/templates/django_ledger/customer/tags/customer_table.html +5 -5
  43. django_ledger/templates/django_ledger/entity/includes/card_entity.html +12 -6
  44. django_ledger/templates/django_ledger/financial_statements/balance_sheet.html +1 -1
  45. django_ledger/templates/django_ledger/financial_statements/cash_flow.html +4 -1
  46. django_ledger/templates/django_ledger/financial_statements/income_statement.html +4 -1
  47. django_ledger/templates/django_ledger/financial_statements/tags/balance_sheet_statement.html +27 -3
  48. django_ledger/templates/django_ledger/financial_statements/tags/cash_flow_statement.html +16 -4
  49. django_ledger/templates/django_ledger/financial_statements/tags/income_statement.html +73 -18
  50. django_ledger/templates/django_ledger/includes/widget_ratios.html +18 -24
  51. django_ledger/templates/django_ledger/invoice/includes/card_invoice.html +3 -3
  52. django_ledger/templates/django_ledger/layouts/base.html +6 -1
  53. django_ledger/templates/django_ledger/vendor/tags/vendor_table.html +9 -5
  54. django_ledger/tests/test_accounts.py +1 -2
  55. django_ledger/tests/test_io.py +17 -0
  56. django_ledger/tests/test_purchase_order.py +3 -3
  57. django_ledger/tests/test_transactions.py +1 -2
  58. django_ledger/urls/__init__.py +0 -4
  59. django_ledger/views/bill.py +8 -13
  60. django_ledger/views/chart_of_accounts.py +6 -4
  61. django_ledger/views/closing_entry.py +11 -7
  62. django_ledger/views/customer.py +13 -17
  63. django_ledger/views/data_import.py +7 -6
  64. django_ledger/views/djl_api.py +3 -5
  65. django_ledger/views/entity.py +2 -4
  66. django_ledger/views/estimate.py +3 -7
  67. django_ledger/views/inventory.py +3 -5
  68. django_ledger/views/invoice.py +4 -6
  69. django_ledger/views/item.py +7 -11
  70. django_ledger/views/journal_entry.py +1 -2
  71. django_ledger/views/mixins.py +25 -19
  72. django_ledger/views/purchase_order.py +24 -35
  73. django_ledger/views/unit.py +1 -2
  74. django_ledger/views/vendor.py +1 -2
  75. {django_ledger-0.7.10.dist-info → django_ledger-0.8.0.dist-info}/METADATA +43 -75
  76. {django_ledger-0.7.10.dist-info → django_ledger-0.8.0.dist-info}/RECORD +80 -108
  77. {django_ledger-0.7.10.dist-info → django_ledger-0.8.0.dist-info}/WHEEL +1 -1
  78. django_ledger-0.8.0.dist-info/top_level.txt +1 -0
  79. django_ledger/contrib/django_ledger_graphene/__init__.py +0 -0
  80. django_ledger/contrib/django_ledger_graphene/accounts/schema.py +0 -33
  81. django_ledger/contrib/django_ledger_graphene/api.py +0 -42
  82. django_ledger/contrib/django_ledger_graphene/apps.py +0 -6
  83. django_ledger/contrib/django_ledger_graphene/auth/mutations.py +0 -49
  84. django_ledger/contrib/django_ledger_graphene/auth/schema.py +0 -6
  85. django_ledger/contrib/django_ledger_graphene/bank_account/mutations.py +0 -61
  86. django_ledger/contrib/django_ledger_graphene/bank_account/schema.py +0 -34
  87. django_ledger/contrib/django_ledger_graphene/bill/mutations.py +0 -0
  88. django_ledger/contrib/django_ledger_graphene/bill/schema.py +0 -34
  89. django_ledger/contrib/django_ledger_graphene/coa/mutations.py +0 -0
  90. django_ledger/contrib/django_ledger_graphene/coa/schema.py +0 -30
  91. django_ledger/contrib/django_ledger_graphene/customers/__init__.py +0 -0
  92. django_ledger/contrib/django_ledger_graphene/customers/mutations.py +0 -71
  93. django_ledger/contrib/django_ledger_graphene/customers/schema.py +0 -43
  94. django_ledger/contrib/django_ledger_graphene/data_import/mutations.py +0 -0
  95. django_ledger/contrib/django_ledger_graphene/data_import/schema.py +0 -0
  96. django_ledger/contrib/django_ledger_graphene/entity/mutations.py +0 -0
  97. django_ledger/contrib/django_ledger_graphene/entity/schema.py +0 -94
  98. django_ledger/contrib/django_ledger_graphene/item/mutations.py +0 -0
  99. django_ledger/contrib/django_ledger_graphene/item/schema.py +0 -31
  100. django_ledger/contrib/django_ledger_graphene/journal_entry/mutations.py +0 -0
  101. django_ledger/contrib/django_ledger_graphene/journal_entry/schema.py +0 -35
  102. django_ledger/contrib/django_ledger_graphene/ledger/mutations.py +0 -0
  103. django_ledger/contrib/django_ledger_graphene/ledger/schema.py +0 -32
  104. django_ledger/contrib/django_ledger_graphene/purchase_order/mutations.py +0 -0
  105. django_ledger/contrib/django_ledger_graphene/purchase_order/schema.py +0 -31
  106. django_ledger/contrib/django_ledger_graphene/transaction/mutations.py +0 -0
  107. django_ledger/contrib/django_ledger_graphene/transaction/schema.py +0 -36
  108. django_ledger/contrib/django_ledger_graphene/unit/mutations.py +0 -0
  109. django_ledger/contrib/django_ledger_graphene/unit/schema.py +0 -27
  110. django_ledger/contrib/django_ledger_graphene/vendor/mutations.py +0 -0
  111. django_ledger/contrib/django_ledger_graphene/vendor/schema.py +0 -37
  112. django_ledger/contrib/django_ledger_graphene/views.py +0 -12
  113. django_ledger-0.7.10.dist-info/top_level.txt +0 -4
  114. {django_ledger-0.7.10.dist-info → django_ledger-0.8.0.dist-info/licenses}/AUTHORS.md +0 -0
  115. {django_ledger-0.7.10.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-3 has-text-left">{% trans 'Operating Revenues' %}</h2></td>
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="icon">{% icon 'bi:arrow-bar-down' 24 %}</span>
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="icon">{% icon 'bi:arrow-bar-up' 24 %}</span>
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-3 has-text-left">{% trans 'Less: Cost of Goods Sold' %}</h2></td>
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="icon">{% icon 'bi:arrow-bar-down' 24 %}</span>
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="icon">{% icon 'bi:arrow-bar-up' 24 %}</span>
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-3 has-text-left">{% trans 'Operating Expenses' %}</h2></td>
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="icon">{% icon 'bi:arrow-bar-down' 24 %}</span>
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="icon">{% icon 'bi:arrow-bar-up' 24 %}</span>
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-3 has-text-left">{% trans 'Other Revenues' %}</h2></td>
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="icon">{% icon 'bi:arrow-bar-down' 24 %}</span>
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="icon">{% icon 'bi:arrow-bar-up' 24 %}</span>
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-3 has-text-left">{% trans 'Other Expenses' %}</h2></td>
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="icon">{% icon 'bi:arrow-bar-down' 24 %}</span>
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="icon">{% icon 'bi:arrow-bar-up' 24 %}</span>
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
- class="progress {{ tx_digest.ratios.current_ratio | fin_ratio_threshold_class:'current_ratio' }}"
11
- value="{{ tx_digest.ratios.current_ratio }}"
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
- class="progress {{ tx_digest.ratios.quick_ratio | fin_ratio_threshold_class:'quick_ratio' }}"
18
- value="{{ tx_digest.ratios.quick_ratio }}"
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
- class="progress {{ tx_digest.ratios.debt_to_equity | fin_ratio_threshold_class:'debt_to_equity' }}"
27
- value="{{ tx_digest.ratios.debt_to_equity }}"
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
- class="progress {{ tx_digest.ratios.quick_ratio | fin_ratio_threshold_class:'quick_ratio' }}"
37
- value="{{ tx_digest.ratios.return_on_equity }}"
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
- class="progress {{ tx_digest.ratios.return_on_assets | fin_ratio_threshold_class:'return_on_assets' }}"
45
- value="{{ tx_digest.ratios.return_on_assets }}"
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
- class="progress {{ tx_digest.ratios.net_profit_margin | fin_ratio_threshold_class:'net_profit_margin' }}"
52
- value="{{ tx_digest.ratios.net_profit_margin }}"
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
- <p class="title">{{ invoice.customer.customer_name }}</p>
12
- <p class="subtitle has-text-weight-light">{{ invoice.customer.address_1 }}</p>
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
- <script src="{% static 'django_ledger/bundle/styles.bundle.js' %}"></script>
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>{{ vendor.address_1 }}</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-dark">
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-dark">
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-dark">
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-dark">
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
- user_model=self.user_model,
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...
@@ -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) -> list[PurchaseOrderModel]:
35
- return PurchaseOrderModel.objects.for_entity(entity_model, self.user_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
- entity_slug=entity_model,
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()
@@ -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
@@ -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
- entity_slug=self.kwargs['entity_slug'],
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
- entity_slug=self.kwargs['entity_slug'],
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
- entity_slug=self.kwargs['entity_slug'],
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
- entity_slug=self.kwargs['entity_slug'],
148
- user_model=self.request.user)
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
- entity_slug=self.kwargs['entity_slug'],
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
 
@@ -253,8 +250,6 @@ class BillModelDetailView(BillModelModelBaseView, DetailView):
253
250
  title = f'Bill {bill_model.bill_number}'
254
251
  context['page_title'] = title
255
252
  context['header_title'] = title
256
-
257
- bill_model: BillModel = self.object
258
253
  bill_items_qs, item_data = bill_model.get_itemtxs_data()
259
254
  context['itemtxs_qs'] = bill_items_qs
260
255
  context['total_amount__sum'] = item_data['total_amount__sum']
@@ -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 super().get_queryset()
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 get_form(self, form_class=None, **kwargs):
85
- return ClosingEntryCreateForm(
86
- **self.get_form_kwargs()
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):
@@ -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
- entity_slug=self.kwargs['entity_slug'],
26
- user_model=self.request.user
26
+ entity_model=self.kwargs['entity_slug'],
27
27
  ).order_by('-updated')
28
- return super().get_queryset()
28
+ return self.queryset
29
29
 
30
30
 
31
- class CustomerModelListView(DjangoLedgerSecurityMixIn,
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(DjangoLedgerSecurityMixIn,
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(DjangoLedgerSecurityMixIn,
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
- # todo: add CustomerDeleteView
96
+
97
+ class CustomerModelDeleteView(CustomerModelModelViewQuerySetMixIn, DeleteView):
98
+ pass