Skip to content

Commit

Permalink
Added several endpoints and actions to the costs sheet
Browse files Browse the repository at this point in the history
  • Loading branch information
Felix Ruf committed Aug 10, 2023
1 parent 2d9ec1b commit f137333
Show file tree
Hide file tree
Showing 14 changed files with 273 additions and 80 deletions.
68 changes: 15 additions & 53 deletions src/Mealz/AccountingBundle/Controller/CostSheetController.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,37 +83,18 @@ public function list(
]);
}

public function hideUserRequest(
Profile $profile,
ParticipantRepositoryInterface $participantRepo,
TransactionRepositoryInterface $transactionRepo
): Response {
$this->denyAccessUnlessGranted('ROLE_KITCHEN_STAFF');

public function hideUser(Profile $profile): JsonResponse
{
if (!$profile->isHidden()) {
$entityManager = $this->getDoctrine()->getManager();
$profile->setHidden(true);
$entityManager->persist($profile);
$entityManager->flush();

$message = $this->get('translator')->trans(
'payment.costsheet.hide_user.request.success',
['%name%' => $profile->getFullName()],
'messages'
);
$severity = 'success';
return new JsonResponse(null, 200);
} else {
$message = $this->get('translator')->trans(
'payment.costsheet.hide_user.request.info',
['%name%' => $profile->getFullName()],
'messages'
);
$severity = 'info';
return new JsonResponse(['message' => 'Profile is already hidden'], 500);
}

$this->addFlashMessage($message, $severity);

return $this->list($participantRepo, $transactionRepo);
}

private function getRemainingCosts($costs, &$transactions)
Expand All @@ -129,46 +110,27 @@ private function getRemainingCosts($costs, &$transactions)
return ($result < 0) ? 0 : $result * -1;
}

public function sendSettlementRequest(
Profile $userProfile,
Wallet $wallet,
ParticipantRepositoryInterface $participantRepo,
TransactionRepositoryInterface $transactionRepo
): Response {
if (null === $userProfile->getSettlementHash() && $wallet->getBalance($userProfile) > 0.00) {
$username = $userProfile->getUsername();
public function postSettlement(Profile $profile, Wallet $wallet): JsonResponse
{
if (null === $profile->getSettlementHash() && $wallet->getBalance($profile) > 0.00) {
$username = $profile->getUsername();
$secret = $this->getParameter('app.secret');
$hashCode = str_replace('/', '', crypt($username, $secret));
$urlEncodedHash = urlencode($hashCode);

$entityManager = $this->getDoctrine()->getManager();
$userProfile->setSettlementHash($hashCode);
$entityManager->persist($userProfile);
$profile->setSettlementHash($hashCode);
$entityManager->persist($profile);
$entityManager->flush();

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

$message = $this->get('translator')->trans(
'payment.costsheet.account_settlement.request.success',
['%name%' => $userProfile->getFullName()],
'messages'
);
$severity = 'success';
} elseif (null !== $userProfile->getSettlementHash() && $wallet->getBalance($userProfile) > 0.00) {
$message = $this->get('translator')->trans(
'payment.costsheet.account_settlement.request.already_sent',
['%name%' => $userProfile->getFullName()],
'messages'
);
$severity = 'danger';
return new JsonResponse($wallet->getBalance($profile), 200);
} elseif (null !== $profile->getSettlementHash() && $wallet->getBalance($profile) > 0.00) {
return new JsonResponse(['message' => 'Settlement request already send'], 500);
} else {
$message = $this->get('translator')->trans('payment.costsheet.account_settlement.request.failure');
$severity = 'danger';
return new JsonResponse(['message' => 'Settlement request failed'], 500);
}

$this->addFlashMessage($message, $severity);

return $this->list($participantRepo, $transactionRepo);
}

public function renderConfirmButton(string $hash, ProfileRepositoryInterface $profileRepo): Response
Expand Down
15 changes: 8 additions & 7 deletions src/Mealz/AccountingBundle/Resources/config/routing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,14 @@ mealz_accounting_api_costs:
defaults: { _controller: App\Mealz\AccountingBundle\Controller\CostSheetController::list }
methods: [ GET ]

mealz_accounting_cost_sheet_hide_user_request:
path: /print/costsheet/hideuser/request/{profile}
defaults: { _controller: App\Mealz\AccountingBundle\Controller\CostSheetController::hideUserRequest }

mealz_accounting_cost_sheet_send_settlement_request:
path: /print/costsheet/settlement/request/{username}
defaults: { _controller: App\Mealz\AccountingBundle\Controller\CostSheetController::sendSettlementRequest }
mealz_accounting_api_costs_hide_user:
path: /api/costs/hideuser/{profile}
defaults: { _controller: App\Mealz\AccountingBundle\Controller\CostSheetController::hideUser }
methods: [ POST ]

mealz_accounting_api_costs_settlement:
path: /api/costs/settlement/{profile}
defaults: { _controller: App\Mealz\AccountingBundle\Controller\CostSheetController::postSettlement }

mealz_accounting_cost_sheet_redirect_to_confirm:
path: /print/costsheet/redirect/confirm/{hash}
Expand Down
13 changes: 13 additions & 0 deletions src/Resources/src/api/postHideUser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { IMessage } from "@/interfaces/IMessage";
import useApi from "./api";

export default async function postHideUser(username: string) {
const { error, request, response } = useApi<IMessage | null>(
'POST',
`api/costs/hideuser/${username}`
);

await request();

return { error, response };
}
13 changes: 13 additions & 0 deletions src/Resources/src/api/postSettlement.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 postSettlement(username: string) {
const { error, request, response } = useApi<IMessage | number>(
'POST',
`api/costs/settlement/${username}`
);

await request();

return { error, response };
}
55 changes: 55 additions & 0 deletions src/Resources/src/components/costs/CostsActionSettlement.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<template>
<Dialog
:open="open"
:initial-focus="initialFocus"
class="relative z-50"
@close="emit('confirm', false)"
>
<div
class="fixed inset-0 flex items-center justify-center p-4"
>
<DialogPanel
class="relative overflow-hidden rounded-lg bg-white p-4 text-left shadow-xl sm:my-8 sm:p-6"
>
<DialogTitle class="text-[18px]">
{{ t('costs.settlement').replace('#name#', getFullNameByUser(username)) }}
</DialogTitle>
<div class="flex flex-row">
<CreateButton
ref="initialFocus"
:managed="true"
:btn-text="t('costs.settle')"
class="flex-1 cursor-pointer"
@click="emit('confirm', true)"
/>
<CancelButton
:btn-text="t('costs.cancel')"
class="flex-1 cursor-pointer"
@click="emit('confirm', false)"
/>
</div>
</DialogPanel>
</div>
</Dialog>
</template>

<script setup lang="ts">
import { Dialog, DialogPanel, DialogTitle } from '@headlessui/vue';
import { useI18n } from 'vue-i18n';
import { useCosts } from '@/stores/costsStore';
import CancelButton from '../misc/CancelButton.vue';
import CreateButton from '@/components/misc/CreateButton.vue';
import { ref } from 'vue';
const { t } = useI18n();
const { getFullNameByUser } = useCosts();
defineProps<{
open: boolean,
username: string
}>();
const emit = defineEmits(['confirm']);
const initialFocus = ref<HTMLElement | null>(null);
</script>
32 changes: 26 additions & 6 deletions src/Resources/src/components/costs/CostsHeader.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
<template>
<div class="mb-8 grid w-full grid-cols-3 gap-3 sm:grid-rows-[minmax(0,1fr)_30px] min-[900px]:grid-cols-[minmax(0,2fr)_minmax(0,1fr)] min-[900px]:gap-1">
<div class="mb-8 grid w-full grid-cols-3 gap-6 sm:grid-rows-[minmax(0,1fr)_minmax(0,1fr)] min-[900px]:grid-cols-[minmax(0,1fr)_minmax(0,1fr)]">
<h2 class="col-span-3 col-start-1 row-span-1 row-start-1 m-0 w-full self-center justify-self-start max-[380px]:text-[24px] min-[900px]:col-span-1">
{{ t('costs.header') }}
</h2>
<CashRegisterLink
class="col-span-3 row-start-2 justify-self-center sm:col-span-1 sm:col-start-1 sm:justify-self-start min-[900px]:row-start-2"
/>
<InputLabel
v-model="filter"
:label-text="t('costs.search')"
:label-visible="false"
class="col-span-3 row-start-2 justify-self-center sm:col-span-1 sm:col-start-1 sm:justify-self-start min-[900px]:row-start-2"
/>
<CashRegisterLink
class="col-span-3 row-start-3 justify-self-center sm:col-span-1 sm:col-start-2 sm:row-start-2 sm:justify-self-end min-[900px]:row-start-1"
/>
<SwitchGroup>
<div
class="col-span-3 row-start-4 grid grid-rows-[auto_minmax(0,1fr)] content-center justify-items-end sm:col-span-1 sm:col-start-3 sm:row-start-2 sm:justify-self-end sm:text-end min-[900px]:col-start-2"
>
<SwitchLabel class="w-full pb-1 text-end text-xs font-medium text-[#173D7A]">
{{ t('costs.showHidden') }}
</SwitchLabel>
<Switch
:sr="t('costs.showHidden')"
:initial="showHidden"
class="self-end justify-self-end"
@toggle="(value) => emit('change:showHidden', value)"
/>
</div>
</SwitchGroup>
</div>
</template>

Expand All @@ -18,14 +35,17 @@ import { useI18n } from 'vue-i18n';
import CashRegisterLink from './CashRegisterLink.vue';
import InputLabel from '../misc/InputLabel.vue';
import { computed } from 'vue';
import Switch from "@/components/misc/Switch.vue"
import { SwitchGroup, SwitchLabel } from '@headlessui/vue';
const { t } = useI18n();
const props = defineProps<{
modelValue: string
modelValue: string,
showHidden: boolean
}>();
const emit = defineEmits(['update:modelValue']);
const emit = defineEmits(['update:modelValue', 'change:showHidden']);
const filter = computed({
get() {
Expand Down
28 changes: 20 additions & 8 deletions src/Resources/src/components/costs/CostsTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@
{{ new Intl.NumberFormat(locale, { style: 'currency', currency: 'EUR' }).format(costs.costs['total']) }}
</td>
<td>
Actions to be implemented
<CostsTableActions
:username="username"
:balance="costs.costs['total']"
/>
</td>
</tr>
</Table>
Expand All @@ -35,28 +38,37 @@ import Table from '@/components/misc/Table.vue';
import { useCosts } from '@/stores/costsStore';
import { computed } from 'vue';
import { useI18n } from 'vue-i18n';
import CostsTableActions from './CostsTableActions.vue';
const { t, locale } = useI18n();
const { CostsState, getColumnNames } = useCosts();
const props = defineProps<{
filter: string
filter: string,
showHidden: boolean
}>();
const columnNames = computed(() => ['Name', t('costs.table.earlier'), ...getColumnNames(locale.value), t('costs.table.total'), t('costs.table.actions')]);
const filterRegex = computed(() => {
const filterStrings = props.filter.split(/[\s,.]+/).map(filterStr => filterStr.toLowerCase());
return createRegexForFilter(filterStrings);
});
const hiddenUsers = computed(() => Object.entries(CostsState.users).filter(user => {
const [key, value] = user;
return value.hidden === false || (value.hidden === true && props.showHidden === true);
}));
const filteredUsers = computed(() => {
if (props.filter === '') {
return Object.entries(CostsState.users)
return hiddenUsers.value
}
const filterStrings = props.filter.split(/[\s,.]+/).map(filterStr => filterStr.toLowerCase());
const regex = createRegexForFilter(filterStrings);
return Object.entries(CostsState.users).filter(user => {
return hiddenUsers.value.filter(user => {
const [key, value] = user;
const searchStrings = [value.firstName, value.name].join(' ');
return regex.test(searchStrings);
return filterRegex.value.test(searchStrings);
});
});
Expand Down
49 changes: 49 additions & 0 deletions src/Resources/src/components/costs/CostsTableActions.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<template>
<div class="ml-auto grid h-full w-fit grid-cols-3 justify-end gap-2">
<ActionButton
:action="Action.HIDE"
:btn-text="''"
@click="hideUser(username)"
/>
<ActionButton
:action="Action.CREATE"
:btn-text="''"
/>
<ActionButton
v-if="balance > 0"
:action="Action.BALANCE"
:btn-text="''"
@click="settlementOpen = true"
/>
</div>
<CostsActionSettlement
:open="settlementOpen"
:username="username"
@confirm="(value) => handleSettlement(value)"
/>
</template>

<script setup lang="ts">
import ActionButton from '../misc/ActionButton.vue';
import { Action } from '@/enums/Actions';
import { useCosts } from '@/stores/costsStore';
import CostsActionSettlement from './CostsActionSettlement.vue';
import { ref } from 'vue';
const { hideUser, sendSettlement } = useCosts();
const settlementOpen = ref(false);
const props = defineProps<{
username: string,
balance: number
}>();
async function handleSettlement(confirm: boolean) {
settlementOpen.value = false;
if (confirm === true) {
await sendSettlement(props.username);
}
}
</script>
Loading

0 comments on commit f137333

Please sign in to comment.