Skip to content

Commit 5c0a4f7

Browse files
committed
- **Added a reset functionality for Import Job Model:**
- Introduced `ImportJobModelResetView` to enable resetting import jobs via a POST request. - Added logic in `post` method to undo import of staged transactions associated with the import job model. - Redirects to the data import URL after reset. - **Updated URL routing for import job reset:** - Added a new endpoint (`/reset/`) for resetting import jobs in `data_import.py` URL configuration. - **Enhanced user interface for job reset:** - Included a reset button in the `data_import_job_txs.html` template. - Updated error handling and improved readability in template files. - Added transaction grouping display for imported transactions with better hierarchical visualization in `data_import_job_txs_imported.html`. - **Refactored form rendering logic:** - Streamlined multiline function calls into single lines for better readability across `data_import.py` view methods. - Adjusted form-related class and method names for clarity. ### **Summary** Added reset functionality for import job models and enhanced user interfaces for transaction imports. Improved function readability and transaction grouping presentation while ensuring all existing processes remain compatible. ### **Backwards Compatibility** These changes are fully backwards-compatible. New functionality does not affect existing workflows unless the reset feature is utilized.
1 parent aa3838e commit 5c0a4f7

File tree

5 files changed

+122
-67
lines changed

5 files changed

+122
-67
lines changed

django_ledger/templates/django_ledger/data_import/data_import_job_txs.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@
1010
<h1 class="is-size-2">{% trans 'Pending Transactions' %}</h1>
1111
{% data_import_job_txs_pending staged_txs_formset %}
1212
</div>
13+
<div class="column is-12 has-text-centered">
14+
<form action="{{ import_job.get_data_import_reset_url }}" method="post">
15+
{% csrf_token %}
16+
<button class="button is-warning is-large">Reset Job</button>
17+
</form>
18+
</div>
1319
<div class="column is-12">
1420
<h2 class="is-size-2">{% trans 'Imported Transactions' %}</h2>
1521
{% data_import_job_txs_imported import_job %}

django_ledger/templates/django_ledger/data_import/tags/data_import_job_txs_imported.html

Lines changed: 66 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,44 +16,74 @@
1616
</tr>
1717
</thead>
1818
<tbody>
19-
{% for imported_tx in imported_txs %}
20-
<tr id="staged-tx-{{ imported_tx.uuid }}">
21-
<td>{{ imported_tx.date_posted }}</td>
22-
<td>{{ imported_tx.name }}</td>
23-
<td class="{% if imported_tx.get_amount < 0.00 %}has-text-danger{% endif %} has-text-centered">
24-
{% currency_symbol %}{{ imported_tx.get_amount }}</td>
25-
<td>{% if imported_tx.activity %}
26-
{{ imported_tx.get_activity_display }}
27-
{% endif %}</td>
28-
<td>{% if imported_tx.entity_unit %}{{ imported_tx.entity_unit }}{% endif %}</td>
29-
<td>{{ imported_tx.account_model }}</td>
30-
<td>{{ imported_tx.transaction_model }}</td>
31-
<td class="has-text-centered">
32-
<div class="dropdown is-hoverable is-right">
33-
<div class="dropdown-trigger">
34-
<button class="button is-small" aria-haspopup="true" aria-controls="actions-{{ imported_tx.uuid }}">
35-
<span>{% trans 'Actions' %}</span>
36-
<span class="icon is-small">{% icon 'mdi:chevron-down' 14 %}</span>
37-
</button>
38-
</div>
39-
<div class="dropdown-menu" id="actions-{{ imported_tx.uuid }}" role="menu">
40-
<div class="dropdown-content">
41-
<a href="{% url 'django_ledger:je-detail' entity_slug=import_job_model.entity_slug ledger_pk=imported_tx.transaction_model.journal_entry.ledger_id je_pk=imported_tx.transaction_model.journal_entry_id %}"
42-
class="dropdown-item">{% trans 'View JE' %}</a>
43-
{% if imported_tx.has_receipt %}
44-
<a href="{% url 'django_ledger:receipt-detail' entity_slug=import_job_model.entity_slug receipt_pk=imported_tx.receiptmodel.uuid %}"
45-
class="dropdown-item">{% trans 'View Receipt' %}</a>
46-
{% endif %}
47-
<hr class="dropdown-divider">
48-
<form method="post" action="{% url 'django_ledger:data-import-staged-tx-undo' entity_slug=import_job_model.entity_slug job_pk=import_job_model.uuid staged_tx_pk=imported_tx.uuid %}">
49-
{% csrf_token %}
50-
<button type="submit" class="dropdown-item has-text-danger">{% trans 'Undo Import' %}</button>
51-
</form>
52-
</div>
53-
</div>
54-
</div>
19+
{% regroup imported_txs by group_uuid as bundled_groups %}
20+
{% for group in bundled_groups %}
21+
<tr class="has-background-light">
22+
<td colspan="8" class="has-text-weight-bold">
23+
{% trans 'Bundle' %} {{ forloop.counter }} —
24+
{% trans 'Parent' %}:
25+
{% with parent_tx=group.list.0.parent|default:group.list.0 %}
26+
{{ parent_tx.date_posted }} · {{ parent_tx.name }} ·
27+
{% currency_symbol %}{{ parent_tx.get_amount }}
28+
{% if parent_tx.get_activity_display %} · {{ parent_tx.get_activity_display }}{% endif %}
29+
{% endwith %}
30+
<span class="tag is-info is-light" style="margin-left: .5rem;">
31+
{% if group.list.0.is_bundled %}{% trans 'Bundled' %}{% else %}
32+
{% trans 'Not Bundled' %}{% endif %}
33+
</span>
5534
</td>
5635
</tr>
36+
{% for imported_tx in group.list %}
37+
<tr id="staged-tx-{{ imported_tx.uuid }}" class="{% if imported_tx.is_children %}is-dark{% endif %}">
38+
<td>{{ imported_tx.date_posted }}</td>
39+
<td>
40+
{% if imported_tx.is_children %}
41+
<span class="tag is-primary is-light">{% trans 'Child' %}</span>
42+
{% else %}
43+
<span class="tag is-dark is-light">{% trans 'Parent' %}</span>
44+
{% endif %}
45+
{{ imported_tx.name }}
46+
</td>
47+
<td class="{% if imported_tx.get_amount < 0.00 %}has-text-danger{% endif %} has-text-centered">
48+
{% currency_symbol %}{{ imported_tx.get_amount }}</td>
49+
<td>{% if imported_tx.activity %}
50+
{{ imported_tx.get_activity_display }}
51+
{% endif %}</td>
52+
<td>{% if imported_tx.entity_unit %}{{ imported_tx.entity_unit }}{% endif %}</td>
53+
<td>{{ imported_tx.account_model }}</td>
54+
<td>{{ imported_tx.transaction_model }}</td>
55+
<td class="has-text-centered">
56+
<div class="dropdown is-hoverable is-right">
57+
<div class="dropdown-trigger">
58+
<button class="button is-small" aria-haspopup="true"
59+
aria-controls="actions-{{ imported_tx.uuid }}">
60+
<span>{% trans 'Actions' %}</span>
61+
<span class="icon is-small">{% icon 'mdi:chevron-down' 14 %}</span>
62+
</button>
63+
</div>
64+
<div class="dropdown-menu" id="actions-{{ imported_tx.uuid }}" role="menu">
65+
<div class="dropdown-content">
66+
<a href="{% url 'django_ledger:je-detail' entity_slug=import_job_model.entity_slug ledger_pk=imported_tx.transaction_model.journal_entry.ledger_id je_pk=imported_tx.transaction_model.journal_entry_id %}"
67+
class="dropdown-item">{% trans 'View JE' %}</a>
68+
{% if imported_tx.has_receipt %}
69+
<a href="{% url 'django_ledger:receipt-detail' entity_slug=import_job_model.entity_slug receipt_pk=imported_tx.receiptmodel.uuid %}"
70+
class="dropdown-item">{% trans 'View Receipt' %}</a>
71+
{% endif %}
72+
<hr class="dropdown-divider">
73+
{% if imported_tx.can_undo_import %}
74+
<form method="post"
75+
action="{% url 'django_ledger:data-import-staged-tx-undo' entity_slug=import_job_model.entity_slug job_pk=import_job_model.uuid staged_tx_pk=imported_tx.uuid %}">
76+
{% csrf_token %}
77+
<button type="submit"
78+
class="dropdown-item has-text-danger">{% trans 'Undo Import' %}</button>
79+
</form>
80+
{% endif %}
81+
</div>
82+
</div>
83+
</div>
84+
</td>
85+
</tr>
86+
{% endfor %}
5787
{% endfor %}
5888
</tbody>
5989
</table>

django_ledger/templates/django_ledger/data_import/tags/data_import_job_txs_table.html

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
<form method="post">
66

77
{{ staged_txs_formset.non_form_errors }}
8-
{# {% if staged_txs_formset.errors %}#}
9-
{# {{ staged_txs_formset.errors }}#}
10-
{# {% endif %}#}
8+
{% if staged_txs_formset.errors %}
9+
{{ staged_txs_formset.errors }}
10+
{% endif %}
1111
{{ staged_txs_formset.management_form }}
1212

1313
{% csrf_token %}
@@ -35,7 +35,8 @@
3535
<tbody>
3636

3737
{% for txf in staged_txs_formset %}
38-
<tr id="staged-tx-{{ txf.instance.uuid }}" class="{% if txf.instance.is_children %}has-background-primary{% elif txf.instance.has_children %}has-background-primary-light{% endif %}">
38+
<tr id="staged-tx-{{ txf.instance.uuid }}"
39+
class="{% if txf.instance.is_children %}has-background-primary{% elif txf.instance.has_children %}has-background-primary-light{% endif %}">
3940
<td>{{ forloop.counter }}</td>
4041
{% for hidden_field in txf.hidden_fields %}{{ hidden_field }}{% endfor %}
4142
{% if txf.instance.is_children %}
@@ -61,7 +62,7 @@
6162
<span class="has-text-weight-bold">
6263
{{ txf.instance.get_prospect_je_activity_display }}
6364
</span>
64-
{% elif not txf.instance.is_children %}
65+
{% elif not txf.instance.is_children and txf.instance.bundle_split %}
6566
<span class="has-text-danger">{% icon 'ooui:block' 16 %}</span>
6667
<span>Invalid or Pending Account Mapping.</span>
6768
{% endif %}
@@ -75,11 +76,10 @@
7576
<td>{{ txf.unit_model }}</td>
7677
<td>{{ txf.receipt_type }}</td>
7778
<td class="has-text-centered">
78-
{% if txf.SHOW_CUSTOMER_FIELD %}
79-
{{ txf.customer_model }}
80-
{% elif txf.SHOW_VENDOR_FIELD %}
81-
{{ txf.vendor_model }}
82-
{% endif %}
79+
{{ txf.customer_model }}
80+
{{ txf.vendor_model }}
81+
{# {{ txf.instance.can_migrate }}#}
82+
{# {{ txf.instance.ready_to_import }}#}
8383
</td>
8484
<td class="has-text-centered">{{ txf.tx_import }}</td>
8585
<td class="has-text-centered">{{ txf.bundle_split }}</td>

django_ledger/urls/data_import.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
path('<slug:entity_slug>/jobs/<uuid:job_pk>/txs/',
1919
views.DataImportJobDetailView.as_view(),
2020
name='data-import-job-txs'),
21+
path('<slug:entity_slug>/jobs/<uuid:job_pk>/reset/',
22+
views.ImportJobModelResetView.as_view(),
23+
name='data-import-job-txs-undo'),
2124
path('<slug:entity_slug>/jobs/<uuid:job_pk>/txs/<uuid:staged_tx_pk>/undo/',
2225
views.StagedTransactionUndoView.as_view(),
2326
name='data-import-staged-tx-undo'),

django_ledger/views/data_import.py

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,7 @@ class ImportJobModelCreateView(ImportJobModelViewBaseView, FormView):
5555
form_class = ImportJobModelCreateForm
5656

5757
def get_form(self, form_class=None, **kwargs):
58-
return self.form_class(
59-
entity_model=self.get_authorized_entity_instance(), **self.get_form_kwargs()
60-
)
58+
return self.form_class(entity_model=self.get_authorized_entity_instance(), **self.get_form_kwargs())
6159

6260
def get_success_url(self):
6361
return reverse(
@@ -74,9 +72,7 @@ def form_valid(self, form):
7472
txs_to_stage = ofx_manager.get_account_txs()
7573
staged_txs_model_list = [
7674
StagedTransactionModel(
77-
date_posted=make_aware(
78-
value=datetime.combine(date=tx.dtposted.date(), time=time.min)
79-
),
75+
date_posted=make_aware(value=datetime.combine(date=tx.dtposted.date(), time=time.min)),
8076
fit_id=tx.fitid,
8177
amount=tx.trnamt,
8278
import_job=import_job,
@@ -166,21 +162,19 @@ def get_form_kwargs(self):
166162
'import_job_model': self.get_object(),
167163
}
168164

169-
def get_context_data(
170-
self, txs_formset: Optional[StagedTransactionModelFormSet] = None, **kwargs
171-
):
165+
def get_context_data(self, txs_formset: Optional[StagedTransactionModelFormSet] = None, **kwargs):
172166
context = super().get_context_data(**kwargs)
173-
job_model: ImportJobModel = self.object
167+
import_job_model: ImportJobModel = self.object
174168

175-
context['page_title'] = job_model.description
169+
context['page_title'] = import_job_model.description
176170
context['header_title'] = self.PAGE_TITLE
177-
context['header_subtitle'] = job_model.description
171+
context['header_subtitle'] = import_job_model.description
178172
context['header_subtitle_icon'] = 'tabler:table-import'
179173

180174
staged_txs_formset = (
181175
StagedTransactionModelFormSet(
182176
entity_model=self.get_authorized_entity_instance(),
183-
import_job_model=job_model,
177+
import_job_model=import_job_model,
184178
)
185179
if not txs_formset
186180
else txs_formset
@@ -195,19 +189,27 @@ def post(self, request, **kwargs):
195189
import_job_model: ImportJobModel = self.object
196190

197191
txs_formset = StagedTransactionModelFormSet(
198-
entity_model=self.get_authorized_entity_instance(),
192+
entity_model=self.AUTHORIZED_ENTITY_MODEL,
199193
import_job_model=import_job_model,
200194
data=request.POST,
201195
)
202196

203197
if txs_formset.has_changed():
198+
for tx_form in txs_formset:
199+
if any(
200+
[
201+
'account_model' in tx_form.changed_data,
202+
'tx_split' in tx_form.changed_data,
203+
]
204+
):
205+
staged_transaction_model: StagedTransactionModel = tx_form.instance
206+
staged_transaction_model.activity = None
207+
204208
if txs_formset.is_valid():
205209
txs_formset.save()
206210
for tx_form in txs_formset:
207211
if tx_form.has_changed():
208-
staged_transaction_model: StagedTransactionModel = (
209-
tx_form.instance
210-
)
212+
staged_transaction_model: StagedTransactionModel = tx_form.instance
211213
is_split = tx_form.cleaned_data['tx_split'] is True
212214
if is_split:
213215
staged_transaction_model.add_split()
@@ -221,9 +223,7 @@ def post(self, request, **kwargs):
221223
receipt_date=staged_transaction_model.date_posted
222224
)
223225
else:
224-
staged_transaction_model.migrate_transactions(
225-
split_txs=not is_bundled
226-
)
226+
staged_transaction_model.migrate_transactions(split_txs=not is_bundled)
227227
else:
228228
context = self.get_context_data(txs_formset=txs_formset, **kwargs)
229229
return self.render_to_response(context)
@@ -236,7 +236,7 @@ def post(self, request, **kwargs):
236236
)
237237

238238
context = self.get_context_data(**kwargs)
239-
return self.render_to_response(context)
239+
return self.render_to_response(context=context)
240240

241241

242242
class StagedTransactionUndoView(ImportJobModelViewBaseView, View):
@@ -268,3 +268,19 @@ def post(self, request, entity_slug, job_pk, staged_tx_pk, *args, **kwargs):
268268
kwargs={'entity_slug': entity_slug, 'job_pk': job_pk},
269269
)
270270
)
271+
272+
273+
class ImportJobModelResetView(ImportJobModelViewBaseView, DetailView):
274+
pk_url_kwarg = 'job_pk'
275+
http_method_names = ['post']
276+
277+
def post(self, request, **kwargs):
278+
import_job_model: ImportJobModel = self.get_object()
279+
imported_staged_txs = import_job_model.stagedtransactionmodel_set.is_imported()
280+
for staged_tx in imported_staged_txs:
281+
staged_tx.undo_import(raise_exception=False)
282+
283+
return redirect(
284+
to=import_job_model.get_data_import_url(),
285+
permanent=False,
286+
)

0 commit comments

Comments
 (0)