Merge branch 'master' into reset-password

This commit is contained in:
Moriz Wahl 2021-04-22 11:12:32 +02:00
commit 2638ab8f3b
41 changed files with 1542 additions and 644 deletions

View File

@ -14,6 +14,7 @@ use Cake\ORM\TableRegistry;
use Cake\Http\Client;
use Cake\Core\Configure;
use Cake\I18n\FrozenTime;
class AppRequestsController extends AppController
{
@ -23,7 +24,7 @@ class AppRequestsController extends AppController
$this->loadComponent('JsonRequestClient');
//$this->loadComponent('JsonRpcRequestClient');
//$this->Auth->allow(['add', 'edit']);
$this->Auth->allow(['index', 'sendCoins', 'createCoins']);
$this->Auth->allow(['index', 'sendCoins', 'createCoins', 'getBalance', 'listTransactions']);
}
@ -265,10 +266,90 @@ class AppRequestsController extends AppController
}
public function getBalance($session_id = 0)
{
$login_result = $this->requestLogin($session_id, false);
if($login_result !== true) {
return $this->returnJson($login_result);
}
$session = $this->getRequest()->getSession();
$user = $session->read('StateUser');
$state_balances_table = TableRegistry::getTableLocator()->get('StateBalances');
$state_balances_table->updateBalances($user['id']);
$state_balance = $state_balances_table->find()->where(['state_user_id' => $user['id']])->first();
if(!$state_balance) {
return $this->returnJson(['state' => 'success', 'balance' => 0]);
}
$now = new FrozenTime();
return $this->returnJson([
'state' => 'success',
'balance' => $state_balance->amount,
'decay' => $state_balance->partDecay($now),
'decay_date' => $now
]);
}
public function listTransactions($page = 1, $count = 25, $orderDirection = 'ASC', $session_id = 0)
{
$startTime = microtime(true);
$login_result = $this->requestLogin($session_id, false);
if($login_result !== true) {
return $this->returnJson($login_result);
}
$session = $this->getRequest()->getSession();
$user = $session->read('StateUser');
$stateBalancesTable = TableRegistry::getTableLocator()->get('StateBalances');
$stateUserTransactionsTable = TableRegistry::getTableLocator()->get('StateUserTransactions');
$transactionsTable = TableRegistry::getTableLocator()->get('Transactions');
$stateBalancesTable->updateBalances($user['id']);
$gdtSum = 0;
$gdtEntries = $this->JsonRequestClient->sendRequestGDT(['email' => $user['email']], 'GdtEntries' . DS . 'sumPerEmailApi');
if('success' == $gdtEntries['state'] && 'success' == $gdtEntries['data']['state']) {
$gdtSum = intval($gdtEntries['data']['sum']);
} else {
$this->addAdminError('StateBalancesController', 'overview', $gdtEntries, $user['id'] ? $user['id'] : 0);
}
}
$stateUserTransactionsQuery = $stateUserTransactionsTable
->find()
->where(['state_user_id' => $user['id']])
->order(['balance_date' => 'ASC'])
->contain([])
->limit($count)
->page($page)
;
$decay = true;
$transactions = $transactionsTable->listTransactionsHumanReadable($stateUserTransactionsQuery->toArray(), $user, $decay);
if($orderDirection == 'DESC') {
$transactions = array_reverse($transactions);
}
return $this->returnJson([
'state' => 'success',
'transactions' => $transactions,
'transactionExecutingCount' => $session->read('Transactions.executing'),
'count' => count($transactions),
'gdtSum' => $gdtSum,
'timeUsed' => microtime(true) - $startTime
]);
}
private function acquireAccessToken($session_id)
{
}
}

View File

@ -42,176 +42,7 @@ class StateBalancesController extends AppController
$this->set(compact('stateBalances'));
}
private function updateBalances($stateUserId)
{
$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()
->where(['state_user_id' => $stateUserId])
->order(['transaction_id ASC'])
//->contain(false);
;
if(!$state_user_transactions || !$state_user_transactions->count()) {
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;
$recalculate_state_user_transactions_balance = true;
}
if($state_balances->count() > 1) {
$clear_state_balance = true;
$create_state_balance = true;
$recalculate_state_user_transactions_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 && $last_state_user_transaction->balance <= 0) {
$recalculate_state_user_transactions_balance = true;
if(!$create_state_balance) {
$update_state_balance = true;
}
} else if(!$last_state_user_transaction) {
$creationsTable = TableRegistry::getTableLocator()->get('TransactionCreations');
$creationTransactions = $creationsTable
->find('all')
->where(['state_user_id' => $stateUserId])
->contain(false);
$transferTable = TableRegistry::getTableLocator()->get('TransactionSendCoins');
$transferTransactions = $transferTable
->find('all')
->where(['OR' => ['state_user_id' => $stateUserId, 'receiver_user_id' => $stateUserId]])
->contain(false);
if($creationTransactions->count() > 0 || $transferTransactions->count() > 0) {
$this->addAdminError(
'StateBalances',
'updateBalance', [
'state' => 'error',
'msg' => 'state_user_transactions is empty but it exist transactions for user'
],
$stateUserId);
}
}
}
// 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) { // creation
$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) { // transfer
$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()
{
@ -230,7 +61,7 @@ class StateBalancesController extends AppController
}
$user = $session->read('StateUser');
$update_balance_result = $this->updateBalances($user['id']);
$update_balance_result = $this->StateBalances->updateBalances($user['id']);
if($update_balance_result !== true) {
$this->addAdminError('StateBalances', 'overview', $update_balance_result, $user['id']);
}
@ -313,7 +144,7 @@ class StateBalancesController extends AppController
'name' => 'Gradido Akademie',
'type' => 'creation',
'transaction_id' => $creation->transaction_id,
'date' => $creation->transaction->received,
'date' => $creation->target_date,
'balance' => $creation->amount,
'memo' => $creation->transaction->memo
]);
@ -360,12 +191,16 @@ class StateBalancesController extends AppController
$current_state_balance = null;
$cursor = 0;
$transactions_reversed = array_reverse($transactions);
$decay_transactions = [];
$maxI = count($transactions_reversed);
foreach($transactions_reversed as $i => $transaction) {
if(!isset($transaction['transaction_id'])) {
//echo "missing transaction<br>";
continue;
}
$transaction_id = $transaction['transaction_id'];
//echo "transaction id: $transaction_id <br>";
$decay_transaction = NULL;
$state_balance = $this->StateBalances->newEntity();
@ -377,28 +212,24 @@ class StateBalancesController extends AppController
'transaction_id IN' => [$transaction_id, $prev_transaction['transaction_id']],
'state_user_id' => $user['id']
])
->order(['transaction_id ASC'])
->order(['balance_date ASC'])
->toArray();
$prev = $stateUserTransactions[0];
if($prev->balance > 0) {
// var_dump($stateUserTransactions);
$current = $stateUserTransactions[1];
//echo "decay between " . $prev->transaction_id . " and " . $current->transaction_id . "<br>";
$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 "<br>";
echo "prev balance: $prev->balance<br>diff: $diff_amount<br>";
echo "current balance: $current->balance<br>";
*
*/
//echo $interval->format('%R%a days');
$decay_transaction = [
//echo "prev balance: " . $prev->balance . ", diff_amount: $diff_amount, summe: " . (-intval($prev->balance - $diff_amount)) . "<br>";
$decay_transaction = [
'type' => 'decay',
'balance' => -($prev->balance - $diff_amount),
'balance' => -intval($prev->balance - $diff_amount),
'decay_duration' => $interval->format('%a days, %H hours, %I minutes, %S seconds'),
'memo' => ''
];
@ -406,8 +237,9 @@ class StateBalancesController extends AppController
}
if($decay_transaction) {
array_splice($transactions_reversed, $i + $cursor, 0, [$decay_transaction]);
$cursor++;
$decay_transactions[] = $decay_transaction;
//array_splice($transactions_reversed, $i + $cursor, 0, [$decay_transaction]);
//$cursor++;
}
if($i == $maxI-1) {
$stateUserTransaction = $stateUserTransactionsTable
@ -417,18 +249,24 @@ class StateBalancesController extends AppController
//var_dump($stateUserTransaction);
$state_balance->amount = $stateUserTransaction->balance;
$state_balance->record_date = $stateUserTransaction->balance_date;
$transactions_reversed[] = [
$decay_transactions[] = [
//$transactions_reversed[] = [
'type' => 'decay',
'balance' => -($stateUserTransaction->balance - $state_balance->decay),
'balance' => -intval($stateUserTransaction->balance - $state_balance->decay),
'decay_duration' => $stateUserTransaction->balance_date->timeAgoInWords(),
'memo' => ''
];
}
}
$final_transactions = [];
foreach($transactions_reversed as $i => $transaction) {
$final_transactions[] = $transaction;
$final_transactions[] = $decay_transactions[$i];
}
// for debugging
$calculated_balance = 0;
foreach($transactions_reversed as $tr) {
foreach($final_transactions as $tr) {
if($tr['type'] == 'send') {
$calculated_balance -= intval($tr['balance']);
} else {
@ -437,7 +275,7 @@ class StateBalancesController extends AppController
}
$this->set('calculated_balance', $calculated_balance);
$this->set('transactions', array_reverse($transactions_reversed));
$this->set('transactions', array_reverse($final_transactions));
$this->set('transactionExecutingCount', $session->read('Transactions.executing'));
$this->set('balance', $session->read('StateUser.balance'));
$this->set('timeUsed', microtime(true) - $startTime);
@ -456,7 +294,7 @@ class StateBalancesController extends AppController
$session = $this->getRequest()->getSession();
$user = $session->read('StateUser');
$this->updateBalances($user['id']);
$this->StateBalances->updateBalances($user['id']);
$state_balance = $this->StateBalances->find()->where(['state_user_id' => $user['id']])->first();
@ -472,7 +310,7 @@ class StateBalancesController extends AppController
}
public function ajaxListTransactions($session_id, $page, $count)
public function ajaxListTransactions($session_id, $page=1, $count=25)
{
if(!$session_id) {
return $this->returnJson(['state' => 'error', 'msg' => 'invalid session id']);
@ -485,6 +323,8 @@ class StateBalancesController extends AppController
}
$session = $this->getRequest()->getSession();
$user = $session->read('StateUser');
$this->StateBalances->updateBalances($user['id']);
$gdtSum = 0;
$gdtEntries = $this->JsonRequestClient->sendRequestGDT(['email' => $user['email']], 'GdtEntries' . DS . 'sumPerEmailApi');

View File

@ -64,7 +64,7 @@ class StateBalance extends Entity
return $this->amount;
}
//return $this->amount;
return $this->amount * pow(0.99999997802044727, $decay_duration);
return intval($this->amount * pow(0.99999997802044727, $decay_duration));
}
public function partDecay($target_date)
@ -74,7 +74,7 @@ class StateBalance extends Entity
return $this->amount;
}
//return 0;
return $this->amount * pow(0.99999997802044727, $decay_duration);
return intval($this->amount * pow(0.99999997802044727, $decay_duration));
}
public function decayDuration($target_date)

View File

@ -0,0 +1,39 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: gradido/BasicTypes.proto
namespace GPBMetadata\Gradido;
class BasicTypes
{
public static $is_initialized = false;
public static function initOnce() {
$pool = \Google\Protobuf\Internal\DescriptorPool::getGeneratedPool();
if (static::$is_initialized == true) {
return;
}
$pool->internalAddGeneratedFile(hex2bin(
"0aca030a186772616469646f2f426173696354797065732e70726f746f12" .
"0d70726f746f2e6772616469646f22380a034b657912110a076564323535" .
"313918022001280c480012170a0d656432353531395f7265663130180320" .
"01280c480042050a036b657922580a0d5369676e61747572655061697212" .
"0e0a067075624b657918012001280c12110a076564323535313918022001" .
"280c480012170a0d656432353531395f726566313018032001280c480042" .
"0b0a097369676e6174757265223d0a0c5369676e61747572654d6170122d" .
"0a077369675061697218012003280b321c2e70726f746f2e677261646964" .
"6f2e5369676e617475726550616972222b0a0954696d657374616d70120f" .
"0a077365636f6e6473180120012803120d0a056e616e6f73180220012805" .
"22230a1054696d657374616d705365636f6e6473120f0a077365636f6e64" .
"7318012001280322300a0e5472616e73666572416d6f756e74120e0a0670" .
"75626b657918012001280c120e0a06616d6f756e7418022001281222400a" .
"08486564657261494412100a0873686172644e756d18012001280312100a" .
"087265616c6d4e756d18022001280312100a08746f7069634e756d180320" .
"012803620670726f746f33"
), true);
static::$is_initialized = true;
}
}

View File

@ -0,0 +1,30 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: gradido/GradidoCreation.proto
namespace GPBMetadata\Gradido;
class GradidoCreation
{
public static $is_initialized = false;
public static function initOnce() {
$pool = \Google\Protobuf\Internal\DescriptorPool::getGeneratedPool();
if (static::$is_initialized == true) {
return;
}
\GPBMetadata\Gradido\BasicTypes::initOnce();
$pool->internalAddGeneratedFile(hex2bin(
"0ab0010a1d6772616469646f2f4772616469646f4372656174696f6e2e70" .
"726f746f120d70726f746f2e6772616469646f22780a0f4772616469646f" .
"4372656174696f6e122f0a08726563656976657218012001280b321d2e70" .
"726f746f2e6772616469646f2e5472616e73666572416d6f756e7412340a" .
"0b7461726765745f6461746518032001280b321f2e70726f746f2e677261" .
"6469646f2e54696d657374616d705365636f6e6473620670726f746f33"
), true);
static::$is_initialized = true;
}
}

View File

@ -0,0 +1,29 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: gradido/GradidoTransaction.proto
namespace GPBMetadata\Gradido;
class GradidoTransaction
{
public static $is_initialized = false;
public static function initOnce() {
$pool = \Google\Protobuf\Internal\DescriptorPool::getGeneratedPool();
if (static::$is_initialized == true) {
return;
}
\GPBMetadata\Gradido\BasicTypes::initOnce();
$pool->internalAddGeneratedFile(hex2bin(
"0a91010a206772616469646f2f4772616469646f5472616e73616374696f" .
"6e2e70726f746f120d70726f746f2e6772616469646f22560a1247726164" .
"69646f5472616e73616374696f6e122c0a077369675f6d61701801200128" .
"0b321b2e70726f746f2e6772616469646f2e5369676e61747572654d6170" .
"12120a0a626f64795f627974657318022001280c620670726f746f33"
), true);
static::$is_initialized = true;
}
}

View File

@ -0,0 +1,41 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: gradido/GradidoTransfer.proto
namespace GPBMetadata\Gradido;
class GradidoTransfer
{
public static $is_initialized = false;
public static function initOnce() {
$pool = \Google\Protobuf\Internal\DescriptorPool::getGeneratedPool();
if (static::$is_initialized == true) {
return;
}
\GPBMetadata\Gradido\BasicTypes::initOnce();
$pool->internalAddGeneratedFile(hex2bin(
"0ae6030a1d6772616469646f2f4772616469646f5472616e736665722e70" .
"726f746f120d70726f746f2e6772616469646f22500a0d4c6f63616c5472" .
"616e73666572122d0a0673656e64657218012001280b321d2e70726f746f" .
"2e6772616469646f2e5472616e73666572416d6f756e7412100a08726563" .
"656976657218022001280c22a3010a1243726f737347726f75705472616e" .
"73666572122d0a0673656e64657218012001280b321d2e70726f746f2e67" .
"72616469646f2e5472616e73666572416d6f756e7412100a087265636569" .
"76657218022001280c12130a0b6f746865725f67726f7570180320012809" .
"12370a157061697265645f7472616e73616374696f6e5f69641804200128" .
"0b32182e70726f746f2e6772616469646f2e54696d657374616d7022b501" .
"0a0f4772616469646f5472616e73666572122d0a056c6f63616c18012001" .
"280b321c2e70726f746f2e6772616469646f2e4c6f63616c5472616e7366" .
"6572480012340a07696e626f756e6418022001280b32212e70726f746f2e" .
"6772616469646f2e43726f737347726f75705472616e7366657248001235" .
"0a086f7574626f756e6418032001280b32212e70726f746f2e6772616469" .
"646f2e43726f737347726f75705472616e73666572480042060a04646174" .
"61620670726f746f33"
), true);
static::$is_initialized = true;
}
}

View File

@ -0,0 +1,30 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: gradido/GroupFriendsUpdate.proto
namespace GPBMetadata\Gradido;
class GroupFriendsUpdate
{
public static $is_initialized = false;
public static function initOnce() {
$pool = \Google\Protobuf\Internal\DescriptorPool::getGeneratedPool();
if (static::$is_initialized == true) {
return;
}
$pool->internalAddGeneratedFile(hex2bin(
"0ac6010a206772616469646f2f47726f7570467269656e64735570646174" .
"652e70726f746f120d70726f746f2e6772616469646f228a010a1247726f" .
"7570467269656e6473557064617465120d0a0567726f7570180120012809" .
"12380a06616374696f6e18022001280e32282e70726f746f2e6772616469" .
"646f2e47726f7570467269656e64735570646174652e416374696f6e222b" .
"0a06416374696f6e120e0a0a4144445f465249454e44100012110a0d5245" .
"4d4f56455f465249454e441001620670726f746f33"
), true);
static::$is_initialized = true;
}
}

View File

@ -0,0 +1,36 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: gradido/GroupMemberUpdate.proto
namespace GPBMetadata\Gradido;
class GroupMemberUpdate
{
public static $is_initialized = false;
public static function initOnce() {
$pool = \Google\Protobuf\Internal\DescriptorPool::getGeneratedPool();
if (static::$is_initialized == true) {
return;
}
\GPBMetadata\Gradido\BasicTypes::initOnce();
$pool->internalAddGeneratedFile(hex2bin(
"0ad2020a1f6772616469646f2f47726f75704d656d626572557064617465" .
"2e70726f746f120d70726f746f2e6772616469646f2297020a1147726f75" .
"704d656d62657255706461746512130a0b757365725f7075626b65791801" .
"2001280c124d0a126d656d6265725f7570646174655f7479706518022001" .
"280e32312e70726f746f2e6772616469646f2e47726f75704d656d626572" .
"5570646174652e4d656d6265725570646174655479706512370a15706169" .
"7265645f7472616e73616374696f6e5f696418032001280b32182e70726f" .
"746f2e6772616469646f2e54696d657374616d7012140a0c746172676574" .
"5f67726f7570180420012809224f0a104d656d6265725570646174655479" .
"7065120c0a084144445f55534552100012150a114d4f56455f555345525f" .
"494e424f554e44100112160a124d4f56455f555345525f4f5554424f554e" .
"441002620670726f746f33"
), true);
static::$is_initialized = true;
}
}

View File

@ -0,0 +1,32 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: gradido/ManageNodeBody.proto
namespace GPBMetadata\Gradido;
class ManageNodeBody
{
public static $is_initialized = false;
public static function initOnce() {
$pool = \Google\Protobuf\Internal\DescriptorPool::getGeneratedPool();
if (static::$is_initialized == true) {
return;
}
\GPBMetadata\Gradido\ManageNodeGroupAdd::initOnce();
\GPBMetadata\Gradido\BasicTypes::initOnce();
$pool->internalAddGeneratedFile(hex2bin(
"0ab9010a1c6772616469646f2f4d616e6167654e6f6465426f64792e7072" .
"6f746f120d70726f746f2e6772616469646f1a186772616469646f2f4261" .
"73696354797065732e70726f746f22680a0e4d616e6167654e6f6465426f" .
"647912160a0e76657273696f6e5f6e756d62657218012001280412360a09" .
"67726f75705f61646418032001280b32212e70726f746f2e677261646964" .
"6f2e4d616e6167654e6f646547726f7570416464480042060a0464617461" .
"620670726f746f33"
), true);
static::$is_initialized = true;
}
}

View File

@ -0,0 +1,30 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: gradido/ManageNodeGroupAdd.proto
namespace GPBMetadata\Gradido;
class ManageNodeGroupAdd
{
public static $is_initialized = false;
public static function initOnce() {
$pool = \Google\Protobuf\Internal\DescriptorPool::getGeneratedPool();
if (static::$is_initialized == true) {
return;
}
\GPBMetadata\Gradido\BasicTypes::initOnce();
$pool->internalAddGeneratedFile(hex2bin(
"0aaa010a206772616469646f2f4d616e6167654e6f646547726f75704164" .
"642e70726f746f120d70726f746f2e6772616469646f226f0a124d616e61" .
"67654e6f646547726f757041646412120a0a67726f75705f6e616d651801" .
"2001280912130a0b67726f75705f616c69617318022001280912300a0f68" .
"65646572615f746f7069635f696418032001280b32172e70726f746f2e67" .
"72616469646f2e4865646572614944620670726f746f33"
), true);
static::$is_initialized = true;
}
}

View File

@ -0,0 +1,36 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: gradido/ManageNodeRequest.proto
namespace GPBMetadata\Gradido;
class ManageNodeRequest
{
public static $is_initialized = false;
public static function initOnce() {
$pool = \Google\Protobuf\Internal\DescriptorPool::getGeneratedPool();
if (static::$is_initialized == true) {
return;
}
\GPBMetadata\Gradido\BasicTypes::initOnce();
$pool->internalAddGeneratedFile(hex2bin(
"0ade020a1f6772616469646f2f4d616e6167654e6f646552657175657374" .
"2e70726f746f120d70726f746f2e6772616469646f22550a114d616e6167" .
"654e6f646552657175657374122c0a077369675f6d617018012001280b32" .
"1b2e70726f746f2e6772616469646f2e5369676e61747572654d61701212" .
"0a0a626f64795f627974657318022001280c22cc010a124d616e6167654e" .
"6f6465526573706f6e7365120f0a0773756363657373180120012808123a" .
"0a056572726f7218022001280e322b2e70726f746f2e6772616469646f2e" .
"4d616e6167654e6f6465526573706f6e73652e4572726f72436f64652269" .
"0a094572726f72436f646512100a0c494e56414c49445f424f4459100012" .
"150a11494e56414c49445f5349474e4154555245100112140a105349474e" .
"45525f4e4f545f4b4e4f574e1002121d0a1947524f55505f414c4941535f" .
"414c52454144595f45584953541003620670726f746f33"
), true);
static::$is_initialized = true;
}
}

View File

@ -0,0 +1,46 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: gradido/TransactionBody.proto
namespace GPBMetadata\Gradido;
class TransactionBody
{
public static $is_initialized = false;
public static function initOnce() {
$pool = \Google\Protobuf\Internal\DescriptorPool::getGeneratedPool();
if (static::$is_initialized == true) {
return;
}
\GPBMetadata\Gradido\GradidoTransfer::initOnce();
\GPBMetadata\Gradido\GradidoCreation::initOnce();
\GPBMetadata\Gradido\GroupFriendsUpdate::initOnce();
\GPBMetadata\Gradido\GroupMemberUpdate::initOnce();
\GPBMetadata\Gradido\BasicTypes::initOnce();
$pool->internalAddGeneratedFile(hex2bin(
"0a92040a1d6772616469646f2f5472616e73616374696f6e426f64792e70" .
"726f746f120d70726f746f2e6772616469646f1a1d6772616469646f2f47" .
"72616469646f4372656174696f6e2e70726f746f1a206772616469646f2f" .
"47726f7570467269656e64735570646174652e70726f746f1a1f67726164" .
"69646f2f47726f75704d656d6265725570646174652e70726f746f1a1867" .
"72616469646f2f426173696354797065732e70726f746f22dd020a0f5472" .
"616e73616374696f6e426f6479120c0a046d656d6f18012001280912300a" .
"076372656174656418022001280b321f2e70726f746f2e6772616469646f" .
"2e54696d657374616d705365636f6e647312160a0e76657273696f6e5f6e" .
"756d62657218032001280412320a087472616e7366657218062001280b32" .
"1e2e70726f746f2e6772616469646f2e4772616469646f5472616e736665" .
"72480012320a086372656174696f6e18072001280b321e2e70726f746f2e" .
"6772616469646f2e4772616469646f4372656174696f6e480012410a1467" .
"726f75705f667269656e64735f75706461746518082001280b32212e7072" .
"6f746f2e6772616469646f2e47726f7570467269656e6473557064617465" .
"4800123f0a1367726f75705f6d656d6265725f7570646174651809200128" .
"0b32202e70726f746f2e6772616469646f2e47726f75704d656d62657255" .
"7064617465480042060a0464617461620670726f746f33"
), true);
static::$is_initialized = true;
}
}

View File

@ -0,0 +1,135 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: gradido/BasicTypes.proto
namespace Proto\Gradido;
use Google\Protobuf\Internal\GPBType;
use Google\Protobuf\Internal\RepeatedField;
use Google\Protobuf\Internal\GPBUtil;
/**
* copyed from hedera topic id
*
* Generated from protobuf message <code>proto.gradido.HederaID</code>
*/
class HederaID extends \Google\Protobuf\Internal\Message
{
/**
* The shard number (nonnegative)
*
* Generated from protobuf field <code>int64 shardNum = 1;</code>
*/
private $shardNum = 0;
/**
* The realm number (nonnegative)
*
* Generated from protobuf field <code>int64 realmNum = 2;</code>
*/
private $realmNum = 0;
/**
* Unique topic identifier within a realm (nonnegative).
*
* Generated from protobuf field <code>int64 topicNum = 3;</code>
*/
private $topicNum = 0;
/**
* Constructor.
*
* @param array $data {
* Optional. Data for populating the Message object.
*
* @type int|string $shardNum
* The shard number (nonnegative)
* @type int|string $realmNum
* The realm number (nonnegative)
* @type int|string $topicNum
* Unique topic identifier within a realm (nonnegative).
* }
*/
public function __construct($data = NULL) {
\GPBMetadata\Gradido\BasicTypes::initOnce();
parent::__construct($data);
}
/**
* The shard number (nonnegative)
*
* Generated from protobuf field <code>int64 shardNum = 1;</code>
* @return int|string
*/
public function getShardNum()
{
return $this->shardNum;
}
/**
* The shard number (nonnegative)
*
* Generated from protobuf field <code>int64 shardNum = 1;</code>
* @param int|string $var
* @return $this
*/
public function setShardNum($var)
{
GPBUtil::checkInt64($var);
$this->shardNum = $var;
return $this;
}
/**
* The realm number (nonnegative)
*
* Generated from protobuf field <code>int64 realmNum = 2;</code>
* @return int|string
*/
public function getRealmNum()
{
return $this->realmNum;
}
/**
* The realm number (nonnegative)
*
* Generated from protobuf field <code>int64 realmNum = 2;</code>
* @param int|string $var
* @return $this
*/
public function setRealmNum($var)
{
GPBUtil::checkInt64($var);
$this->realmNum = $var;
return $this;
}
/**
* Unique topic identifier within a realm (nonnegative).
*
* Generated from protobuf field <code>int64 topicNum = 3;</code>
* @return int|string
*/
public function getTopicNum()
{
return $this->topicNum;
}
/**
* Unique topic identifier within a realm (nonnegative).
*
* Generated from protobuf field <code>int64 topicNum = 3;</code>
* @param int|string $var
* @return $this
*/
public function setTopicNum($var)
{
GPBUtil::checkInt64($var);
$this->topicNum = $var;
return $this;
}
}

View File

@ -0,0 +1,90 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: gradido/ManageNodeBody.proto
namespace Proto\Gradido;
use Google\Protobuf\Internal\GPBType;
use Google\Protobuf\Internal\RepeatedField;
use Google\Protobuf\Internal\GPBUtil;
/**
* Generated from protobuf message <code>proto.gradido.ManageNodeBody</code>
*/
class ManageNodeBody extends \Google\Protobuf\Internal\Message
{
/**
* Generated from protobuf field <code>uint64 version_number = 1;</code>
*/
private $version_number = 0;
protected $data;
/**
* Constructor.
*
* @param array $data {
* Optional. Data for populating the Message object.
*
* @type int|string $version_number
* @type \Proto\Gradido\ManageNodeGroupAdd $group_add
* }
*/
public function __construct($data = NULL) {
\GPBMetadata\Gradido\ManageNodeBody::initOnce();
parent::__construct($data);
}
/**
* Generated from protobuf field <code>uint64 version_number = 1;</code>
* @return int|string
*/
public function getVersionNumber()
{
return $this->version_number;
}
/**
* Generated from protobuf field <code>uint64 version_number = 1;</code>
* @param int|string $var
* @return $this
*/
public function setVersionNumber($var)
{
GPBUtil::checkUint64($var);
$this->version_number = $var;
return $this;
}
/**
* Generated from protobuf field <code>.proto.gradido.ManageNodeGroupAdd group_add = 3;</code>
* @return \Proto\Gradido\ManageNodeGroupAdd
*/
public function getGroupAdd()
{
return $this->readOneof(3);
}
/**
* Generated from protobuf field <code>.proto.gradido.ManageNodeGroupAdd group_add = 3;</code>
* @param \Proto\Gradido\ManageNodeGroupAdd $var
* @return $this
*/
public function setGroupAdd($var)
{
GPBUtil::checkMessage($var, \Proto\Gradido\ManageNodeGroupAdd::class);
$this->writeOneof(3, $var);
return $this;
}
/**
* @return string
*/
public function getData()
{
return $this->whichOneof("data");
}
}

View File

@ -0,0 +1,119 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: gradido/ManageNodeGroupAdd.proto
namespace Proto\Gradido;
use Google\Protobuf\Internal\GPBType;
use Google\Protobuf\Internal\RepeatedField;
use Google\Protobuf\Internal\GPBUtil;
/**
* Generated from protobuf message <code>proto.gradido.ManageNodeGroupAdd</code>
*/
class ManageNodeGroupAdd extends \Google\Protobuf\Internal\Message
{
/**
* Generated from protobuf field <code>string group_name = 1;</code>
*/
private $group_name = '';
/**
* Generated from protobuf field <code>string group_alias = 2;</code>
*/
private $group_alias = '';
/**
*string parent_group_alias = 4;
*
* Generated from protobuf field <code>.proto.gradido.HederaID hedera_topic_id = 3;</code>
*/
private $hedera_topic_id = null;
/**
* Constructor.
*
* @param array $data {
* Optional. Data for populating the Message object.
*
* @type string $group_name
* @type string $group_alias
* @type \Proto\Gradido\HederaID $hedera_topic_id
* string parent_group_alias = 4;
* }
*/
public function __construct($data = NULL) {
\GPBMetadata\Gradido\ManageNodeGroupAdd::initOnce();
parent::__construct($data);
}
/**
* Generated from protobuf field <code>string group_name = 1;</code>
* @return string
*/
public function getGroupName()
{
return $this->group_name;
}
/**
* Generated from protobuf field <code>string group_name = 1;</code>
* @param string $var
* @return $this
*/
public function setGroupName($var)
{
GPBUtil::checkString($var, True);
$this->group_name = $var;
return $this;
}
/**
* Generated from protobuf field <code>string group_alias = 2;</code>
* @return string
*/
public function getGroupAlias()
{
return $this->group_alias;
}
/**
* Generated from protobuf field <code>string group_alias = 2;</code>
* @param string $var
* @return $this
*/
public function setGroupAlias($var)
{
GPBUtil::checkString($var, True);
$this->group_alias = $var;
return $this;
}
/**
*string parent_group_alias = 4;
*
* Generated from protobuf field <code>.proto.gradido.HederaID hedera_topic_id = 3;</code>
* @return \Proto\Gradido\HederaID
*/
public function getHederaTopicId()
{
return $this->hedera_topic_id;
}
/**
*string parent_group_alias = 4;
*
* Generated from protobuf field <code>.proto.gradido.HederaID hedera_topic_id = 3;</code>
* @param \Proto\Gradido\HederaID $var
* @return $this
*/
public function setHederaTopicId($var)
{
GPBUtil::checkMessage($var, \Proto\Gradido\HederaID::class);
$this->hedera_topic_id = $var;
return $this;
}
}

View File

@ -0,0 +1,92 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: gradido/ManageNodeRequest.proto
namespace Proto\Gradido;
use Google\Protobuf\Internal\GPBType;
use Google\Protobuf\Internal\RepeatedField;
use Google\Protobuf\Internal\GPBUtil;
/**
* Generated from protobuf message <code>proto.gradido.ManageNodeRequest</code>
*/
class ManageNodeRequest extends \Google\Protobuf\Internal\Message
{
/**
* Generated from protobuf field <code>.proto.gradido.SignatureMap sig_map = 1;</code>
*/
private $sig_map = null;
/**
* ManageNodeBody
*
* Generated from protobuf field <code>bytes body_bytes = 2;</code>
*/
private $body_bytes = '';
/**
* Constructor.
*
* @param array $data {
* Optional. Data for populating the Message object.
*
* @type \Proto\Gradido\SignatureMap $sig_map
* @type string $body_bytes
* ManageNodeBody
* }
*/
public function __construct($data = NULL) {
\GPBMetadata\Gradido\ManageNodeRequest::initOnce();
parent::__construct($data);
}
/**
* Generated from protobuf field <code>.proto.gradido.SignatureMap sig_map = 1;</code>
* @return \Proto\Gradido\SignatureMap
*/
public function getSigMap()
{
return $this->sig_map;
}
/**
* Generated from protobuf field <code>.proto.gradido.SignatureMap sig_map = 1;</code>
* @param \Proto\Gradido\SignatureMap $var
* @return $this
*/
public function setSigMap($var)
{
GPBUtil::checkMessage($var, \Proto\Gradido\SignatureMap::class);
$this->sig_map = $var;
return $this;
}
/**
* ManageNodeBody
*
* Generated from protobuf field <code>bytes body_bytes = 2;</code>
* @return string
*/
public function getBodyBytes()
{
return $this->body_bytes;
}
/**
* ManageNodeBody
*
* Generated from protobuf field <code>bytes body_bytes = 2;</code>
* @param string $var
* @return $this
*/
public function setBodyBytes($var)
{
GPBUtil::checkString($var, False);
$this->body_bytes = $var;
return $this;
}
}

View File

@ -0,0 +1,85 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: gradido/ManageNodeRequest.proto
namespace Proto\Gradido;
use Google\Protobuf\Internal\GPBType;
use Google\Protobuf\Internal\RepeatedField;
use Google\Protobuf\Internal\GPBUtil;
/**
* Generated from protobuf message <code>proto.gradido.ManageNodeResponse</code>
*/
class ManageNodeResponse extends \Google\Protobuf\Internal\Message
{
/**
* Generated from protobuf field <code>bool success = 1;</code>
*/
private $success = false;
/**
* Generated from protobuf field <code>.proto.gradido.ManageNodeResponse.ErrorCode error = 2;</code>
*/
private $error = 0;
/**
* Constructor.
*
* @param array $data {
* Optional. Data for populating the Message object.
*
* @type bool $success
* @type int $error
* }
*/
public function __construct($data = NULL) {
\GPBMetadata\Gradido\ManageNodeRequest::initOnce();
parent::__construct($data);
}
/**
* Generated from protobuf field <code>bool success = 1;</code>
* @return bool
*/
public function getSuccess()
{
return $this->success;
}
/**
* Generated from protobuf field <code>bool success = 1;</code>
* @param bool $var
* @return $this
*/
public function setSuccess($var)
{
GPBUtil::checkBool($var);
$this->success = $var;
return $this;
}
/**
* Generated from protobuf field <code>.proto.gradido.ManageNodeResponse.ErrorCode error = 2;</code>
* @return int
*/
public function getError()
{
return $this->error;
}
/**
* Generated from protobuf field <code>.proto.gradido.ManageNodeResponse.ErrorCode error = 2;</code>
* @param int $var
* @return $this
*/
public function setError($var)
{
GPBUtil::checkEnum($var, \Proto\Gradido\ManageNodeResponse_ErrorCode::class);
$this->error = $var;
return $this;
}
}

View File

@ -0,0 +1,61 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: gradido/ManageNodeRequest.proto
namespace Proto\Gradido\ManageNodeResponse;
use UnexpectedValueException;
/**
* Protobuf type <code>proto.gradido.ManageNodeResponse.ErrorCode</code>
*/
class ErrorCode
{
/**
* Generated from protobuf enum <code>INVALID_BODY = 0;</code>
*/
const INVALID_BODY = 0;
/**
* Generated from protobuf enum <code>INVALID_SIGNATURE = 1;</code>
*/
const INVALID_SIGNATURE = 1;
/**
* Generated from protobuf enum <code>SIGNER_NOT_KNOWN = 2;</code>
*/
const SIGNER_NOT_KNOWN = 2;
/**
* Generated from protobuf enum <code>GROUP_ALIAS_ALREADY_EXIST = 3;</code>
*/
const GROUP_ALIAS_ALREADY_EXIST = 3;
private static $valueToName = [
self::INVALID_BODY => 'INVALID_BODY',
self::INVALID_SIGNATURE => 'INVALID_SIGNATURE',
self::SIGNER_NOT_KNOWN => 'SIGNER_NOT_KNOWN',
self::GROUP_ALIAS_ALREADY_EXIST => 'GROUP_ALIAS_ALREADY_EXIST',
];
public static function name($value)
{
if (!isset(self::$valueToName[$value])) {
throw new UnexpectedValueException(sprintf(
'Enum %s has no name defined for value %s', __CLASS__, $value));
}
return self::$valueToName[$value];
}
public static function value($name)
{
$const = __CLASS__ . '::' . strtoupper($name);
if (!defined($const)) {
throw new UnexpectedValueException(sprintf(
'Enum %s has no value defined for name %s', __CLASS__, $name));
}
return constant($const);
}
}
// Adding a class alias for backwards compatibility with the previous class name.
class_alias(ErrorCode::class, \Proto\Gradido\ManageNodeResponse_ErrorCode::class);

View File

@ -0,0 +1,16 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: gradido/ManageNodeRequest.proto
namespace Proto\Gradido;
if (false) {
/**
* This class is deprecated. Use Proto\Gradido\ManageNodeResponse\ErrorCode instead.
* @deprecated
*/
class ManageNodeResponse_ErrorCode {}
}
class_exists(ManageNodeResponse\ErrorCode::class);
@trigger_error('Proto\Gradido\ManageNodeResponse_ErrorCode is deprecated and will be removed in the next major release. Use Proto\Gradido\ManageNodeResponse\ErrorCode instead', E_USER_DEPRECATED);

View File

@ -82,29 +82,7 @@ class StateBalancesTable extends Table
return $rules;
}
/*
* create new state balance at beginning of next month from $previousStateBalance
* calculate decay for the time diff
*/
private function calculateStateBalance($previousStateBalance)
{
$entity = $this->newEntity();
$entity->state_user_id = $previousStateBalance->state_user_id;
$newDate = $previousStateBalance->record_date;
$newDate->day(1);
if($newDate->month <= 12) {
$newDate->month($newDate->month + 1);
} else {
$newDate->month(1);
$newDate->year($newDate->year + 1);
}
$entity->record_date = $newDate;
$entity->amount = $previousStateBalance->partDecay($newDate);
if($this->save($entity)) {
return $entity;
}
return ['state' => 'error', 'msg' => 'couldn\'t save', 'details' => $entity->getErrors()];
}
public function sortTransactions($a, $b)
{
@ -113,240 +91,178 @@ class StateBalancesTable extends Table
}
return ($a['date'] > $b['date']) ? -1 : 1;
}
/*
* calculate balance at end of month
* work only if state balance at begin of month exist
* use transaction_send_coins and transaction_creations
*/
public function updateLastStateBalanceOfMonth($month, $year, $state_user_id)
public function updateBalances($stateUserId)
{
$first_of_month = new Time("$year-$month-01 00:00");
$last_of_month = new Time($first_of_month);
$last_of_month->addMonth(1);
$last_of_month->subSecond(1);
$query = $this->find('all')
->where(['AND' => [
'state_user_id' => $state_user_id,
'record_date >=' => $first_of_month,
'record_date <=' => $last_of_month
]])
->order(['record_date' => 'ASC']);
if($query->isEmpty()) {
return [
'state' => 'error',
'msg' => 'no state balance in this month found',
'details' => [
'month' => $month,
'year' => $year,
'state_user_id' => $state_user_id
]
];
}
// get transactions from this month
$balance_changes = [];
$transactionCreationsTable = TableRegistry::getTableLocator()->get('TransactionCreations');
$transactionTransfersTable = TableRegistry::getTableLocator()->get('TransactionSendCoins');
$relevant_creations = $transactionCreationsTable
->find('all')
->where(['AND' => [
'state_user_id' => $state_user_id,
'target_date >=' => $first_of_month,
'target_date <=' => $last_of_month
]])->contain(false);
foreach($relevant_creations as $creation) {
$balance_changes[] = ['amount' => $creation->amount, 'date' => $creation->target_date];
}
$relevant_transfers = $transactionTransfersTable
->find('all')
->where(['AND' => [
'OR' => [
'state_user_id' => $state_user_id,
'receiver_user_id' => $state_user_id
],
'transaction.received >= ' => $first_of_month,
'transaction.received <=' => $last_of_month
]])->contain(['Transactions']);
debug($relevant_transfers);
foreach($relevant_transfers as $transfer) {
$amount = $transfer->amount;
// if it is a send transaction, negate the value
if($transfer->state_user_id == $state_user_id) {
$amount *= -1.0;
}
$balance_changes[] = ['amount' => $amount, 'date' => $transfer->transaction->received];
}
uasort($balance_changes, array($this, 'sortTransactions'));
$current_state_balance = null;
if($query->count() == 1) {
$current_state_balance = $this->newEntity();
$current_state_balance->amount = $query->first()->amount;
$current_state_balance->state_user_id = $state_user_id;
$current_state_balance->record_date = $query->first()->record_date;
} else if($query->count() == 2) {
$array = $query->toArray();
$current_state_balance = $array[1];
} else {
throw new Exception('Should\'n occure, never');
$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->find('all')->where(['state_user_id' => $stateUserId]);
$state_user_transactions = $stateUserTransactionsTable
->find()
->where(['state_user_id' => $stateUserId])
->order(['balance_date ASC'])
//->contain(false);
;
if(!$state_user_transactions || !$state_user_transactions->count()) {
return true;
}
foreach($balance_changes as $change) {
$current_state_balance->amount = $current_state_balance->getDecay($change['date']);
$current_state_balance->amount += $change['amount'];
$current_state_balance->record_date = $change['date'];
// 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;
$recalculate_state_user_transactions_balance = true;
}
if(!$this->save($current_state_balance)) {
return ['state' => 'error', 'msg' => 'couldn\'t save', 'details' => $current_state_balance->getErrors()];
if($state_balances->count() > 1) {
$clear_state_balance = true;
$create_state_balance = true;
$recalculate_state_user_transactions_balance = true;
}
return $current_state_balance;
}
/*
* getting start balance for month
* create and create all missing state_balances before if not exist
* in while loop
*/
public function chooseForMonthAndUser($month, $year, $state_user_id)
{
$first_of_month = new Time("$year-$month-01 00:00");
$last_of_month = new Time($first_of_month);
$last_of_month->addMonth(1);
$last_of_month->subSecond(1);
//echo "first of month: " . $first_of_month->i18nFormat() . ", last of month: " . $last_of_month->i18nFormat() . "<br>";
$query = $this->find('all');
$query->select([
'month' => $query->func()->month(['record_date' => 'identifier']),
'year' => $query->func()->year(['record_date' => 'identifier'])
])->select($this)
//->where(['month' => $month, 'year' => $year, 'state_user_id' => $state_user_id])
->where(['AND' => [
'state_user_id' => $state_user_id,
'record_date >=' => $first_of_month,
'record_date <=' => $last_of_month
]
])
->order(['record_date' => 'ASC'])
->limit(1)
->contain([]);
if($query->count() == 0)
{
// if any state balance for user exist, pick last one
$state_balances = $this->find('all')
->where(['state_user_id' => $state_user_id])
->limit(1)
->order(['record_date' => 'DESC'])
;
// create one for first user transaction
if($state_balances->isEmpty())
{
$state_user_transactions_table = TableRegistry::getTableLocator()->get('StateUserTransactions');
$state_user_transaction = $state_user_transactions_table->find('all')
->where(['state_user_id' => $state_user_id, 'StateUserTransactions.transaction_type_id <' => 3])
->contain(['Transactions' => ['TransactionCreations', 'TransactionSendCoins']])
->limit(1)
->order(['transaction_id' => 'ASC'])
->first()
;
if(!$state_user_transaction) {
return null;
}
$entity = $this->newEntity();
$entity->state_user_id = $state_user_id;
if($state_user_transaction->transaction_type_id == 1) {
$creation = $state_user_transaction->transaction->transaction_creations[0];
$entity->amount = $creation->amount;
$entity->record_date = $creation->target_date;
} else if($state_user_transaction->transaction_type_id == 2) {
$transfer = $state_user_transaction->transaction->transaction_send_coins[0];
$entity->amount = $transfer->amount;
$entity->record_date = $state_user_transaction->transaction->received;
}
if(!$this->save($entity)) {
return ['state' => 'error', 'msg' => 'error by saving state balance', 'details' => $entity->getErrors()];
}
}
$state_balances = $this->find('all')
->where(['state_user_id' => $state_user_id])
->limit(1)
->order(['record_date' => 'DESC'])
;
if($state_balances->count() == 1)
{
$current_state_balance = $state_balances->first();
while(true)
{
$new_state_balance_begin = $this->calculateStateBalance($current_state_balance);
if(is_array($new_state_balance_begin)) {
return ['state' => 'error', 'msg' => 'error calculate state balance', 'details' => $new_state_balance_begin];
}
$record_date = $new_state_balance_begin->record_date;
if($record_date->month === $month && $record_date->year === $year) {
return $new_state_balance_begin;
}
$current_state_balance = $this->updateLastStateBalanceOfMonth($month, $year, $state_user_id);
}
}
else
{
return ['state' => 'error', 'msg' => 'creation of first state_balance failes'];
}
}
return $query->first();
}
public function updateBalanceWithTransaction($newBalance, $recordDate, $userId)
{
// max 2 StateBalance Entrys per month:
// 1. first of month or first transaction of user
// 2. last of month or last transaction of user
$first_state_balance_of_month = $this->chooseForMonthAndUser($recordDate->month, $recordDate->year, $userId);
$updated_state_balance = null;
if($first_state_balance_of_month == null || is_array($first_state_balance_of_month)) {
return $first_state_balance_of_month;
}
if($first_state_balance_of_month->record_date->day == $recordDate->day &&
$recordDate > $first_state_balance_of_month->record_date) {
if($first_state_balance_of_month->amount == $newBalance) {
// nothing to do here
return true;
}
$updated_state_balance = $first_state_balance_of_month;
$updated_state_balance->amount = $newBalance;
// copy complete record date, inclusive time
$first_state_balance_of_month->record_date = $recordDate;
} else {
$query = $this->find('all')
->where(['AND' => [
'record_date >' => $first_state_balance_of_month->record_date,
'record_date <=' => $recordDate,
'state_user_id' => $userId
]]);
if(!$query->isEmpty()) {
$updated_state_balance = $query->first();
if($updated_state_balance->record_date == $recordDate) {
return true;
}
if($state_balances->count() == 1) {
if($state_user_transactions->count() == 0){
$clear_state_balance = true;
} else {
$updated_state_balance = $this->newEntity();
$updated_state_balance->state_user_id = $userId;
$last_state_user_transaction = $state_user_transactions->last();
$last_transaction = $this->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;
}
}
$updated_state_balance->record_date = $recordDate;
$updated_state_balance->amount = $newBalance;
}
if($updated_state_balance) {
if(!$this->save($updated_state_balance)) {
return ['state' => 'error', 'msg' => 'error by saving state balance', 'details' => $entity->getErrors()];
}
// delete all state_balances which came after
// they will be automaticlly recovered by next call of chooseForMonthAndUser
$this->deleteAll(['state_user_id' => $userId, 'record_date >' => $recordDate]);
}
if(!$recalculate_state_user_transactions_balance) {
$last_state_user_transaction = $state_user_transactions->last();
if($last_state_user_transaction && $last_state_user_transaction->balance <= 0) {
$recalculate_state_user_transactions_balance = true;
if(!$create_state_balance) {
$update_state_balance = true;
}
} else if(!$last_state_user_transaction) {
$creationsTable = TableRegistry::getTableLocator()->get('TransactionCreations');
$creationTransactions = $creationsTable
->find('all')
->where(['state_user_id' => $stateUserId])
->contain(false);
$transferTable = TableRegistry::getTableLocator()->get('TransactionSendCoins');
$transferTransactions = $transferTable
->find('all')
->where(['OR' => ['state_user_id' => $stateUserId, 'receiver_user_id' => $stateUserId]])
->contain(false);
if($creationTransactions->count() > 0 || $transferTransactions->count() > 0) {
return ['success' => false, 'error' => 'state_user_transactions is empty but it exist transactions for user'];
}
}
}
// second: do what is needed
if($clear_state_balance) {
$this->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']);
$transactions_indiced = [];
foreach($transactions as $transaction) {
$transactions_indiced[$transaction->id] = $transaction;
}
$balance_cursor = $this->newEntity();
$i = 0;
foreach($state_user_transactions_array as $state_user_transaction) {
$transaction = $transactions_indiced[$state_user_transaction->transaction_id];
if($transaction->transaction_type_id > 2) {
continue;
}
//echo "transaction id: ".$transaction->id . "<br>";
$amount_date = null;
$amount = 0;
if($transaction->transaction_type_id == 1) { // creation
$temp = $transaction->transaction_creations[0];
/*$balance_temp = $this->newEntity();
$balance_temp->amount = $temp->amount;
$balance_temp->record_date = $temp->target_date;
*/
$amount = intval($temp->amount);//$balance_temp->partDecay($transaction->received);
$amount_date = $temp->target_date;
//$amount_date =
} else if($transaction->transaction_type_id == 2) { // transfer
$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;
}
//echo "new balance: " . $balance_cursor->amount . "<br>";
$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->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->save($state_balance)) {
return ['success' => false, 'error' => 'error saving state balance', 'details' => $state_balance->getErrors()];
}
}
return true;
return true;
}
}

View File

@ -115,4 +115,22 @@ class StateUsersTable extends Table
}
return $receiverProposal;
}
public function getUsersIndiced($user_ids)
{
$involvedUser_temp = array_flip($user_ids);
// exchange back
$involvedUserIds = array_flip($involvedUser_temp);
$involvedUser = $this->find('all', [
'contain' => false,
'where' => ['id IN' => $involvedUserIds],
'fields' => ['id', 'first_name', 'last_name', 'email']
]);
//var_dump($involvedUser->toArray());
$involvedUserIndices = [];
foreach ($involvedUser as $involvedUser) {
$involvedUserIndices[$involvedUser->id] = $involvedUser;
}
return $involvedUserIndices;
}
}

View File

@ -65,6 +65,10 @@ class TransactionSendCoinsTable extends Table
->integer('id')
->allowEmptyString('id', null, 'create');
$validator
->requirePresence('sender_public_key', 'create')
->notEmptyString('sender_public_key');
$validator
->requirePresence('receiver_public_key', 'create')
->notEmptyString('receiver_public_key');

View File

@ -5,6 +5,7 @@ use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
use Cake\ORM\TableRegistry;
/**
* Transactions Model
@ -59,19 +60,19 @@ class TransactionsTable extends Table
$this->hasMany('StateCreated', [
'foreignKey' => 'transaction_id'
]);
$this->hasMany('TransactionCreations', [
$this->hasOne('TransactionCreations', [
'foreignKey' => 'transaction_id'
]);
$this->hasMany('TransactionGroupAddaddress', [
$this->hasOne('TransactionGroupAddaddress', [
'foreignKey' => 'transaction_id'
]);
$this->hasMany('TransactionGroupAllowtrades', [
$this->hasOne('TransactionGroupAllowtrades', [
'foreignKey' => 'transaction_id'
]);
$this->hasMany('TransactionGroupCreates', [
$this->hasOne('TransactionGroupCreates', [
'foreignKey' => 'transaction_id'
]);
$this->hasMany('TransactionSendCoins', [
$this->hasOne('TransactionSendCoins', [
'foreignKey' => 'transaction_id'
]);
$this->hasMany('TransactionSignatures', [
@ -122,4 +123,147 @@ class TransactionsTable extends Table
return $rules;
}
public function sortTransactions($a, $b)
{
if ($a['date'] == $b['date']) {
return 0;
}
return ($a['date'] > $b['date']) ? -1 : 1;
}
public function listTransactionsHumanReadable($stateUserTransactions, array $user, $decay = true)
{
$stateUsersTable = TableRegistry::getTableLocator()->get('StateUsers');
$stateBalancesTable = TableRegistry::getTableLocator()->get('StateBalances');
$transaction_ids = [];
$involved_user_ids = [];
$stateUserTransactionsCount = 0;
foreach($stateUserTransactions as $su_transaction) {
$transaction_ids[] = $su_transaction->transaction_id;
$involved_user_ids[] = $su_transaction->state_user_id;
$stateUserTransactionsCount++;
}
$involved_users = $stateUsersTable->getUsersIndiced($involved_user_ids);
$transactions = $this
->find()
->where(['Transactions.id IN' => $transaction_ids])
->contain(['TransactionSendCoins', 'TransactionCreations'])
;
$transaction_indiced = [];
foreach($transactions as $tr) {
$transaction_indiced[$tr->id] = $tr;
}
$state_balance = $stateBalancesTable->newEntity();
$final_transactions = [];
foreach($stateUserTransactions as $i => $su_transaction)
{
/*echo "i: $i<br>";
echo "state user transaction: <br>";
var_dump($su_transaction);
echo "<br>";*/
//var_dump($su_transaction);
//die("step");
// add decay transactions
if($i > 0 && $decay == true)
{
$prev = $stateUserTransactions[$i-1];
if($prev->balance > 0) {
// var_dump($stateUserTransactions);
$current = $su_transaction;
//echo "decay between " . $prev->transaction_id . " and " . $current->transaction_id . "<br>";
$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 $interval->format('%R%a days');
//echo "prev balance: " . $prev->balance . ", diff_amount: $diff_amount, summe: " . (-intval($prev->balance - $diff_amount)) . "<br>";
$final_transactions[] = [
'type' => 'decay',
'balance' => -intval($prev->balance - $diff_amount),
'decay_duration' => $interval->format('%a days, %H hours, %I minutes, %S seconds'),
'memo' => ''
];
}
}
// sender or receiver when user has sended money
// group name if creation
// type: gesendet / empfangen / geschöpft
// transaktion nr / id
// date
// balance
$transaction = $transaction_indiced[$su_transaction->transaction_id];
/*echo "transaction: <br>";
var_dump($transaction);
echo "<br>";*/
if($su_transaction->transaction_type_id == 1) { // creation
$creation = $transaction->transaction_creation;
$final_transactions[] = [
'name' => 'Gradido Akademie',
'type' => 'creation',
'transaction_id' => $transaction->id,
'date' => $creation->target_date,
'balance' => $creation->amount,
'memo' => $transaction->memo
];
} else if($su_transaction->transaction_type_id == 2) { // transfer or send coins
$sendCoins = $transaction->transaction_send_coin;
$type = '';
$otherUser = null;
$other_user_public = '';
if ($sendCoins->state_user_id == $user['id']) {
$type = 'send';
if(isset($involved_users[$sendCoins->receiver_user_id])) {
$otherUser = $involved_users[$sendCoins->receiver_user_id];
}
$other_user_public = bin2hex(stream_get_contents($sendCoins->receiver_public_key));
} else if ($sendCoins->receiver_user_id == $user['id']) {
$type = 'receive';
if(isset($involved_users[$sendCoins->state_user_id])) {
$otherUser = $involved_users[$sendCoins->state_user_id];
}
if($sendCoins->sender_public_key) {
$other_user_public = bin2hex(stream_get_contents($sendCoins->sender_public_key));
}
}
if(null == $otherUser) {
$otherUser = $stateUsersTable->newEntity();
}
$final_transactions[] = [
'name' => $otherUser->first_name . ' ' . $otherUser->last_name,
'email' => $otherUser->email,
'type' => $type,
'transaction_id' => $sendCoins->transaction_id,
'date' => $transaction->received,
'balance' => $sendCoins->amount,
'memo' => $transaction->memo,
'pubkey' => $other_user_public
];
}
if($i == $stateUserTransactionsCount-1 && $decay == true) {
$state_balance->amount = $su_transaction->balance;
$state_balance->record_date = $su_transaction->balance_date;
$final_transactions[] = [
'type' => 'decay',
'balance' => -intval($su_transaction->balance - $state_balance->decay),
'decay_duration' => $su_transaction->balance_date->timeAgoInWords(),
'memo' => ''
];
}
}
return $final_transactions;
}
}

View File

@ -63,7 +63,6 @@ class TransactionBase {
protected function updateStateBalance($stateUserId, $addAmountCent, $recordDate) {
$finalBalance = 0;
$stateBalancesTable = self::getTable('stateBalances');
$stateBalanceQuery = $stateBalancesTable
->find('all')
@ -92,7 +91,7 @@ class TransactionBase {
return $finalBalance;
}
protected function addStateUserTransaction($stateUserId, $transactionId, $transactionTypeId, $balance) {
protected function addStateUserTransaction($stateUserId, $transactionId, $transactionTypeId, $balance, $balance_date) {
$stateUserTransactionTable = self::getTable('state_user_transactions');
$stateUserTransactions = $stateUserTransactionTable
->find('all')
@ -116,12 +115,24 @@ class TransactionBase {
$entity->transaction_id = $transactionId;
$entity->transaction_type_id = $transactionTypeId;
$entity->balance = $balance;
$entity->balance_date = $balance_date;
if(!$stateUserTransactionTable->save($entity)) {
$errors = $entity->getErrors();
$this->addError('TransactionBase::addStateUserTransaction', 'error saving state user balance with: ' . json_encode($errors));
return false;
}
// set balance from all state_user_transactions which came after (sorted by balance_date) to 0
// because creation transaction can be added before other transaction which already happend
$state_user_transactions = $stateUserTransactionTable
->find()
->select(['id', 'balance'])
->where(['state_user_id' => $stateUserId, 'balance_date >' => $balance_date])
;
foreach($state_user_transactions as $t) {
$t->balance = 0;
}
$stateUserTransactionTable->saveMany($state_user_transactions);
return true;
}
}

View File

@ -7,6 +7,7 @@ namespace Model\Transactions;
use Cake\ORM\TableRegistry;
use Cake\Core\Configure;
use Cake\Mailer\Email;
use Cake\I18n\FrozenTime;
use Cake\I18n\FrozenDate;
@ -153,24 +154,20 @@ class TransactionCreation extends TransactionBase {
$transactionCreationEntity->state_user_id = $receiverUserId;
$transactionCreationEntity->amount = $this->getAmount();
$transactionCreationEntity->target_date = $this->protoTransactionCreation->getTargetDate()->getSeconds();
$target_date = new FrozenTime($transactionCreationEntity->target_date);
if(!$this->transactionCreationsTable->save($transactionCreationEntity)) {
$this->addError('TransactionCreation::save', 'error saving transactionCreation with errors: ' . json_encode($transactionCreationEntity->getErrors()));
return false;
}
// update state balance
$final_balance = $this->updateStateBalance($receiverUserId, $this->getAmount(), $transactionCreationEntity->target_date);
$final_balance = $this->updateStateBalance($receiverUserId, $this->getAmount(), $target_date);
if(false === $final_balance) {
return false;
}
$target_date = new FrozenDate($transactionCreationEntity->target_date);
$stateBalancesTable = self::getTable('stateBalances');
$state_balance = $stateBalancesTable->newEntity();
$state_balance->amount = $this->getAmount();
$state_balance->record_date = $target_date;
// decay is a virtual field which is calculated from amount and now() - record_date
if(!$this->addStateUserTransaction($receiverUserId, $transaction_id, 1, $state_balance->decay)) {
if(!$this->addStateUserTransaction($receiverUserId, $transaction_id, 1, $this->getAmount(), $target_date)) {
return false;
}

View File

@ -154,6 +154,7 @@ class TransactionTransfer extends TransactionBase {
$transactionTransferEntity = $transactionTransferTable->newEntity();
$transactionTransferEntity->transaction_id = $transaction_id;
$transactionTransferEntity->state_user_id = $senderUserId;
$transactionTransferEntity->sender_public_key = $senderAmount->getPubkey();
$transactionTransferEntity->receiver_public_key = $receiver;
$transactionTransferEntity->receiver_user_id = $receiverUserId;
$transactionTransferEntity->amount = $senderAmount->getAmount();
@ -164,10 +165,10 @@ class TransactionTransfer extends TransactionBase {
return false;
}
if(!$this->addStateUserTransaction($senderUserId, $transaction_id, 2, $senderAmount->getAmount())) {
if(!$this->addStateUserTransaction($senderUserId, $transaction_id, 2, $senderAmount->getAmount(), $received)) {
return false;
}
if(!$this->addStateUserTransaction($receiverUserId, $transaction_id, 2, -$senderAmount->getAmount())) {
if(!$this->addStateUserTransaction($receiverUserId, $transaction_id, 2, -$senderAmount->getAmount(), $received)) {
return false;
}

View File

@ -90,8 +90,8 @@ class StateBalancesControllerTest extends TestCase
$response = $this->getAndParseWithoutCompare('/state-balances/ajaxGetBalance/' . $session_id);
$this->assertEquals('success', $response->state);
$this->assertEquals(7321828, $response->balance);
$this->assertLessThan(7321828, $response->decay);
$this->assertEquals(7321825, $response->balance);
$this->assertLessThan(7321825, $response->decay);
}
@ -127,8 +127,8 @@ class StateBalancesControllerTest extends TestCase
$response = $this->getAndParseWithoutCompare('/state-balances/ajaxGetBalance/' . $session_id);
$this->assertEquals('success', $response->state);
$this->assertEquals(9112595, $response->balance);
$this->assertLessThan(9112595, $response->decay);
$this->assertEquals(9112592, $response->balance);
$this->assertLessThan(9112592, $response->decay);
}
public function testAjaxGetBalanceInvalidSession()

View File

@ -12,26 +12,45 @@ This document describes the community server API. The community server is writte
Returns the current account balance
### Request
`GET http://localhost/state-balances/ajaxGetBalance/-127182`
`GET http://localhost/api/getBalance/`
### Response
Assuming: session is valid
Session will be searched in php session and GRADIDO_LOGIN cookie.
Additional session can be provided as GET-Parameter
`GET http://localhost/api/getBalance/-127182`
```json
{
"state":"success",
"balance":174500
"balance":15906078,
"decay":15873851,
"decay_date":"2021-04-16T11:47:21+00:00"
}
```
- `balance`: balance describes gradido cents which are 4 digits behind the separator. A balance value of 174500 equals therefor 17,45 GDD
- `balance` : balance describes gradido cents which are 4 digits behind the separator. A balance value of 174500 equals therefor 17,45 GDD
- `decay` : balance with decay on it at the time in decay_date, so it is the precise balance of user at time of calling this function
- `decay_date`: date and time for decay amount, should be the time and date of function call
## List transactions
List all transactions for logged in user
### Request
`GET http://localhost/state-balances/ajaxListTransactions/-127182/[DESC]`
(The `DESC` part is optional symbolized by [])
`GET http://localhost/api/listTransactions/[1]/[25]/[DESC]/[session_id]`
Parts symbolized by [] are optional
- first parameter (1) is page for paging
- second parameter (25) is count for paging
- third parameter is ordering of resulting array, default is ASC
- fourth parameter is session_id (session will be searched in php session and GRADIDO_LOGIN cookie and if not found use this )
#### Paging
With count you say how many entrys you like to have in the result.
With page you say on which page you are.
For example 50 transactions are in db.
With 1/25 you get the first 25 transactions (1-25)
With 2/20 you get the second 20 transactions (21-40)
### Response
Assuming: session is valid

View File

@ -48,11 +48,36 @@ In case of success returns:
```json
{
"state":"success",
"user": {
"created": 1614782270,
"disabled": false,
"email": "max.musterman@gmail.de",
"email_checked": true,
"first_name": "Max",
"group_alias": "gdd1",
"ident_hash": 323769895,
"last_name": "Mustermann",
"public_hex": "131c7f68dd94b2be4c913400ff7ff4cdc03ac2bda99c2d29edcacb3b065c67e6",
"role": "none",
"username": ""
}
"session_id": -127182,
"clientIP":"123.123.123.123"
}
```
- `user`: contain user object
- `created`: timestamp on which account was created
- `disabled`: true if account was disabled, if disabled no login or coin transfer is possible
- `email`: email of user
- `email_checked`: true if user has successfully clicked on activation link in email
- `first_name`: first name of user
- `group_alias`: alias of group/community to which user belong
- `ident_hash`: currently hash of email, will be later a identification hash to prevent multiple accounts and therefore multiple creations per user
- `last_name`: last name of user
- `public_hex`: public key of user in hex format
- `role`: role of user currently only "none" or "admin"
- `username`: not used yet
- `clientIP`: should be the same as where the js-client is running, else maybe a man-in-the-middle attacks is happening or
nginx was wrong configured.
- `session_id`: can be also negative
@ -265,6 +290,19 @@ In case of success returns:
{
"state":"success", "email_verification_code_type":"resetPassword",
"info":[],
"user": {
"created": 1614782270,
"disabled": false,
"email": "max.musterman@gmail.de",
"email_checked": true,
"first_name": "Max",
"group_alias": "gdd1",
"ident_hash": 323769895,
"last_name": "Mustermann",
"public_hex": "131c7f68dd94b2be4c913400ff7ff4cdc03ac2bda99c2d29edcacb3b065c67e6",
"role": "none",
"username": ""
}
"session_id":1853761475
}
```
@ -274,8 +312,20 @@ In case of success returns:
- `registerDirect`: code generated by register for check email
- `register`: code generated by auto-register via elopage for check email
- `info`: can contain additional info strings
- "user hasn't password": if user hasn't set a password yet (for example if he was registered via elopage)
- "user has no password": if user hasn't set a password yet (for example if he was registered via elopage)
- "email already activated": if email was already checked
- `user`: contain user object
- `created`: timestamp on which account was created
- `disabled`: true if account was disabled, if disabled no login or coin transfer is possible
- `email`: email of user
- `email_checked`: true if user has successfully clicked on activation link in email
- `first_name`: first name of user
- `group_alias`: alias of group/community to which user belong
- `ident_hash`: currently hash of email, will be later a identification hash to prevent multiple accounts and therefore multiple creations per user
- `last_name`: last name of user
- `public_hex`: public key of user in hex format
- `role`: role of user currently only "none" or "admin"
- `username`: not used yet
- `session_id`: session_id for new session
## Send E-Mails

View File

@ -1,6 +1,3 @@
LOGIN_API_URL=http://localhost/login_api/
COMMUNITY_API_STATE_BALANCE_URL=http://localhost/state-balances/
COMMUNITY_API_TRANSACTION_CREATION_URL=http://localhost/transaction-creations/
COMMUNITY_API_TRANSACTION_SEND_COINS=http://localhost/api
COMMUNITY_API_URL=http://localhost/api/
VUE_PATH=/vue
APP_VERSION=0

View File

@ -33,10 +33,10 @@ const apiPost = async (url, payload) => {
const communityAPI = {
balance: async (session_id) => {
return apiGet(CONFIG.COMMUNITY_API_STATE_BALANCE_URL + 'ajaxGetBalance/' + session_id)
return apiGet(CONFIG.COMMUNITY_API_URL + 'getBalance/' + session_id)
},
transactions: async (session_id) => {
return apiGet(CONFIG.COMMUNITY_API_STATE_BALANCE_URL + 'ajaxListTransactions/' + session_id)
return apiGet(CONFIG.COMMUNITY_API__URL + 'listTransactions/1/25/ASC/' + session_id)
},
/*create: async (session_id, email, amount, memo, target_date = new Date() ) => {
const payload = {
@ -47,7 +47,7 @@ const communityAPI = {
memo,
auto_sign: true,
}
return apiPost(CONFIG.COMMUNITY_API_TRANSACTION_CREATION_URL + 'ajaxCreate/', payload)
return apiPost(CONFIG.COMMUNITY_API__URL + 'createCoins/', payload)
},*/
send: async (session_id, email, amount, memo) => {
const payload = {
@ -57,7 +57,7 @@ const communityAPI = {
memo,
auto_sign: true,
}
return apiPost(CONFIG.COMMUNITY_API_TRANSACTION_SEND_COINS + 'sendCoins/', payload)
return apiPost(CONFIG.COMMUNITY_API__URL + 'sendCoins/', payload)
},
}

View File

@ -14,43 +14,13 @@
</b-row>
<slot name="mobile-right">
<ul class="nav align-items-center d-md-none">
<base-dropdown class="nav-item" menu-on-right tag="li" title-tag="a">
<a slot="title-container" class="nav-link" role="button">
<div class="media align-items-center">
<span class="avatar avatar-sm">
<vue-qrcode :value="$store.state.email" type="image/png"></vue-qrcode>
</span>
</div>
</a>
xx
<router-link to="/overview" class="dropdown-item text-lg text-muted">
<i class="ni ni-single-02"></i>
<span>{{ $t('site.overview.account_overview') }}</span>
</router-link>
<router-link to="/profile" class="dropdown-item text-lg text-muted">
<i class="ni ni-single-02"></i>
<span>{{ $t('site.navbar.my-profil') }}</span>
</router-link>
<router-link to="/profileedit" class="dropdown-item text-lg text-muted">
<i class="ni ni-settings-gear-65"></i>
<span>{{ $t('site.navbar.settings') }}</span>
</router-link>
<router-link to="/transactions" class="dropdown-item text-lg text-muted">
<i class="ni ni-calendar-grid-58"></i>
<span>Transactions</span>
</router-link>
<!--
<router-link to="/activity" class="dropdown-item text-lg text-muted">
<i class="ni ni-calendar-grid-58"></i>
<span>{{ $t('site.navbar.activity') }}</span>
</router-link>
-->
<div class="dropdown-divider"></div>
<div @click="logout" class="dropdown-item text-lg text-muted">
<i class="ni ni-support-16"></i>
{{ $t('logout') }}
<a slot="title-container" class="nav-link" role="button">
<div class="media align-items-center">
<span class="avatar avatar-sm">
<vue-qrcode :value="$store.state.email" type="image/png"></vue-qrcode>
</span>
</div>
</base-dropdown>
</a>
</ul>
</slot>
<slot></slot>
@ -132,14 +102,8 @@ export default {
this.$sidebar.displaySidebar(true)
},
logout() {
this.$store.dispatch('logout')
this.$router.push('/login')
this.$emit('logout')
},
},
beforeDestroy() {
if (this.$sidebar.showSidebar) {
this.$sidebar.showSidebar = false
}
},
}
</script>

View File

@ -7,17 +7,11 @@ const environment = {
NODE_ENV: process.env.NODE_ENV,
DEBUG: process.env.NODE_ENV !== 'production' || false,
PRODUCTION: process.env.NODE_ENV === 'production' || false,
APP_VERSION: process.env.APP_VERSION || require('../../package.json').version,
}
const server = {
LOGIN_API_URL: process.env.LOGIN_API_URL || 'http://localhost/login_api/',
COMMUNITY_API_STATE_BALANCE_URL:
process.env.COMMUNITY_API_STATE_BALANCE_URL || 'http://localhost/state-balances/',
// Schöpfung
// COMMUNITY_API_TRANSACTION_CREATION_URL: process.env.COMMUNITY_API_TRANSACTION_CREATION_URL || 'http://localhost/transaction-creations/',
COMMUNITY_API_TRANSACTION_SEND_COINS:
process.env.COMMUNITY_API_TRANSACTION_SEND_COINS || 'http://localhost/api/',
COMMUNITY_API_URL: process.env.COMMUNITY_API_URL || 'http://localhost/api/',
}
const CONFIG = {

View File

@ -1,7 +1,6 @@
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
import loginAPI from '../apis/loginAPI'
import communityAPI from '../apis/communityAPI'
import createPersistedState from 'vuex-persistedstate'
@ -49,23 +48,19 @@ export const store = new Vuex.Store({
},
},
actions: {
login: async ({ dispatch, commit }, data) => {
login: ({ dispatch, commit }, data) => {
commit('session_id', data.session_id)
commit('email', data.email)
},
passwordReset: async (data) => {},
schoepfen: async (data) => {
passwordReset: (data) => {},
schoepfen: (data) => {
// http://localhost/transaction-creations/ajaxCreate
},
createUser: async ({ commit, dispatch }, data) => {
createUser: ({ commit, dispatch }, data) => {
commit('session_id', data.session_id)
commit('email', data.email)
},
logout: async ({ commit, state }) => {
if (state.session_id) {
const result = await loginAPI.logout(state.session_id)
// The result can be error, but thats ok with us
}
logout: ({ commit, state }) => {
commit('session_id', null)
commit('email', null)
sessionStorage.clear()

View File

@ -1,7 +1,7 @@
<template>
<div class="wrapper">
<notifications></notifications>
<side-bar>
<side-bar @logout="logout">
<template slot="links">
<b-nav-item href="#!" to="/overview">
<b-nav-text class="p-0 text-lg text-muted">{{ $t('send') }}</b-nav-text>
@ -36,6 +36,7 @@
<script>
import PerfectScrollbar from 'perfect-scrollbar'
import 'perfect-scrollbar/css/perfect-scrollbar.css'
import loginAPI from '../../apis/loginAPI'
function hasElement(className) {
return document.getElementsByClassName(className).length > 0
@ -71,6 +72,12 @@ export default {
initScrollbar('sidenav')
}
},
async logout() {
const result = await loginAPI.logout(this.$store.state.session_id)
// do we have to check success?
this.$store.dispatch('logout')
this.$router.push('/login')
},
},
mounted() {
this.initScrollbar()

View File

@ -4,120 +4,39 @@
class="navbar-top navbar-expand"
:class="{ 'navbar-dark': type === 'default' }"
>
<a
href="#"
aria-current="page"
class="h4 mb-0 text-uppercase d-none d-lg-inline-block active router-link-active"
>
{{ $route.name }}
</a>
<!-- Navbar links -->
<b-navbar-nav class="align-items-center ml-md-auto">
<!-- This item dont have <b-nav-item> because item have data-action/data-target on tag <a>, wich we cant add -->
<li class="nav-item d-sm-none">
<a class="nav-link" href="#" data-action="search-show" data-target="#navbar-search-main">
<i class="ni ni-zoom-split-in"></i>
</a>
</li>
<li class="nav-item d-sm-none"></li>
</b-navbar-nav>
<b-navbar-nav class="align-items-center ml-auto ml-md-0">
<base-dropdown
menu-on-right
class="nav-item"
tag="li"
title-tag="a"
title-classes="nav-link pr-0"
>
<a href="#" class="pr-1" @click.prevent slot="title-container">
<b-media no-body class="align-items-center">
<span class="pb-2 text-lg font-weight-bold">
{{ $store.state.email }}
<a class="pr-1" slot="title-container">
<b-media no-body class="align-items-center">
<span class="pb-2 text-lg font-weight-bold">
{{ $store.state.email }}
</span>
<b-media-body class="ml-2 d-none d-lg-block">
<span class="avatar">
<vue-qrcode :value="$store.state.email" type="image/png"></vue-qrcode>
</span>
<b-media-body class="ml-2 d-none d-lg-block">
<span class="avatar">
<vue-qrcode :value="$store.state.email" type="image/png"></vue-qrcode>
</span>
</b-media-body>
</b-media>
</a>
<template>
<b-dropdown-header class="noti-title">
<h6 class="text-overflow m-0">{{ $t('welcome') }}</h6>
</b-dropdown-header>
<b-dropdown-item to="/overview">
<i class="ni ni-single-02"></i>
<span>{{ $t('site.overview.account_overview') }}</span>
</b-dropdown-item>
<b-dropdown-item to="/profile">
<i class="ni ni-single-02"></i>
<span>{{ $t('site.navbar.my-profil') }}</span>
</b-dropdown-item>
<b-dropdown-item to="/profileedit">
<i class="ni ni-settings-gear-65"></i>
<span>{{ $t('site.navbar.settings') }}</span>
</b-dropdown-item>
<!--
<b-dropdown-item href="#!">
<i class="ni ni-calendar-grid-58"></i>
<span>{{ $t('site.navbar.activity') }}</span>
</b-dropdown-item>
-->
<div class="dropdown-divider"></div>
<b-dropdown-item @click="logout">
<i class="ni ni-user-run"></i>
<span>{{ $t('logout') }}</span>
</b-dropdown-item>
</template>
</base-dropdown>
</b-media-body>
</b-media>
</a>
</b-navbar-nav>
</base-nav>
</template>
<script>
import { CollapseTransition } from 'vue2-transitions'
import { BaseNav } from '@/components'
import VueQrcode from 'vue-qrcode'
export default {
components: {
//CollapseTransition,
BaseNav,
VueQrcode,
},
props: {
type: {
type: String,
default: 'default', // default|light
description: 'Look of the dashboard navbar. Default (Green) or light (gray)',
},
},
computed: {
routeName() {
const { name } = this.$route
return this.capitalizeFirstLetter(name)
},
},
data() {
return {
activeNotifications: false,
showMenu: false,
searchModalVisible: false,
searchQuery: '',
}
},
methods: {
capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1)
},
toggleNotificationDropDown() {
this.activeNotifications = !this.activeNotifications
},
closeDropDown() {
this.activeNotifications = false
},
logout() {
this.$store.dispatch('logout')
this.$router.push('/login')
},
},
}

View File

@ -93,14 +93,6 @@ describe('Login', () => {
'The Email field must be a valid email',
)
})
it('shows a warning when password is too short', async () => {
wrapper.find('input[placeholder="form.password"]').setValue('1234')
await flushPromises()
await expect(wrapper.find('.invalid-feedback').text()).toEqual(
'The Password field must be at least 6 characters',
)
})
})
// to do: test submit button

View File

@ -38,7 +38,6 @@
alternative
class="mb-3"
name="Password"
:rules="{ required: true, min: 6 }"
prepend-icon="ni ni-lock-circle-open"
type="password"
:placeholder="$t('form.password')"

View File

@ -53,18 +53,20 @@ Poco::JSON::Object* JsonLoginViaEmailVerificationCode::handle(Poco::Dynamic::Var
result->set("session_id", session->getHandle());
result->set("email_verification_code_type", model::table::EmailOptIn::typeToString(session->getEmailVerificationType()));
Poco::JSON::Array info;
if (!session->getNewUser()->getModel()->getPasswordHashed()) {
info.add("user hasn't password");
auto user = session->getNewUser();
if (!user->getModel()->getPasswordHashed()) {
info.add("user has no password");
}
auto update_email_verification_result = session->updateEmailVerification(code);
if (1 == update_email_verification_result) {
info.add("email already activated");
}
result->set("user", user->getJson());
result->set("info", info);
return result;
}
}

View File

@ -102,6 +102,7 @@ Poco::JSON::Object* JsonUnsecureLogin::handle(Poco::Dynamic::Var params)
case USER_COMPLETE:
case USER_EMAIL_NOT_ACTIVATED:
result->set("state", "success");
result->set("user", session->getNewUser()->getJson());
result->set("session_id", session->getHandle());
session->setClientIp(mClientIP);
return result;