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

Files changed (31) hide show
  1. django_ledger/__init__.py +1 -1
  2. django_ledger/contrib/django_ledger_graphene/bank_account/schema.py +1 -1
  3. django_ledger/forms/bank_account.py +16 -12
  4. django_ledger/forms/data_import.py +70 -33
  5. django_ledger/io/io_core.py +945 -127
  6. django_ledger/io/io_generator.py +7 -3
  7. django_ledger/io/ofx.py +37 -16
  8. django_ledger/migrations/0020_remove_bankaccountmodel_django_ledg_cash_ac_59a8af_idx_and_more.py +44 -0
  9. django_ledger/migrations/0021_alter_bankaccountmodel_account_model_and_more.py +33 -0
  10. django_ledger/models/bank_account.py +14 -11
  11. django_ledger/models/customer.py +3 -13
  12. django_ledger/models/data_import.py +690 -35
  13. django_ledger/models/entity.py +39 -24
  14. django_ledger/models/journal_entry.py +18 -8
  15. django_ledger/models/mixins.py +17 -3
  16. django_ledger/models/vendor.py +2 -2
  17. django_ledger/settings.py +18 -22
  18. django_ledger/templates/django_ledger/bank_account/tags/bank_accounts_table.html +2 -2
  19. django_ledger/templates/django_ledger/data_import/data_import_job_txs.html +1 -1
  20. django_ledger/templates/django_ledger/data_import/import_job_create.html +11 -2
  21. django_ledger/templates/django_ledger/data_import/tags/data_import_job_txs_imported.html +1 -1
  22. django_ledger/templates/django_ledger/data_import/tags/data_import_job_txs_table.html +5 -2
  23. django_ledger/templatetags/django_ledger.py +6 -7
  24. django_ledger/views/bank_account.py +1 -1
  25. django_ledger/views/data_import.py +60 -134
  26. {django_ledger-0.7.4.1.dist-info → django_ledger-0.7.5.dist-info}/METADATA +17 -17
  27. {django_ledger-0.7.4.1.dist-info → django_ledger-0.7.5.dist-info}/RECORD +31 -29
  28. {django_ledger-0.7.4.1.dist-info → django_ledger-0.7.5.dist-info}/WHEEL +1 -1
  29. {django_ledger-0.7.4.1.dist-info → django_ledger-0.7.5.dist-info}/top_level.txt +1 -0
  30. {django_ledger-0.7.4.1.dist-info → django_ledger-0.7.5.dist-info}/AUTHORS.md +0 -0
  31. {django_ledger-0.7.4.1.dist-info → django_ledger-0.7.5.dist-info}/LICENSE +0 -0
@@ -2,17 +2,90 @@
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
- Contributions to this module:
6
- * Miguel Sanda <msanda@arrobalytics.com>
7
-
8
- This module provides the building block interface for Django Ledger. The classes and functions contained in this module
9
- provide an interface to Django Ledger to create and manage Transactions into the Database. It also provides an
10
- optimized interface to push as much work as possible to the database without having to pull transactions from the
11
- database into the Python memory.
12
-
13
- The database records the individual transactions associated with each Journal Entry. However, this interface aggregates
14
- transactions during the digest method based on a specific request. The Python interpreter is responsible for applying
15
- accounting rules to the transactions associated with each Journal Entry so the appropriate account balances are computed.
5
+ This module serves as a core component of the Django Ledger framework, providing an optimized interface and
6
+ utilities for handling financial transactions, journal entries, and generating financial statements.
7
+ It is designed to allow efficient interaction with the database, minimizing Python's memory usage by emphasizing
8
+ database-level aggregations while enforcing proper accounting principles.
9
+
10
+ Key Features:
11
+ -------------
12
+ 1. **Transaction and Journal Management**:
13
+ - Provides capabilities for creating, validating, and balancing transactions at the database level.
14
+ - Supports integration of Closing Entries as optimizations for financial data aggregation.
15
+
16
+ 2. **Validation Utilities**:
17
+ - Validates transaction balances, timestamps, and input data for minimal errors.
18
+ - Ensures consistency with Django Ledger configurations, such as handling timezone-awareness and closing dates.
19
+
20
+ 3. **Database Interaction**:
21
+ - Aggregation and querying of financial data at the database layer to improve memory efficiency.
22
+ - Implements advanced filtering options for transactions based on attributes like activity, account roles, business units, and time periods.
23
+ - Leverages Closing Entries as "checkpoints" to minimize aggregation workload on large datasets.
24
+
25
+ 4. **Financial Statement Reports**:
26
+ - Offers functionalities to generate financial statements, including:
27
+ - Balance Sheet
28
+ - Income Statement
29
+ - Cash Flow Statement
30
+ - Supports PDF generation for these reports (if PDF support is enabled).
31
+
32
+ 5. **Extensibility**:
33
+ - Implements a layered architecture with reusable mixins (`IODatabaseMixIn`, `IOReportMixIn`, `IOMixIn`) that allow developers to customize behavior for specific use cases or extend functionality when needed.
34
+
35
+ 6. **Middleware and Ratios**:
36
+ - Includes middleware support for handling roles, financial groups, and ratios as additional processing layers for generated reports.
37
+
38
+ Classes:
39
+ --------
40
+ - `IOResult`:
41
+ A data carrier class for managing aggregated transaction and account balance data during digest operations.
42
+
43
+ - `IODatabaseMixIn`:
44
+ Handles database interactions for aggregating transaction data efficiently and applying accounting rules.
45
+
46
+ - `IOReportMixIn`:
47
+ Offers methods for generating financial reports, including support for PDF output (if enabled).
48
+
49
+ - `IOMixIn`:
50
+ Combines database operations and reporting features into a single reusable mixin for comprehensive I/O management.
51
+
52
+ Functions:
53
+ ----------
54
+ - **Utility Functions**:
55
+ - `diff_tx_data()`: Validates whether the credits and debits for a given transaction dataset are balanced.
56
+ - `check_tx_balance()`: Ensures transaction datasets are corrected to be balanced if requested.
57
+ - `validate_io_timestamp()`: Ensures that input dates or timestamps are valid and timezone-aware.
58
+ - `get_localtime()`, `get_localdate()`: Retrieve local time or local date based on Django's timezone settings.
59
+ - `validate_dates()`: Validates and parses `from_date` and `to_date` inputs.
60
+
61
+ - **Digest Operations**:
62
+ - `database_digest()`: Processes and aggregates transactions directly in the database with support for filters such as activity, role, and period.
63
+ - `python_digest()`: Applies additional processing and group-by operations on transaction data after database-level aggregation.
64
+ - `digest()`: A unified entry point for performing database digests and Python-level post-processing, with options to process roles, groups, ratios, and financial statements.
65
+
66
+ - **Report Data Generation**:
67
+ - `get_balance_sheet_statement()`, `get_income_statement()`, `get_cash_flow_statement()`: Generate specific financial statements, with optional PDF output.
68
+ - `get_financial_statements()`: Generate all key financial statements together (Balance Sheet, Income Statement, Cash Flow Statement).
69
+
70
+ Error Handling:
71
+ ---------------
72
+ - **Custom Exceptions**:
73
+ - `IOValidationError`: Raised for input validation errors during transaction or journal entry processing.
74
+
75
+ Supported Features:
76
+ -------------------
77
+ - Database-level transaction aggregation for performance optimization.
78
+ - Timezone-aware operations for timestamp handling.
79
+ - Modular architecture leveraging mixins for different components.
80
+ - Middleware for advanced role processing and financial grouping.
81
+ - Optional PDF generation using settings from Django Ledger.
82
+
83
+ Notes:
84
+ ------
85
+ - Ensure `DJANGO_LEDGER_PDF_SUPPORT_ENABLED` is properly configured to enable PDF output in financial reports.
86
+ - Closing Entries play a critical role in improving aggregation times for entities with significant transactions.
87
+ - The module is designed to work seamlessly with Django's ORM and custom models through utilities provided in
88
+ the Django Ledger framework.
16
89
  """
17
90
  from collections import namedtuple
18
91
  from dataclasses import dataclass
@@ -35,7 +108,7 @@ from django.utils.translation import gettext_lazy as _
35
108
 
36
109
  from django_ledger import settings
37
110
  from django_ledger.exceptions import InvalidDateInputError, TransactionNotInBalanceError
38
- from django_ledger.io import roles as roles_module
111
+ from django_ledger.io import roles as roles_module, CREDIT, DEBIT
39
112
  from django_ledger.io.io_context import IODigestContextManager
40
113
  from django_ledger.io.io_middleware import (
41
114
  AccountRoleIOMiddleware,
@@ -53,26 +126,64 @@ UserModel = get_user_model()
53
126
 
54
127
 
55
128
  def diff_tx_data(tx_data: list, raise_exception: bool = True):
129
+ """
130
+ Calculates the difference between credits and debits in a transaction dataset
131
+ and validates if the transactions are in balance. Supports both dictionary-like
132
+ data and `TransactionModel` objects.
133
+
134
+ The function checks whether the provided transaction data is in balance by
135
+ comparing the sum of credits and debits. If they are not in balance, the function
136
+ optionally raises an exception when the discrepancy exceeds the defined tolerance
137
+ limit. It also indicates whether the transaction data is modeled using
138
+ `TransactionModel`.
139
+
140
+ Parameters
141
+ ----------
142
+ tx_data : list
143
+ A list of transactions, which can either be dictionaries or instances
144
+ of `TransactionModel`. Each transaction must have an `amount` field and
145
+ a `tx_type` field. The `tx_type` should be either 'credit' or 'debit'.
146
+ raise_exception : bool, optional
147
+ Whether to raise an exception if the transactions are not balanced and
148
+ the difference exceeds the defined tolerance value. Defaults to True.
149
+
150
+ Returns
151
+ -------
152
+ tuple
153
+ A tuple containing the following:
154
+ 1. `IS_TX_MODEL` (bool): Indicates whether the transaction data uses the `TransactionModel`.
155
+ 2. `is_valid` (bool): Indicates whether the total credits match the total debits within the defined tolerance.
156
+ 3. `diff` (float): The difference between the sum of credits and the sum of debits.
157
+
158
+ Raises
159
+ ------
160
+ ValidationError
161
+ If the `tx_data` is neither a list of dictionaries nor a list of
162
+ `TransactionModel` instances.
163
+ TransactionNotInBalanceError
164
+ If the transactions are not in balance (when `is_valid` is False), the
165
+ difference exceeds the maximum tolerance, and `raise_exception` is True.
166
+ """
56
167
  IS_TX_MODEL = False
57
168
  TransactionModel = lazy_loader.get_txs_model()
58
169
 
59
170
  if isinstance(tx_data[0], TransactionModel):
60
- CREDITS = sum(tx.amount for tx in tx_data if tx.tx_type == 'credit')
61
- DEBITS = sum(tx.amount for tx in tx_data if tx.tx_type == 'debit')
171
+ credits = sum(tx.amount for tx in tx_data if tx.tx_type == CREDIT)
172
+ debits = sum(tx.amount for tx in tx_data if tx.tx_type == DEBIT)
62
173
  IS_TX_MODEL = True
63
174
  elif isinstance(tx_data[0], dict):
64
- CREDITS = sum(tx['amount'] for tx in tx_data if tx['tx_type'] == 'credit')
65
- DEBITS = sum(tx['amount'] for tx in tx_data if tx['tx_type'] == 'debit')
175
+ credits = sum(tx['amount'] for tx in tx_data if tx['tx_type'] == CREDIT)
176
+ debits = sum(tx['amount'] for tx in tx_data if tx['tx_type'] == DEBIT)
66
177
  else:
67
178
  raise ValidationError('Only Dictionary or TransactionModel allowed.')
68
179
 
69
- is_valid = (CREDITS == DEBITS)
70
- diff = CREDITS - DEBITS
180
+ is_valid = (credits == debits)
181
+ diff = credits - debits
71
182
 
72
183
  if not is_valid and abs(diff) > settings.DJANGO_LEDGER_TRANSACTION_MAX_TOLERANCE:
73
184
  if raise_exception:
74
185
  raise TransactionNotInBalanceError(
75
- f'Invalid tx data. Credits and debits must match. Currently cr: {CREDITS}, db {DEBITS}.'
186
+ f'Invalid tx data. Credits and debits must match. Currently cr: {credits}, db {debits}.'
76
187
  f'Max Tolerance {settings.DJANGO_LEDGER_TRANSACTION_MAX_TOLERANCE}'
77
188
  )
78
189
 
@@ -80,6 +191,27 @@ def diff_tx_data(tx_data: list, raise_exception: bool = True):
80
191
 
81
192
 
82
193
  def check_tx_balance(tx_data: list, perform_correction: bool = False) -> bool:
194
+ """
195
+ Checks the validity of a list of transactions and optionally corrects the balance
196
+ discrepancy if any. The function assesses whether the total balance from the
197
+ transactions satisfies the predefined tolerance settings. If `perform_correction`
198
+ is enabled, it adjusts the transactions iteratively to correct discrepancies.
199
+
200
+ Parameters
201
+ ----------
202
+ tx_data : list
203
+ A list of transaction data where each transaction contains information
204
+ such as amount and type ('debit' or 'credit').
205
+ perform_correction : bool, optional
206
+ A flag indicating whether to attempt to correct balance discrepancies
207
+ in the transaction data. Defaults to False.
208
+
209
+ Returns
210
+ -------
211
+ bool
212
+ Returns True if the transactions are valid and satisfy the balance
213
+ tolerance (with or without correction). Returns False otherwise.
214
+ """
83
215
  if tx_data:
84
216
 
85
217
  IS_TX_MODEL, is_valid, diff = diff_tx_data(tx_data, raise_exception=perform_correction)
@@ -91,19 +223,19 @@ def check_tx_balance(tx_data: list, perform_correction: bool = False) -> bool:
91
223
  return False
92
224
 
93
225
  while not is_valid:
94
- tx_type_choice = choice(['debit', 'credit'])
226
+ tx_type_choice = choice([DEBIT, CREDIT])
95
227
  txs_candidates = list(tx for tx in tx_data if tx['tx_type'] == tx_type_choice)
96
228
  if len(txs_candidates) > 0:
97
229
  tx = choice(list(tx for tx in tx_data if tx['tx_type'] == tx_type_choice))
98
- if any([diff > 0 and tx_type_choice == 'debit',
99
- diff < 0 and tx_type_choice == 'credit']):
230
+ if any([diff > 0 and tx_type_choice == DEBIT,
231
+ diff < 0 and tx_type_choice == CREDIT]):
100
232
  if IS_TX_MODEL:
101
233
  tx.amount += settings.DJANGO_LEDGER_TRANSACTION_CORRECTION
102
234
  else:
103
235
  tx['amount'] += settings.DJANGO_LEDGER_TRANSACTION_CORRECTION
104
236
 
105
- elif any([diff < 0 and tx_type_choice == 'debit',
106
- diff > 0 and tx_type_choice == 'credit']):
237
+ elif any([diff < 0 and tx_type_choice == DEBIT,
238
+ diff > 0 and tx_type_choice == CREDIT]):
107
239
  if IS_TX_MODEL:
108
240
  tx.amount -= settings.DJANGO_LEDGER_TRANSACTION_CORRECTION
109
241
  else:
@@ -116,16 +248,24 @@ def check_tx_balance(tx_data: list, perform_correction: bool = False) -> bool:
116
248
 
117
249
  def get_localtime(tz=None) -> datetime:
118
250
  """
119
- Convenience function to retrieve the localtime of the current system based on the USE_TZ django setting.
251
+ Retrieve the local time based on the specified timezone.
252
+
253
+ Determines the local time depending on whether timezone support (``USE_TZ``) is
254
+ enabled in global settings. If timezone support is enabled, it uses the
255
+ `localtime` function to obtain the local time according to the provided
256
+ timezone. If timezone support is disabled, it defaults to the current time
257
+ with respect to the given timezone.
120
258
 
121
259
  Parameters
122
260
  ----------
123
- tz: ZoneInfo
124
- Optional timezone to use, otherwise the system timezone is used.
261
+ tz : timezone or None, optional
262
+ The timezone to determine the local time. If `None`, defaults to the system
263
+ timezone.
125
264
 
126
265
  Returns
127
266
  -------
128
267
  datetime
268
+ A datetime object representing the calculated local time.
129
269
  """
130
270
  if global_settings.USE_TZ:
131
271
  return localtime(timezone=tz)
@@ -134,11 +274,17 @@ def get_localtime(tz=None) -> datetime:
134
274
 
135
275
  def get_localdate() -> date:
136
276
  """
137
- Convenience function to retrieve the local date of the current system based on the USE_TZ django setting.
277
+ Fetches the current local date, optionally considering time zone settings.
138
278
 
139
- Returns
140
- -------
141
- date
279
+ This function retrieves the current local date. If the global settings indicate
280
+ the use of time zones (`USE_TZ` is True), the date is determined based on the
281
+ local time zone. Otherwise, the date is based on the system's local time without
282
+ any time zone consideration.
283
+
284
+ Returns
285
+ -------
286
+ date
287
+ The current local date, adjusted for the time zone setting if applicable.
142
288
  """
143
289
  if global_settings.USE_TZ:
144
290
  return localdate()
@@ -149,6 +295,46 @@ def validate_io_timestamp(
149
295
  dt: Union[str, date, datetime],
150
296
  no_parse_localdate: bool = True
151
297
  ) -> Optional[Union[datetime, date]]:
298
+ """
299
+ Validates and processes a given date or datetime input and returns a processed
300
+ datetime or date object depending on the parsing and processing results. This
301
+ function is designed to handle multiple types of inputs including strings,
302
+ date objects, and datetime objects, while accounting for timezone awareness
303
+ and other scenarios where inputs may be invalid or improperly formed.
304
+
305
+ Parameters
306
+ ----------
307
+ dt : Union[str, date, datetime]
308
+ The input value to be validated and processed. This can be a string
309
+ representing a date or datetime, a `date` object, or a `datetime` object.
310
+ If the input is invalid or cannot be parsed, an error will be raised.
311
+ no_parse_localdate : bool, optional
312
+ A flag to indicate if the local date should be returned directly when the
313
+ input cannot be parsed or is unavailable. Defaults to True.
314
+
315
+ Returns
316
+ -------
317
+ Optional[Union[datetime, date]]
318
+ Returns a timezone-aware or naive `datetime` object that was processed
319
+ depending on input and timezone settings. May also return a `date` object
320
+ in specific scenarios. Returns `None` if the input is empty or invalid.
321
+
322
+ Raises
323
+ ------
324
+ InvalidDateInputError
325
+ Raised when the provided string cannot be parsed into a valid `date` or
326
+ `datetime` object.
327
+
328
+ Notes
329
+ -----
330
+ - The function handles both timezone-aware and naive datetime objects, and
331
+ attempts to make objects timezone-aware when global time zone settings
332
+ are enabled.
333
+ - String inputs are first attempted to be parsed into `date` objects before
334
+ attempting to parse them into `datetime` objects if the initial attempt fails.
335
+ - When `no_parse_localdate` is True, the function defaults to returning the
336
+ local time for cases where parsing is not possible.
337
+ """
152
338
  if not dt:
153
339
  return
154
340
 
@@ -191,15 +377,69 @@ def validate_io_timestamp(
191
377
 
192
378
 
193
379
  def validate_dates(
194
- from_date: Union[str, datetime, date] = None,
195
- to_date: Union[str, datetime, date] = None
380
+ from_date: Optional[Union[str, datetime, date]] = None,
381
+ to_date: Optional[Union[str, datetime, date]] = None
196
382
  ) -> Tuple[date, date]:
383
+ """
384
+ Validates and converts the input dates to date objects. This function ensures that the
385
+ provided `from_date` and `to_date` are correctly parsed into `date` objects. If the dates
386
+ are given as strings or datetime objects, they will be validated and converted to `date`
387
+ objects accordingly.
388
+
389
+ Parameters
390
+ ----------
391
+ from_date : str, datetime, date, optional
392
+ The start date, which can be provided as a string, datetime, or date object.
393
+ If not provided, it may default depending on the implementation of the
394
+ `validate_io_timestamp` function.
395
+ to_date : str, datetime, date, optional
396
+ The end date, which can be provided as a string, datetime, or date object.
397
+ If not provided, it may default depending on the implementation of the
398
+ `validate_io_timestamp` function.
399
+
400
+ Returns
401
+ -------
402
+ Tuple[date, date]
403
+ A tuple containing the validated `from_date` and `to_date` as `date` objects.
404
+ The first element is the validated start date, and the second element is the
405
+ validated end date.
406
+ """
197
407
  from_date = validate_io_timestamp(from_date, no_parse_localdate=False)
198
408
  to_date = validate_io_timestamp(to_date)
199
409
  return from_date, to_date
200
410
 
201
411
 
202
412
  def validate_activity(activity: str, raise_404: bool = False):
413
+ """
414
+ Validates the given activity against the list of valid activities and raises
415
+ appropriate exceptions if it is invalid.
416
+
417
+ This function checks whether the provided activity is included in the list
418
+ of valid activities defined within the JournalEntryModel. It raises a
419
+ ValidationError or Http404 error depending on the value of `raise_404`
420
+ if the activity is not valid. If the activity is valid or not provided,
421
+ it returns the activity unaltered.
422
+
423
+ Parameters
424
+ ----------
425
+ activity : str
426
+ The activity string to validate against the valid activities.
427
+ raise_404 : bool, optional
428
+ Whether to raise an Http404 error instead of ValidationError when the
429
+ activity is invalid. Default is False.
430
+
431
+ Returns
432
+ -------
433
+ str
434
+ The activity string if it is valid.
435
+
436
+ Raises
437
+ ------
438
+ ValidationError
439
+ If the activity is invalid and `raise_404` is False.
440
+ Http404
441
+ If the activity is invalid and `raise_404` is True.
442
+ """
203
443
  # idea: move to model???...
204
444
  JournalEntryModel = lazy_loader.get_journal_entry_model()
205
445
  valid = activity in JournalEntryModel.VALID_ACTIVITIES
@@ -218,7 +458,30 @@ class IOValidationError(ValidationError):
218
458
  @dataclass
219
459
  class IOResult:
220
460
  """
221
- A carrier class to store IO digest information during the digest call.
461
+ Represents the input-output result schema for data aggregation and processing.
462
+
463
+ This class encapsulates details related to database aggregation, closing entry
464
+ lookup parameters, and the final dataset used for evaluation. It also provides
465
+ utility to check bounded date constraints for closing entry lookups.
466
+
467
+ Attributes
468
+ ----------
469
+ db_from_date : Optional[date]
470
+ The starting date for database aggregation queries.
471
+ db_to_date : Optional[date]
472
+ The ending date for database aggregation queries.
473
+ ce_match : bool
474
+ Indicates whether closing entry matches are applicable.
475
+ ce_from_date : Optional[date]
476
+ The starting date for closing entry lookups.
477
+ ce_to_date : Optional[date]
478
+ The ending date for closing entry lookups.
479
+ txs_queryset
480
+ The final queryset used for evaluation, typically containing processed
481
+ transaction data.
482
+ accounts_digest : Optional[List[Dict]]
483
+ A summary or aggregation of account balances derived from the processed
484
+ data.
222
485
  """
223
486
  # DB Aggregation...
224
487
  db_from_date: Optional[date] = None
@@ -245,46 +508,117 @@ class IOResult:
245
508
 
246
509
  class IODatabaseMixIn:
247
510
  """
248
- The main entry point to query DB for transactions. The database_digest method pushes as much load as possible
249
- to the Database so transactions are aggregated at the database layer and are not pulled into memory.
250
- This is important por performance purposes since Entities may have a large amount of transactions to be
251
- aggregated.
252
-
253
- The python_digest method aggregates and processes the raw data stored in the database and applies accounting
254
- rules to stored transactions.
255
-
256
- This method also makes use of Closing Entries whenever possible to minimize the amount of data to aggregate
257
- during a specific call. Closing Entries can be considered "checkpoints", which create materialized aggregation
258
- of transactions for commonly used dates. (i.e. Fiscal Year End, Month End, Quarter End, etc.). This approach
259
- helps minimize the number of transactions to aggregate for a given request.
511
+ Mix-in class for database interactions and aggregation for IO models.
512
+
513
+ This class is designed to encapsulate several common behaviors for models
514
+ related to database queries and aggregations in the context of IO operations
515
+ such as transactions, ledger models, and entity models. It facilitates the
516
+ derivation of specific models, validation of query parameters, and execution
517
+ of data aggregation queries with flexible filtering and grouping options.
518
+
519
+ Attributes
520
+ ----------
521
+ TRANSACTION_MODEL_CLASS : NoneType or Type
522
+ Specifies the Django model class for transactions. If None, a lazy loader
523
+ will be used to determine the model dynamically.
524
+ JOURNAL_ENTRY_MODEL_CLASS : NoneType or Type
525
+ Specifies the Django model class for journal entries. If None, a lazy
526
+ loader will be used to determine the model dynamically.
260
527
  """
261
528
 
262
529
  TRANSACTION_MODEL_CLASS = None
263
530
  JOURNAL_ENTRY_MODEL_CLASS = None
264
531
 
265
532
  def is_entity_model(self):
533
+ """
534
+ Check if the instance is an EntityModel.
535
+
536
+ Returns
537
+ -------
538
+ bool
539
+ True if the instance is an entity model, False otherwise.
540
+ """
266
541
  return isinstance(self, lazy_loader.get_entity_model())
267
542
 
268
543
  def is_ledger_model(self):
544
+ """
545
+ Checks if the current instance is a LedgerModel.
546
+
547
+ Returns
548
+ -------
549
+ bool
550
+ True if the instance is of type ledger model, False otherwise.
551
+ """
269
552
  return isinstance(self, lazy_loader.get_ledger_model())
270
553
 
271
554
  def is_entity_unit_model(self):
555
+ """
556
+ Checks if the current instance is an EntityUnitModel.
557
+
558
+ Returns
559
+ -------
560
+ bool
561
+ `True` if the object is an instance of the entity unit model;
562
+ `False` otherwise.
563
+ """
272
564
  return isinstance(self, lazy_loader.get_entity_unit_model())
273
565
 
274
566
  def get_entity_model_from_io(self):
567
+ """
568
+ Retrieves the entity model associated with the current instance.
569
+
570
+ This method determines the type of the current model instance and retrieves
571
+ the corresponding entity model if applicable. If the instance itself is an
572
+ EntityModel, it returns itself. If the instance is a ledgerModel or EntityUnitModel
573
+ model, the associated entity model is retrieved from its attributes.
574
+
575
+ Returns
576
+ -------
577
+ entity : EntityModel
578
+ Retrieves the associated entity model if the instance is a ledger
579
+ or entity unit model.
580
+ """
275
581
  if self.is_entity_model():
276
582
  return self
277
583
  elif self.is_ledger_model():
278
- return self.entity
584
+ return getattr(self, 'entity')
279
585
  elif self.is_entity_unit_model():
280
- return self.entity
586
+ return getattr(self, 'entity')
587
+ raise IOValidationError(
588
+ message=_(f'IODatabaseMixIn not compatible with {self.__class__.__name__} model.')
589
+ )
281
590
 
282
591
  def get_transaction_model(self):
592
+ """
593
+ Retrieve the transaction model class used for handling transactions.
594
+
595
+ The method checks whether a specific transaction model class is explicitly
596
+ set via the `TRANSACTION_MODEL_CLASS` attribute. If set, it returns that
597
+ class as the transaction model. If not set, it falls back to a default
598
+ transaction model obtained from the `lazy_loader.get_txs_model()` method.
599
+
600
+ Returns
601
+ -------
602
+ type
603
+ The transaction model class defined in `TRANSACTION_MODEL_CLASS` or
604
+ the default transaction model provided by `lazy_loader.get_txs_model()`.
605
+ """
283
606
  if self.TRANSACTION_MODEL_CLASS is not None:
284
607
  return self.TRANSACTION_MODEL_CLASS
285
608
  return lazy_loader.get_txs_model()
286
609
 
287
610
  def get_journal_entry_model(self):
611
+ """
612
+ Retrieves the class model for journal entries. If the `JOURNAL_ENTRY_MODEL_CLASS`
613
+ attribute is set, it returns its value. Otherwise, it dynamically loads and
614
+ returns the journal entry model using the `lazy_loader`.
615
+
616
+ Returns
617
+ -------
618
+ Type
619
+ The journal entry model class, either explicitly defined in
620
+ `JOURNAL_ENTRY_MODEL_CLASS` or loaded dynamically.
621
+ """
288
622
  if self.JOURNAL_ENTRY_MODEL_CLASS is not None:
289
623
  return self.JOURNAL_ENTRY_MODEL_CLASS
290
624
  return lazy_loader.get_journal_entry_model()
@@ -307,47 +641,71 @@ class IODatabaseMixIn:
307
641
  use_closing_entries: bool = False,
308
642
  **kwargs) -> IOResult:
309
643
  """
310
- Performs the appropriate database aggregation query for a given request.
311
-
644
+ Aggregates transaction data based on the provided parameters to generate a
645
+ digest of financial entries. This method is designed to work with various
646
+ models (EntityModel, EntityUnitModel, LedgerModel) and processes
647
+ transactions, including handling closing entries to optimize database
648
+ queries. The resulting data can be customized based on specific filters
649
+ or aggregation criteria, such as activity, transaction type, or time periods.
312
650
 
313
651
  Parameters
314
652
  ----------
315
- entity_slug: str
316
- EntityModel slug to use. If not provided it will be derived from the EntityModel instance.
317
- Will be validated against current EntityModel instance for safety. Defaults to None.
318
- unit_slug: str
319
- EntityUnitModel used to query transactions. If provided will be validated against current EntityModelUnit
320
- instance. Defaults to None.
321
- user_model: UserModel
322
- The django UserModel to validate against transaction ownership and permissions (i.e. Admin and Manager).
323
- Defaults to None.
324
- from_date: date or datetime
325
- Stating date or datetime to query from (inclusive).
326
- to_date: date or datetime
327
- End date or datetime to query to (inclusive).
328
- activity: str
329
- Filters transactions to match specific activity. Defaults to None.
330
- role: str
331
- Filters transactions to match specific role. Defaults to None.
332
- accounts: str or List[str] ot Set[str]
333
- Optional list of accounts to query. Defaults to None (all).
334
- posted: bool
335
- Consider only posted transactions. Defaults to True.
336
- exclude_zero_bal: bool
337
- Excludes transactions with zero balance, if any.
338
- by_activity: bool
339
- Returns results aggregated by activity if needed. Defaults to False.
340
- by_tx_type: bool
341
- Returns results aggregated by DEBIT/CREDIT if needed. Defaults to False.
342
- by_period: bool
343
- Returns results aggregated by accounting if needed. Defaults to False.
344
- by_unit: bool
345
- Returns results aggregated by unit if needed. Defaults to False.
346
- use_closing_entries: bool
347
- Overrides the DJANGO_LEDGER_USE_CLOSING_ENTRIES setting.
653
+ entity_slug : Optional[str]
654
+ The identifier for the entity. Used to filter transactions for a specific
655
+ entity in cases where the model operates at the entity level.
656
+ unit_slug : Optional[str]
657
+ The identifier for the unit. Valid when filtering transactions for a
658
+ specific unit within an entity.
659
+ user_model : Optional[UserModel]
660
+ The user model instance. Represents the user context in which the
661
+ transaction filtering applies.
662
+ from_date : Optional[Union[date, datetime]]
663
+ The starting date for filtering transactions. Aggregates data from
664
+ the given date if specified.
665
+ to_date : Optional[Union[date, datetime]]
666
+ The ending date for filtering transactions. Aggregates data up to this
667
+ date if specified.
668
+ by_activity : bool
669
+ Determines whether results should be aggregated by activity. Defaults
670
+ to False.
671
+ by_tx_type : bool
672
+ Indicates if results should be grouped by transaction type. Defaults
673
+ to False.
674
+ by_period : bool
675
+ Determines if results should be grouped by time period (e.g., months).
676
+ Defaults to False.
677
+ by_unit : bool
678
+ Indicates whether transactions should be grouped by entity unit.
679
+ Defaults to False.
680
+ activity : Optional[str]
681
+ A specific activity identifier to filter results. If provided, only
682
+ transactions related to this activity will be included.
683
+ role : Optional[str]
684
+ A specific role to filter transactions by. Transactions matching this
685
+ role will be included in the aggregation.
686
+ accounts : Optional[Union[str, List[str], Set[str]]]
687
+ Specifies accounts to filter by. Can be a single account or a list/set
688
+ of accounts. Filters transactions associated with the provided accounts.
689
+ posted : bool
690
+ Indicates whether to filter transactions that are posted (committed).
691
+ Defaults to True.
692
+ exclude_zero_bal : bool
693
+ If True, transactions with zero-balance amounts will be excluded.
694
+ Defaults to True.
695
+ use_closing_entries : bool
696
+ Specifies whether closing entries should be used to optimize database
697
+ aggregation. If not provided, the value is determined by the system-global
698
+ setting.
699
+ kwargs : dict
700
+ Additional parameters that can be passed for extended flexibility or
701
+ customization when filtering and processing transactions.
702
+
348
703
  Returns
349
704
  -------
350
705
  IOResult
706
+ An object containing the aggregated results, filtered transaction querysets,
707
+ and metadata such as database query bounds or details about closing
708
+ entry matches.
351
709
  """
352
710
 
353
711
  TransactionModel = self.get_transaction_model()
@@ -477,7 +835,7 @@ class IODatabaseMixIn:
477
835
  txs_queryset = txs_queryset.posted()
478
836
 
479
837
  if accounts:
480
- if not isinstance(accounts, str):
838
+ if isinstance(accounts, str):
481
839
  accounts = [accounts]
482
840
  txs_queryset = txs_queryset.for_accounts(account_list=accounts)
483
841
 
@@ -553,53 +911,55 @@ class IODatabaseMixIn:
553
911
  force_queryset_sorting: bool = False,
554
912
  **kwargs) -> IOResult:
555
913
  """
556
- Performs the appropriate transaction post-processing after DB aggregation.
557
-
914
+ Computes and returns the digest of transactions for a given entity, unit,
915
+ and optional filters such as date range, account role, and activity. The
916
+ digest includes aggregated balances by specified group-by keys.
558
917
 
559
918
  Parameters
560
919
  ----------
561
- entity_slug: str
562
- EntityModel slug to use. If not provided it will be derived from the EntityModel instance.
563
- Will be validated against current EntityModel instance for safety. Defaults to None.
564
- unit_slug: str
565
- EntityUnitModel used to query transactions. If provided will be validated against current EntityModelUnit
566
- instance. Defaults to None.
567
- user_model: UserModel
568
- The django UserModel to validate against transaction ownership and permissions (i.e. Admin and Manager).
569
- Defaults to None.
570
- from_date: date or datetime
571
- Stating date or datetime to query from (inclusive).
572
- to_date: date or datetime
573
- End date or datetime to query to (inclusive).
574
- activity: str
575
- Filters transactions to match specific activity. Defaults to None.
576
- role: str
577
- Filters transactions to match specific role. Defaults to None.
578
- accounts: str or List[str] ot Set[str]
579
- Optional list of accounts to query. Defaults to None (all).
580
- by_activity: bool
581
- Returns results aggregated by activity if needed. Defaults to False.
582
- by_tx_type: bool
583
- Returns results aggregated by DEBIT/CREDIT if needed. Defaults to False.
584
- by_period: bool
585
- Returns results aggregated by accounting if needed. Defaults to False.
586
- by_unit: bool
587
- Returns results aggregated by unit if needed. Defaults to False.
588
- equity_only: bool
589
- Performs aggregation only on accounts that impact equity only (i.e. Income Statement Generation).
590
- Avoids unnecessary inclusion of accounts not relevant to what's needed.
591
- signs: bool
592
- Changes the balance of an account to negative if it represents a "negative" for display purposes.
593
- (i.e. Expense accounts will show balance as negative and Income accounts as positive.)
594
- use_closing_entries: bool
595
- Forces the use of closing entries if DJANGO_LEDGER_USE_CLOSING_ENTRIES setting is set to False.
596
- force_queryset_sorting: bool
597
- Forces sorting of the TransactionModelQuerySet before aggregation balances.
598
- Defaults to false.
920
+ user_model : Optional[UserModel]
921
+ The user model to be used for the computation. Defaults to None.
922
+ entity_slug : Optional[str]
923
+ The slug representing the entity to compute the digest for. Defaults
924
+ to None.
925
+ unit_slug : Optional[str]
926
+ The slug representing the unit within the entity. Defaults to None.
927
+ to_date : Optional[Union[date, datetime, str]]
928
+ The end date for the transaction filter. Defaults to None.
929
+ from_date : Optional[Union[date, datetime, str]]
930
+ The start date for the transaction filter. Defaults to None.
931
+ equity_only : bool
932
+ Whether to compute results only for earnings-related accounts. Defaults
933
+ to False.
934
+ activity : str
935
+ An optional activity filter for transactions. Defaults to None.
936
+ role : Optional[Union[Set[str], List[str]]]
937
+ The account roles to include in the digest. Defaults to None.
938
+ accounts : Optional[Union[Set[str], List[str]]]
939
+ A list or set of specific accounts to include. Defaults to None.
940
+ signs : bool
941
+ Whether to adjust the signs for the account balances based on balance type and account role.
942
+ Defaults to True.
943
+ by_unit : bool
944
+ Whether to group the results by unit. Defaults to False.
945
+ by_activity : bool
946
+ Whether to group the results by activity. Defaults to False.
947
+ by_tx_type : bool
948
+ Whether to group the results by transaction type. Defaults to False.
949
+ by_period : bool
950
+ Whether to group the results by period (year and month). Defaults to False.
951
+ use_closing_entries : bool
952
+ Whether to include closing entries in the computation. Defaults to False.
953
+ force_queryset_sorting : bool
954
+ Whether to force sorting of the transaction queryset. Defaults to False.
955
+ **kwargs : dict
956
+ Additional keyword arguments passed to the computation.
599
957
 
600
958
  Returns
601
959
  -------
602
960
  IOResult
961
+ An object containing the transaction queryset, grouped and aggregated
962
+ account balances, and other relevant digest information.
603
963
  """
604
964
 
605
965
  if equity_only:
@@ -650,12 +1010,12 @@ class IODatabaseMixIn:
650
1010
  for acc in accounts_digest:
651
1011
  if any([
652
1012
  all([acc['role_bs'] == roles_module.BS_ASSET_ROLE,
653
- acc['balance_type'] == TransactionModel.CREDIT]),
1013
+ acc['balance_type'] == CREDIT]),
654
1014
  all([acc['role_bs'] in (
655
1015
  roles_module.BS_LIABILITIES_ROLE,
656
1016
  roles_module.BS_EQUITY_ROLE
657
1017
  ),
658
- acc['balance_type'] == TransactionModel.DEBIT])
1018
+ acc['balance_type'] == DEBIT])
659
1019
  ]):
660
1020
  acc['balance'] = -acc['balance']
661
1021
 
@@ -664,6 +1024,45 @@ class IODatabaseMixIn:
664
1024
 
665
1025
  @staticmethod
666
1026
  def aggregate_balances(k, g):
1027
+ """
1028
+ Aggregates balances from grouped data, providing a summarized representation of
1029
+ account attributes and their balances over a specified period. The function is
1030
+ used to compile essential details such as account identifiers, roles, activity,
1031
+ and balance, summarizing them for further processing or presentation.
1032
+
1033
+ Parameters
1034
+ ----------
1035
+ k : tuple
1036
+ A tuple consisting of grouped key values. Expected structure:
1037
+ - The first element represents the account UUID.
1038
+ - The second element represents the unit UUID.
1039
+ - The third and fourth elements represent the period year and month, respectively.
1040
+ - The sixth element represents the transaction type.
1041
+
1042
+ g : iterable
1043
+ An iterable of grouped account data, typically containing dictionaries with
1044
+ detailed account properties and their respective balance information.
1045
+
1046
+ Returns
1047
+ -------
1048
+ dict
1049
+ A dictionary containing the aggregated balance information and related
1050
+ attributes, structured with the following keys:
1051
+ - 'account_uuid'
1052
+ - 'coa_slug'
1053
+ - 'unit_uuid'
1054
+ - 'unit_name'
1055
+ - 'activity'
1056
+ - 'period_year'
1057
+ - 'period_month'
1058
+ - 'role_bs'
1059
+ - 'role'
1060
+ - 'code'
1061
+ - 'name'
1062
+ - 'balance_type'
1063
+ - 'tx_type'
1064
+ - 'balance'
1065
+ """
667
1066
  gl = list(g)
668
1067
  return {
669
1068
  'account_uuid': k[0],
@@ -706,7 +1105,70 @@ class IODatabaseMixIn:
706
1105
  cash_flow_statement: bool = False,
707
1106
  use_closing_entry: Optional[bool] = None,
708
1107
  **kwargs) -> IODigestContextManager:
1108
+ """
1109
+ Processes financial data and generates various financial statements, ratios, or activity digests
1110
+ based on the provided arguments. The method applies specific processing pipelines, such as role
1111
+ processing, grouping, ratio computation, activity-based operations, or generating financial
1112
+ statements like balance sheet, income statement, and cash flow statement.
709
1113
 
1114
+ Parameters
1115
+ ----------
1116
+ entity_slug : Optional[str]
1117
+ The slug identifier for the entity to process the financial data for.
1118
+ unit_slug : Optional[str]
1119
+ The slug identifier for the specific unit of the entity to filter the data for.
1120
+ to_date : Optional[Union[date, datetime, str]]
1121
+ The upper limit of the date range for which the data will be processed. Can be a `date`,
1122
+ `datetime`, or ISO 8601 formatted `str`.
1123
+ from_date : Optional[Union[date, datetime, str]]
1124
+ The lower limit of the date range for which the data will be processed. Can be a `date`,
1125
+ `datetime`, or ISO 8601 formatted `str`.
1126
+ user_model : Optional[UserModel]
1127
+ A user model instance to filter data or apply user-specific permissions during processing.
1128
+ accounts : Optional[Union[Set[str], List[str]]]
1129
+ A collection of account identifiers to filter the financial data to process.
1130
+ role : Optional[Union[Set[str], List[str]]]
1131
+ A collection of roles used to filter or organize the data during processing.
1132
+ activity : Optional[str]
1133
+ Specific activity identifier to filter or process the data for.
1134
+ signs : bool, default=True
1135
+ If `True`, account roles with predefined signs will be applied to the financial data.
1136
+ process_roles : bool, default=False
1137
+ If `True`, processes account roles and organizes financial data accordingly.
1138
+ process_groups : bool, default=False
1139
+ If `True`, processes and organizes the financial data into predetermined account groups.
1140
+ process_ratios : bool, default=False
1141
+ If `True`, computes and processes financial ratios for the provided data.
1142
+ process_activity : bool, default=False
1143
+ If `True`, specific activity-based computation or segregation will be executed.
1144
+ equity_only : bool, default=False
1145
+ Processes only equity-specific accounts if set to `True`.
1146
+ by_period : bool, default=False
1147
+ Organizes or groups the output by accounting periods if `True`.
1148
+ by_unit : bool, default=False
1149
+ Organizes or processes data specific to each unit when set to `True`.
1150
+ by_activity : bool, default=False
1151
+ Groups or segregates the processed data by activity when `True`.
1152
+ by_tx_type : bool, default=False
1153
+ Groups or organizes the result by transaction type when `True`.
1154
+ balance_sheet_statement : bool, default=False
1155
+ If `True`, prepares a balance sheet statement as part of the processing.
1156
+ income_statement : bool, default=False
1157
+ If `True`, generates an income statement as part of the processed result.
1158
+ cash_flow_statement : bool, default=False
1159
+ If `True`, prepares a cash flow statement based on the provided data.
1160
+ use_closing_entry : Optional[bool]
1161
+ Specifies whether to account for closing entries in the financial statement computation.
1162
+ **kwargs
1163
+ Additional named arguments that can be passed to adjust the behavior of specific processing
1164
+ modules or middleware.
1165
+
1166
+ Returns
1167
+ -------
1168
+ IODigestContextManager
1169
+ A context manager instance containing the processed financial data and results, including
1170
+ financial statements, ratios, or any activity digest generated as per the input parameters.
1171
+ """
710
1172
  if balance_sheet_statement:
711
1173
  from_date = None
712
1174
 
@@ -817,7 +1279,55 @@ class IODatabaseMixIn:
817
1279
  je_origin=None,
818
1280
  force_je_retrieval: bool = False,
819
1281
  **kwargs):
1282
+ """
1283
+ Commits a set of financial transactions to a journal entry, after performing
1284
+ validation checks. Validations include ensuring balanced transactions, ensuring
1285
+ timestamp integrity, and verifying relationships between models. This method
1286
+ creates or retrieves a journal entry based on the provided details and assigns
1287
+ the given transactions accordingly, supporting conditional posting.
1288
+
1289
+ Parameters
1290
+ ----------
1291
+ je_timestamp : str or datetime or date
1292
+ The timestamp for the journal entry. Validates against entity's closed
1293
+ periods and is required to be in a valid format.
1294
+ je_txs : list of dict
1295
+ A list of transactions to be committed, each represented as a dictionary.
1296
+ Each transaction should include keys such as 'account', 'amount', 'tx_type',
1297
+ and 'description'.
1298
+ je_posted : bool, optional
1299
+ Whether the created or retrieved journal entry should be marked as posted
1300
+ after verification. Defaults to False.
1301
+ je_ledger_model : object, optional
1302
+ An optional instance of LedgerModel used for validating ledger associations.
1303
+ If not provided, defaults to the current ledger model.
1304
+ je_unit_model : object, optional
1305
+ An optional instance of EntityUnitModel used for validating entity unit
1306
+ associations with transactions.
1307
+ je_desc : str, optional
1308
+ A description for the journal entry. Defaults to None if not provided.
1309
+ je_origin : str, optional
1310
+ Specifies the origin or source of the journal entry. Defaults to None.
1311
+ force_je_retrieval : bool, optional
1312
+ Whether to force retrieval of an existing journal entry with the given
1313
+ timestamp instead of creating a new one. Defaults to False.
1314
+ **kwargs : dict
1315
+ Additional keyword arguments that may be required for handling specific
1316
+ customization details during journal entry creation or retrieval.
820
1317
 
1318
+ Returns
1319
+ -------
1320
+ tuple
1321
+ A tuple containing the journal entry model (je_model) and a list of
1322
+ transaction models (txs_models) created or associated with the journal entry.
1323
+
1324
+ Raises
1325
+ ------
1326
+ IOValidationError
1327
+ Raised for various validation errors including invalid timestamps, attempting
1328
+ to commit on locked or closed ledgers, association errors with ledger or
1329
+ entity models, and inability to retrieve a required journal entry.
1330
+ """
821
1331
  TransactionModel = self.get_transaction_model()
822
1332
  JournalEntryModel = self.get_journal_entry_model()
823
1333
 
@@ -920,6 +1430,31 @@ class IODatabaseMixIn:
920
1430
 
921
1431
 
922
1432
  class IOReportMixIn:
1433
+ """
1434
+ Provides functionality for generating and managing financial reports.
1435
+
1436
+ This mixin class facilitates the generation, processing, and management of
1437
+ various financial statements, including balance sheet statements, income
1438
+ statements, and cash flow statements. Reports can be created in multiple
1439
+ formats, such as plain data representation or as PDF files. The class
1440
+ integrates support for financial data digestion and report serialization,
1441
+ including saving PDF reports to specified file paths. Common use cases include
1442
+ generating summarized financial data for reporting purposes and outputting
1443
+ them in PDF format.
1444
+
1445
+ Attributes
1446
+ ----------
1447
+ PDF_REPORT_ORIENTATION : str
1448
+ Indicates the orientation of the generated PDF reports ('P' for portrait, 'L' for landscape).
1449
+ PDF_REPORT_MEASURE_UNIT : str
1450
+ Specifies the measurement unit used in the PDF reports (e.g., 'mm', 'cm').
1451
+ PDF_REPORT_PAGE_SIZE : str
1452
+ Defines the size of the pages in the generated PDF reports (e.g., 'Letter', 'A4').
1453
+ ReportTuple : namedtuple
1454
+ A tuple structure containing three fields: `balance_sheet_statement`,
1455
+ `income_statement`, and `cash_flow_statement`. Each field represents a
1456
+ respective financial report.
1457
+ """
923
1458
  PDF_REPORT_ORIENTATION = 'P'
924
1459
  PDF_REPORT_MEASURE_UNIT = 'mm'
925
1460
  PDF_REPORT_PAGE_SIZE = 'Letter'
@@ -936,6 +1471,33 @@ class IOReportMixIn:
936
1471
  user_model: Optional[UserModel] = None,
937
1472
  txs_queryset: Optional[QuerySet] = None,
938
1473
  **kwargs: Dict) -> IODigestContextManager:
1474
+ """
1475
+ Digest the balance sheet for a specific time period, user, and optionally a specific set
1476
+ of transactions. Returns a context manager for digesting the specified balance sheet data.
1477
+
1478
+ Parameters
1479
+ ----------
1480
+ to_date : Union[date, datetime]
1481
+ The date till which the balance sheet needs to be digested, including transactions
1482
+ occurring on this date.
1483
+
1484
+ user_model : Optional[UserModel], optional
1485
+ The model instance representing the user whose balance sheet is to be
1486
+ digested. This can be `None` to indicate no specific user.
1487
+
1488
+ txs_queryset : Optional[QuerySet], optional
1489
+ A queryset containing specific transactions to be included in the calculation.
1490
+ If not provided, all transactions up to `to_date` will be considered.
1491
+
1492
+ kwargs : Dict
1493
+ Additional keyword arguments that can be used for the digestion process.
1494
+ Allows flexible filtering or additional specifications.
1495
+
1496
+ Returns
1497
+ -------
1498
+ IODigestContextManager
1499
+ A context manager for handling the digestion process of the balance sheet.
1500
+ """
939
1501
  return self.digest(
940
1502
  user_model=user_model,
941
1503
  to_date=to_date,
@@ -955,7 +1517,50 @@ class IOReportMixIn:
955
1517
  save_pdf: bool = False,
956
1518
  **kwargs
957
1519
  ) -> IODigestContextManager:
1520
+ """
1521
+ Generates a balance sheet statement with an option to save it as a PDF file.
1522
+
1523
+ This method fetches and processes financial data to create a balance sheet
1524
+ statement using the provided date range, user model, and additional optional
1525
+ settings. It supports generating a PDF output for the balance sheet if
1526
+ configured. The class responsible for PDF generation is dynamically loaded,
1527
+ allowing modularity and flexibility.
1528
+
1529
+ Note that PDF generation functionality is dependent on the presence of the
1530
+ `DJANGO_LEDGER_PDF_SUPPORT_ENABLED` flag. If the flag is not enabled, the
1531
+ method raises an exception.
958
1532
 
1533
+ Parameters
1534
+ ----------
1535
+ to_date : Union[date, datetime]
1536
+ The end date for the balance sheet report. The data will be considered
1537
+ up to this date for the financial statement.
1538
+ subtitle : Optional[str], default=None
1539
+ An optional subtitle for the generated report. This can be used to include
1540
+ additional context or descriptions in the report.
1541
+ filepath : Optional[Path], default=None
1542
+ Specifies the directory path where the PDF file should be saved. If not
1543
+ provided, a default directory is used based on application settings.
1544
+ filename : Optional[str], default=None
1545
+ Name of the file to save the PDF report. Defaults to an automatically
1546
+ generated filename if not provided.
1547
+ user_model : Optional[UserModel], default=None
1548
+ The user context associated with the balance sheet report. Includes
1549
+ permissions or specific user-related data.
1550
+ save_pdf : bool, default=False
1551
+ A flag indicating whether the balance sheet should be saved as a PDF. If
1552
+ True, the method generates and stores the PDF in the specified location.
1553
+ **kwargs
1554
+ Additional keyword arguments required for generating the balance sheet.
1555
+ It may include filtering, formatting, or any other relevant parameters.
1556
+
1557
+ Returns
1558
+ -------
1559
+ IODigestContextManager
1560
+ The context manager object handling the generated balance sheet, either in
1561
+ memory or in saved PDF format. If the `save_pdf` option is enabled, the PDF
1562
+ report is saved at the specified location.
1563
+ """
959
1564
  if not DJANGO_LEDGER_PDF_SUPPORT_ENABLED:
960
1565
  raise IOValidationError(
961
1566
  message=_('PDF support not enabled. Install PDF support from Pipfile.')
@@ -989,6 +1594,36 @@ class IOReportMixIn:
989
1594
  user_model: Optional[UserModel] = None,
990
1595
  txs_queryset: Optional[QuerySet] = None,
991
1596
  **kwargs) -> IODigestContextManager:
1597
+ """
1598
+ Digest the income statement within the specified date range and optionally filter
1599
+ by user and transaction queryset.
1600
+
1601
+ This method generates a digest context manager for the income statement,
1602
+ allowing granular control over the data derived within the requested range
1603
+ and applying any additional filters provided. It supports optional user and
1604
+ transaction queryset filters for tailored data processing.
1605
+
1606
+ Parameters
1607
+ ----------
1608
+ from_date : Union[date, datetime]
1609
+ The starting date for filtering the income statement.
1610
+ to_date : Union[date, datetime]
1611
+ The ending date for filtering the income statement.
1612
+ user_model : Optional[UserModel], optional
1613
+ An optional user model instance to filter income statement data
1614
+ by a specific user.
1615
+ txs_queryset : Optional[QuerySet], optional
1616
+ An optional transaction queryset to filter the income statement
1617
+ by specific transaction records.
1618
+ **kwargs : dict
1619
+ Additional keyword arguments for customization or requirements in
1620
+ the income statement digest generation process.
1621
+
1622
+ Returns
1623
+ -------
1624
+ IODigestContextManager
1625
+ A context manager containing the processed income statement data.
1626
+ """
992
1627
  return self.digest(
993
1628
  user_model=user_model,
994
1629
  from_date=from_date,
@@ -1011,6 +1646,50 @@ class IOReportMixIn:
1011
1646
  save_pdf: bool = False,
1012
1647
  **kwargs
1013
1648
  ):
1649
+ """
1650
+ Generates an income statement report for a specific time period and allows optional PDF
1651
+ saving functionality. The function utilizes configurations, user-provided parameters,
1652
+ and transactions data to create an income statement report, which can either be returned
1653
+ as a report object or saved as a PDF on the filesystem.
1654
+
1655
+ Parameters
1656
+ ----------
1657
+ from_date : date or datetime
1658
+ The starting date of the income statement period. Must be provided.
1659
+ to_date : date or datetime
1660
+ The ending date of the income statement period. Must be provided.
1661
+ subtitle : str, optional
1662
+ An optional subtitle for the income statement report.
1663
+ filepath : Path, optional
1664
+ The directory path where the PDF report should be saved, if `save_pdf` is set to True.
1665
+ filename : str, optional
1666
+ The filename for the PDF report. If not provided, a default filename is generated.
1667
+ user_model : UserModel, optional
1668
+ A user model instance, providing additional context or filtering for the income
1669
+ statement generation.
1670
+ txs_queryset : QuerySet, optional
1671
+ A queryset containing transactions data to be included in the income statement
1672
+ report. If not provided, transactions may be automatically determined by other
1673
+ parameters or configurations.
1674
+ save_pdf : bool
1675
+ Indicates whether the generated income statement should be saved as a PDF file.
1676
+ Defaults to False.
1677
+ **kwargs :
1678
+ Additional optional keyword arguments for further customization or filtering.
1679
+
1680
+ Raises
1681
+ ------
1682
+ IOValidationError
1683
+ Raised if PDF support is not enabled in the configuration. The error provides a
1684
+ message suggesting installing PDF support.
1685
+
1686
+ Returns
1687
+ -------
1688
+ IncomeStatementReport
1689
+ A configured instance of the IncomeStatementReport class, representing the
1690
+ generated income statement report. If `save_pdf` is True, the report will also
1691
+ be saved as a PDF file at the specified location.
1692
+ """
1014
1693
  if not DJANGO_LEDGER_PDF_SUPPORT_ENABLED:
1015
1694
  raise IOValidationError(
1016
1695
  message=_('PDF support not enabled. Install PDF support from Pipfile.')
@@ -1045,6 +1724,31 @@ class IOReportMixIn:
1045
1724
  user_model: Optional[UserModel] = None,
1046
1725
  txs_queryset: Optional[QuerySet] = None,
1047
1726
  **kwargs) -> IODigestContextManager:
1727
+ """
1728
+ Generates a digest of the cash flow statement for a specified date range, user model,
1729
+ and optional transaction query set. This method utilizes an internal digest
1730
+ mechanism to compile and return the cash flow statement context.
1731
+
1732
+ Parameters
1733
+ ----------
1734
+ from_date : Union[date, datetime]
1735
+ The start date of the period for the cash flow statement.
1736
+ to_date : Union[date, datetime]
1737
+ The end date of the period for the cash flow statement.
1738
+ user_model : Optional[UserModel]
1739
+ The user model instance for which the digest is generated. If None, the
1740
+ default user model is used.
1741
+ txs_queryset : Optional[QuerySet]
1742
+ An optional queryset of transactions to include in the digest. If None,
1743
+ defaults to all transactions within the date range.
1744
+ **kwargs : dict
1745
+ Additional keyword arguments passed to the digest function.
1746
+
1747
+ Returns
1748
+ -------
1749
+ IODigestContextManager
1750
+ Context manager providing the digested cash flow statement.
1751
+ """
1048
1752
  return self.digest(
1049
1753
  user_model=user_model,
1050
1754
  from_date=from_date,
@@ -1065,7 +1769,44 @@ class IOReportMixIn:
1065
1769
  user_model: Optional[UserModel] = None,
1066
1770
  save_pdf: bool = False,
1067
1771
  **kwargs):
1772
+ """
1773
+ Generates a cash flow statement report within a specified date range and provides
1774
+ an option to save the report as a PDF file. The method retrieves financial data, processes
1775
+ it into a structured cash flow statement, and uses a PDF report generator to create the report.
1776
+
1777
+ Parameters
1778
+ ----------
1779
+ from_date : Union[date, datetime]
1780
+ The start date for the cash flow statement.
1781
+ to_date : Union[date, datetime]
1782
+ The end date for the cash flow statement.
1783
+ subtitle : Optional[str], default=None
1784
+ Subtitle text for the report.
1785
+ filepath : Optional[Path], default=None
1786
+ The directory path where the PDF report will be saved. Defaults to the
1787
+ base directory if not provided.
1788
+ filename : Optional[str], default=None
1789
+ The name of the PDF report file. If not provided, a default name is generated.
1790
+ user_model : Optional[UserModel], default=None
1791
+ The user model instance, optionally used for filtering or customization of the
1792
+ report content.
1793
+ save_pdf : bool, default=False
1794
+ Flag to save the generated report as a PDF file.
1795
+ kwargs : dict
1796
+ Additional keyword arguments that are passed to the `digest_cash_flow_statement`
1797
+ method for further customization or additional processing.
1068
1798
 
1799
+ Returns
1800
+ -------
1801
+ CashFlowStatementReport
1802
+ An instance of the cash flow statement report class, either saved as a PDF if
1803
+ `save_pdf` is True or ready for further processing or display.
1804
+
1805
+ Raises
1806
+ ------
1807
+ IOValidationError
1808
+ If PDF support is not enabled in the system's Django ledger configuration.
1809
+ """
1069
1810
  if not DJANGO_LEDGER_PDF_SUPPORT_ENABLED:
1070
1811
  raise IOValidationError(
1071
1812
  message=_('PDF support not enabled. Install PDF support from Pipfile.')
@@ -1099,7 +1840,40 @@ class IOReportMixIn:
1099
1840
  to_date: Union[date, datetime],
1100
1841
  user_model: Optional[UserModel] = None,
1101
1842
  **kwargs) -> IODigestContextManager:
1843
+ """
1844
+ Digest financial statements within a given date range, allowing optional
1845
+ customization through `kwargs`. The method processes and provides access
1846
+ to balance sheet statements, income statements, and cash flow statements
1847
+ for the specified date range. Results are encapsulated in an
1848
+ IODigestContextManager for ease of management.
1849
+
1850
+ The function provides flexibility to include a user model for domain-specific
1851
+ processing needs while ensuring support for both date and datetime objects.
1852
+
1853
+ Parameters
1854
+ ----------
1855
+ from_date : date or datetime
1856
+ The starting date of the range for which financial statements are
1857
+ to be processed.
1858
+
1859
+ to_date : date or datetime
1860
+ The ending date of the range for which financial statements are
1861
+ to be processed.
1862
+
1863
+ user_model : Optional[UserModel], default=None
1864
+ A user model instance that allows for context-specific processing
1865
+ during the digestion of financial statements.
1102
1866
 
1867
+ kwargs : dict
1868
+ Additional optional parameters that may be used to further
1869
+ customize the processing of financial statements.
1870
+
1871
+ Returns
1872
+ -------
1873
+ IODigestContextManager
1874
+ Represents the context manager containing the digested financial
1875
+ statements for the specified date range.
1876
+ """
1103
1877
  return self.digest(
1104
1878
  from_date=from_date,
1105
1879
  to_date=to_date,
@@ -1119,7 +1893,44 @@ class IOReportMixIn:
1119
1893
  save_pdf: bool = False,
1120
1894
  filepath: Optional[Path] = None,
1121
1895
  **kwargs) -> ReportTuple:
1896
+ """
1897
+ Generates financial statements for a specified date range, optionally saving them as
1898
+ PDF files. This method consolidates the balance sheet, income statement, and cash flow
1899
+ statement for the given dates. If PDF saving is enabled, the financial statements will
1900
+ be saved to the specified path or the application's base directory. The results are
1901
+ returned as a tuple containing the reports.
1902
+
1903
+ Parameters
1904
+ ----------
1905
+ from_date : Union[date, datetime]
1906
+ The start date for the financial statements.
1907
+ to_date : Union[date, datetime]
1908
+ The end date for the financial statements.
1909
+ dt_strfmt : str
1910
+ The string format used for the filenames of the generated PDF reports. Defaults to
1911
+ '%Y%m%d'.
1912
+ user_model : Optional[UserModel], optional
1913
+ The user model instance, if applicable. Defaults to None.
1914
+ save_pdf : bool, optional
1915
+ Determines whether to save the generated financial statements as PDF files.
1916
+ Defaults to False.
1917
+ filepath : Optional[Path], optional
1918
+ The directory path where the PDF files will be saved. If not provided, the files will
1919
+ be saved in the application's base directory. Defaults to None.
1920
+ **kwargs
1921
+ Additional keyword arguments for customizing financial statement generation.
1122
1922
 
1923
+ Returns
1924
+ -------
1925
+ ReportTuple
1926
+ A named tuple containing the generated balance sheet, income statement, and cash
1927
+ flow statement as objects.
1928
+
1929
+ Raises
1930
+ ------
1931
+ IOValidationError
1932
+ Raised if PDF support is not enabled in the application configuration.
1933
+ """
1123
1934
  if not DJANGO_LEDGER_PDF_SUPPORT_ENABLED:
1124
1935
  raise IOValidationError(
1125
1936
  message=_('PDF support not enabled. Install PDF support from Pipfile.')
@@ -1176,4 +1987,11 @@ class IOMixIn(
1176
1987
  IODatabaseMixIn,
1177
1988
  IOReportMixIn
1178
1989
  ):
1179
- pass
1990
+ """
1991
+ Provides input and output functionalities by mixing in database and
1992
+ reporting environments.
1993
+
1994
+ This class is a mixin that combines functionalities from IODatabaseMixIn
1995
+ and IOReportMixIn. It is designed to integrate database input/output
1996
+ operations with report generation features seamlessly.
1997
+ """