Skip to content

Commit

Permalink
added api routes for adding cash, getting the transaction history and…
Browse files Browse the repository at this point in the history
… the initial process for settlement. Also added corresponding services, views and components
  • Loading branch information
Felix Ruf committed Aug 11, 2023
1 parent f137333 commit 51566a3
Show file tree
Hide file tree
Showing 24 changed files with 512 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@
use Qipsius\TCPDFBundle\Controller\TCPDFController;
use ReflectionException;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;

/**
* @Security("is_granted('ROLE_FINANCE')")
*/
class AccountingBookController extends BaseController
{
public function list(TransactionRepositoryInterface $transactionRepo): Response
public function list(TransactionRepositoryInterface $transactionRepo): JsonResponse
{
// Get first and last day of previous month
$minDateFirst = new DateTime('first day of previous month');
Expand All @@ -42,12 +43,12 @@ public function list(TransactionRepositoryInterface $transactionRepo): Response
// Get array of users with their amount of transactions in actual month
$users = $transactionRepo->findUserDataAndTransactionAmountForGivenPeriod($minDate, $maxDate);

return $this->render('MealzAccountingBundle:Accounting/Admin:accountingBook.html.twig', [
'headingFirst' => $headingFirst,
'heading' => $heading,
'usersFirst' => $usersFirst,
'users' => $users,
]);
return new JsonResponse([
'lastMonth' => $headingFirst,
'thisMonth' => $heading,
'usersLastMonth' => $usersFirst,
'usersThisMonth' => $users,
], 200);
}

/**
Expand Down
19 changes: 17 additions & 2 deletions src/Mealz/AccountingBundle/Controller/CostSheetController.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,14 +125,29 @@ public function postSettlement(Profile $profile, Wallet $wallet): JsonResponse

$this->sendSettlementRequestMail($profile, $urlEncodedHash);

return new JsonResponse($wallet->getBalance($profile), 200);
return new JsonResponse(null, 200);
} elseif (null !== $profile->getSettlementHash() && $wallet->getBalance($profile) > 0.00) {
return new JsonResponse(['message' => 'Settlement request already send'], 500);
} else {
return new JsonResponse(['message' => 'Settlement request failed'], 500);
}
}

public function getProfileFromHash(string $hash, ProfileRepositoryInterface $profileRepository): JsonResponse
{
$queryResult = $profileRepository->findBy(['settlementHash' => urldecode($hash)]);
$profile = $queryResult[0];

if (null === $profile) {
return new JsonResponse(['message' => 'Not found'], 404);
}
return new JsonResponse([
'user' => $profile->getUsername(),
'fullName' => $profile->getFullName(),
'roles' => $profile->getRoles(),
], 200);
}

public function renderConfirmButton(string $hash, ProfileRepositoryInterface $profileRepo): Response
{
$profile = null;
Expand Down Expand Up @@ -218,7 +233,7 @@ private function sendSettlementRequestMail(Profile $profile, string $urlEncodedH
'%admin%' => $this->getProfile()->getFullName(),
'%fullname%' => $profile->getFullName(),
'%link%' => rtrim($this->getParameter('app.base_url'), '/') . $this->generateUrl(
'mealz_accounting_cost_sheet_redirect_to_confirm',
'MealzMealBundle_costs_settlement_confirm',
['hash' => $urlEncodedHash]
),
],
Expand Down
62 changes: 62 additions & 0 deletions src/Mealz/AccountingBundle/Controller/Payment/CashController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

namespace App\Mealz\AccountingBundle\Controller\Payment;

use App\Mealz\AccountingBundle\Entity\Transaction;
use App\Mealz\AccountingBundle\Form\CashPaymentAdminForm;
use App\Mealz\AccountingBundle\Repository\TransactionRepositoryInterface;
use App\Mealz\AccountingBundle\Service\Wallet;
use App\Mealz\MealBundle\Controller\BaseController;
use App\Mealz\MealBundle\Repository\ParticipantRepositoryInterface;
use App\Mealz\UserBundle\Entity\Profile;
use DateTime;
use Doctrine\ORM\EntityManager;
use Exception;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

/**
* @Security("is_granted('ROLE_KITCHEN_STAFF')")
*/
class CashController extends BaseController
{
/**
* @Security("is_granted('ROLE_KITCHEN_STAFF')")
*/
public function postPaymentCash(Profile $profile, Request $request): JsonResponse
{
try {
$transaction = new Transaction();
$transaction->setProfile($profile);
$amount = (float) $request->query->get('amount');

if ($amount > 0) {
$transaction->setAmount($amount);

$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($transaction);
$entityManager->flush();

$logger = $this->get('monolog.logger.balance');
$logger->info('admin added {amount}€ into wallet of {profile} (Transaction: {transactionId})', [
'profile' => $transaction->getProfile(),
'amount' => $transaction->getAmount(),
'transactionId' => $transaction->getId(),
]);

return new JsonResponse($transaction->getAmount(), 200);
} else {
throw new Exception('Amount less than 0');
}

} catch (Exception $e) {
$logger = $this->get('monolog.logger.balance');
$logger->info($e->getMessage());

return new JsonResponse(['message' => $e->getMessage()], 500);
}
}
}
19 changes: 11 additions & 8 deletions src/Mealz/AccountingBundle/Resources/config/routing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ mealz_accounting_payment_ecash_transaction_failure:
path: /payment/ecash/transaction/failure
defaults: { _controller: App\Mealz\AccountingBundle\Controller\Payment\EcashController::transactionFailure }

mealz_accounting_payment_cash_form_submit:
path: /payment/cash/form/submit
defaults: { _controller: App\Mealz\AccountingBundle\Controller\Payment\CashController::paymentFormHandling }
mealz_accounting_api_payment_cash:
path: /api/payment/cash/{profile}
defaults: { _controller: App\Mealz\AccountingBundle\Controller\Payment\CashController::postPaymentCash }
methods: [ POST ]

mealz_accounting_payment_cash_form:
path: /payment/cash/form/{profile}
Expand All @@ -27,8 +28,8 @@ mealz_accounting_payment_transaction_history:
path: /accounting/transactions
defaults: { _controller: App\Mealz\AccountingBundle\Controller\Payment\CashController::showTransactionData }

mealz_accounting_accounting_book:
path: /accounting/book
mealz_accounting_api_accounting_book:
path: /api/accounting/book
defaults: { _controller: App\Mealz\AccountingBundle\Controller\AccountingBookController::list }

mealz_accounting_accounting_book_finance:
Expand All @@ -52,10 +53,12 @@ mealz_accounting_api_costs_hide_user:
mealz_accounting_api_costs_settlement:
path: /api/costs/settlement/{profile}
defaults: { _controller: App\Mealz\AccountingBundle\Controller\CostSheetController::postSettlement }
methods: [ POST ]

mealz_accounting_cost_sheet_redirect_to_confirm:
path: /print/costsheet/redirect/confirm/{hash}
defaults: { _controller: App\Mealz\AccountingBundle\Controller\CostSheetController::renderConfirmButton }
mealz_accounting_costs_settlement_get_profile:
path: /api/costs/profile/{hash}
defaults: { _controller: App\Mealz\AccountingBundle\Controller\CostSheetController::getProfileFromHash }
methods: [ GET ]

mealz_accounting_cost_sheet_confirm_settlement_request:
path: /print/costsheet/settlement/confirm/{hash}
Expand Down
8 changes: 8 additions & 0 deletions src/Mealz/MealBundle/Resources/config/routing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ MealzMealBundle_costs:
path: /costs
defaults: { _controller: App\Mealz\MealBundle\Controller\FrontendController::renderIndex }

MealzMealBundle_cash_register:
path: /cash-register
defaults: { _controller: App\Mealz\MealBundle\Controller\FrontendController::renderIndex }

MealzMealBundle_costs_settlement_confirm:
path: /costs/settlement/confirm/{hash}
defaults: { _controller: App\Mealz\MealBundle\Controller\FrontendController::renderIndex }

MealzMealBundle_finance:
path: /finance
defaults: { _controller: App\Mealz\MealBundle\Controller\FrontendController::renderIndex }
Expand Down
16 changes: 16 additions & 0 deletions src/Resources/src/api/getProfileWithHash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { IMessage } from "@/interfaces/IMessage";
import useApi from "./api";
import { IProfile } from "@/stores/profilesStore";
/**
* Fetches a profile that was requested for settlement by their settlement hash
*/
export default async function getTransactionHistory(hash: string) {
const { error, response: profile, request } = useApi<IProfile | IMessage>(
'GET',
`api/costs/profile/${hash}`
);

await request();

return { error, profile };
}
16 changes: 16 additions & 0 deletions src/Resources/src/api/getTransactionHistory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import useApi from "./api";
import { ITransactionHistory } from "@/stores/accountingStore";

/**
* Fetches two lists of transactions per user for the past month and the current month
*/
export default async function getTransactionHistory() {
const { error, response: transactions, request } = useApi<ITransactionHistory>(
'GET',
'api/accounting/book'
);

await request();

return { error, transactions };
}
13 changes: 13 additions & 0 deletions src/Resources/src/api/postCashPayment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import useApi from '@/api/api';
import { IMessage } from '@/interfaces/IMessage';

export default async function postCashPayment(username: string, amount: number) {
const { error, request, response } = useApi<IMessage | number>(
'POST',
`api/payment/cash/${username}?amount=${amount}`
);

await request();

return { error, response };
}
2 changes: 1 addition & 1 deletion src/Resources/src/api/postSettlement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import useApi from '@/api/api';
import { IMessage } from '@/interfaces/IMessage';

export default async function postSettlement(username: string) {
const { error, request, response } = useApi<IMessage | number>(
const { error, request, response } = useApi<IMessage | null>(
'POST',
`api/costs/settlement/${username}`
);
Expand Down
76 changes: 76 additions & 0 deletions src/Resources/src/components/cashRegister/CashRegisterTable.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<template>
<div>
<h3>{{ dateRange }}</h3>
<Table
:labels="['Name', t('costs.amount'), t('costs.type')]"
:header-text-position="'lfr'"
>
<tr
v-for="[user, transaction] in Object.entries(transactions)"
:key="user"
class="max-h-[62px] border-b-2 border-gray-200 text-right text-[12px] xl:text-[18px]"
>
<td class="py-2 text-left">
{{ `${transaction.name}, ${transaction.firstName}` }}
</td>
<td class="py-2 text-right">
{{ new Intl.NumberFormat(locale, { style: 'currency', currency: 'EUR' }).format(parseFloat((transaction as IUserTransaction).amount)) }}
</td>
<td>
<div
class="flex h-full w-full content-center justify-end text-primary"
>
<svg
v-if="transaction.paymethod !== null && transaction.paymethod === '0'"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
viewBox="0 0 1000 1000"
xml:space="preserve"
class="h-[24px] w-[24px]"
>
<g>
<path d="M918,239.3c-9.2-11.4-20-21.8-31.9-31.1c1.6,29.5-1.1,61-8.3,94.2c-19.4,90.9-65.8,168.2-134.2,223.5c-68.4,55.2-153.7,84.4-246.8,84.4H361l-44,205.9c-8.6,40.4-44.9,69.7-86.2,69.7h-61.2l-11,50.6c-2.8,13,0.4,26.6,8.7,37c8.4,10.4,21,16.4,34.3,16.4h149.6c20.8,0,38.8-14.5,43.1-34.9l51.4-240.8h171.4c82.9,0,158.7-25.8,219.1-74.6c60.5-48.8,101.6-117.4,118.8-198.4C972.7,359.5,960.2,291.5,918,239.3z"/>

Check warning on line 35 in src/Resources/src/components/cashRegister/CashRegisterTable.vue

View workflow job for this annotation

GitHub Actions / FE Asset Linting

Expected a space before '/>', but not found
<path d="M273.9,807l51.4-240.8h171.4c82.9,0,158.7-25.8,219.1-74.6c60.5-48.8,101.6-117.4,118.8-198.4c17.5-81.9,5-149.8-37.2-202C757,41.1,687.7,10,616.7,10H243.2c-20.7,0-38.6,14.4-43.1,34.7l-162,743.8c-2.8,13,0.4,26.6,8.7,37c8.4,10.4,21,16.4,34.3,16.4h149.6C251.6,841.9,269.6,827.4,273.9,807z M411.1,179.4h117.5c27.7,0,52.4,11.6,67.8,31.8c16.4,21.5,21.2,51.1,13.2,81.4c-0.1,0.4-0.2,0.8-0.3,1.2C594.5,356.2,533,407,472.3,407H359.7L411.1,179.4z"/>

Check warning on line 36 in src/Resources/src/components/cashRegister/CashRegisterTable.vue

View workflow job for this annotation

GitHub Actions / FE Asset Linting

Expected a space before '/>', but not found
</g>
</svg>
<CurrencyEuroIcon
v-else
class="h-8 w-8"
/>
</div>
</td>
</tr>
<tr class="max-h-[62px] border-b-2 border-gray-200 text-right text-[12px] xl:text-[18px]">
<td class="py-2 text-left font-bold">
{{ t('costs.table.total') }}
</td>
<td class="py-2 text-right font-bold">
{{ new Intl.NumberFormat(locale, { style: 'currency', currency: 'EUR' }).format(getTotalAmount()) }}
</td>
<td />
</tr>
</Table>
</div>
</template>

<script setup lang="ts">
import Table from '@/components/misc/Table.vue';
import { IUserTransaction } from '@/stores/accountingStore';
import { Dictionary } from 'types/types';
import { useI18n } from 'vue-i18n';
import { CurrencyEuroIcon } from '@heroicons/vue/outline';
const { t, locale } = useI18n();
const props = defineProps<{
transactions: Dictionary<IUserTransaction>,
dateRange: string
}>();
function getTotalAmount() {
return Object.values(props.transactions).map(transaction => parseFloat((transaction as IUserTransaction).amount)).reduce((total, transaction) => total + transaction, 0);
}
</script>
40 changes: 40 additions & 0 deletions src/Resources/src/components/costs/CashPaymentPanel.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<template>
<form
class="w-[300px] p-4 sm:w-[400px]"
@submit.prevent="onSubmit"
>
<h3 class="w-full text-center">
{{ t('costs.payment').replace('#name#', getFullNameByUser(username)) }}
</h3>
<InputLabel
v-model="amount"
:type="'number'"
:min="0"
/>
<SubmitButton />
</form>
</template>

<script setup lang="ts">
import { useI18n } from 'vue-i18n';
import { useCosts } from '@/stores/costsStore';
import InputLabel from '../misc/InputLabel.vue';
import { ref } from 'vue';
import SubmitButton from '../misc/SubmitButton.vue';
const { t } = useI18n();
const { sendCashPayment, getFullNameByUser } = useCosts();
const amount = ref('0');
const props = defineProps<{
username: string
}>();
async function onSubmit() {
const parsedAmount = parseFloat(amount.value);
if (parsedAmount > 0) {
await sendCashPayment(props.username, parsedAmount);
}
}
</script>
4 changes: 2 additions & 2 deletions src/Resources/src/components/costs/CashRegisterLink.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<router-link
:to="'/costs'"
class="flex flex-row items-center gap-2"
:to="'/cash-register'"
class="flex flex-row items-center gap-2 text-[#173D7A] hover:text-highlight"
>
<CurrencyEuroIcon class="h-6 w-6" />
<span>{{ t('costs.cashRegister') }}</span>
Expand Down
Loading

0 comments on commit 51566a3

Please sign in to comment.