From 25b0ae28edcd99de4a7a700e9912e9712ab539c2 Mon Sep 17 00:00:00 2001 From: Dario Rekowski on RockPI Date: Thu, 4 Mar 2021 15:58:03 +0000 Subject: [PATCH] update decay code, use different approach as before, more easy coding --- .../state_user_transactions.sql | 2 +- .../JsonRequestHandlerController.php | 2 +- src/Controller/StateBalancesController.php | 272 +++++++++++++----- src/Model/Entity/StateBalance.php | 16 +- src/Model/Transactions/Record.php | 27 +- src/Template/StateBalances/overview.ctp | 2 + 6 files changed, 249 insertions(+), 72 deletions(-) diff --git a/skeema/gradido_community/state_user_transactions.sql b/skeema/gradido_community/state_user_transactions.sql index 272552845..5e01cac4a 100644 --- a/skeema/gradido_community/state_user_transactions.sql +++ b/skeema/gradido_community/state_user_transactions.sql @@ -4,6 +4,6 @@ CREATE TABLE `state_user_transactions` ( `transaction_id` int UNSIGNED NOT NULL, `transaction_type_id` int UNSIGNED NOT NULL, `balance` bigint(20) DEFAULT 0, - `balance_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `balance_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/src/Controller/JsonRequestHandlerController.php b/src/Controller/JsonRequestHandlerController.php index 7162d4d86..ba68f2b5a 100644 --- a/src/Controller/JsonRequestHandlerController.php +++ b/src/Controller/JsonRequestHandlerController.php @@ -108,7 +108,7 @@ class JsonRequestHandlerController extends AppController { if($last_transaction_query->count() < $last_transaction_id) { $last_transaction_id = $last_transaction_query->count(); } - $last_transaction_id = 0; + //$last_transaction_id = 0; $group_alias = Configure::read('GroupAlias'); diff --git a/src/Controller/StateBalancesController.php b/src/Controller/StateBalancesController.php index 435a9329f..3daa37fa9 100644 --- a/src/Controller/StateBalancesController.php +++ b/src/Controller/StateBalancesController.php @@ -41,33 +41,145 @@ class StateBalancesController extends AppController $this->set(compact('stateBalances')); } - - private function updateBalances($state_user_id) + + private function updateBalance($stateUserId) { - $state_balances = $this->StateBalances->find('all')->where(['state_user_id' => $state_user_id]); - if($state_balances->count() == 1) { - $stateUserTransactionsTable = TableRegistry::getTableLocator()->get('StateUserTransactions'); - $state_user_transactions = $stateUserTransactionsTable + $stateUserTransactionsTable = TableRegistry::getTableLocator()->get('StateUserTransactions'); + $transactionsTable = TableRegistry::getTableLocator()->get('Transactions'); + // info: cakephp use lazy loading, query will be executed later only if needed + $state_balances = $this->StateBalances->find('all')->where(['state_user_id' => $stateUserId]); + $state_user_transactions = $stateUserTransactionsTable ->find('all') - ->where(['state_user_id' => $state_user_id]) + ->where(['state_user_id' => $stateUserId]) ->order(['transaction_id ASC']) - ->contain(['']); - if($state_user_transactions->count() == 0){ - return; - } - $last_state_user_transaction = $state_user_transactions->last(); - $last_transaction = $this->StateBalance->newEntity(); - $last_transaction->amount = $last_state_user_transaction->balance; - $last_transaction->record_date = $last_state_user_transaction->balance_date; - // if entrys are nearly the same, we don't need doing anything - if(abs($last_transaction->decay - $state_balances->decay) < 100) { - return; - } - foreach($state_user_transactions as $state_user_transaction) { - - } - + ->contain(false); + + if(!$state_user_transactions) { + //debug($state_user_transactions); + return true; } + + // first: decide what todo + $create_state_balance = false; + $recalculate_state_user_transactions_balance = false; + $clear_state_balance = false; + $update_state_balance = false; + if($state_balances->count() == 0) { + $create_state_balance = true; + } + if($state_balances->count() > 1) { + $clear_state_balance = true; + $create_state_balance = true; + } + if($state_balances->count() == 1) { + if($state_user_transactions->count() == 0){ + $clear_state_balance = true; + } else { + $last_state_user_transaction = $state_user_transactions->last(); + $last_transaction = $this->StateBalances->newEntity(); + $last_transaction->amount = $last_state_user_transaction->balance; + $last_transaction->record_date = $last_state_user_transaction->balance_date; + // if entrys are nearly the same, we don't need doing anything + if(abs($last_transaction->decay - $state_balances->first()->decay) > 100) { + $recalculate_state_user_transactions_balance = true; + $update_state_balance = true; + } + } + } + + if(!$recalculate_state_user_transactions_balance) { + $last_state_user_transaction = $state_user_transactions->last(); + if($last_state_user_transaction->balance <= 0) { + $recalculate_state_user_transactions_balance = true; + if(!$create_state_balance) { + $update_state_balance = true; + } + } + } + // second: do what is needed + if($clear_state_balance) { + $this->StateBalances->deleteAll(['state_user_id' => $stateUserId]); + } + + $transaction_ids = []; + if($recalculate_state_user_transactions_balance) { + $state_user_transactions_array = $state_user_transactions->toArray(); + foreach($state_user_transactions_array as $i => $state_user_transaction) { + $transaction_ids[$state_user_transaction->transaction_id] = $i; + } + + $transactions = $transactionsTable + ->find('all') + ->where(['id IN' => array_keys($transaction_ids)]) + ->contain(['TransactionCreations', 'TransactionSendCoins']); + + $balance_cursor = $this->StateBalances->newEntity(); + $i = 0; + foreach($transactions as $transaction) { + if($transaction->transaction_type_id > 2) { + continue; + } + $amount_date = null; + $amount = 0; + + if($transaction->transaction_type_id == 1) { + $temp = $transaction->transaction_creations[0]; + + $balance_temp = $this->StateBalances->newEntity(); + $balance_temp->amount = $temp->amount; + $balance_temp->record_date = $temp->target_date; + + $amount = $balance_temp->partDecay($transaction->received); + $amount_date = $transaction->received; + //$amount_date = + } else if($transaction->transaction_type_id == 2) { + $temp = $transaction->transaction_send_coins[0]; + $amount = intval($temp->amount); + // reverse if sender + if($stateUserId == $temp->state_user_id) { + $amount *= -1.0; + } + $amount_date = $transaction->received; + } + if($i == 0) { + $balance_cursor->amount = $amount; + } else { + $balance_cursor->amount = $balance_cursor->partDecay($amount_date) + $amount; + } + $balance_cursor->record_date = $amount_date; + $state_user_transaction_index = $transaction_ids[$transaction->id]; + $state_user_transactions_array[$state_user_transaction_index]->balance = $balance_cursor->amount; + $state_user_transactions_array[$state_user_transaction_index]->balance_date = $balance_cursor->record_date; + $i++; + } + $results = $stateUserTransactionsTable->saveMany($state_user_transactions_array); + $errors = []; + foreach($results as $i => $result) { + if(!$result) { + $errors[$i] = $state_user_transactions_array[$i]->getErrors(); + } + } + if(count($errors)) { + return ['success' => false, 'error' => 'error saving one ore more state user transactions', 'details' => $errors]; + } + } + $state_balance = null; + if($update_state_balance) { + $state_balance = $state_balances->first(); + } + else if($create_state_balance) { + $state_balance = $this->StateBalances->newEntity(); + $state_balance->state_user_id = $stateUserId; + } + if($state_balance) { + $state_balance->amount = $state_user_transactions->last()->balance; + $state_balance->record_date = $state_user_transactions->last()->balance_date; + if(!$this->StateBalances->save($state_balance)) { + return ['success' => false, 'error' => 'error saving state balance', 'details' => $state_balance->getErrors()]; + } + } + return true; + } public function overview() @@ -86,6 +198,10 @@ class StateBalancesController extends AppController return $result; } $user = $session->read('StateUser'); + $update_balance_result = $this->updateBalance($user['id']); + if($update_balance_result !== true) { + $this->addAdminError('StateBalances', 'overview', $update_balance_result, $user['id']); + } // sendRequestGDT // listPerEmailApi @@ -105,6 +221,7 @@ class StateBalancesController extends AppController //} // // + $stateUserTransactionsTable = TableRegistry::getTableLocator()->get('StateUserTransactions'); $creationsTable = TableRegistry::getTableLocator()->get('TransactionCreations'); $creationTransactions = $creationsTable @@ -211,55 +328,82 @@ class StateBalancesController extends AppController $current_state_balance = null; $cursor = 0; $transactions_reversed = array_reverse($transactions); + $maxI = count($transactions_reversed); foreach($transactions_reversed as $i => $transaction) { - $date = $transaction['date']; - $month = $date->month; - $year = $date->year; - if(!$month_start_state_balance) { - $month_start_state_balance = $this->StateBalances->chooseForMonthAndUser($month, $year, $user['id']); - if(is_array($month_start_state_balance)) { - $this->Flash->error(__('Error in state balance: ' . json_encode($month_start_state_balance))); - break; - } - $current_state_balance = $month_start_state_balance; - + if(!isset($transaction['transaction_id'])) { + continue; } - $prev_amount = $current_state_balance->amount; - $decay_duration = $current_state_balance->decayDuration($date); - $current_state_balance->amount = $current_state_balance->partDecay($date); + $transaction_id = $transaction['transaction_id']; + $decay_transaction = NULL; + $state_balance = $this->StateBalances->newEntity(); - echo "amount: ". ($current_state_balance->amount / 10000) . ", duration: " . $decay_duration . "
"; - $decay_transaction = [ - 'type' => 'decay', - 'balance' => -($prev_amount - $current_state_balance->amount), - 'decay_duration' => $decay_duration . ' ' . __('seconds'), - 'memo' => '' - ]; - if($decay_duration > 0) { + if($i > 0 && isset($transactions_reversed[$i-1]['transaction_id'])) { + $prev_transaction = $transactions_reversed[$i-1]; + $stateUserTransactions = $stateUserTransactionsTable + ->find() + ->where([ + 'transaction_id IN' => [$transaction_id, $prev_transaction['transaction_id']], + 'state_user_id' => $user['id'] + ]) + ->order(['transaction_id ASC']) + ->toArray(); + + $prev = $stateUserTransactions[0]; + if($prev->balance > 0) { + // var_dump($stateUserTransactions); + $current = $stateUserTransactions[1]; + $interval = $current->balance_date->diff($prev->balance_date); + $state_balance->amount = $prev->balance; + $state_balance->record_date = $prev->balance_date; + $diff_amount = $state_balance->partDecay($current->balance_date); + /* echo "prev date: $prev->balance_date, current date: $current->balance_date, interval: "; + var_dump($interval); + echo "
"; + echo "prev balance: $prev->balance
diff: $diff_amount
"; + echo "current balance: $current->balance
"; + * + */ + //echo $interval->format('%R%a days'); + $decay_transaction = [ + 'type' => 'decay', + 'balance' => -($prev->balance - $diff_amount), + 'decay_duration' => $interval->format('%a days, %H hours, %I minutes, %S seconds'), + 'memo' => '' + ]; + } + } + + if($decay_transaction) { array_splice($transactions_reversed, $i + $cursor, 0, [$decay_transaction]); $cursor++; + } + if($i == $maxI-1) { + $stateUserTransaction = $stateUserTransactionsTable + ->find() + ->where(['transaction_id' => $transaction_id, 'state_user_id' => $user['id']]) + ->order(['transaction_id ASC'])->first(); + //var_dump($stateUserTransaction); + $state_balance->amount = $stateUserTransaction->balance; + $state_balance->record_date = $stateUserTransaction->balance_date; + $transactions_reversed[] = [ + 'type' => 'decay', + 'balance' => -($stateUserTransaction->balance - $state_balance->decay), + 'decay_duration' => $stateUserTransaction->balance_date->timeAgoInWords(), + 'memo' => '' + ]; + } - - if($current_state_balance->record_date != $date) { - if($transaction['type'] == 'send') { - $current_state_balance->amount -= $transaction['balance']; - } else { - $current_state_balance->amount += $transaction['balance']; - } + } + // for debugging + $calculated_balance = 0; + foreach($transactions_reversed as $tr) { + if($tr['type'] == 'send') { + $calculated_balance -= intval($tr['balance']); + } else { + $calculated_balance += intval($tr['balance']); } - $current_state_balance->record_date = $date; - - } - if($current_state_balance) { - echo "amount: ". ($current_state_balance->amount / 10000) . ", duration: " . $current_state_balance->decayDuration(Time::now()) . "
"; - - array_push($transactions_reversed, [ - 'type' => 'decay', - 'balance' => -($current_state_balance->amount - $current_state_balance->decay), - 'decay_duration' => $current_state_balance->record_date->timeAgoInWords(),// $current_state_balance->decayDuration(Time::now()), - 'memo' => '' - ]); } + $this->set('calculated_balance', $calculated_balance); $this->set('transactions', array_reverse($transactions_reversed)); $this->set('transactionExecutingCount', $session->read('Transaction.executing')); diff --git a/src/Model/Entity/StateBalance.php b/src/Model/Entity/StateBalance.php index 35af8477b..0381864b6 100644 --- a/src/Model/Entity/StateBalance.php +++ b/src/Model/Entity/StateBalance.php @@ -36,6 +36,15 @@ class StateBalance extends Entity protected $_virtual = ['decay']; + private function convertToTimestamp($dateOrTime) + { + if(method_exists($dateOrTime, 'getTimestamp')) { + return $dateOrTime->getTimestamp(); + } else { + return $dateOrTime->i18nFormat(Time::UNIX_TIMESTAMP_FORMAT); + } + } + protected function _getDecay() { // decay factor in seconds per year @@ -45,7 +54,8 @@ class StateBalance extends Entity // SELECT TIMESTAMPDIFF(SECOND, modified, CURDATE()) AS age_in_seconds from state_balances // decay_for_duration = decay_factor^seconds // decay = gradido_cent * decay_for_duration - $decay_duration = intval(Time::now()->getTimestamp() - $this->record_date->getTimestamp()); + + $decay_duration = intval(Time::now()->getTimestamp() - $this->convertToTimestamp($this->record_date)); if($decay_duration === 0) { return $this->amount; } @@ -54,7 +64,7 @@ class StateBalance extends Entity } public function partDecay($target_date) { - $decay_duration = intval($target_date->getTimestamp() - $this->record_date->getTimestamp()); + $decay_duration = intval($this->convertToTimestamp($target_date) - $this->convertToTimestamp($this->record_date)); if($decay_duration <= 0) { return $this->amount; } @@ -63,7 +73,7 @@ class StateBalance extends Entity public function decayDuration($target_date) { - return intval($target_date->getTimestamp() - $this->record_date->getTimestamp()); + return intval($this->convertToTimestamp($target_date) - $this->convertToTimestamp($this->record_date)); } } diff --git a/src/Model/Transactions/Record.php b/src/Model/Transactions/Record.php index a0da48836..862399af5 100644 --- a/src/Model/Transactions/Record.php +++ b/src/Model/Transactions/Record.php @@ -58,10 +58,10 @@ class Signature class GradidoModifieUserBalance { private $state_users = []; + private $user_balances = []; public function getUserId($userPublicKey) { - $userPublicBin = hex2bin($userPublicKey); $stateUsersTable = TableRegistry::getTableLocator()->get('StateUsers'); $stateUser = $stateUsersTable->find('all')->where(['public_key' => hex2bin($userPublicKey)]); @@ -77,9 +77,23 @@ class GradidoModifieUserBalance public function updateBalance($newBalance, $recordDate, $userId) { - $stateBalancesTable = TableRegistry::getTableLocator()->get('StateBalances'); - return $stateBalancesTable->updateBalanceWithTransaction($newBalance, $recordDate, $userId); + $state_balance_query = $stateBalancesTable->find()->where(['state_user_id' => $userId])->order(['record_date ASC']); + $state_balance = null; + if($state_balance_query->count() > 0) { + $state_balance = $state_balance_query->last(); + } else { + $state_balance = $stateBalancesTable->newEntity(); + $state_balance->state_user_id = $userId; + } + $state_balance->amount = $newBalance; + $state_balance->record_date = $recordDate; + $this->user_balances[$userId] = $state_balance; + $stateBalancesTable->save($state_balance); + return true; + //$this->user_balances[$userId] = ['balance' => $newBalance, ''] + // + //return $stateBalancesTable->updateBalanceWithTransaction($newBalance, $recordDate, $userId); /*$first_of_month = new Time("$year-$month-01 00:00"); $stateBalanceQuery = $stateBalancesTable @@ -111,6 +125,10 @@ class GradidoModifieUserBalance { return $this->state_users; } + public function getAllStateUserBalances() + { + return $this->user_balances; + } } class ManageNodeGroupAdd extends GradidoModifieUserBalance @@ -457,11 +475,14 @@ class Record } $state_users = $this->transactionObj->getAllStateUsers(); $sut_entities = []; + $state_user_balances = $this->transactionObj->getAllStateUserBalances(); foreach($state_users as $state_user_id) { $entity = $stateUserTransactionsTable->newEntity(); $entity->state_user_id = $state_user_id; $entity->transaction_id = $newTransaction->id; $entity->transaction_type_id = $newTransaction->transaction_type_id; + $entity->balance = $state_user_balances[$state_user_id]->amount; + $entity->balance_date = $state_user_balances[$state_user_id]->record_date; $sut_entities[] = $entity; } $sut_results = $stateUserTransactionsTable->saveMany($sut_entities); diff --git a/src/Template/StateBalances/overview.ctp b/src/Template/StateBalances/overview.ctp index 94564e3e5..1f2f4c811 100644 --- a/src/Template/StateBalances/overview.ctp +++ b/src/Template/StateBalances/overview.ctp @@ -9,6 +9,7 @@ $this->assign('title', __('Kontoübersicht')); $header = '

' . __('Aktueller Kontostand: ') . '

' . '

' . $this->element('printGradido', ['number' => $balance]) . '

'; + if($gdtSum > 0) { $header .= '

'.$this->Html->link( $this->element('printGDT', ['number' => $gdtSum]), @@ -27,6 +28,7 @@ $this->assign('header', $header);

Überweisungen

+

Berechnet (debugging): element('printGradido', ['number' => $calculated_balance]) ?>