diff --git a/Jenkinsfile b/Jenkinsfile index e88d4e6..e3e225c 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -35,7 +35,7 @@ pipeline { sh 'vendor/bin/phpunit --log-junit unit_test_results.xml --testdox tests' } post { - success { + always { archiveArtifacts 'unit_test_results.xml' } } @@ -53,7 +53,7 @@ pipeline { sh 'php vendor/bin/phpstan analyse -c phpstan.neon --error-format=prettyJson > static_code_analysis_results.json' } post { - success { + always { archiveArtifacts 'static_code_analysis_results.json' } } diff --git a/composer.json b/composer.json index b62335d..f6fae7b 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,7 @@ } ], "require": { - "esoko/soko-web": "0.11", + "esoko/soko-web": "0.12.1", "firebase/php-jwt": "^6.4" }, "require-dev": { diff --git a/composer.lock b/composer.lock index d10597a..d7a56cf 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c17dd5eb82ff8f509a17404bd4c471b8", + "content-hash": "a6f50b31b14027eec094629b13cc4097", "packages": [ { "name": "cocur/slugify", @@ -82,11 +82,11 @@ }, { "name": "esoko/soko-web", - "version": "v0.11", + "version": "v0.12.1", "source": { "type": "git", "url": "https://git.esoko.eu/esoko/soko-web.git", - "reference": "78f891fbff0dc8e5de5607529f4c8282f1dff40f" + "reference": "bda12177ebc201f04fdad5493b93039676a983ca" }, "require": { "cocur/slugify": "^4.3", @@ -108,7 +108,7 @@ "GNU GPL 3.0" ], "description": "Lightweight web framework", - "time": "2023-05-06T23:52:34+00:00" + "time": "2023-05-28T13:56:14+00:00" }, { "name": "firebase/php-jwt", diff --git a/database/migrations/structure/20230526_1551_events.sql b/database/migrations/structure/20230526_1551_events.sql new file mode 100644 index 0000000..aae1234 --- /dev/null +++ b/database/migrations/structure/20230526_1551_events.sql @@ -0,0 +1,18 @@ +CREATE TABLE `events` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `slug` varchar(255) CHARACTER SET ascii COLLATE ascii_bin DEFAULT NULL, + `community_id` int(10) unsigned NOT NULL, + `start` timestamp NOT NULL DEFAULT current_timestamp(), + `end` timestamp NOT NULL DEFAULT current_timestamp(), + `title` varchar(255) NOT NULL, + `description` text NOT NULL, + PRIMARY KEY (`id`), + KEY `community_id` (`community_id`), + UNIQUE KEY `slug` (`slug`), + CONSTRAINT `events_community_id` FOREIGN KEY (`community_id`) REFERENCES `communities` (`id`) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; + +ALTER TABLE `transactions` +ADD `event_id` int(10) unsigned NULL, +ADD KEY `event_id` (`event_id`) AFTER `community_id`, +ADD CONSTRAINT `transactions_event_id` FOREIGN KEY (`event_id`) REFERENCES `events` (`id`); diff --git a/public/static/css/rvr.css b/public/static/css/rvr.css index 59ecdb4..eb49866 100644 --- a/public/static/css/rvr.css +++ b/public/static/css/rvr.css @@ -108,6 +108,10 @@ p.small, span.small, td.small { font-size: 14px; } +p.big, span.big, td.big { + font-size: 18px; +} + .red { color: #a80908; } @@ -255,11 +259,11 @@ button.red:enabled:hover, button.red:enabled:focus, a.button.red:hover, a.button } button.yellow, a.button.yellow { - background-color: #e8a349; + background-color: #daa520; } button.yellow:enabled:hover, button.yellow:enabled:focus, a.button.yellow:hover, a.button.yellow:focus { - background-color: #c37713; + background-color: #b8860b; } button.green, a.button.green { @@ -397,7 +401,7 @@ header>p { } header>p>span { - padding-left: 6px; + padding-left: 8px; } header>p>span>a:link, header>p>span>a:visited { @@ -410,7 +414,7 @@ header>p>span>a:hover, header>p>span>a:focus { header>p>span:not(:last-child) { border-right: solid white 1px; - padding-right: 6px; + padding-right: 8px; } main { @@ -537,6 +541,15 @@ p.formLabel { font-size: 14px; } +span.label { + border-radius: 3px; + background-color: #555555; + color: #ffffff; + padding: 2px 4px; + font-weight: bold; + font-size: 13px; +} + @media screen and (max-width: 424px) { div.gridContainer { grid-template-columns: auto; diff --git a/public/static/js/communities/transaction.js b/public/static/js/communities/transaction.js new file mode 100644 index 0000000..0abaf6c --- /dev/null +++ b/public/static/js/communities/transaction.js @@ -0,0 +1,30 @@ +(function () { + const element = document.getElementById('transactionForm').elements['event_id']; + const select = new TomSelect(element, { + valueField: 'value', + labelField: 'label', + searchField: 'label', + loadThrottle: 300, + load: function (query, callback) { + var self = this; + RVR.httpRequest('GET', searchEventUrl.replace('QUERY', encodeURIComponent(query)), function () { + self.clearOptions(); + callback(this.response.results); + }); + }, + }); + + select.on('change', function (value) { + this.clearOptions(); + }); + + select.on('blur', function (value) { + this.clearOptions(); + }); + + select.on('type', function (value) { + if (value === '') { + this.clearOptions(); + } + }); +})(); diff --git a/src/Controller/CommunityController.php b/src/Controller/CommunityController.php index 84735b3..5366638 100644 --- a/src/Controller/CommunityController.php +++ b/src/Controller/CommunityController.php @@ -11,6 +11,7 @@ use RVR\Repository\CommunityRepository; use RVR\Repository\CommunityMemberRepository; use RVR\Repository\CurrencyExchangeRateRepository; use RVR\Repository\CurrencyRepository; +use RVR\Repository\EventRepository; use RVR\Repository\TransactionRepository; use RVR\Repository\UserRepository; use SokoWeb\Interfaces\Authentication\IAuthenticationRequired; @@ -32,6 +33,8 @@ class CommunityController implements IAuthenticationRequired private TransactionRepository $transactionRepository; + private EventRepository $eventRepository; + public function __construct() { $this->userRepository = new UserRepository(); @@ -40,6 +43,7 @@ class CommunityController implements IAuthenticationRequired $this->currencyRepository = new CurrencyRepository(); $this->currencyExchangeRatesRepository = new CurrencyExchangeRateRepository(); $this->transactionRepository = new TransactionRepository(); + $this->eventRepository = new EventRepository(); } public function isAuthenticationRequired(): bool @@ -75,7 +79,7 @@ class CommunityController implements IAuthenticationRequired return new HtmlContent('communities/community', [ 'community' => $community, - 'upcomingEvents' => [], + 'upcomingEvents' => iterator_to_array($this->eventRepository->getUpcomingByCommunity($community, new DateTime(), 3)), 'debtItems' => $debtItems, 'debtBalance' => $debtBalance, 'outstandingItems' => $outstandingItems, diff --git a/src/Controller/EventController.php b/src/Controller/EventController.php new file mode 100644 index 0000000..be72ca1 --- /dev/null +++ b/src/Controller/EventController.php @@ -0,0 +1,181 @@ +communityRepository = new CommunityRepository(); + $this->communityMemberRepository = new CommunityMemberRepository(); + $this->eventRepository = new EventRepository(); + $this->transactionRepository = new TransactionRepository(); + } + + public function isAuthenticationRequired(): bool + { + return true; + } + + public function authorize(): bool + { + $communitySlug = \Container::$request->query('communitySlug'); + $this->community = $this->communityRepository->getBySlug($communitySlug); + 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 getEvents(): IContent + { + $itemsPerPage = 10; + $numberOfEvents = $this->eventRepository->countAllByCommunity($this->community); + $pages = ceil($numberOfEvents / $itemsPerPage); + $currentPage = Container::$request->query('page') ?: 0; + $events = $this->eventRepository->getPagedByCommunity( + $this->community, + $currentPage * $itemsPerPage, + $itemsPerPage + ); + + return new HtmlContent('events/events', [ + 'community' => $this->community, + 'pages' => $pages, + 'currentPage' => $currentPage, + 'numberOfEvents' => $numberOfEvents, + 'events' => $events + ]); + } + + public function searchEvent(): IContent + { + $events = iterator_to_array($this->eventRepository->searchByTitle($this->community, Container::$request->query('q'))); + $results = []; + foreach ($events as $event) { + $results[] = ['value' => $event->getId(), 'label' => $event->getTitle()]; + } + + return new JsonContent([ + 'results' => $results + ]); + } + + public function getEvent(): ?IContent + { + $event = $this->eventRepository->getBySlug(Container::$request->query('eventSlug')); + if (!$event) { + return null; + } + Container::$persistentDataManager->loadRelationsFromDb($this->community, true, ['main_currency']); + + return new HtmlContent('events/event', [ + 'community' => $this->community, + 'event' => $event, + 'totalCost' => $this->sumTransactions($event) + ]); + } + + public function getEventEdit(): ?IContent + { + $eventSlug = Container::$request->query('eventSlug'); + if ($eventSlug) { + $event = $this->eventRepository->getBySlug($eventSlug); + if ($event === null) { + return null; + } + } else { + $event = null; + } + + return new HtmlContent('events/event_edit', [ + 'community' => $this->community, + 'event' => $event + ]); + } + + public function saveEvent(): ?IContent + { + $eventSlug = Container::$request->query('eventSlug'); + if ($eventSlug) { + $event = $this->eventRepository->getBySlug($eventSlug); + } else { + $event = new Event(); + $event->setCommunity($this->community); + } + + $event->setTitle(Container::$request->post('title')); + $event->setDescription(Container::$request->post('description')); + $event->setStartDate(new DateTime(Container::$request->post('start'))); + $event->setEndDate(new DateTime(Container::$request->post('end'))); + Container::$persistentDataManager->saveToDb($event); + + return new JsonContent([ + 'redirect' => ['target' => \Container::$routeCollection->getRoute('community.event')->generateLink(['communitySlug' => $this->community->getSlug(), 'eventSlug' => $event->getSlug()])] + ]); + } + + public function deleteEvent(): IContent + { + $event = $this->eventRepository->getBySlug(Container::$request->query('eventSlug')); + + foreach ($this->transactionRepository->getAllByEvent($event) as $transaction) { + $transaction->setEventId(null); + Container::$persistentDataManager->saveToDb($transaction); + } + + Container::$persistentDataManager->deleteFromDb($event); + + return new JsonContent(['success' => true]); + } + + private function sumTransactions(Event $event): float + { + $exchangeRateCalculator = new ExchangeRateCalculator($this->community->getMainCurrency()); + $transactions = $this->transactionRepository->getAllByEvent($event, true, ['currency']); + $sum = 0.0; + + foreach ($transactions as $transaction) { + $sum += $exchangeRateCalculator->calculate($transaction->getSum(), $transaction->getCurrency(), $transaction->getTimeDate()); + } + + return $sum; + } +} diff --git a/src/Controller/HomeController.php b/src/Controller/HomeController.php index d3a4960..a5af7ff 100644 --- a/src/Controller/HomeController.php +++ b/src/Controller/HomeController.php @@ -1,8 +1,10 @@ communityMemberRepository = new CommunityMemberRepository(); + $this->eventRepository = new EventRepository(); } public function isAuthenticationRequired(): bool @@ -39,7 +44,7 @@ class HomeController implements IAuthenticationRequired return new HtmlContent('home', [ 'communities' => $communities, - 'upcomingEvents' => [] + 'upcomingEvents' => iterator_to_array($this->eventRepository->getUpcomingByUser($user, new DateTime(), 3, true, ['community'])) ]); } } diff --git a/src/Controller/TransactionController.php b/src/Controller/TransactionController.php index aafbea0..2a08ff5 100644 --- a/src/Controller/TransactionController.php +++ b/src/Controller/TransactionController.php @@ -11,6 +11,7 @@ use RVR\Repository\CommunityMemberRepository; use RVR\Repository\CommunityRepository; use RVR\Repository\CurrencyRepository; use RVR\Repository\TransactionRepository; +use RVR\Repository\EventRepository; use SokoWeb\Interfaces\Authentication\IAuthenticationRequired; use SokoWeb\Interfaces\Authorization\ISecured; use SokoWeb\Interfaces\Response\IContent; @@ -27,9 +28,11 @@ class TransactionController implements IAuthenticationRequired, ISecured private TransactionRepository $transactionRepository; - private Community $community; + private EventRepository $eventRepository; - private CommunityMember $ownCommunityMember; + private ?Community $community; + + private ?CommunityMember $ownCommunityMember; public function __construct() { @@ -37,6 +40,7 @@ class TransactionController implements IAuthenticationRequired, ISecured $this->communityMemberRepository = new CommunityMemberRepository(); $this->currencyRepository = new CurrencyRepository(); $this->transactionRepository = new TransactionRepository(); + $this->eventRepository = new EventRepository(); } public function isAuthenticationRequired(): bool @@ -69,20 +73,38 @@ class TransactionController implements IAuthenticationRequired, ISecured Container::$persistentDataManager->loadRelationsFromDb($this->community, true, ['main_currency']); $exchangeRateCalculator = new ExchangeRateCalculator($this->community->getMainCurrency()); + $eventSlug = Container::$request->query('event'); + if ($eventSlug) { + $event = $this->eventRepository->getBySlug($eventSlug); + } else { + $event = null; + } + $itemsPerPage = 50; - $numberOfTransactions = $this->transactionRepository->countAllByCommunity($this->community); + $numberOfTransactions = $event ? + $this->transactionRepository->countAllByEvent($event) : + $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'] - ); + $transactions = $event ? + $this->transactionRepository->getPagedByEvent( + $event, + $currentPage * $itemsPerPage, + $itemsPerPage, + true, + ['currency', 'payer_user', 'payee_user'] + ) : + $this->transactionRepository->getPagedByCommunity( + $this->community, + $currentPage * $itemsPerPage, + $itemsPerPage, + true, + ['event', 'currency', 'payer_user', 'payee_user'] + ); return new HtmlContent('communities/transactions', [ 'community' => $this->community, + 'event' => $event, 'exchangeRateCalculator' => $exchangeRateCalculator, 'pages' => $pages, 'currentPage' => $currentPage, @@ -99,13 +121,22 @@ class TransactionController implements IAuthenticationRequired, ISecured if ($transaction === null) { return null; } + Container::$persistentDataManager->loadRelationsFromDb($transaction, false, ['event']); + $event = $transaction->getEvent(); } else { $transaction = null; + $eventSlug = Container::$request->query('event'); + if ($eventSlug) { + $event = $this->eventRepository->getBySlug($eventSlug); + } else { + $event = null; + } } return new HtmlContent('communities/transaction_edit', [ 'community' => $this->community, 'transaction' => $transaction, + 'event' => $event, 'members' => $this->getMembers($this->community), 'currencies' => $this->getCurrencies($this->community) ]); @@ -121,6 +152,7 @@ class TransactionController implements IAuthenticationRequired, ISecured $transaction->setCommunity($this->community); } + $transaction->setEventId(Container::$request->post('event_id') ?: null); $transaction->setCurrencyId(Container::$request->post('currency_id')); $transaction->setPayerUserId(Container::$request->post('payer_user_id')); $transaction->setPayeeUserId(Container::$request->post('payee_user_id') ?: null); @@ -143,7 +175,7 @@ class TransactionController implements IAuthenticationRequired, ISecured private function getMembers(Community $community): array { $members = iterator_to_array($this->communityMemberRepository->getAllByCommunity($community, true, ['user'])); - usort($members, function($a, $b) { + usort($members, function ($a, $b) { return strnatcmp($a->getUser()->getDisplayName(), $b->getUser()->getDisplayName()); }); return $members; @@ -152,10 +184,10 @@ class TransactionController implements IAuthenticationRequired, ISecured private function getCurrencies(Community $community): array { $currencies = iterator_to_array($this->currencyRepository->getAllByCommunity($community)); - usort($currencies, function($a, $b) { + usort($currencies, function ($a, $b) { return strnatcmp($a->getCode(), $b->getCode()); }); - usort($currencies, function($a, $b) use ($community) { + usort($currencies, function ($a, $b) use ($community) { return (int)($b->getId() === $community->getMainCurrencyId()) - (int)($a->getId() === $community->getMainCurrencyId()); }); return $currencies; diff --git a/src/PersistentData/Model/Event.php b/src/PersistentData/Model/Event.php new file mode 100644 index 0000000..0812971 --- /dev/null +++ b/src/PersistentData/Model/Event.php @@ -0,0 +1,107 @@ + Community::class]; + + protected static string $slugSource = 'title'; + + private ?Community $community = null; + + private int $communityId; + + private DateTime $start; + + private DateTime $end; + + private string $title = ''; + + private string $description = ''; + + public function setCommunity(Community $community): void + { + $this->community = $community; + } + + public function setCommunityId(int $communityId): void + { + $this->communityId = $communityId; + } + + public function setStartDate(DateTime $start): void + { + $this->start = $start; + } + + public function setStart(string $start): void + { + $this->start = new DateTime($start); + } + + public function setEndDate(DateTime $end): void + { + $this->end = $end; + } + + public function setEnd(string $end): void + { + $this->end = new DateTime($end); + } + + public function setTitle(string $title): void + { + $this->title = $title; + } + + public function setDescription(string $description): void + { + $this->description = $description; + } + + public function getCommunity(): ?Community + { + return $this->community; + } + + public function getCommunityId(): int + { + return $this->communityId; + } + + public function getStartDate(): DateTime + { + return $this->start; + } + + public function getStart(): string + { + return $this->start->format('Y-m-d H:i:s'); + } + + public function getEndDate(): DateTime + { + return $this->end; + } + + public function getEnd(): string + { + return $this->end->format('Y-m-d H:i:s'); + } + + public function getTitle(): string + { + return $this->title; + } + + public function getDescription(): string + { + return $this->description; + } +} diff --git a/src/PersistentData/Model/Transaction.php b/src/PersistentData/Model/Transaction.php index a4a9b48..fe73d49 100644 --- a/src/PersistentData/Model/Transaction.php +++ b/src/PersistentData/Model/Transaction.php @@ -7,10 +7,11 @@ 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 $fields = ['community_id', 'event_id', 'currency_id', 'payer_user_id', 'payee_user_id', 'description', 'sum', 'time']; protected static array $relations = [ 'community' => Community::class, + 'event' => Event::class, 'currency' => Currency::class, 'payer_user' => User::class, 'payee_user' => User::class @@ -20,6 +21,10 @@ class Transaction extends Model private int $communityId; + private ?Event $event = null; + + private ?int $eventId = null; + private ?Currency $currency = null; private int $currencyId; @@ -48,6 +53,16 @@ class Transaction extends Model $this->communityId = $communityId; } + public function setEvent(?Event $event): void + { + $this->event = $event; + } + + public function setEventId(?int $eventId): void + { + $this->eventId = $eventId; + } + public function setCurrency(Currency $currency): void { $this->currency = $currency; @@ -108,6 +123,16 @@ class Transaction extends Model return $this->communityId; } + public function getEvent(): ?Event + { + return $this->event; + } + + public function getEventId(): ?int + { + return $this->eventId; + } + public function getCurrency(): ?Currency { return $this->currency; diff --git a/src/Repository/EventRepository.php b/src/Repository/EventRepository.php new file mode 100644 index 0000000..9e568e4 --- /dev/null +++ b/src/Repository/EventRepository.php @@ -0,0 +1,89 @@ +selectFromDbById($id, Event::class); + } + + public function getBySlug(string $slug): ?Event + { + return \Container::$persistentDataManager->selectFromDbBySlug($slug, Event::class); + } + + public function getAllByCommunity(Community $community, bool $useRelations = false, array $withRelations = []): Generator + { + $select = $this->selectAllByCommunity($community); + + yield from Container::$persistentDataManager->selectMultipleFromDb($select, Event::class, $useRelations, $withRelations); + } + + public function countAllByCommunity(Community $community): int + { + return $this->selectAllByCommunity($community)->count(); + } + + public function getUpcomingByCommunity(Community $community, DateTime $from, int $limit, bool $useRelations = false, array $withRelations = []): Generator + { + $select = $this->selectAllByCommunity($community); + $select->where('end', '>', $from->format('Y-m-d H:i:s')); + $select->orderBy('start', 'ASC'); + $select->limit($limit, 0); + + yield from Container::$persistentDataManager->selectMultipleFromDb($select, Event::class, $useRelations, $withRelations); + } + + public function getUpcomingByUser(User $user, DateTime $from, int $limit, bool $useRelations = false, array $withRelations = []): Generator + { + $select = $this->selectAllByUser($user); + $select->where('end', '>', $from->format('Y-m-d H:i:s')); + $select->orderBy('start', 'ASC'); + $select->limit($limit, 0); + + yield from Container::$persistentDataManager->selectMultipleFromDb($select, Event::class, $useRelations, $withRelations); + } + + public function getPagedByCommunity(Community $community, int $start, int $limit, bool $useRelations = false, array $withRelations = []): Generator + { + $select = $this->selectAllByCommunity($community); + $select->orderBy('start', 'DESC'); + $select->limit($limit, $start); + + yield from Container::$persistentDataManager->selectMultipleFromDb($select, Event::class, $useRelations, $withRelations); + } + + public function searchByTitle(Community $community, string $title): Generator + { + $select = $this->selectAllByCommunity($community); + $select->where('title', 'LIKE', '%' . $title . '%'); + $select->orderBy('start', 'DESC'); + $select->limit(10); + + yield from Container::$persistentDataManager->selectMultipleFromDb($select, Event::class); + } + + private function selectAllByCommunity(Community $community) + { + $select = new Select(Container::$dbConnection, Event::getTable()); + $select->where('community_id', '=', $community->getId()); + return $select; + } + + private function selectAllByUser(User $user) + { + $select = new Select(Container::$dbConnection, Event::getTable()); + $select->innerJoin('communities', ['communities', 'id'], '=', ['events', 'community_id']); + $select->innerJoin('community_members', ['communities', 'id'], '=', ['community_members', 'community_id']); + $select->where(['community_members', 'user_id'], '=', $user->getId()); + return $select; + } +} diff --git a/src/Repository/TransactionRepository.php b/src/Repository/TransactionRepository.php index 4535e74..8d2c5b0 100644 --- a/src/Repository/TransactionRepository.php +++ b/src/Repository/TransactionRepository.php @@ -4,6 +4,7 @@ use Container; use Generator; use RVR\PersistentData\Model\Community; use RVR\PersistentData\Model\Currency; +use RVR\PersistentData\Model\Event; use RVR\PersistentData\Model\Transaction; use RVR\PersistentData\Model\User; use SokoWeb\Database\Query\Select; @@ -22,11 +23,23 @@ class TransactionRepository yield from Container::$persistentDataManager->selectMultipleFromDb($select, Transaction::class, $useRelations, $withRelations); } + public function getAllByEvent(Event $event, bool $useRelations = false, array $withRelations = []): Generator + { + $select = $this->selectAllByEvent($event); + + yield from Container::$persistentDataManager->selectMultipleFromDb($select, Transaction::class, $useRelations, $withRelations); + } + public function countAllByCommunity(Community $community): int { return $this->selectAllByCommunity($community)->count(); } + public function countAllByEvent(Event $event): int + { + return $this->selectAllByEvent($event)->count(); + } + public function isAnyForUser(User $user): bool { $select = new Select(Container::$dbConnection, Transaction::getTable()); @@ -55,8 +68,16 @@ class TransactionRepository 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 = $this->selectAllByCommunity($community); + $select->orderBy('time', 'DESC'); + $select->limit($limit, $start); + + yield from Container::$persistentDataManager->selectMultipleFromDb($select, Transaction::class, $useRelations, $withRelations); + } + + public function getPagedByEvent(Event $event, int $start, int $limit, bool $useRelations = false, array $withRelations = []): Generator + { + $select = $this->selectAllByEvent($event); $select->orderBy('time', 'DESC'); $select->limit($limit, $start); @@ -69,4 +90,11 @@ class TransactionRepository $select->where('community_id', '=', $community->getId()); return $select; } + + private function selectAllByEvent(Event $event) + { + $select = new Select(Container::$dbConnection, Transaction::getTable()); + $select->where('event_id', '=', $event->getId()); + return $select; + } } diff --git a/views/communities/community.php b/views/communities/community.php index b401d27..78406ec 100644 --- a/views/communities/community.php +++ b/views/communities/community.php @@ -15,11 +15,15 @@
+ = $event->getTitle() ?> + = $event->getStartDate()->format('Y-m-d') ?> – = $event->getEndDate()->format('Y-m-d') ?> +
There is no upcoming event.
+Transactions | New transaction
You owe | diff --git a/views/communities/community_members.php b/views/communities/community_members.php index 0fcc42d..372358c 100644 --- a/views/communities/community_members.php +++ b/views/communities/community_members.php @@ -41,7 +41,7 @@|
- |
diff --git a/views/communities/transaction_edit.php b/views/communities/transaction_edit.php
index 76c3392..2d98c7f 100644
--- a/views/communities/transaction_edit.php
+++ b/views/communities/transaction_edit.php
@@ -1,9 +1,17 @@
+@css(/static/node_modules/tom-select/dist/css/tom-select.min.css)
+@js(/static/node_modules/tom-select/dist/js/tom-select.base.min.js)
+@js(js/communities/transaction.js)
+
@extends(templates/layout_normal)
@section(main)
= $community->getName() ?> » Transactions » + + Events » + = $event->getTitle() ?> » + Edit transaction @@ -16,8 +24,15 @@ Container::$routeCollection->getRoute('community.transactions.edit-action')->generateLink(['communitySlug' => $community->getSlug(), 'transactionId' => $transaction->getId()]) : Container::$routeCollection->getRoute('community.transactions.new-action')->generateLink(['communitySlug' => $community->getSlug()]); ?> - |