Skip to content

Commit

Permalink
Documenting Computation Fields
Browse files Browse the repository at this point in the history
  • Loading branch information
RamezIssac committed Oct 1, 2023
1 parent 49d9de5 commit 3e0ed3b
Show file tree
Hide file tree
Showing 6 changed files with 269 additions and 148 deletions.
103 changes: 1 addition & 102 deletions docs/source/ref/computation_field.rst
Original file line number Diff line number Diff line change
@@ -1,105 +1,4 @@
.. _computation_field:


Computation Field API
=====================

Responsible for preforming the calculation.


ReportField Basic Structure:
----------------------------

Earlier in he docs you saw the computation fields ``'__total__quantity__'``
Let's see how it's written in `slick_reporting.fields`


.. code-block:: python
from slick_reporting.fields import ComputationField
from slick_reporting.decorators import report_field_register
@report_field_register
class TotalQTYReportField(ComputationField):
# The name to use when using this field in the generator
name = '__total_quantity__'
# the field we want to compute on
calculation_field = 'quantity'
# What method we want
calculation_method = Sum # the default
# A verbose name
verbose name = 'Total quantity'
If you want AVG to the field `price` then the ReportField would look like this

.. code-block:: python
from django.db.models import Avg
@report_field_register
class TotalQTYReportField(ComputationField):
name = '__avg_price__'
calculation_field = 'price'
calculation_method = Avg
verbose name = 'Avg. Price'
How it works ?
--------------
The ReportGenerator is initialized with the needed configuration,
it generates a list of the needed fields to be displayed and computed.
For each computation field, it's given the filters needed and
asked to get all the results prepared. **The preparation is a duty of the ReportField anyway**,
then for each report_model record, the ReportGenerator again asks each ComputationField to get the data it has for each record and map it where it belongs.



Bundled Report Fields
---------------------

* __total__ : Sum of the field names value
* __total_quantity__ :Sum of the field names 'quantity'
* __fb__ : Sum of the field value on the start date (or the start date of the active time series window)
* __balance__: Compound some of the field `value` .

Difference between total and balance is:

The field __total__ will return that client 1 bought 10 in Jan, 12 in Feb , 13 in March. while __balance__ will report
client compound buy: 10 in Jan, 22 in Feb and 35 in March


Registering Report Field
------------------------

To make this ReportField class available to the report, it has to be registered via ``report_field_register``


Say you want to further customize your calculation, maybe you need to run a complex query

You can override both of those method and control the calculation

Calculation Flow:
-----------------

ReportGenerator call

1. prepare
2. resolve


Two side calculation
--------------------

# todo:
# Document how a single field can be computed like a debit and credit.

.. _computation_field_ref:

ComputationField API
--------------------
Expand Down
145 changes: 145 additions & 0 deletions docs/source/topics/computation_field.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
.. _computation_field:


Computation Field
=================

ComputationFields are the basic unit in a report.they represent a number that is being computed.

Computation Fields can be add to a report as a class, as you saw in other examples , or by name.


Reusing Computation Fields
---------------------------

You do not have to create the Computation Field each time you need one. You can create one and register it and reuse it.

Let's say you want to compute the total quantity of a product in a report.

.. code-block:: python
from slick_reporting.fields import ComputationField
from slick_reporting.decorators import report_field_register
@report_field_register
class TotalQTYReportField(ComputationField):
name = '__total_quantity__' # The name to use when using this field in the generator
calculation_field = 'quantity' # the field we want to compute on
calculation_method = Sum # What method we want, default to Sum
verbose name = 'Total quantity' # A verbose name
class ProductSales(ReportView):
report_title = _("Product Sales")
report_model = SalesTransaction
date_field = "date"
group_by = "product"
columns = [
"name",
"__total_quantity__",
]
Above we created the Computation Field
1. Created a ComputationField subclass and gave it the needed attributes
2. Register it via ``report_field_register`` so it can be picked up by the framework
3. Used it by name inside the columns attribute (or in time_series_columns, or in crosstab_columns)

So another example, If you want AVG to the field `price` then it would look like this

.. code-block:: python
from django.db.models import Avg
from slick_reporting.decorators import report_field_register
@report_field_register
class TotalQTYReportField(ComputationField):
name = '__avg_price__'
calculation_field = 'price'
calculation_method = Avg
verbose name = 'Avg. Price'
class ProductSales(ReportView):
# ..
columns = [
"name",
"__avg_price__",
]
Using Value of a Computation Field within a another
---------------------------------------------------

Sometime you want to stack values on top of each other. For example: Net revenue = Gross revenue - Discounts.

.. code-block:: python
class PercentageToTotalBalance(ComputationField):
requires = [BalanceReportField]
name = "__percent_to_total_balance__"
verbose_name = _("%")
calculation_method = Sum
calculation_field = "value"
prevent_group_by = True
def final_calculation(self, debit: float, credit: float, required_results: dict):
obj_balance = required_results.get("__balance__")
total = debit - credit
return (obj_balance / total) * 100
How it works ?
--------------
When the `ReportGenerator` is initialized, it generates a list of the needed fields to be displayed and computed.
Each computation field in the report is given the filters needed and asked to get all the results prepared.
Then for each record, the ReportGenerator again asks each ComputationField to get the data it has for each record and map it where it belongs.


Customizing the Calculation Flow:
---------------------------------

ReportGenerator call

1. prepare
2. resolve


.. code-block:: python
class MyCustomComputationField(ComputationField):
name = "__custom_field__"
def prepare(self, q_filters: list | object = None, kwargs_filters: dict = None, queryset=None, **kwargs):
# do all you calculation here for the whole set if any and return the prepared results
# The main implementation for example
Bundled Report Fields
---------------------
As this project came form an ERP background, there are some bundled report fields that you can use out of the box.

* __total__ : `Sum` of the field named `value`
* __total_quantity__ : `Sum` of the field named `quantity`
* __fb__ : First Balance, Sum of the field `value` on the start date (or period in case of time series)
* __balance__: Compound Sum of the field `value`. IE: the sum of the field `value` on end date.
* __credit__: Sum of field Value for the minus_list
* __debit__: Sum of the field value for the plus list
* __percent_to_total_balance__: Percent of the field value to the balance

Difference between total and balance is:

Total: Sum of the value for the period
Balance: Sum of the value for the period + all the previous periods.

Example:
Case: You have a client that buys 10 in Jan, 12 in Feb and 13 in March.
`__total__` will return 10 in Jan, 12 in Feb and 13 in March.
`__balance__` will return 10 in Jan, 22 in Feb and 35 in March



Two side calculation
--------------------

# todo:
# Document how a single field can be computed like a debit and credit.
Loading

0 comments on commit 3e0ed3b

Please sign in to comment.