Merge pull request 'feature/RVRNEXT-6-transactions' (!36) from feature/RVRNEXT-6-transactions into master
All checks were successful
rvr-nextgen/pipeline/head This commit looks good
All checks were successful
rvr-nextgen/pipeline/head This commit looks good
Reviewed-on: #36
This commit is contained in:
commit
3e6514a2e5
@ -10,7 +10,7 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"esoko/soko-web": "0.7",
|
||||
"esoko/soko-web": "0.8",
|
||||
"firebase/php-jwt": "^6.4"
|
||||
},
|
||||
"require-dev": {
|
||||
|
8
composer.lock
generated
8
composer.lock
generated
@ -4,15 +4,15 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "71f3adc97b2d83ac1d57112ecce65fac",
|
||||
"content-hash": "a89a42e04596ab159fc41abbd9390068",
|
||||
"packages": [
|
||||
{
|
||||
"name": "esoko/soko-web",
|
||||
"version": "v0.7",
|
||||
"version": "v0.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://git.esoko.eu/esoko/soko-web.git",
|
||||
"reference": "88a2a99527b51dfb240ec78ac7070dc36a1022b6"
|
||||
"reference": "219b42f995b8e34432da4dde77e53e24b75d78dd"
|
||||
},
|
||||
"require": {
|
||||
"phpmailer/phpmailer": "^6.8",
|
||||
@ -33,7 +33,7 @@
|
||||
"GNU GPL 3.0"
|
||||
],
|
||||
"description": "Lightweight web framework",
|
||||
"time": "2023-04-30T18:20:27+00:00"
|
||||
"time": "2023-05-01T17:08:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "firebase/php-jwt",
|
||||
|
19
database/migrations/structure/20230428_2150_transactions.sql
Normal file
19
database/migrations/structure/20230428_2150_transactions.sql
Normal file
@ -0,0 +1,19 @@
|
||||
CREATE TABLE `transactions` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`community_id` int(10) unsigned NOT NULL,
|
||||
`currency_id` int(10) unsigned NOT NULL,
|
||||
`payer_user_id` int(10) unsigned NOT NULL,
|
||||
`payee_user_id` int(10) unsigned NULL,
|
||||
`description` varchar(255) NOT NULL,
|
||||
`sum` decimal(19,9) NOT NULL,
|
||||
`time` timestamp NOT NULL DEFAULT current_timestamp(),
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `community_id` (`community_id`),
|
||||
KEY `currency_id` (`currency_id`),
|
||||
KEY `payer_user_id` (`payer_user_id`),
|
||||
KEY `payee_user_id` (`payee_user_id`),
|
||||
CONSTRAINT `transactions_community_id` FOREIGN KEY (`community_id`) REFERENCES `communities` (`id`),
|
||||
CONSTRAINT `transactions_currency_id` FOREIGN KEY (`currency_id`) REFERENCES `currencies` (`id`),
|
||||
CONSTRAINT `transactions_payer_user_id` FOREIGN KEY (`payer_user_id`) REFERENCES `users` (`id`),
|
||||
CONSTRAINT `transactions_payee_user_id` FOREIGN KEY (`payee_user_id`) REFERENCES `users` (`id`)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
|
@ -31,6 +31,10 @@ main {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
::placeholder, select > option[value=""] {
|
||||
color: #8e8e8e;
|
||||
}
|
||||
|
||||
p, h1, h2, h3, input, textarea, select, button, a, table, label {
|
||||
font-family: 'Oxygen', sans-serif;
|
||||
}
|
||||
@ -150,6 +154,12 @@ a:hover, a:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a.block {
|
||||
color: initial;
|
||||
font-weight: initial;
|
||||
text-decoration: initial;
|
||||
}
|
||||
|
||||
button, a.button {
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
@ -421,7 +431,6 @@ div.buttonContainer>button {
|
||||
}
|
||||
|
||||
div.box {
|
||||
width: 576px;
|
||||
background-color: #eeeef4;
|
||||
border-radius: 3px;
|
||||
margin: 10px auto;
|
||||
@ -429,6 +438,15 @@ div.box {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
div.compactBox {
|
||||
width: 576px;
|
||||
}
|
||||
|
||||
div.transaction {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto;
|
||||
}
|
||||
|
||||
div.gridContainer {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
|
||||
@ -451,7 +469,7 @@ table.fullWidth {
|
||||
}
|
||||
|
||||
table th {
|
||||
font-weight: bold;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
table th, table td {
|
||||
@ -467,6 +485,34 @@ table th:not(:last-child), table td:not(:last-child) {
|
||||
padding-right: 3px;
|
||||
}
|
||||
|
||||
p.paginateContainer {
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
p.paginateContainer > * {
|
||||
font-size: initial;
|
||||
background-color: #5e77aa;
|
||||
border: solid #5e77aa 1px;
|
||||
color: #ffffff;
|
||||
font-weight: 700;
|
||||
padding: 3px 6px;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
width: 25px;
|
||||
}
|
||||
|
||||
p.paginateContainer > a:hover, p.paginateContainer > .selected {
|
||||
background-color: #3b5998;
|
||||
border: solid #29457f 1px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
p.paginateContainer > *:not(:last-child) {
|
||||
border-right: solid #869ab9 1px;
|
||||
}
|
||||
|
||||
.choices__inner {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@ -501,7 +547,7 @@ table th:not(:last-child), table td:not(:last-child) {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
div.box {
|
||||
div.compactBox {
|
||||
width: initial;
|
||||
}
|
||||
}
|
||||
|
162
src/Controller/TransactionController.php
Normal file
162
src/Controller/TransactionController.php
Normal file
@ -0,0 +1,162 @@
|
||||
<?php namespace RVR\Controller;
|
||||
|
||||
use Container;
|
||||
use DateTime;
|
||||
use RVR\Finance\ExchangeRateCalculator;
|
||||
use RVR\PersistentData\Model\Community;
|
||||
use RVR\PersistentData\Model\CommunityMember;
|
||||
use RVR\PersistentData\Model\Transaction;
|
||||
use RVR\PersistentData\Model\User;
|
||||
use RVR\Repository\CommunityMemberRepository;
|
||||
use RVR\Repository\CommunityRepository;
|
||||
use RVR\Repository\CurrencyRepository;
|
||||
use RVR\Repository\TransactionRepository;
|
||||
use SokoWeb\Interfaces\Authentication\IAuthenticationRequired;
|
||||
use SokoWeb\Interfaces\Authorization\ISecured;
|
||||
use SokoWeb\Interfaces\Response\IContent;
|
||||
use SokoWeb\Response\HtmlContent;
|
||||
use SokoWeb\Response\JsonContent;
|
||||
|
||||
class TransactionController implements IAuthenticationRequired, ISecured
|
||||
{
|
||||
private CommunityRepository $communityRepository;
|
||||
|
||||
private CommunityMemberRepository $communityMemberRepository;
|
||||
|
||||
private CurrencyRepository $currencyRepository;
|
||||
|
||||
private TransactionRepository $transactionRepository;
|
||||
|
||||
private Community $community;
|
||||
|
||||
private CommunityMember $ownCommunityMember;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->communityRepository = new CommunityRepository();
|
||||
$this->communityMemberRepository = new CommunityMemberRepository();
|
||||
$this->currencyRepository = new CurrencyRepository();
|
||||
$this->transactionRepository = new TransactionRepository();
|
||||
}
|
||||
|
||||
public function isAuthenticationRequired(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
$communityId = \Container::$request->query('communityId');
|
||||
$this->community = $this->communityRepository->getById($communityId);
|
||||
if ($this->community === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var User $user
|
||||
*/
|
||||
$user = \Container::$request->user();
|
||||
$this->ownCommunityMember = $this->communityMemberRepository->getByCommunityAndUser($this->community, $user);
|
||||
if ($this->ownCommunityMember === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getTransactions(): IContent
|
||||
{
|
||||
Container::$persistentDataManager->loadRelationsFromDb($this->community, true, ['main_currency']);
|
||||
$exchangeRateCalculator = new ExchangeRateCalculator($this->community->getMainCurrency());
|
||||
|
||||
$itemsPerPage = 50;
|
||||
$numberOfTransactions = $this->transactionRepository->countAllByCommunity($this->community);
|
||||
$pages = ceil($numberOfTransactions / $itemsPerPage);
|
||||
$currentPage = Container::$request->query('page') ?: 0;
|
||||
$transactions = $this->transactionRepository->getPagedByCommunity(
|
||||
$this->community,
|
||||
$currentPage * $itemsPerPage,
|
||||
$itemsPerPage,
|
||||
true,
|
||||
['currency', 'payer_user', 'payee_user']
|
||||
);
|
||||
|
||||
return new HtmlContent('communities/transactions', [
|
||||
'community' => $this->community,
|
||||
'exchangeRateCalculator' => $exchangeRateCalculator,
|
||||
'pages' => $pages,
|
||||
'currentPage' => $currentPage,
|
||||
'transactions' => $transactions
|
||||
]);
|
||||
}
|
||||
|
||||
public function getTransactionEdit(): ?IContent
|
||||
{
|
||||
$transactionId = Container::$request->query('transactionId');
|
||||
if ($transactionId) {
|
||||
$transaction = $this->transactionRepository->getById($transactionId);
|
||||
if ($transaction === null) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
$transaction = null;
|
||||
}
|
||||
|
||||
return new HtmlContent('communities/transaction_edit', [
|
||||
'community' => $this->community,
|
||||
'transaction' => $transaction,
|
||||
'members' => $this->getMembers($this->community),
|
||||
'currencies' => $this->getCurrencies($this->community)
|
||||
]);
|
||||
}
|
||||
|
||||
public function saveTransaction(): ?IContent
|
||||
{
|
||||
$transactionId = Container::$request->query('transactionId');
|
||||
if ($transactionId) {
|
||||
$transaction = $this->transactionRepository->getById($transactionId);
|
||||
} else {
|
||||
$transaction = new Transaction();
|
||||
$transaction->setCommunity($this->community);
|
||||
}
|
||||
|
||||
$transaction->setCurrencyId(Container::$request->post('currency_id'));
|
||||
$transaction->setPayerUserId(Container::$request->post('payer_user_id'));
|
||||
$transaction->setPayeeUserId(Container::$request->post('payee_user_id') ?: null);
|
||||
$transaction->setDescription(Container::$request->post('description'));
|
||||
$transaction->setSum(Container::$request->post('sum'));
|
||||
$transaction->setTimeDate(new DateTime(Container::$request->post('time')));
|
||||
Container::$persistentDataManager->saveToDb($transaction);
|
||||
|
||||
return new JsonContent(['success' => true]);
|
||||
}
|
||||
|
||||
public function deleteTransaction(): IContent
|
||||
{
|
||||
$transaction = $this->transactionRepository->getById(Container::$request->query('transactionId'));
|
||||
Container::$persistentDataManager->deleteFromDb($transaction);
|
||||
|
||||
return new JsonContent(['success' => true]);
|
||||
}
|
||||
|
||||
private function getMembers(Community $community): array
|
||||
{
|
||||
$members = iterator_to_array($this->communityMemberRepository->getAllByCommunity($community, true, ['user']));
|
||||
usort($members, function($a, $b) {
|
||||
return strnatcmp($a->getUser()->getDisplayName(), $b->getUser()->getDisplayName());
|
||||
});
|
||||
return $members;
|
||||
}
|
||||
|
||||
private function getCurrencies(Community $community): array
|
||||
{
|
||||
$currencies = iterator_to_array($this->currencyRepository->getAllByCommunity($community));
|
||||
usort($currencies, function($a, $b) {
|
||||
return strnatcmp($a->getCode(), $b->getCode());
|
||||
});
|
||||
usort($currencies, function($a, $b) use ($community) {
|
||||
return (int)($b->getId() === $community->getMainCurrencyId()) - (int)($a->getId() === $community->getMainCurrencyId());
|
||||
});
|
||||
return $currencies;
|
||||
}
|
||||
}
|
52
src/Finance/ExchangeRateCalculator.php
Normal file
52
src/Finance/ExchangeRateCalculator.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php namespace RVR\Finance;
|
||||
|
||||
use DateTime;
|
||||
use RVR\PersistentData\Model\Currency;
|
||||
use RVR\PersistentData\Model\CurrencyExchangeRate;
|
||||
use RVR\Repository\CurrencyExchangeRateRepository;
|
||||
|
||||
class ExchangeRateCalculator
|
||||
{
|
||||
private Currency $mainCurrency;
|
||||
|
||||
private CurrencyExchangeRateRepository $currencyExchangeRateRepository;
|
||||
|
||||
private array $exchangeRates = [];
|
||||
|
||||
public function __construct(Currency $mainCurrency)
|
||||
{
|
||||
$this->mainCurrency = $mainCurrency;
|
||||
$this->currencyExchangeRateRepository = new CurrencyExchangeRateRepository();
|
||||
}
|
||||
|
||||
public function calculate(float $sumInCurrency, Currency $currency, DateTime $time): float
|
||||
{
|
||||
if ($currency->getId() === $this->mainCurrency->getId()) {
|
||||
return $sumInCurrency;
|
||||
}
|
||||
|
||||
$currentExchangeRate = $this->getCurrentExchangeRate($currency, $time);
|
||||
if ($currentExchangeRate === null) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return $sumInCurrency * $currentExchangeRate->getExchangeRate();
|
||||
}
|
||||
|
||||
private function getCurrentExchangeRate(Currency $currency, DateTime $time): ?CurrencyExchangeRate
|
||||
{
|
||||
if (!isset($this->exchangeRates[$currency->getId()])) {
|
||||
$this->exchangeRates[$currency->getId()] = iterator_to_array($this->currencyExchangeRateRepository->getAllByCurrency($currency));
|
||||
}
|
||||
|
||||
$currentExchangeRate = null;
|
||||
foreach ($this->exchangeRates[$currency->getId()] as $exchangeRate) {
|
||||
if ($exchangeRate->getValidFrom() > $time) {
|
||||
break;
|
||||
}
|
||||
$currentExchangeRate = $exchangeRate;
|
||||
}
|
||||
|
||||
return $currentExchangeRate;
|
||||
}
|
||||
}
|
160
src/PersistentData/Model/Transaction.php
Normal file
160
src/PersistentData/Model/Transaction.php
Normal file
@ -0,0 +1,160 @@
|
||||
<?php namespace RVR\PersistentData\Model;
|
||||
|
||||
use DateTime;
|
||||
use SokoWeb\PersistentData\Model\Model;
|
||||
|
||||
class Transaction extends Model
|
||||
{
|
||||
protected static string $table = 'transactions';
|
||||
|
||||
protected static array $fields = ['community_id', 'currency_id', 'payer_user_id', 'payee_user_id', 'description', 'sum', 'time'];
|
||||
|
||||
protected static array $relations = [
|
||||
'community' => Community::class,
|
||||
'currency' => Currency::class,
|
||||
'payer_user' => User::class,
|
||||
'payee_user' => User::class
|
||||
];
|
||||
|
||||
private ?Community $community = null;
|
||||
|
||||
private int $communityId;
|
||||
|
||||
private ?Currency $currency = null;
|
||||
|
||||
private int $currencyId;
|
||||
|
||||
private ?User $payerUser = null;
|
||||
|
||||
private int $payerUserId;
|
||||
|
||||
private ?User $payeeUser = null;
|
||||
|
||||
private ?int $payeeUserId = null;
|
||||
|
||||
private string $description = '';
|
||||
|
||||
private float $sum = 0.0;
|
||||
|
||||
private DateTime $time;
|
||||
|
||||
public function setCommunity(Community $community): void
|
||||
{
|
||||
$this->community = $community;
|
||||
}
|
||||
|
||||
public function setCommunityId(int $communityId): void
|
||||
{
|
||||
$this->communityId = $communityId;
|
||||
}
|
||||
|
||||
public function setCurrency(Currency $currency): void
|
||||
{
|
||||
$this->currency = $currency;
|
||||
}
|
||||
|
||||
public function setCurrencyId(int $currencyId): void
|
||||
{
|
||||
$this->currencyId = $currencyId;
|
||||
}
|
||||
|
||||
public function setPayerUser(User $payerUser): void
|
||||
{
|
||||
$this->payerUser = $payerUser;
|
||||
}
|
||||
|
||||
public function setPayerUserId(int $payerUserId): void
|
||||
{
|
||||
$this->payerUserId = $payerUserId;
|
||||
}
|
||||
|
||||
public function setPayeeUser(?User $payeeUser): void
|
||||
{
|
||||
$this->payeeUser = $payeeUser;
|
||||
}
|
||||
|
||||
public function setPayeeUserId(?int $payeeUserId): void
|
||||
{
|
||||
$this->payeeUserId = $payeeUserId;
|
||||
}
|
||||
|
||||
public function setDescription(string $description): void
|
||||
{
|
||||
$this->description = $description;
|
||||
}
|
||||
|
||||
public function setSum(float $sum): void
|
||||
{
|
||||
$this->sum = $sum;
|
||||
}
|
||||
|
||||
public function setTimeDate(DateTime $time): void
|
||||
{
|
||||
$this->time = $time;
|
||||
}
|
||||
|
||||
public function setTime(string $time): void
|
||||
{
|
||||
$this->time = new DateTime($time);
|
||||
}
|
||||
|
||||
public function getCommunity(): ?Community
|
||||
{
|
||||
return $this->community;
|
||||
}
|
||||
|
||||
public function getCommunityId(): int
|
||||
{
|
||||
return $this->communityId;
|
||||
}
|
||||
|
||||
public function getCurrency(): ?Currency
|
||||
{
|
||||
return $this->currency;
|
||||
}
|
||||
|
||||
public function getCurrencyId(): int
|
||||
{
|
||||
return $this->currencyId;
|
||||
}
|
||||
|
||||
public function getPayerUser(): ?User
|
||||
{
|
||||
return $this->payerUser;
|
||||
}
|
||||
|
||||
public function getPayerUserId(): int
|
||||
{
|
||||
return $this->payerUserId;
|
||||
}
|
||||
|
||||
public function getPayeeUser(): ?User
|
||||
{
|
||||
return $this->payeeUser;
|
||||
}
|
||||
|
||||
public function getPayeeUserId(): ?int
|
||||
{
|
||||
return $this->payeeUserId;
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function getSum(): float
|
||||
{
|
||||
return $this->sum;
|
||||
}
|
||||
|
||||
public function getTimeDate(): DateTime
|
||||
{
|
||||
return $this->time;
|
||||
}
|
||||
|
||||
public function getTime(): string
|
||||
{
|
||||
return $this->time->format('Y-m-d H:i:s');
|
||||
}
|
||||
}
|
44
src/Repository/TransactionRepository.php
Normal file
44
src/Repository/TransactionRepository.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php namespace RVR\Repository;
|
||||
|
||||
use Container;
|
||||
use Generator;
|
||||
use RVR\PersistentData\Model\Community;
|
||||
use RVR\PersistentData\Model\Transaction;
|
||||
use SokoWeb\Database\Query\Select;
|
||||
|
||||
class TransactionRepository
|
||||
{
|
||||
public function getById(int $id): ?Transaction
|
||||
{
|
||||
return Container::$persistentDataManager->selectFromDbById($id, Transaction::class);
|
||||
}
|
||||
|
||||
public function getAllByCommunity(Community $community, bool $useRelations = false, array $withRelations = []): Generator
|
||||
{
|
||||
$select = $this->selectAllByCommunity($community);
|
||||
|
||||
yield from Container::$persistentDataManager->selectMultipleFromDb($select, Transaction::class, $useRelations, $withRelations);
|
||||
}
|
||||
|
||||
public function countAllByCommunity(Community $community): int
|
||||
{
|
||||
return $this->selectAllByCommunity($community)->count();
|
||||
}
|
||||
|
||||
public function getPagedByCommunity(Community $community, int $start, int $limit, bool $useRelations = false, array $withRelations = []): Generator
|
||||
{
|
||||
$select = new Select(Container::$dbConnection);
|
||||
$select->where('community_id', '=', $community->getId());
|
||||
$select->orderBy('time', 'DESC');
|
||||
$select->limit($limit, $start);
|
||||
|
||||
yield from Container::$persistentDataManager->selectMultipleFromDb($select, Transaction::class, $useRelations, $withRelations);
|
||||
}
|
||||
|
||||
private function selectAllByCommunity(Community $community)
|
||||
{
|
||||
$select = new Select(Container::$dbConnection, Transaction::getTable());
|
||||
$select->where('community_id', '=', $community->getId());
|
||||
return $select;
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
|
||||
@section(main)
|
||||
<h2>Account</h2>
|
||||
<div class="box">
|
||||
<div class="box compactBox">
|
||||
<form id="accountForm" action="<?= Container::$routeCollection->getRoute('account-action')->generateLink() ?>" method="post" data-reload-on-success="true" data-observe-inputs="email,username,password_new,password_new_confirm,nickname,phone,id_number">
|
||||
<?php if ($user['password'] !== null && $user['google_sub'] !== null): ?>
|
||||
<p class="justify small">Please confirm your identity with your password or with Google to modify your account.</p>
|
||||
|
@ -5,7 +5,7 @@
|
||||
@section(main)
|
||||
<h2>Authenticate with Google</h2>
|
||||
<?php if (!$success): ?>
|
||||
<div class="box">
|
||||
<div class="box compactBox">
|
||||
<p class="error justify">
|
||||
<?php if (isset($errorText)): ?>
|
||||
<?= $errorText ?>
|
||||
|
@ -1,8 +1,11 @@
|
||||
@extends(templates/layout_normal)
|
||||
|
||||
@section(main)
|
||||
<h2><a href="<?= Container::$routeCollection->getRoute('community')->generateLink(['communityId' => $community->getId()]) ?>"><?= $community->getName() ?></a> - Edit currencies</h2>
|
||||
<div class="box">
|
||||
<h2>
|
||||
<a href="<?= Container::$routeCollection->getRoute('community')->generateLink(['communityId' => $community->getId()]) ?>"><?= $community->getName() ?></a> »
|
||||
Edit currencies
|
||||
</h2>
|
||||
<div class="box compactBox">
|
||||
<table class="fullWidth">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -3,12 +3,13 @@
|
||||
@section(main)
|
||||
<h2>
|
||||
<?php if (isset($community)): ?>
|
||||
<a href="<?= Container::$routeCollection->getRoute('community')->generateLink(['communityId' => $community->getId()]) ?>"><?= $community->getName() ?></a> - Edit
|
||||
<a href="<?= Container::$routeCollection->getRoute('community')->generateLink(['communityId' => $community->getId()]) ?>"><?= $community->getName() ?></a> »
|
||||
Edit
|
||||
<?php else: ?>
|
||||
New community
|
||||
<?php endif; ?>
|
||||
</h2>
|
||||
<div class="box">
|
||||
<div class="box compactBox">
|
||||
<?php
|
||||
$formAction = isset($community) ?
|
||||
Container::$routeCollection->getRoute('community-edit-action')->generateLink(['communityId' => $community->getId()]) :
|
||||
|
@ -5,8 +5,11 @@
|
||||
@extends(templates/layout_normal)
|
||||
|
||||
@section(main)
|
||||
<h2><a href="<?= Container::$routeCollection->getRoute('community')->generateLink(['communityId' => $community->getId()]) ?>"><?= $community->getName() ?></a> - Edit members</h2>
|
||||
<div class="box">
|
||||
<h2>
|
||||
<a href="<?= Container::$routeCollection->getRoute('community')->generateLink(['communityId' => $community->getId()]) ?>"><?= $community->getName() ?></a> »
|
||||
Edit members
|
||||
</h2>
|
||||
<div class="box compactBox">
|
||||
<table class="fullWidth">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -1,8 +1,11 @@
|
||||
@extends(templates/layout_normal)
|
||||
|
||||
@section(main)
|
||||
<h2><a href="<?= Container::$routeCollection->getRoute('community')->generateLink(['communityId' => $community->getId()]) ?>"><?= $community->getName() ?></a> - Exchange rates for <?= $currency->getCode() ?></h2>
|
||||
<div class="box">
|
||||
<h2>
|
||||
<a href="<?= Container::$routeCollection->getRoute('community')->generateLink(['communityId' => $community->getId()]) ?>"><?= $community->getName() ?></a> »
|
||||
Exchange rates for <?= $currency->getCode() ?>
|
||||
</h2>
|
||||
<div class="box compactBox">
|
||||
<table class="fullWidth">
|
||||
<thead>
|
||||
<tr>
|
||||
|
53
views/communities/transaction_edit.php
Normal file
53
views/communities/transaction_edit.php
Normal file
@ -0,0 +1,53 @@
|
||||
@extends(templates/layout_normal)
|
||||
|
||||
@section(main)
|
||||
<h2>
|
||||
<a href="<?= Container::$routeCollection->getRoute('community')->generateLink(['communityId' => $community->getId()]) ?>"><?= $community->getName() ?></a> »
|
||||
<a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId()]) ?>">Transactions</a> »
|
||||
<?php if (isset($transaction)): ?>
|
||||
Edit transaction
|
||||
<?php else: ?>
|
||||
New transaction
|
||||
<?php endif; ?>
|
||||
</h2>
|
||||
<div class="box compactBox">
|
||||
<?php
|
||||
$formAction = isset($transaction) ?
|
||||
Container::$routeCollection->getRoute('community.transactions.edit-action')->generateLink(['communityId' => $community->getId(), 'transactionId' => $transaction->getId()]) :
|
||||
Container::$routeCollection->getRoute('community.transactions.new-action')->generateLink(['communityId' => $community->getId()]);
|
||||
?>
|
||||
<form id="transactionForm" action="<?= $formAction ?>" method="post" data-redirect-on-success="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId()]) ?>">
|
||||
<select class="big fullWidth" name="payer_user_id" required>
|
||||
<option value="" hidden>[Payer]</option>
|
||||
<?php foreach ($members as $member): ?>
|
||||
<option value="<?= $member->getUser()->getId() ?>" <?= isset($transaction) && $transaction->getPayerUserId() === $member->getUser()->getId() ? 'selected' : '' ?>><?= $member->getUser()->getDisplayName() ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<select class="big fullWidth marginTop" name="payee_user_id">
|
||||
<option value="">[Payee]</option>
|
||||
<?php foreach ($members as $member): ?>
|
||||
<option value="<?= $member->getUser()->getId() ?>" <?= isset($transaction) && $transaction->getPayeeUserId() === $member->getUser()->getId() ? 'selected' : '' ?>><?= $member->getUser()->getDisplayName() ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<input type="text" class="text big fullWidth marginTop" name="description" placeholder="Description" value="<?= isset($transaction) ? $transaction->getDescription() : '' ?>" required>
|
||||
<input type="number" class="text big fullWidth marginTop" name="sum" placeholder="Sum" value="<?= isset($transaction) ? $transaction->getSum() : '' ?>" min="0" step="0.000000001" required>
|
||||
<select class="big fullWidth marginTop" name="currency_id" required>
|
||||
<option value="" hidden>[Currency]</option>
|
||||
<?php foreach ($currencies as $currency): ?>
|
||||
<option value="<?= $currency->getId() ?>" <?= isset($transaction) && $transaction->getCurrencyId() === $currency->getId() ? 'selected' : '' ?>><?= $currency->getCode() ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<input type="datetime-local" class="text big fullWidth marginTop" name="time" placeholder="Time" value="<?= isset($transaction) ? $transaction->getTimeDate()->format('Y-m-d\TH:i') : (new DateTime())->format('Y-m-d\TH:i') ?>" required>
|
||||
<p class="formError justify marginTop"></p>
|
||||
<div class="right marginTop" style="font-size: 0;">
|
||||
<?php if (isset($transaction)): ?>
|
||||
<button type="submit" form="deleteTransaction" class="red marginRight">Delete</button>
|
||||
<?php endif; ?>
|
||||
<button type="submit" name="submit"><?= isset($transaction) ? 'Save' : 'Create' ?></button>
|
||||
</div>
|
||||
</form>
|
||||
<?php if (isset($transaction)): ?>
|
||||
<form id="deleteTransaction" action="<?= Container::$routeCollection->getRoute('community.transactions.delete')->generateLink(['communityId' => $community->getId(), 'transactionId' => $transaction->getId()]) ?>" method="post" data-redirect-on-success="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId()]) ?>"></form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
@endsection
|
62
views/communities/transactions.php
Normal file
62
views/communities/transactions.php
Normal file
@ -0,0 +1,62 @@
|
||||
@extends(templates/layout_normal)
|
||||
|
||||
@section(main)
|
||||
<h2>
|
||||
<a href="<?= Container::$routeCollection->getRoute('community')->generateLink(['communityId' => $community->getId()]) ?>"><?= $community->getName() ?></a> »
|
||||
Transactions
|
||||
</h2>
|
||||
|
||||
<p class="marginTop"><a href="<?= Container::$routeCollection->getRoute('community.transactions.new')->generateLink(['communityId' => $community->getId()]) ?>">New transaction</a></p>
|
||||
|
||||
<?php if ($pages > 1): ?>
|
||||
<p class="paginateContainer marginTop">
|
||||
<a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId(), 'page' => 0]) ?>">«</a>
|
||||
<a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId(), 'page' => max(0, $currentPage - 1)]) ?>">‹</a>
|
||||
<?php for ($i = 0; $i < $pages; $i++): ?>
|
||||
<?php if ($currentPage == $i): ?>
|
||||
<span class="selected"><?= $i + 1 ?></span>
|
||||
<?php else: ?>
|
||||
<a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId(), 'page' => $i]) ?>"><?= $i + 1 ?></a>
|
||||
<?php endif; ?>
|
||||
<?php endfor; ?>
|
||||
<a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId(), 'page' => min($pages - 1, $currentPage + 1)]) ?>">›</a>
|
||||
<a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId(), 'page' => $pages - 1]) ?>">»</a>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php foreach ($transactions as $transaction): ?>
|
||||
<a class="block" href="<?= Container::$routeCollection->getRoute('community.transactions.edit')->generateLink(['communityId' => $community->getId(), 'transactionId' => $transaction->getId()]) ?>">
|
||||
<div class="box transaction">
|
||||
<div>
|
||||
<p style="font-weight: bold;"><?= $transaction->getDescription() ?></p>
|
||||
<p class="small"><?= $transaction->getPayerUser()->getDisplayName() ?> ► <?= $transaction->getPayeeUser() ? $transaction->getPayeeUser()->getDisplayName() : '[common]' ?></p>
|
||||
<p class="small"><?= $transaction->getTimeDate()->format('Y-m-d H:i') ?></p>
|
||||
</div>
|
||||
<div style="text-align: right;">
|
||||
<h3><?= number_format($exchangeRateCalculator->calculate($transaction->getSum(), $transaction->getCurrency(), $transaction->getTimeDate()), $community->getMainCurrency()->getRoundDigits()) ?> <?= $community->getMainCurrency()->getCode() ?></h3>
|
||||
<?php if ($community->getMainCurrencyId() !== $transaction->getCurrencyId()): ?>
|
||||
<p style="color: #8e8e8e; font-weight: bold;"><?= number_format($transaction->getSum(), $transaction->getCurrency()->getRoundDigits()) ?> <?= $transaction->getCurrency()->getCode() ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php if ($pages > 1): ?>
|
||||
<p class="paginateContainer marginTop">
|
||||
<a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId(), 'page' => 0]) ?>">«</a>
|
||||
<a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId(), 'page' => max(0, $currentPage - 1)]) ?>">‹</a>
|
||||
<?php for ($i = 0; $i < $pages; $i++): ?>
|
||||
<?php if ($currentPage == $i): ?>
|
||||
<span class="selected"><?= $i + 1 ?></span>
|
||||
<?php else: ?>
|
||||
<a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId(), 'page' => $i]) ?>"><?= $i + 1 ?></a>
|
||||
<?php endif; ?>
|
||||
<?php endfor; ?>
|
||||
<a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId(), 'page' => min($pages - 1, $currentPage + 1)]) ?>">›</a>
|
||||
<a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId(), 'page' => $pages - 1]) ?>">»</a>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
|
||||
<p class="marginTop"><a href="<?= Container::$routeCollection->getRoute('community.transactions.new')->generateLink(['communityId' => $community->getId()]) ?>">New transaction</a></p>
|
||||
@endsection
|
@ -2,7 +2,7 @@
|
||||
|
||||
@section(main)
|
||||
<h2>Login up with Google</h2>
|
||||
<div class="box">
|
||||
<div class="box compactBox">
|
||||
<p class="error justify"><?= $error ?></p>
|
||||
</div>
|
||||
@endsection
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
@section(main)
|
||||
<h2>Login</h2>
|
||||
<div class="box">
|
||||
<div class="box compactBox">
|
||||
<form id="loginForm" action="<?= Container::$routeCollection->getRoute('login-action')->generateLink() ?>" method="post" data-redirect-on-success="<?= $redirectUrl ?>">
|
||||
<input type="text" class="text big fullWidth" name="email" placeholder="Email address / Username" required autofocus>
|
||||
<input type="password" class="text big fullWidth marginTop" name="password" placeholder="Password" required minlength="6">
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
@section(main)
|
||||
<h2>Request password reset</h2>
|
||||
<div class="box">
|
||||
<div class="box compactBox">
|
||||
<form id="passwordResetForm" action="<?= Container::$routeCollection->getRoute('password-requestReset-action')->generateLink() ?>" method="post" data-redirect-on-success="<?= Container::$routeCollection->getRoute('password-requestReset.success')->generateLink() ?>">
|
||||
<input type="email" class="text big fullWidth" name="email" placeholder="Email address" value="<?= isset($email) ? $email : '' ?>" required autofocus>
|
||||
<?php if (!empty($_ENV['RECAPTCHA_SITEKEY'])): ?>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
@section(main)
|
||||
<h2>Request password reset</h2>
|
||||
<div class="box">
|
||||
<div class="box compactBox">
|
||||
<p class="justify">Password reset was successfully requested. Please check your email and click on the link to reset your password!</p>
|
||||
</div>
|
||||
@endsection
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
@section(main)
|
||||
<h2>Reset password</h2>
|
||||
<div class="box">
|
||||
<div class="box compactBox">
|
||||
<?php if ($success) : ?>
|
||||
<form id="resetPasswordForm" action="<?= Container::$routeCollection->getRoute('password-reset.action')->generateLink(['token' => $token]) ?>"" method="post" data-redirect-on-success="<?= $redirectUrl ?>">
|
||||
<input type="email" class="text big fullWidth" name="email" placeholder="Email address" value="<?= $email ?>" disabled>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
@section(main)
|
||||
<h2>OAuth error</h2>
|
||||
<div class="box">
|
||||
<div class="box compactBox">
|
||||
<p class="error justify"><?= $error ?></p>
|
||||
</div>
|
||||
@endsection
|
||||
|
9
web.php
9
web.php
@ -11,6 +11,7 @@ use RVR\Controller\OAuthController;
|
||||
use RVR\Controller\UserController;
|
||||
use RVR\Controller\UserSearchController;
|
||||
use RVR\Controller\CommunityController;
|
||||
use RVR\Controller\TransactionController;
|
||||
use RVR\Repository\UserRepository;
|
||||
|
||||
require 'app.php';
|
||||
@ -76,6 +77,14 @@ Container::$routeCollection->group('communities', function (RouteCollection $rou
|
||||
$routeCollection->post('community-currency-exchange-rates-edit', '{code}/edit', [CommunityController::class, 'saveCurrencyExchangeRate']);
|
||||
$routeCollection->post('community-currency-exchange-rates-delete', '{code}/delete', [CommunityController::class, 'deleteCurrencyExchangeRate']);
|
||||
});
|
||||
$routeCollection->group('transactions', function (RouteCollection $routeCollection) {
|
||||
$routeCollection->get('community.transactions', '', [TransactionController::class, 'getTransactions']);
|
||||
$routeCollection->get('community.transactions.new', 'new', [TransactionController::class, 'getTransactionEdit']);
|
||||
$routeCollection->post('community.transactions.new-action', 'new', [TransactionController::class, 'saveTransaction']);
|
||||
$routeCollection->get('community.transactions.edit', '{transactionId}', [TransactionController::class, 'getTransactionEdit']);
|
||||
$routeCollection->post('community.transactions.edit-action', '{transactionId}', [TransactionController::class, 'saveTransaction']);
|
||||
$routeCollection->post('community.transactions.delete', '{transactionId}/delete', [TransactionController::class, 'deleteTransaction']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user