From 2ba1c4d18180bc8ed37cdd1e047b15a45b0c0116 Mon Sep 17 00:00:00 2001
From: vtexier <vit@free.fr>
Date: Sun, 19 Apr 2020 16:31:46 +0200
Subject: [PATCH] [enh] #807 transfer amount can be given in current
 referential

---
 src/sakia/gui/sub/transfer/controller.py | 56 +++++++++++++++++-------
 src/sakia/gui/sub/transfer/model.py      | 28 +++++++-----
 src/sakia/gui/sub/transfer/transfer.ui   | 19 ++++++--
 src/sakia/gui/sub/transfer/view.py       | 12 ++---
 src/sakia/money/base_referential.py      |  7 +++
 src/sakia/money/percent_of_average.py    | 22 ++++++++++
 src/sakia/money/quant_zerosum.py         | 29 ++++++++++++
 src/sakia/money/quantitative.py          | 19 ++++++++
 src/sakia/money/relative.py              | 22 +++++++++-
 src/sakia/money/relative_zerosum.py      | 32 ++++++++++++++
 10 files changed, 207 insertions(+), 39 deletions(-)

diff --git a/src/sakia/gui/sub/transfer/controller.py b/src/sakia/gui/sub/transfer/controller.py
index 25fb718a..da33d90b 100644
--- a/src/sakia/gui/sub/transfer/controller.py
+++ b/src/sakia/gui/sub/transfer/controller.py
@@ -52,7 +52,9 @@ class TransferController(QObject):
             self.change_current_connection
         )
         self.view.spinbox_amount.valueChanged.connect(self.handle_amount_change)
-        self.view.spinbox_relative.valueChanged.connect(self.handle_relative_change)
+        self.view.spinbox_referential.valueChanged.connect(
+            self.handle_referential_change
+        )
         self.view.button_source_check.clicked.connect(self.check_source_dialog)
 
     @classmethod
@@ -78,6 +80,10 @@ class TransferController(QObject):
         controller = cls(view, model, search_user, user_information, password_input)
 
         search_user.identity_selected.connect(user_information.search_identity)
+        app.referential_changed.connect(controller.refresh)
+        controller.view.label_referential_units.setText(
+            app.current_ref.instance(0, app.currency, app, None).diff_units
+        )
 
         view.set_keys(controller.model.available_connections())
         view.set_contacts(controller.model.contacts())
@@ -116,7 +122,7 @@ class TransferController(QObject):
             )
             controller.set_amount_value(source.amount, source.base)
             controller.view.spinbox_amount.setDisabled(True)
-            controller.view.spinbox_relative.setDisabled(True)
+            controller.view.spinbox_referential.setDisabled(True)
             result, _ = controller.check_source(source)
             # by default, source is unlocked, if not...
             if not result:
@@ -293,6 +299,19 @@ class TransferController(QObject):
         current_base_amount = amount / pow(10, current_base)
         total_text = self.model.localized_amount(amount)
         self.view.refresh_labels(total_text)
+        self.view.label_referential_units.setText(
+            self.model.app.current_ref.instance(
+                amount, self.model.app.currency, self.model.app, None
+            ).diff_units
+        )
+
+        # if referential = units, then hide useless referential spinbox
+        if self.model.app.current_ref == Quantitative:
+            self.view.spinbox_referential.hide()
+            self.view.label_referential_units.hide()
+        else:
+            self.view.spinbox_referential.show()
+            self.view.label_referential_units.show()
 
         if amount == 0:
             self.view.set_button_box(TransferView.ButtonBoxState.NO_AMOUNT)
@@ -310,19 +329,23 @@ class TransferController(QObject):
         if self.model.current_source and self.view.button_source_check.isEnabled():
             self.view.set_button_box(TransferView.ButtonBoxState.SOURCE_LOCKED)
 
-        max_relative = self.model.quant_to_rel(current_base_amount / 100)
+        max_relative = self.model.quantitative_to_referential(amount / 100)
         self.view.spinbox_amount.setSuffix(Quantitative.base_str(current_base))
 
         self.view.set_spinboxes_parameters(current_base_amount / 100, max_relative)
 
     def handle_amount_change(self, value):
-        relative = self.model.quant_to_rel(value)
-        self.view.change_relative_amount(relative)
+        current_base = self.model.current_base()
+        current_base_value = value / pow(10, current_base)
+        referential_amount = self.model.quantitative_to_referential(current_base_value)
+        self.view.change_referential_amount(referential_amount)
         self.refresh()
 
-    def handle_relative_change(self, value):
-        amount = self.model.rel_to_quant(value)
-        self.view.change_quantitative_amount(amount)
+    def handle_referential_change(self, value):
+        amount = self.model.referential_to_quantitative(value)
+        current_base = self.model.current_base()
+        current_base_amount = amount / 100 / pow(10, current_base)
+        self.view.change_quantitative_amount(current_base_amount)
         self.refresh()
 
     def change_current_connection(self, index):
@@ -408,16 +431,17 @@ class TransferController(QObject):
 
     def set_amount_value(self, amount, base):
         """
-        Set quantitative and relative amounts from amount and base given
+        Set quantitative and referential amounts from amount and base given
         :param int amount: Amount to display
         :param int base: Base of the amount given
         :return:
         """
-        # calculate amount for current base
+        # calculate value (from money cents) for current base
         current_base = self.model.current_base()
-        current_base_amount = amount / pow(10, base - current_base)
-        # display quantitative and relative amounts
-        relative = self.model.quant_to_rel(current_base_amount / 100)
-        self.view.set_spinboxes_parameters(current_base_amount / 100, relative)
-        self.view.change_relative_amount(relative)
-        self.view.change_quantitative_amount(current_base_amount / 100)
+        current_base_value = amount / pow(10, base - current_base) / 100
+
+        # display quantitative and referential amounts
+        referential_amount = self.model.quantitative_to_referential(current_base_value)
+        self.view.set_spinboxes_parameters(current_base_value, referential_amount)
+        self.view.change_referential_amount(referential_amount)
+        self.view.change_quantitative_amount(current_base_value)
diff --git a/src/sakia/gui/sub/transfer/model.py b/src/sakia/gui/sub/transfer/model.py
index 7b2c632d..35c76f56 100644
--- a/src/sakia/gui/sub/transfer/model.py
+++ b/src/sakia/gui/sub/transfer/model.py
@@ -6,6 +6,7 @@ from sakia.data.processors import (
     ConnectionsProcessor,
     ContactsProcessor,
 )
+from sakia.money.base_referential import BaseReferential
 
 
 @attr.s()
@@ -33,27 +34,30 @@ class TransferModel(QObject):
         self._connections_processor = ConnectionsProcessor.instanciate(self.app)
         self._contacts_processor = ContactsProcessor.instanciate(self.app)
 
-    def rel_to_quant(self, rel_value):
+    def referential_to_quantitative(self, value):
         """
-        Get the quantitative value of a relative amount
-        :param float rel_value:
+        Get the quantitative value of a referential amount
+        :param float value:
         :rtype: int
         """
-        dividend, base = self._blockchain_processor.last_ud(self.connection.currency)
-        amount = rel_value * dividend
+        referential = self.app.current_ref(
+            0, self.app.currency, self.app
+        )  # type: BaseReferential
+        referential.set_diff_referential(value)
         # amount is rounded to the nearest power of 10 depending of last ud base
         # rounded = int(pow(10, base) * round(float(amount) / pow(10, base)))
-        return int(amount) / 100
+        return int(referential.amount)
 
-    def quant_to_rel(self, amount):
+    def quantitative_to_referential(self, value):
         """
-        Get the relative value of a given amount
-        :param int amount:
+        Get the referential value of a given amount
+        :param int value:
         :rtype: float
         """
-        dividend, base = self._blockchain_processor.last_ud(self.connection.currency)
-        relative = amount * 100 / dividend
-        return relative
+        referential = self.app.current_ref(
+            value * 100, self.app.currency, self.app
+        )  # type: BaseReferential
+        return referential.differential()
 
     def wallet_value(self):
         """
diff --git a/src/sakia/gui/sub/transfer/transfer.ui b/src/sakia/gui/sub/transfer/transfer.ui
index 15341145..2bdc79d0 100644
--- a/src/sakia/gui/sub/transfer/transfer.ui
+++ b/src/sakia/gui/sub/transfer/transfer.ui
@@ -317,10 +317,7 @@
          </widget>
         </item>
         <item>
-         <widget class="QDoubleSpinBox" name="spinbox_relative">
-          <property name="suffix">
-           <string> UD</string>
-          </property>
+         <widget class="QDoubleSpinBox" name="spinbox_referential">
           <property name="decimals">
            <number>6</number>
           </property>
@@ -332,6 +329,13 @@
           </property>
          </widget>
         </item>
+        <item>
+         <widget class="QLabel" name="label_referential_units">
+          <property name="text">
+           <string>Units</string>
+          </property>
+         </widget>
+        </item>
         <item>
          <widget class="QDoubleSpinBox" name="spinbox_amount">
           <property name="wrapping">
@@ -360,6 +364,13 @@
           </property>
          </widget>
         </item>
+        <item>
+         <widget class="QLabel" name="label_amount_units">
+          <property name="text">
+           <string>Units</string>
+          </property>
+         </widget>
+        </item>
        </layout>
       </item>
      </layout>
diff --git a/src/sakia/gui/sub/transfer/view.py b/src/sakia/gui/sub/transfer/view.py
index 45e1fd31..d606246a 100644
--- a/src/sakia/gui/sub/transfer/view.py
+++ b/src/sakia/gui/sub/transfer/view.py
@@ -159,14 +159,14 @@ class TransferView(QWidget, Ui_TransferMoneyWidget):
         self.spinbox_amount.setValue(amount)
         self.spinbox_amount.blockSignals(False)
 
-    def change_relative_amount(self, relative):
+    def change_referential_amount(self, referential_amount):
         """
         Change the quantitative amount with signals blocks
-        :param relative:
+        :param referential_amount:
         """
-        self.spinbox_relative.blockSignals(True)
-        self.spinbox_relative.setValue(relative)
-        self.spinbox_relative.blockSignals(False)
+        self.spinbox_referential.blockSignals(True)
+        self.spinbox_referential.setValue(referential_amount)
+        self.spinbox_referential.blockSignals(False)
 
     def set_spinboxes_parameters(self, max_quant, max_rel):
         """
@@ -176,7 +176,7 @@ class TransferView(QWidget, Ui_TransferMoneyWidget):
         :param float max_rel:
         """
         self.spinbox_amount.setMaximum(max_quant)
-        self.spinbox_relative.setMaximum(max_rel)
+        self.spinbox_referential.setMaximum(max_rel)
 
     def refresh_labels(self, total_text):
         """
diff --git a/src/sakia/money/base_referential.py b/src/sakia/money/base_referential.py
index 64180343..5b0b6bd6 100644
--- a/src/sakia/money/base_referential.py
+++ b/src/sakia/money/base_referential.py
@@ -22,6 +22,7 @@ class BaseReferential:
         self.currency = currency
         self._block_number = block_number
 
+    # todo: remove this useless class method and replace all occurence with a classic Object() creation.
     @classmethod
     def instance(cls, amount, currency, app, block_number=None):
         return cls(amount, currency, app, block_number)
@@ -44,6 +45,12 @@ class BaseReferential:
     def differential(self):
         raise NotImplementedError()
 
+    def set_referential(self, value):
+        raise NotImplementedError()
+
+    def set_diff_referential(self, value):
+        raise NotImplementedError()
+
     @staticmethod
     def to_si(value, base):
         raise NotImplementedError()
diff --git a/src/sakia/money/percent_of_average.py b/src/sakia/money/percent_of_average.py
index 7d030121..ffcf584c 100644
--- a/src/sakia/money/percent_of_average.py
+++ b/src/sakia/money/percent_of_average.py
@@ -104,6 +104,28 @@ class PercentOfAverage(BaseReferential):
     def differential(self):
         return self.value()
 
+    def set_referential(self, value):
+        """
+        Set quantitative amount from referential value
+
+        :param value: Value in referential units
+        :return:
+        """
+        mass = self._blockchain_processor.last_mass(self.currency)
+        members = self._blockchain_processor.last_members_count(self.currency)
+        average = mass / members
+        self.amount = value / 100 * average
+        return self
+
+    def set_diff_referential(self, value):
+        """
+        Set quantitative amount from differential referential value
+
+        :param value:
+        :return:
+        """
+        return self.set_referential(value)
+
     def localized(self, units=False, show_base=False):
         value = self.value()
         localized_value = QLocale().toString(
diff --git a/src/sakia/money/quant_zerosum.py b/src/sakia/money/quant_zerosum.py
index e20f5326..26cd6bb4 100644
--- a/src/sakia/money/quant_zerosum.py
+++ b/src/sakia/money/quant_zerosum.py
@@ -102,6 +102,35 @@ class QuantitativeZSum(BaseReferential):
     def differential(self):
         return Quantitative(self.amount, self.currency, self.app).value()
 
+    def set_referential(self, value):
+        """
+        Set quantitative amount from referential value
+
+        :param value: Value in referential units
+        :return:
+        """
+        last_members_count = self._blockchain_processor.last_members_count(
+            self.currency
+        )
+        monetary_mass = self._blockchain_processor.current_mass(self.currency)
+        if last_members_count != 0:
+            average = int(monetary_mass / last_members_count)
+        else:
+            average = 0
+
+        self.amount = (value + average) * 100
+        return self
+
+    def set_diff_referential(self, value):
+        """
+        Set quantitative amount from differential referential value
+
+        :param value:
+        :return:
+        """
+        self.amount = value * 100
+        return self
+
     def localized(self, units=False, show_base=False):
         value = self.value()
         dividend, base = self._blockchain_processor.last_ud(self.currency)
diff --git a/src/sakia/money/quantitative.py b/src/sakia/money/quantitative.py
index 17cd7196..d45a148b 100644
--- a/src/sakia/money/quantitative.py
+++ b/src/sakia/money/quantitative.py
@@ -61,6 +61,25 @@ class Quantitative(BaseReferential):
     def differential(self):
         return self.value()
 
+    def set_referential(self, value):
+        """
+        Set quantitative amount from referential value
+
+        :param value: Value in referential units
+        :return:
+        """
+        self.amount = value * 100
+        return self
+
+    def set_diff_referential(self, value):
+        """
+        Set quantitative amount from differential referential value
+
+        :param value:
+        :return:
+        """
+        return self.set_referential(value)
+
     @staticmethod
     def base_str(base):
         unicodes = {
diff --git a/src/sakia/money/relative.py b/src/sakia/money/relative.py
index 8402fc8e..d0a11e9d 100644
--- a/src/sakia/money/relative.py
+++ b/src/sakia/money/relative.py
@@ -85,11 +85,31 @@ class Relative(BaseReferential):
         if dividend > 0:
             return self.amount / (float(dividend * (10 ** base)))
         else:
-            return self.amount
+            return self.amount / 100
 
     def differential(self):
         return self.value()
 
+    def set_referential(self, value):
+        """
+        Set quantitative amount from referential value
+
+        :param value: Value in referential units
+        :return:
+        """
+        dividend, base = self._blockchain_processor.last_ud(self.currency)
+        self.amount = value * (float(dividend * (10 ** base)))
+        return self
+
+    def set_diff_referential(self, value):
+        """
+        Set quantitative amount from differential referential value
+
+        :param value:
+        :return:
+        """
+        return self.set_referential(value)
+
     def localized(self, units=False, show_base=False):
         value = self.value()
         localized_value = QLocale().toString(
diff --git a/src/sakia/money/relative_zerosum.py b/src/sakia/money/relative_zerosum.py
index 466f6f77..db171f87 100644
--- a/src/sakia/money/relative_zerosum.py
+++ b/src/sakia/money/relative_zerosum.py
@@ -93,6 +93,38 @@ class RelativeZSum(BaseReferential):
     def differential(self):
         return Relative(self.amount, self.currency, self.app).value()
 
+    def set_referential(self, value):
+        """
+        Set quantitative amount from referential value
+
+        :param value: Value in referential units
+        :return:
+        """
+        dividend, base = self._blockchain_processor.previous_ud(self.currency)
+        previous_monetary_mass = self._blockchain_processor.previous_monetary_mass(
+            self.currency
+        )
+        members_count = self._blockchain_processor.current_members_count(self.currency)
+        if previous_monetary_mass and members_count > 0:
+            median = previous_monetary_mass / members_count
+            relative_median = median / float(dividend * 10 ** base)
+        else:
+            relative_median = 0
+
+            self.amount = (value + relative_median) * float(dividend * 10 ** base)
+        return self
+
+    def set_diff_referential(self, value):
+        """
+        Set quantitative amount from differential referential value
+
+        :param value:
+        :return:
+        """
+        dividend, base = self._blockchain_processor.previous_ud(self.currency)
+        self.amount = value * float(dividend * 10 ** base)
+        return self
+
     def localized(self, units=False, show_base=False):
         value = self.value()
 
-- 
GitLab