django-ledger 0.7.4__py3-none-any.whl → 0.7.5__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/contrib/django_ledger_graphene/bank_account/schema.py +1 -1
- django_ledger/forms/bank_account.py +16 -12
- django_ledger/forms/data_import.py +70 -33
- django_ledger/io/io_core.py +945 -127
- django_ledger/io/io_generator.py +7 -3
- django_ledger/io/ofx.py +37 -16
- django_ledger/migrations/0020_remove_bankaccountmodel_django_ledg_cash_ac_59a8af_idx_and_more.py +44 -0
- django_ledger/migrations/0021_alter_bankaccountmodel_account_model_and_more.py +33 -0
- django_ledger/models/bank_account.py +14 -11
- django_ledger/models/customer.py +3 -13
- django_ledger/models/data_import.py +690 -35
- django_ledger/models/entity.py +39 -24
- django_ledger/models/journal_entry.py +18 -8
- django_ledger/models/mixins.py +17 -3
- django_ledger/models/vendor.py +2 -2
- django_ledger/settings.py +18 -22
- django_ledger/templates/django_ledger/bank_account/tags/bank_accounts_table.html +2 -2
- django_ledger/templates/django_ledger/data_import/data_import_job_txs.html +1 -1
- django_ledger/templates/django_ledger/data_import/import_job_create.html +11 -2
- django_ledger/templates/django_ledger/data_import/tags/data_import_job_txs_imported.html +1 -1
- django_ledger/templates/django_ledger/data_import/tags/data_import_job_txs_table.html +5 -2
- django_ledger/templatetags/django_ledger.py +12 -12
- django_ledger/views/bank_account.py +1 -1
- django_ledger/views/data_import.py +60 -134
- {django_ledger-0.7.4.dist-info → django_ledger-0.7.5.dist-info}/METADATA +17 -17
- {django_ledger-0.7.4.dist-info → django_ledger-0.7.5.dist-info}/RECORD +31 -29
- {django_ledger-0.7.4.dist-info → django_ledger-0.7.5.dist-info}/WHEEL +1 -1
- {django_ledger-0.7.4.dist-info → django_ledger-0.7.5.dist-info}/top_level.txt +1 -0
- {django_ledger-0.7.4.dist-info → django_ledger-0.7.5.dist-info}/AUTHORS.md +0 -0
- {django_ledger-0.7.4.dist-info → django_ledger-0.7.5.dist-info}/LICENSE +0 -0
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
|
|
3
3
|
Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
|
|
4
|
+
|
|
5
|
+
This module provides core functionality for handling data import jobs and staged transactions in the `Django Ledger`
|
|
6
|
+
application. It introduces two primary models to facilitate the import and processing of transactions:
|
|
7
|
+
|
|
8
|
+
1. `ImportJobModel` - Represents jobs that handle financial data import tasks.
|
|
9
|
+
2. `StagedTransactionModel` - Represents individual transactions, including those that are staged for review, mapping,
|
|
10
|
+
or further processing.
|
|
11
|
+
|
|
4
12
|
"""
|
|
5
13
|
|
|
6
14
|
from decimal import Decimal
|
|
7
15
|
from typing import Optional, Set, Dict, List
|
|
8
|
-
from uuid import uuid4
|
|
16
|
+
from uuid import uuid4, UUID
|
|
9
17
|
|
|
10
18
|
from django.core.exceptions import ValidationError
|
|
11
19
|
from django.db import models
|
|
@@ -29,16 +37,51 @@ class ImportJobModelQuerySet(QuerySet):
|
|
|
29
37
|
|
|
30
38
|
|
|
31
39
|
class ImportJobModelManager(Manager):
|
|
40
|
+
"""
|
|
41
|
+
Manages queryset operations related to import jobs.
|
|
42
|
+
|
|
43
|
+
This manager provides custom queryset handling for import job models, including
|
|
44
|
+
annotations for custom fields like transaction counts, user-specific filters,
|
|
45
|
+
and entity-specific filters. It is integrated with the ImportJobModel, designed
|
|
46
|
+
to support complex query requirements with field annotations and related object
|
|
47
|
+
optimizations for performance efficiency.
|
|
48
|
+
|
|
49
|
+
"""
|
|
32
50
|
|
|
33
51
|
def get_queryset(self):
|
|
34
|
-
|
|
52
|
+
"""
|
|
53
|
+
Generates a QuerySet with annotated data for ImportJobModel.
|
|
54
|
+
|
|
55
|
+
This method constructs a custom QuerySet for ImportJobModel with multiple
|
|
56
|
+
annotations and related fields. It includes counts for specific transaction
|
|
57
|
+
states, calculates pending transactions, and checks for completion status
|
|
58
|
+
of the import job. The QuerySet uses annotations and filters to derive
|
|
59
|
+
various properties required for processing.
|
|
60
|
+
|
|
61
|
+
Returns
|
|
62
|
+
-------
|
|
63
|
+
QuerySet
|
|
64
|
+
A QuerySet with additional annotations:
|
|
65
|
+
- _entity_uuid : UUID of the entity associated with the ledger model.
|
|
66
|
+
- _entity_slug : Slug of the entity associated with the ledger model.
|
|
67
|
+
- txs_count : Integer count of non-root transactions.
|
|
68
|
+
- txs_mapped_count : Integer count of mapped transactions based on specific
|
|
69
|
+
conditions.
|
|
70
|
+
- txs_pending : Integer count of pending transactions, calculated as
|
|
71
|
+
txs_count - txs_mapped_count.
|
|
72
|
+
- is_complete : Boolean value indicating if the import job is complete
|
|
73
|
+
(no pending transactions or total count is zero).
|
|
74
|
+
"""
|
|
75
|
+
qs = ImportJobModelQuerySet(self.model, using=self._db)
|
|
35
76
|
return qs.annotate(
|
|
77
|
+
_entity_uuid=F('ledger_model__entity__uuid'),
|
|
78
|
+
_entity_slug=F('ledger_model__entity__slug'),
|
|
36
79
|
txs_count=Count('stagedtransactionmodel',
|
|
37
80
|
filter=Q(stagedtransactionmodel__parent__isnull=False)),
|
|
38
81
|
txs_mapped_count=Count(
|
|
39
82
|
'stagedtransactionmodel__account_model_id',
|
|
40
|
-
filter=Q(stagedtransactionmodel__parent__isnull=False) |
|
|
41
|
-
|
|
83
|
+
filter=Q(stagedtransactionmodel__parent__isnull=False) |
|
|
84
|
+
Q(stagedtransactionmodel__parent__parent__isnull=False)
|
|
42
85
|
|
|
43
86
|
),
|
|
44
87
|
).annotate(
|
|
@@ -52,11 +95,30 @@ class ImportJobModelManager(Manager):
|
|
|
52
95
|
),
|
|
53
96
|
).select_related(
|
|
54
97
|
'bank_account_model',
|
|
55
|
-
'
|
|
98
|
+
'bank_account_model__account_model',
|
|
56
99
|
'ledger_model'
|
|
57
100
|
)
|
|
58
101
|
|
|
59
102
|
def for_user(self, user_model):
|
|
103
|
+
"""
|
|
104
|
+
Filters the queryset based on the user's permissions for accessing the data
|
|
105
|
+
related to bank accounts and entities they manage or administer.
|
|
106
|
+
|
|
107
|
+
This method first retrieves the default queryset. If the user is a superuser,
|
|
108
|
+
the query will return the full queryset without any filters. Otherwise, the
|
|
109
|
+
query will be limited to the entities that the user either administers or is
|
|
110
|
+
listed as a manager for.
|
|
111
|
+
|
|
112
|
+
Parameters
|
|
113
|
+
----------
|
|
114
|
+
user_model : User
|
|
115
|
+
The user model instance whose permissions determine the filtering of the queryset.
|
|
116
|
+
|
|
117
|
+
Returns
|
|
118
|
+
-------
|
|
119
|
+
QuerySet
|
|
120
|
+
A filtered queryset based on the user's role and associated permissions.
|
|
121
|
+
"""
|
|
60
122
|
qs = self.get_queryset()
|
|
61
123
|
if user_model.is_superuser:
|
|
62
124
|
return qs
|
|
@@ -74,10 +136,33 @@ class ImportJobModelManager(Manager):
|
|
|
74
136
|
|
|
75
137
|
|
|
76
138
|
class ImportJobModelAbstract(CreateUpdateMixIn):
|
|
139
|
+
"""
|
|
140
|
+
Abstract model for managing import jobs within a financial system.
|
|
141
|
+
|
|
142
|
+
This abstract model serves as a foundational base for managing import jobs involving
|
|
143
|
+
bank accounts and ledger models. It provides functionalities such as linking to an
|
|
144
|
+
associated bank account and ledger model, determining completion status of the
|
|
145
|
+
import job, and properties for UUID and slug identifiers. Additionally, helper
|
|
146
|
+
methods are provided for configuration and deletion confirmation.
|
|
147
|
+
|
|
148
|
+
Attributes
|
|
149
|
+
----------
|
|
150
|
+
uuid : UUID
|
|
151
|
+
Unique identifier for the import job instance.
|
|
152
|
+
description : str
|
|
153
|
+
Descriptive label or description for the import job.
|
|
154
|
+
bank_account_model : BankAccountModel
|
|
155
|
+
Foreign key linking the import job to a bank account model.
|
|
156
|
+
ledger_model : LedgerModel or None
|
|
157
|
+
One-to-one field linking the import job to a ledger model. Can be null or blank.
|
|
158
|
+
completed : bool
|
|
159
|
+
Indicates whether the import job has been completed.
|
|
160
|
+
objects : ImportJobModelManager
|
|
161
|
+
Manager for handling query operations and model lifecycle.
|
|
162
|
+
"""
|
|
77
163
|
uuid = models.UUIDField(default=uuid4, editable=False, primary_key=True)
|
|
78
164
|
description = models.CharField(max_length=200, verbose_name=_('Description'))
|
|
79
165
|
bank_account_model = models.ForeignKey('django_ledger.BankAccountModel',
|
|
80
|
-
editable=False,
|
|
81
166
|
on_delete=models.CASCADE,
|
|
82
167
|
verbose_name=_('Associated Bank Account Model'))
|
|
83
168
|
ledger_model = models.OneToOneField('django_ledger.LedgerModel',
|
|
@@ -87,7 +172,7 @@ class ImportJobModelAbstract(CreateUpdateMixIn):
|
|
|
87
172
|
null=True,
|
|
88
173
|
blank=True)
|
|
89
174
|
completed = models.BooleanField(default=False, verbose_name=_('Import Job Completed'))
|
|
90
|
-
objects = ImportJobModelManager
|
|
175
|
+
objects = ImportJobModelManager()
|
|
91
176
|
|
|
92
177
|
class Meta:
|
|
93
178
|
abstract = True
|
|
@@ -98,53 +183,207 @@ class ImportJobModelAbstract(CreateUpdateMixIn):
|
|
|
98
183
|
models.Index(fields=['completed']),
|
|
99
184
|
]
|
|
100
185
|
|
|
186
|
+
@property
|
|
187
|
+
def entity_uuid(self) -> UUID:
|
|
188
|
+
"""
|
|
189
|
+
Get the UUID of the entity associated with the ledger model.
|
|
190
|
+
|
|
191
|
+
This property retrieves the UUID of the entity. If the `_entity_uuid`
|
|
192
|
+
attribute exists, it is returned. Otherwise, the UUID is fetched
|
|
193
|
+
from the `entity_model_id` attribute of the `ledger_model` instance.
|
|
194
|
+
|
|
195
|
+
Returns
|
|
196
|
+
-------
|
|
197
|
+
str
|
|
198
|
+
The UUID of the entity as a string.
|
|
199
|
+
"""
|
|
200
|
+
try:
|
|
201
|
+
return getattr(self, '_entity_uuid')
|
|
202
|
+
except AttributeError:
|
|
203
|
+
pass
|
|
204
|
+
return self.ledger_model.entity_model_id
|
|
205
|
+
|
|
206
|
+
@property
|
|
207
|
+
def entity_slug(self) -> str:
|
|
208
|
+
"""
|
|
209
|
+
Returns the slug identifier for the entity associated with the current instance.
|
|
210
|
+
|
|
211
|
+
The entity slug is a unique string that represents the associated entity in a
|
|
212
|
+
human-readable format. If the `_entity_slug` property is explicitly set, it is
|
|
213
|
+
returned. Otherwise, the slug associated with the `entity_model` within the
|
|
214
|
+
`ledger_model` is used as the default value.
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
Returns
|
|
218
|
+
-------
|
|
219
|
+
str
|
|
220
|
+
The slug identifier related to the entity.
|
|
221
|
+
"""
|
|
222
|
+
try:
|
|
223
|
+
return getattr(self, '_entity_slug')
|
|
224
|
+
except AttributeError:
|
|
225
|
+
pass
|
|
226
|
+
return self.ledger_model.entity_model.slug
|
|
227
|
+
|
|
101
228
|
def is_configured(self):
|
|
229
|
+
"""
|
|
230
|
+
Checks if the configuration for the instance is complete.
|
|
231
|
+
|
|
232
|
+
This method verifies whether the necessary attributes for ledger model ID
|
|
233
|
+
and bank account model ID are set. Only when both attributes are
|
|
234
|
+
non-None, the configuration is considered complete.
|
|
235
|
+
|
|
236
|
+
Returns
|
|
237
|
+
-------
|
|
238
|
+
bool
|
|
239
|
+
True if both `ledger_model_id` and `bank_account_model_id` attributes
|
|
240
|
+
are set (not None), otherwise False.
|
|
241
|
+
"""
|
|
102
242
|
return all([
|
|
103
243
|
self.ledger_model_id is not None,
|
|
104
244
|
self.bank_account_model_id is not None
|
|
105
245
|
])
|
|
106
246
|
|
|
107
247
|
def configure(self, commit: bool = True):
|
|
248
|
+
"""
|
|
249
|
+
Configures the ledger model if not already configured and optionally commits the changes.
|
|
250
|
+
|
|
251
|
+
This method checks if the ledger model is configured, and if not, it creates a new ledger
|
|
252
|
+
based on the associated bank account model's entity model. Additionally, it can commit
|
|
253
|
+
the changes to update the database based on the given parameter.
|
|
254
|
+
|
|
255
|
+
Parameters
|
|
256
|
+
----------
|
|
257
|
+
commit : bool, optional
|
|
258
|
+
Determines whether to persist the changes to the database. Defaults to `True`.
|
|
259
|
+
"""
|
|
108
260
|
if not self.is_configured():
|
|
109
261
|
if self.ledger_model_id is None:
|
|
110
262
|
self.ledger_model = self.bank_account_model.entity_model.create_ledger(
|
|
111
263
|
name=self.description
|
|
112
264
|
)
|
|
113
265
|
if commit:
|
|
114
|
-
self.save(
|
|
115
|
-
|
|
116
|
-
|
|
266
|
+
self.save(
|
|
267
|
+
update_fields=[
|
|
268
|
+
'ledger_model'
|
|
269
|
+
])
|
|
117
270
|
|
|
118
271
|
def get_delete_message(self) -> str:
|
|
119
272
|
return _(f'Are you sure you want to delete Import Job {self.description}?')
|
|
120
273
|
|
|
121
274
|
|
|
122
275
|
class StagedTransactionModelQuerySet(QuerySet):
|
|
276
|
+
"""
|
|
277
|
+
Represents a custom QuerySet for handling staged transaction models.
|
|
278
|
+
|
|
279
|
+
This class extends the standard Django QuerySet to add custom filtering methods
|
|
280
|
+
for staged transaction models. These methods help in querying the data based
|
|
281
|
+
on certain conditions specific to the staged transaction model's state or
|
|
282
|
+
relationships.
|
|
283
|
+
"""
|
|
123
284
|
|
|
124
285
|
def is_pending(self):
|
|
286
|
+
"""
|
|
287
|
+
Determines if there are any pending transactions.
|
|
288
|
+
|
|
289
|
+
This method filters the objects in the queryset to determine whether there
|
|
290
|
+
are any transactions that are pending (i.e., have a null transaction_model).
|
|
291
|
+
Pending transactions are identified by checking if the `transaction_model` is
|
|
292
|
+
null for any of the objects in the queryset.
|
|
293
|
+
|
|
294
|
+
Returns
|
|
295
|
+
-------
|
|
296
|
+
QuerySet
|
|
297
|
+
A QuerySet containing objects with a null `transaction_model`.
|
|
298
|
+
|
|
299
|
+
"""
|
|
125
300
|
return self.filter(transaction_model__isnull=True)
|
|
126
301
|
|
|
127
302
|
def is_imported(self):
|
|
303
|
+
"""
|
|
304
|
+
Filter method to determine if the objects in a queryset have been linked with a
|
|
305
|
+
related transaction model. This function checks whether the `transaction_model`
|
|
306
|
+
field in the related objects is non-null.
|
|
307
|
+
|
|
308
|
+
Returns
|
|
309
|
+
-------
|
|
310
|
+
QuerySet
|
|
311
|
+
A filtered queryset containing only objects where the `transaction_model`
|
|
312
|
+
is not null.
|
|
313
|
+
"""
|
|
128
314
|
return self.filter(transaction_model__isnull=False)
|
|
129
315
|
|
|
130
316
|
def is_parent(self):
|
|
317
|
+
"""
|
|
318
|
+
Determines whether the current queryset refers to parent objects based on a
|
|
319
|
+
null check for the `parent_id` field.
|
|
320
|
+
|
|
321
|
+
This method applies a filter to the queryset and restricts it to objects
|
|
322
|
+
where the `parent_id` is null. It is often used in hierarchical or
|
|
323
|
+
parent-child data structures to fetch only parent items in the structure.
|
|
324
|
+
|
|
325
|
+
Returns
|
|
326
|
+
-------
|
|
327
|
+
QuerySet
|
|
328
|
+
A filtered queryset containing only the objects with `parent_id` set
|
|
329
|
+
to null. The type of the queryset depends on the model class used
|
|
330
|
+
when invoking this method.
|
|
331
|
+
"""
|
|
131
332
|
return self.filter(parent_id__isnull=True)
|
|
132
333
|
|
|
133
334
|
def is_ready_to_import(self):
|
|
335
|
+
"""
|
|
336
|
+
Checks whether items are ready to be imported by applying a filter.
|
|
337
|
+
|
|
338
|
+
This function filters elements based on the `ready_to_import` attribute.
|
|
339
|
+
It is typically used to identify and retrieve items marked as ready for
|
|
340
|
+
further processing or importing.
|
|
341
|
+
|
|
342
|
+
Returns
|
|
343
|
+
-------
|
|
344
|
+
QuerySet
|
|
345
|
+
A QuerySet of elements that satisfy the `ready_to_import` condition.
|
|
346
|
+
"""
|
|
134
347
|
return self.filter(ready_to_import=True)
|
|
135
348
|
|
|
136
349
|
|
|
137
350
|
class StagedTransactionModelManager(Manager):
|
|
138
351
|
|
|
139
352
|
def get_queryset(self):
|
|
140
|
-
|
|
353
|
+
"""
|
|
354
|
+
Fetch and annotate the queryset for staged transaction models to include additional
|
|
355
|
+
related fields and calculated annotations for further processing and sorting.
|
|
356
|
+
|
|
357
|
+
The method constructs a queryset with various related fields selected and annotated
|
|
358
|
+
for convenience. It includes fields for related account models, units, transactions,
|
|
359
|
+
journal entries, and import jobs. Annotations are added to calculate properties such
|
|
360
|
+
as the number of child transactions, the total amount split, and whether the transaction
|
|
361
|
+
is ready to import or can be split into journal entries.
|
|
362
|
+
|
|
363
|
+
Returns
|
|
364
|
+
-------
|
|
365
|
+
QuerySet
|
|
366
|
+
A Django QuerySet preconfigured with selected related fields and annotations
|
|
367
|
+
for staged transaction models.
|
|
368
|
+
"""
|
|
369
|
+
qs = StagedTransactionModelQuerySet(self.model, using=self._db)
|
|
141
370
|
return qs.select_related(
|
|
142
|
-
'parent',
|
|
143
371
|
'account_model',
|
|
144
372
|
'unit_model',
|
|
145
373
|
'transaction_model',
|
|
146
374
|
'transaction_model__journal_entry',
|
|
147
|
-
'transaction_model__account'
|
|
375
|
+
'transaction_model__account',
|
|
376
|
+
|
|
377
|
+
'import_job',
|
|
378
|
+
'import_job__bank_account_model__account_model',
|
|
379
|
+
|
|
380
|
+
# selecting parent data....
|
|
381
|
+
'parent',
|
|
382
|
+
'parent__account_model',
|
|
383
|
+
'parent__unit_model',
|
|
384
|
+
).annotate(
|
|
385
|
+
entity_slug=F('import_job__bank_account_model__entity_model__slug'),
|
|
386
|
+
entity_unit=F('transaction_model__journal_entry__entity_unit__name'),
|
|
148
387
|
children_count=Count('split_transaction_set'),
|
|
149
388
|
children_mapped_count=Count('split_transaction_set__account_model_id'),
|
|
150
389
|
total_amount_split=Coalesce(
|
|
@@ -156,7 +395,6 @@ class StagedTransactionModelManager(Manager):
|
|
|
156
395
|
When(parent_id__isnull=False, then=F('parent_id'))
|
|
157
396
|
),
|
|
158
397
|
).annotate(
|
|
159
|
-
entity_unit=F('transaction_model__journal_entry__entity_unit__name'),
|
|
160
398
|
ready_to_import=Case(
|
|
161
399
|
# is mapped singleton...
|
|
162
400
|
When(
|
|
@@ -202,19 +440,51 @@ class StagedTransactionModelManager(Manager):
|
|
|
202
440
|
'-children_count'
|
|
203
441
|
)
|
|
204
442
|
|
|
205
|
-
def for_job(self, entity_slug: str, user_model, job_pk):
|
|
206
|
-
qs = self.get_queryset()
|
|
207
|
-
return qs.filter(
|
|
208
|
-
Q(import_job__bank_account_model__entity__slug__exact=entity_slug) &
|
|
209
|
-
(
|
|
210
|
-
Q(import_job__bank_account_model__entity__admin=user_model) |
|
|
211
|
-
Q(import_job__bank_account_model__entity__managers__in=[user_model])
|
|
212
|
-
) &
|
|
213
|
-
Q(import_job__uuid__exact=job_pk)
|
|
214
|
-
).prefetch_related('split_transaction_set')
|
|
215
|
-
|
|
216
443
|
|
|
217
444
|
class StagedTransactionModelAbstract(CreateUpdateMixIn):
|
|
445
|
+
"""
|
|
446
|
+
Represents an abstract model for staged transactions in a financial application.
|
|
447
|
+
|
|
448
|
+
This abstract class is designed to handle and manage staged transactions that may be
|
|
449
|
+
split into multiple child transactions for financial processing purposes. It includes
|
|
450
|
+
various attributes and methods to validate, process, and structure financial data for
|
|
451
|
+
import and transaction management. The model supports hierarchical relationships,
|
|
452
|
+
role mapping, unit handling, and other important functionalities required for staged
|
|
453
|
+
transactions.
|
|
454
|
+
|
|
455
|
+
Attributes
|
|
456
|
+
----------
|
|
457
|
+
uuid : UUIDField
|
|
458
|
+
The unique identifier for the transaction.
|
|
459
|
+
parent : ForeignKey
|
|
460
|
+
Reference to the parent transaction if this is a child split transaction.
|
|
461
|
+
import_job : ForeignKey
|
|
462
|
+
Reference to the related import job that this staged transaction is part of.
|
|
463
|
+
fit_id : CharField
|
|
464
|
+
Identifier related to the financial institution transaction (FIT ID).
|
|
465
|
+
date_posted : DateField
|
|
466
|
+
The date on which the transaction was posted.
|
|
467
|
+
bundle_split : BooleanField
|
|
468
|
+
Indicates whether the transaction's split children are bundled into one record.
|
|
469
|
+
activity : CharField
|
|
470
|
+
Proposed activity for the staged transaction (e.g., spending, income categorization).
|
|
471
|
+
amount : DecimalField
|
|
472
|
+
The transaction amount, representing the value of the main transaction.
|
|
473
|
+
amount_split : DecimalField
|
|
474
|
+
The split amount for children when the transaction is split.
|
|
475
|
+
name : CharField
|
|
476
|
+
The name or title for the transaction description.
|
|
477
|
+
memo : CharField
|
|
478
|
+
Additional information or notes attached to the transaction.
|
|
479
|
+
account_model : ForeignKey
|
|
480
|
+
The related account model this transaction is associated with.
|
|
481
|
+
unit_model : ForeignKey
|
|
482
|
+
The unit model or entity associated with this transaction for accounting purposes.
|
|
483
|
+
transaction_model : OneToOneField
|
|
484
|
+
The actual transaction model associated with this staged transaction post-import.
|
|
485
|
+
objects : Manager
|
|
486
|
+
Custom manager for handling queries related to `StagedTransactionModel`.
|
|
487
|
+
"""
|
|
218
488
|
uuid = models.UUIDField(default=uuid4, editable=False, primary_key=True)
|
|
219
489
|
parent = models.ForeignKey('self',
|
|
220
490
|
null=True,
|
|
@@ -257,7 +527,7 @@ class StagedTransactionModelAbstract(CreateUpdateMixIn):
|
|
|
257
527
|
null=True,
|
|
258
528
|
blank=True)
|
|
259
529
|
|
|
260
|
-
objects = StagedTransactionModelManager
|
|
530
|
+
objects = StagedTransactionModelManager()
|
|
261
531
|
|
|
262
532
|
class Meta:
|
|
263
533
|
abstract = True
|
|
@@ -277,9 +547,35 @@ class StagedTransactionModelAbstract(CreateUpdateMixIn):
|
|
|
277
547
|
return f'{self.__class__.__name__}: {self.get_amount()}'
|
|
278
548
|
|
|
279
549
|
def from_commit_dict(self, split_amount: Optional[Decimal] = None) -> List[Dict]:
|
|
550
|
+
"""
|
|
551
|
+
Converts a commit dictionary to a list of dictionaries containing
|
|
552
|
+
transactional data. The method processes the transaction's amount,
|
|
553
|
+
determines its type (DEBIT or CREDIT), and bundles relevant information
|
|
554
|
+
from the current object's attributes.
|
|
555
|
+
|
|
556
|
+
Parameters
|
|
557
|
+
----------
|
|
558
|
+
split_amount : Optional[Decimal], optional
|
|
559
|
+
A specific amount to override the transaction's default amount
|
|
560
|
+
(`self.amount`). If not provided, `self.amount` will be used.
|
|
561
|
+
|
|
562
|
+
Returns
|
|
563
|
+
-------
|
|
564
|
+
List[Dict]
|
|
565
|
+
A list containing a single dictionary with the following keys:
|
|
566
|
+
- 'account': The account associated with the transaction,
|
|
567
|
+
derived from `self.import_job.bank_account_model.account_model`.
|
|
568
|
+
- 'amount': The absolute value of the transaction amount.
|
|
569
|
+
- 'tx_type': The type of transaction, either DEBIT if the amount
|
|
570
|
+
is positive or CREDIT if negative.
|
|
571
|
+
- 'description': A descriptor for the transaction, taken from
|
|
572
|
+
`self.name`.
|
|
573
|
+
- 'staged_tx_model': A reference to the current object, representing
|
|
574
|
+
the staged transaction model.
|
|
575
|
+
"""
|
|
280
576
|
amt = split_amount if split_amount else self.amount
|
|
281
577
|
return [{
|
|
282
|
-
'account': self.import_job.bank_account_model.
|
|
578
|
+
'account': self.import_job.bank_account_model.account_model,
|
|
283
579
|
'amount': abs(amt),
|
|
284
580
|
'tx_type': DEBIT if not amt < 0.00 else CREDIT,
|
|
285
581
|
'description': self.name,
|
|
@@ -287,8 +583,27 @@ class StagedTransactionModelAbstract(CreateUpdateMixIn):
|
|
|
287
583
|
}]
|
|
288
584
|
|
|
289
585
|
def to_commit_dict(self) -> List[Dict]:
|
|
586
|
+
"""
|
|
587
|
+
Converts the current transaction or its children into a list of commit dictionaries.
|
|
588
|
+
|
|
589
|
+
Summarizes information about the transaction or its split children into
|
|
590
|
+
dictionaries containing essential details for committing the transaction.
|
|
591
|
+
Depending on whether the current transaction has child transactions, it processes
|
|
592
|
+
either the child transactions or itself to construct the dictionaries.
|
|
593
|
+
|
|
594
|
+
Returns
|
|
595
|
+
-------
|
|
596
|
+
List[Dict]
|
|
597
|
+
A list of dictionaries, each representing a transaction with fields such as
|
|
598
|
+
account, absolute amount, staged amount, unit model, transaction type, description,
|
|
599
|
+
and the corresponding staged transaction model.
|
|
600
|
+
"""
|
|
290
601
|
if self.has_children():
|
|
291
|
-
children_qs = self.split_transaction_set.all()
|
|
602
|
+
children_qs = self.split_transaction_set.all().prefetch_related(
|
|
603
|
+
'split_transaction_set',
|
|
604
|
+
'split_transaction_set__account_model',
|
|
605
|
+
'split_transaction_set__unit_model'
|
|
606
|
+
)
|
|
292
607
|
return [{
|
|
293
608
|
'account': child_txs_model.account_model,
|
|
294
609
|
'amount': abs(child_txs_model.amount_split),
|
|
@@ -309,53 +624,202 @@ class StagedTransactionModelAbstract(CreateUpdateMixIn):
|
|
|
309
624
|
}]
|
|
310
625
|
|
|
311
626
|
def commit_dict(self, split_txs: bool = False):
|
|
627
|
+
"""
|
|
628
|
+
Generates a list of commit dictionaries or splits commit dictionaries based
|
|
629
|
+
on staged amounts if specified.
|
|
630
|
+
|
|
631
|
+
Parameters
|
|
632
|
+
----------
|
|
633
|
+
split_txs : bool, optional
|
|
634
|
+
A flag indicating whether to split transactions by their staged amounts.
|
|
635
|
+
If True, the function will generate a split for each staged amount in
|
|
636
|
+
the commit dictionary. Defaults to False.
|
|
637
|
+
|
|
638
|
+
Returns
|
|
639
|
+
-------
|
|
640
|
+
list
|
|
641
|
+
A list representing the commit data. If `split_txs` is True, each entry
|
|
642
|
+
contains pairs of split commit dictionaries and their corresponding
|
|
643
|
+
data. Otherwise, it contains combined commit dictionary data.
|
|
644
|
+
"""
|
|
312
645
|
if split_txs:
|
|
313
646
|
to_commit = self.to_commit_dict()
|
|
314
647
|
return [
|
|
315
|
-
[
|
|
648
|
+
[
|
|
649
|
+
self.from_commit_dict(split_amount=to_split['amount_staged'])[0], to_split
|
|
650
|
+
] for to_split in
|
|
651
|
+
to_commit
|
|
316
652
|
]
|
|
317
653
|
return [self.from_commit_dict() + self.to_commit_dict()]
|
|
318
654
|
|
|
319
655
|
def get_amount(self) -> Decimal:
|
|
656
|
+
"""
|
|
657
|
+
Returns the appropriate amount based on the object's state.
|
|
658
|
+
|
|
659
|
+
This method determines the amount to be returned based on whether the object
|
|
660
|
+
is classified as a "children" or not. If the `is_children` method returns True,
|
|
661
|
+
it returns the value of `amount_split`. Otherwise, it returns the value of the
|
|
662
|
+
`amount` attribute.
|
|
663
|
+
|
|
664
|
+
Returns
|
|
665
|
+
-------
|
|
666
|
+
Decimal
|
|
667
|
+
The calculated amount based on the object's state.
|
|
668
|
+
"""
|
|
320
669
|
if self.is_children():
|
|
321
670
|
return self.amount_split
|
|
322
671
|
return self.amount
|
|
323
672
|
|
|
324
673
|
def is_imported(self) -> bool:
|
|
674
|
+
"""
|
|
675
|
+
Determines if the necessary models have been imported for the system to function
|
|
676
|
+
properly. This method checks whether both `account_model_id` and
|
|
677
|
+
`transaction_model_id` are set.
|
|
678
|
+
|
|
679
|
+
Returns
|
|
680
|
+
-------
|
|
681
|
+
bool
|
|
682
|
+
True if both `account_model_id` and `transaction_model_id` are not None,
|
|
683
|
+
indicating that the models have been successfully imported. False otherwise.
|
|
684
|
+
"""
|
|
325
685
|
return all([
|
|
326
686
|
self.account_model_id is not None,
|
|
327
687
|
self.transaction_model_id is not None,
|
|
328
688
|
])
|
|
329
689
|
|
|
330
690
|
def is_pending(self) -> bool:
|
|
691
|
+
"""
|
|
692
|
+
Determine if the transaction is pending.
|
|
693
|
+
|
|
694
|
+
A transaction is considered pending if it has not been assigned a
|
|
695
|
+
`transaction_model_id`. This function checks the attribute and returns
|
|
696
|
+
a boolean indicating the status.
|
|
697
|
+
|
|
698
|
+
Returns
|
|
699
|
+
-------
|
|
700
|
+
bool
|
|
701
|
+
True if the transaction is pending (i.e., `transaction_model_id`
|
|
702
|
+
is None), False otherwise.
|
|
703
|
+
"""
|
|
331
704
|
return self.transaction_model_id is None
|
|
332
705
|
|
|
333
706
|
def is_mapped(self) -> bool:
|
|
707
|
+
"""
|
|
708
|
+
Determines if an account model is mapped.
|
|
709
|
+
|
|
710
|
+
This method checks whether the `account_model_id` is assigned a value,
|
|
711
|
+
indicating that the account model has been mapped. It returns a boolean
|
|
712
|
+
result based on the presence of the `account_model_id`.
|
|
713
|
+
|
|
714
|
+
Returns
|
|
715
|
+
-------
|
|
716
|
+
bool
|
|
717
|
+
True if `account_model_id` is not None, indicating the account
|
|
718
|
+
model is mapped. False otherwise.
|
|
719
|
+
"""
|
|
334
720
|
return self.account_model_id is not None
|
|
335
721
|
|
|
336
722
|
def is_single(self) -> bool:
|
|
723
|
+
"""
|
|
724
|
+
Checks whether the current instance represents a single entry.
|
|
725
|
+
|
|
726
|
+
This method determines if the current object qualifies as a single entry
|
|
727
|
+
by ensuring that it both does not have children and is not considered a
|
|
728
|
+
child of any other entry. The result is a boolean value indicating
|
|
729
|
+
whether the entry meets these criteria.
|
|
730
|
+
|
|
731
|
+
Returns
|
|
732
|
+
-------
|
|
733
|
+
bool
|
|
734
|
+
True if the entry is a single, standalone entry; False otherwise.
|
|
735
|
+
"""
|
|
337
736
|
return all([
|
|
338
737
|
not self.is_children(),
|
|
339
738
|
not self.has_children()
|
|
340
739
|
])
|
|
341
740
|
|
|
342
741
|
def is_children(self) -> bool:
|
|
742
|
+
"""
|
|
743
|
+
Determines if the current instance qualifies as a child entity based on the existence of a parent ID.
|
|
744
|
+
|
|
745
|
+
Checks whether the current object is associated with a parent by verifying the presence of `parent_id`.
|
|
746
|
+
The method returns `True` if the `parent_id` attribute is not `None`, indicating that the object is indeed a child.
|
|
747
|
+
|
|
748
|
+
Returns
|
|
749
|
+
-------
|
|
750
|
+
bool
|
|
751
|
+
True if the object has a valid `parent_id`, indicating it is a child entity;
|
|
752
|
+
False otherwise.
|
|
753
|
+
"""
|
|
343
754
|
return all([
|
|
344
755
|
self.parent_id is not None,
|
|
345
756
|
])
|
|
346
757
|
|
|
347
758
|
def has_activity(self) -> bool:
|
|
759
|
+
"""
|
|
760
|
+
Determine if an activity is present.
|
|
761
|
+
|
|
762
|
+
This method checks whether the `activity` attribute is assigned a value
|
|
763
|
+
or not. If a value is set, it indicates that there is an associated
|
|
764
|
+
activity. Otherwise, no activity is present.
|
|
765
|
+
|
|
766
|
+
Returns
|
|
767
|
+
-------
|
|
768
|
+
bool
|
|
769
|
+
True if the `activity` attribute is not None, indicating the
|
|
770
|
+
presence of an activity. False otherwise.
|
|
771
|
+
"""
|
|
348
772
|
return self.activity is not None
|
|
349
773
|
|
|
350
774
|
def has_children(self) -> bool:
|
|
775
|
+
"""
|
|
776
|
+
Determines if the current instance has children.
|
|
777
|
+
|
|
778
|
+
The method checks the state of the instance to determine whether
|
|
779
|
+
it is in the process of adding. If so, it directly returns False,
|
|
780
|
+
signifying no children are present at that moment. Otherwise,
|
|
781
|
+
it evaluates the `children_count` attribute to decide.
|
|
782
|
+
|
|
783
|
+
Returns
|
|
784
|
+
-------
|
|
785
|
+
bool
|
|
786
|
+
True if the instance has children and is not in the process of
|
|
787
|
+
adding; otherwise, False.
|
|
788
|
+
"""
|
|
351
789
|
if self._state.adding:
|
|
352
790
|
return False
|
|
353
791
|
return getattr(self, 'children_count') > 0
|
|
354
792
|
|
|
355
793
|
def can_split(self) -> bool:
|
|
794
|
+
"""
|
|
795
|
+
Determines if the current object can be split based on its child status.
|
|
796
|
+
|
|
797
|
+
This method checks whether the object does not have any children and, as
|
|
798
|
+
a result, is capable of being split.
|
|
799
|
+
|
|
800
|
+
Returns
|
|
801
|
+
-------
|
|
802
|
+
bool
|
|
803
|
+
`True` if the object has no children and can be split, otherwise
|
|
804
|
+
`False`.
|
|
805
|
+
"""
|
|
356
806
|
return not self.is_children()
|
|
357
807
|
|
|
358
808
|
def can_have_unit(self) -> bool:
|
|
809
|
+
"""
|
|
810
|
+
Check if the entity can have a unit.
|
|
811
|
+
|
|
812
|
+
This method evaluates the conditions under which an entity may have a
|
|
813
|
+
unit assigned. It considers several factors including the state of
|
|
814
|
+
the entity, whether it has children, if all children are mapped, and
|
|
815
|
+
its relationship to its parent entity.
|
|
816
|
+
|
|
817
|
+
Returns
|
|
818
|
+
-------
|
|
819
|
+
bool
|
|
820
|
+
A boolean value indicating whether the entity can have a unit.
|
|
821
|
+
Returns `True` if the conditions are satisfied, otherwise `False`.
|
|
822
|
+
"""
|
|
359
823
|
if self._state.adding:
|
|
360
824
|
return False
|
|
361
825
|
|
|
@@ -377,14 +841,48 @@ class StagedTransactionModelAbstract(CreateUpdateMixIn):
|
|
|
377
841
|
]):
|
|
378
842
|
return True
|
|
379
843
|
|
|
380
|
-
# if getattr(self.parent, 'can_split_into_je'):
|
|
381
|
-
# return True
|
|
382
844
|
return False
|
|
383
845
|
|
|
384
846
|
def can_have_account(self) -> bool:
|
|
847
|
+
"""
|
|
848
|
+
Determines if an account can be created based on the current state.
|
|
849
|
+
|
|
850
|
+
This method assesses whether an account can be created for an entity
|
|
851
|
+
by checking if the entity has any children. The account creation is
|
|
852
|
+
prohibited if the entity has children and allowed otherwise.
|
|
853
|
+
|
|
854
|
+
Returns
|
|
855
|
+
-------
|
|
856
|
+
bool
|
|
857
|
+
True if the entity can have an account, False otherwise.
|
|
858
|
+
"""
|
|
385
859
|
return not self.has_children()
|
|
386
860
|
|
|
387
861
|
def can_import(self, as_split: bool = False) -> bool:
|
|
862
|
+
"""
|
|
863
|
+
Determines whether the object is ready for importing data and can optionally
|
|
864
|
+
be split into "je" (journal entries) for import if applicable.
|
|
865
|
+
|
|
866
|
+
This method evaluates the readiness of the object for importing based on
|
|
867
|
+
its attributes and conditions. It first checks if the object is marked as
|
|
868
|
+
ready for import. If the object supports splitting into journal entries
|
|
869
|
+
and the `as_split` argument is True, the method considers it eligible for
|
|
870
|
+
import as split entries. If neither of the above conditions are met, it
|
|
871
|
+
checks whether the role mapping is valid without raising exceptions and
|
|
872
|
+
returns the result.
|
|
873
|
+
|
|
874
|
+
Parameters
|
|
875
|
+
----------
|
|
876
|
+
as_split : bool, optional
|
|
877
|
+
Specifies if the object should be checked for readiness to be split
|
|
878
|
+
into "je" (journal entries) for import. Defaults to False.
|
|
879
|
+
|
|
880
|
+
Returns
|
|
881
|
+
-------
|
|
882
|
+
bool
|
|
883
|
+
True if the object is ready to import (optionally as split entries),
|
|
884
|
+
otherwise False.
|
|
885
|
+
"""
|
|
388
886
|
ready_to_import = getattr(self, 'ready_to_import')
|
|
389
887
|
if not ready_to_import:
|
|
390
888
|
return False
|
|
@@ -397,6 +895,31 @@ class StagedTransactionModelAbstract(CreateUpdateMixIn):
|
|
|
397
895
|
])
|
|
398
896
|
|
|
399
897
|
def add_split(self, raise_exception: bool = True, commit: bool = True, n: int = 1):
|
|
898
|
+
"""
|
|
899
|
+
Adds a specified number of split transactions to the staged transaction.
|
|
900
|
+
|
|
901
|
+
The method checks whether the staged transaction can be split and ensures it
|
|
902
|
+
has no children before proceeding. If requested, it raises an exception if a split
|
|
903
|
+
transaction is not allowed. New split transactions are created and validated before
|
|
904
|
+
optionally committing them to the database.
|
|
905
|
+
|
|
906
|
+
Parameters
|
|
907
|
+
----------
|
|
908
|
+
raise_exception : bool
|
|
909
|
+
Determines if an exception should be raised when splitting is not allowed.
|
|
910
|
+
Default is True.
|
|
911
|
+
commit : bool
|
|
912
|
+
Indicates whether to commit the newly created split transactions to the
|
|
913
|
+
database. Default is True.
|
|
914
|
+
n : int, optional
|
|
915
|
+
The number of split transactions to create. If the staged transaction has
|
|
916
|
+
no children, one additional split transaction is created. Default is 1.
|
|
917
|
+
|
|
918
|
+
Returns
|
|
919
|
+
-------
|
|
920
|
+
list of StagedTransactionModel
|
|
921
|
+
List of newly created staged transactions in the split.
|
|
922
|
+
"""
|
|
400
923
|
if not self.can_split():
|
|
401
924
|
if raise_exception:
|
|
402
925
|
raise ImportJobModelValidationError(
|
|
@@ -428,20 +951,83 @@ class StagedTransactionModelAbstract(CreateUpdateMixIn):
|
|
|
428
951
|
return new_txs
|
|
429
952
|
|
|
430
953
|
def is_total_amount_split(self) -> bool:
|
|
954
|
+
"""
|
|
955
|
+
Indicates whether the total amount is distributed as per the split rules.
|
|
956
|
+
|
|
957
|
+
Returns
|
|
958
|
+
-------
|
|
959
|
+
bool
|
|
960
|
+
True if the `amount` attribute equals the `total_amount_split` attribute,
|
|
961
|
+
indicating the total amount is split correctly; False otherwise.
|
|
962
|
+
"""
|
|
431
963
|
return self.amount == getattr(self, 'total_amount_split')
|
|
432
964
|
|
|
433
965
|
def are_all_children_mapped(self) -> bool:
|
|
966
|
+
"""
|
|
967
|
+
Determines whether all children have been mapped.
|
|
968
|
+
|
|
969
|
+
This method compares the total number of children with the number
|
|
970
|
+
of mapped children to check whether all children have been mapped.
|
|
971
|
+
|
|
972
|
+
Returns
|
|
973
|
+
-------
|
|
974
|
+
bool
|
|
975
|
+
True if the number of children equals the number of mapped children,
|
|
976
|
+
otherwise False.
|
|
977
|
+
"""
|
|
434
978
|
return getattr(self, 'children_count') == getattr(self, 'children_mapped_count')
|
|
435
979
|
|
|
436
|
-
def get_import_role_set(self) ->
|
|
980
|
+
def get_import_role_set(self) -> Set[str]:
|
|
981
|
+
"""
|
|
982
|
+
Retrieves the set of roles associated with import.
|
|
983
|
+
|
|
984
|
+
This method determines the role(s) based on the current instance's state and
|
|
985
|
+
its associated transactions. If the instance is single and mapped, the role
|
|
986
|
+
directly tied to its account model is returned. If the instance has child
|
|
987
|
+
split transactions and all of them are mapped, the roles associated with
|
|
988
|
+
each transaction's account model, excluding a specific type of role, are
|
|
989
|
+
aggregated and returned as a set.
|
|
990
|
+
|
|
991
|
+
Returns
|
|
992
|
+
-------
|
|
993
|
+
Set[str]
|
|
994
|
+
A set of roles derived from the account model(s) relating to the
|
|
995
|
+
instance or its child transactions. Returns empty set if no roles
|
|
996
|
+
can be determined.
|
|
997
|
+
"""
|
|
437
998
|
if self.is_single() and self.is_mapped():
|
|
438
999
|
return {self.account_model.role}
|
|
439
1000
|
if self.has_children():
|
|
440
1001
|
split_txs_qs = self.split_transaction_set.all()
|
|
441
1002
|
if all([txs.is_mapped() for txs in split_txs_qs]):
|
|
442
1003
|
return set([txs.account_model.role for txs in split_txs_qs if txs.account_model.role != ASSET_CA_CASH])
|
|
1004
|
+
return set()
|
|
443
1005
|
|
|
444
1006
|
def get_prospect_je_activity_try(self, raise_exception: bool = True, force_update: bool = False) -> Optional[str]:
|
|
1007
|
+
"""
|
|
1008
|
+
Retrieve or attempt to fetch the journal entry activity for the current prospect object.
|
|
1009
|
+
|
|
1010
|
+
The method determines whether the activity should be updated or fetched based on the
|
|
1011
|
+
current state. If the `force_update` flag is set to True or required conditions are met,
|
|
1012
|
+
it attempts to retrieve the activity from the associated roles of the journal entry.
|
|
1013
|
+
The activity is then saved and optionally returned. If an exception occurs during this
|
|
1014
|
+
process and `raise_exception` is set to True, the exception is propagated.
|
|
1015
|
+
|
|
1016
|
+
Parameters
|
|
1017
|
+
----------
|
|
1018
|
+
raise_exception : bool, optional
|
|
1019
|
+
Specifies whether to raise exceptions in case of validation errors.
|
|
1020
|
+
Default is True.
|
|
1021
|
+
force_update : bool, optional
|
|
1022
|
+
Forces the method to fetch and update the activity even if it already exists.
|
|
1023
|
+
Default is False.
|
|
1024
|
+
|
|
1025
|
+
Returns
|
|
1026
|
+
-------
|
|
1027
|
+
Optional[str]
|
|
1028
|
+
The journal entry activity if successfully retrieved or updated; otherwise,
|
|
1029
|
+
returns the existing activity or None if no activity is present.
|
|
1030
|
+
"""
|
|
445
1031
|
ready_to_import = getattr(self, 'ready_to_import')
|
|
446
1032
|
if (not self.has_activity() and ready_to_import) or force_update:
|
|
447
1033
|
JournalEntryModel = lazy_loader.get_journal_entry_model()
|
|
@@ -457,15 +1043,62 @@ class StagedTransactionModelAbstract(CreateUpdateMixIn):
|
|
|
457
1043
|
return self.activity
|
|
458
1044
|
|
|
459
1045
|
def get_prospect_je_activity(self) -> Optional[str]:
|
|
1046
|
+
"""
|
|
1047
|
+
Gets the activity of the prospect JE (Journal Entry) in a safe manner.
|
|
1048
|
+
|
|
1049
|
+
This method retrieves the activity of the prospect journal entry (JE). If the
|
|
1050
|
+
activity cannot be retrieved, it will return `None` instead of raising an
|
|
1051
|
+
exception. It serves as a wrapper for the `get_prospect_je_activity_try`
|
|
1052
|
+
method by specifying that exceptions should not be raised during retrieval.
|
|
1053
|
+
|
|
1054
|
+
Returns
|
|
1055
|
+
-------
|
|
1056
|
+
Optional[str]
|
|
1057
|
+
The activity of the prospect journal entry if available, otherwise `None`.
|
|
1058
|
+
"""
|
|
460
1059
|
return self.get_prospect_je_activity_try(raise_exception=False)
|
|
461
1060
|
|
|
462
1061
|
def get_prospect_je_activity_display(self) -> Optional[str]:
|
|
1062
|
+
"""
|
|
1063
|
+
Provides functionality to retrieve and display the prospect journal entry activity
|
|
1064
|
+
based on the mapped activity associated with a prospect. The method attempts to
|
|
1065
|
+
fetch the journal entry activity safely and returns its display name if available.
|
|
1066
|
+
|
|
1067
|
+
Returns
|
|
1068
|
+
-------
|
|
1069
|
+
Optional[str]
|
|
1070
|
+
The display name of the prospect journal entry activity if it exists,
|
|
1071
|
+
otherwise None.
|
|
1072
|
+
"""
|
|
463
1073
|
activity = self.get_prospect_je_activity_try(raise_exception=False)
|
|
464
1074
|
if activity is not None:
|
|
465
1075
|
JournalEntryModel = lazy_loader.get_journal_entry_model()
|
|
466
1076
|
return JournalEntryModel.MAP_ACTIVITIES[activity]
|
|
467
1077
|
|
|
468
1078
|
def is_role_mapping_valid(self, raise_exception: bool = False) -> bool:
|
|
1079
|
+
"""
|
|
1080
|
+
Determines if the role mapping is valid by verifying associated activities.
|
|
1081
|
+
|
|
1082
|
+
The method checks for the presence of an activity linked to the object.
|
|
1083
|
+
If no activity is found, it attempts to fetch one. The validity of the
|
|
1084
|
+
role mapping is determined by the success of this process.
|
|
1085
|
+
|
|
1086
|
+
Parameters
|
|
1087
|
+
----------
|
|
1088
|
+
raise_exception : bool, optional
|
|
1089
|
+
Determines whether to raise an exception if validation fails
|
|
1090
|
+
(default is False).
|
|
1091
|
+
|
|
1092
|
+
Returns
|
|
1093
|
+
-------
|
|
1094
|
+
bool
|
|
1095
|
+
True if the role mapping is valid, otherwise False.
|
|
1096
|
+
|
|
1097
|
+
Raises
|
|
1098
|
+
------
|
|
1099
|
+
ValidationError
|
|
1100
|
+
If raise_exception is set to True and the validation process fails.
|
|
1101
|
+
"""
|
|
469
1102
|
if not self.has_activity():
|
|
470
1103
|
try:
|
|
471
1104
|
activity = self.get_prospect_je_activity_try(raise_exception=raise_exception)
|
|
@@ -480,15 +1113,35 @@ class StagedTransactionModelAbstract(CreateUpdateMixIn):
|
|
|
480
1113
|
return True
|
|
481
1114
|
|
|
482
1115
|
def migrate(self, split_txs: bool = False):
|
|
1116
|
+
"""
|
|
1117
|
+
Migrate transactional data to the ledger model by processing the provided
|
|
1118
|
+
transactions and committing them. This process involves using the provided
|
|
1119
|
+
parameter to determine transaction splitting and subsequently saving the
|
|
1120
|
+
processed transactional data for each entry in the commit dictionary.
|
|
1121
|
+
|
|
1122
|
+
Parameters
|
|
1123
|
+
----------
|
|
1124
|
+
split_txs : bool, optional
|
|
1125
|
+
A flag that determines whether the transactions should be split into
|
|
1126
|
+
multiple entries based on the associated commit data. Defaults to False.
|
|
1127
|
+
|
|
1128
|
+
Notes
|
|
1129
|
+
-----
|
|
1130
|
+
The method checks if the transactional data can be imported using the
|
|
1131
|
+
`can_import` method. If successful, it creates a commit dictionary and
|
|
1132
|
+
processes it by committing all transactional data to the ledger model.
|
|
1133
|
+
The saved objects are staged with appropriate models to retain the
|
|
1134
|
+
transaction state.
|
|
1135
|
+
"""
|
|
483
1136
|
if self.can_import(as_split=split_txs):
|
|
484
1137
|
commit_dict = self.commit_dict(split_txs=split_txs)
|
|
485
1138
|
import_job = self.import_job
|
|
486
1139
|
ledger_model = import_job.ledger_model
|
|
487
1140
|
|
|
488
|
-
if len(commit_dict):
|
|
1141
|
+
if len(commit_dict) > 0:
|
|
489
1142
|
for je_data in commit_dict:
|
|
490
1143
|
unit_model = self.unit_model if not split_txs else commit_dict[0][1]['unit_model']
|
|
491
|
-
|
|
1144
|
+
_, _ = ledger_model.commit_txs(
|
|
492
1145
|
je_timestamp=self.date_posted,
|
|
493
1146
|
je_unit_model=unit_model,
|
|
494
1147
|
je_txs=je_data,
|
|
@@ -498,7 +1151,9 @@ class StagedTransactionModelAbstract(CreateUpdateMixIn):
|
|
|
498
1151
|
)
|
|
499
1152
|
staged_to_save = [i['staged_tx_model'] for i in je_data]
|
|
500
1153
|
for i in staged_to_save:
|
|
501
|
-
i.save(
|
|
1154
|
+
i.save(
|
|
1155
|
+
update_fields=['transaction_model', 'updated']
|
|
1156
|
+
)
|
|
502
1157
|
|
|
503
1158
|
def clean(self, verify: bool = False):
|
|
504
1159
|
if self.has_children():
|