[Vue3] Fixed pricelist

This commit is contained in:
Ferdinand Thiessen 2021-03-14 13:52:58 +01:00
parent 62aa627f0c
commit 5153f074b5
7 changed files with 224 additions and 117 deletions

View File

@ -13,10 +13,10 @@ declare namespace FG {
firstname: string; firstname: string;
lastname: string; lastname: string;
mail: string; mail: string;
birthday?: Date; birthday?: any;
roles: Array<string>; roles: Array<string>;
permissions?: Array<string>; permissions?: any;
avatar_url?: string; avatar_url?: any;
} }
type Permission = string; type Permission = string;
interface Role { interface Role {
@ -29,58 +29,41 @@ declare namespace FG {
time: Date; time: Date;
amount: number; amount: number;
reversal_id: number; reversal_id: number;
sender_id?: string; sender_id?: any;
receiver_id?: string; receiver_id?: any;
author_id?: string; author_id?: any;
original_id?: number; original_id?: any;
} }
interface Drink { interface Drink {
id: number; id: number;
article_id?: string;
package_size?: number;
name: string; name: string;
volume?: number;
cost_price_pro_volume?: number;
cost_price_package_netto?: number;
tags?: Array<Tag>;
type?: DrinkType;
volumes: Array<DrinkPriceVolume>;
}
interface DrinkIngredient {
id: number;
volume: number; volume: number;
drink_ingredient_id: number; cost_price: number;
discount: number;
extra_charge?: any;
prices: Array<DrinkPrice>;
ingredients: Array<Ingredient>;
tags: Array<any>;
} }
interface DrinkPrice { interface DrinkPrice {
id: number;
price: number;
public: boolean;
description?: string;
}
interface DrinkPriceVolume {
id: number; id: number;
volume: number; volume: number;
min_prices: Array<MinPrices>; price: number;
prices: Array<DrinkPrice>; no_auto: boolean;
ingredients: Array<Ingredient>; public: boolean;
description?: any;
round_step: number;
} }
interface DrinkType { interface DrinkType {
id: number; id: number;
name: string; name: string;
} }
interface ExtraIngredient {
id: number;
name: string;
price: number;
}
interface Ingredient { interface Ingredient {
id: number; id: number;
drink_ingredient?: DrinkIngredient; volume: number;
extra_ingredient?: ExtraIngredient; drink_parent_id: number;
} drink_ingredient_id: number;
interface MinPrices { drink_ingredient?: any;
percentage: number;
price: number;
} }
interface Tag { interface Tag {
id: number; id: number;
@ -90,7 +73,7 @@ declare namespace FG {
id: number; id: number;
start: Date; start: Date;
end: Date; end: Date;
description?: string; description?: any;
type: EventType; type: EventType;
jobs: Array<Job>; jobs: Array<Job>;
} }
@ -101,7 +84,7 @@ declare namespace FG {
interface Job { interface Job {
id: number; id: number;
start: Date; start: Date;
end?: Date; end?: any;
comment: string; comment: string;
type: JobType; type: JobType;
services: Array<Service>; services: Array<Service>;

View File

@ -0,0 +1,162 @@
<template>
<div>
<q-card>
<q-card-section>
<div class="text-h4">Neues Getränk</div>
</q-card-section>
<q-form @submit="save">
<q-card-section>
<div class="text-h5">Getränkinformationen</div>
<div class="row">
<q-input
v-model="drink.name"
class="col-12 col-sm-6 q-px-sm q-py-md"
filled
label="Name"
/>
<q-input
v-model="drink.volume"
class="col-12 col-sm-6 q-px-sm q-py-md"
filled
label="Inhalt in Liter"
type="number"
step="0.01"
/>
<q-input
v-model="drink.cost_price"
class="col-12 col-sm-6 q-px-sm q-py-md"
filled
label="Einkaufspreis"
type="number"
step="0.01"
/>
<q-input
v-model="drink.discount"
class="col-12 col-sm-6 q-px-sm q-py-md"
filled
label="Aufschlag in Prozent"
type="number"
hint="Wenn nicht gesetzt wird default-wert genommen."
step="0.01"
/>
<q-input
v-model="drink.extra_charge"
class="col-12 col-sm-6 q-px-sm q-py-md"
filled
label="Extra Aufschlag in Euro"
type="number"
step="0.1"
/>
<q-input class="col-12 col-sm-6 q-px-sm q-py-md" filled label="Tags" />
</div>
</q-card-section>
<q-card-section>
<div class="row justify-between">
<div class="text-h5">Preise</div>
<q-btn round icon="mdi-plus" color="primary" @click="addPrice" />
</div>
<q-card v-for="(price, index) in drink.prices" :key="index" class="q-ma-sm">
<div class="row">
<q-input
v-model="price.volume"
class="col-12 col-sm-6 q-px-sm q-py-md"
label="Inhalt in Liter"
filled
type="number"
step="0.01"
/>
<q-input
v-model="price.price"
class="col-12 col-sm-6 q-px-sm q-py-md"
label="Preis in €"
filled
:disable="price.no_auto"
type="number"
step="0.1"
/>
<q-toggle
v-model="price.no_auto"
class="col-12 col-sm-6 q-px-sm q-py-md"
label="Automatische Preiskalkulation"
color="primary"
/>
<q-input
v-model="price.round_step"
class="col-12 col-sm-6 q-px-sm q-py-md"
label="Rundungsschritt"
type="number"
filled
step="0.1"
/>
<q-toggle
v-model="price.public"
class="col-12 col-sm-6 q-px-sm q-py-md"
label="Öffentlich"
color="primary"
/>
<q-input
v-model="price.description"
class="col-12 col-sm-6 q-px-sm q-py-md"
label="Beschreibung"
filled
/>
</div>
</q-card>
</q-card-section>
<q-card-section>
<div class="row justify-between">
<div class="text-h5">Zutaten</div>
<q-btn round icon="mdi-plus" color="primary" />
</div>
</q-card-section>
<q-card-actions align="right">
<q-btn type="submit" label="Speichern" color="primary" />
</q-card-actions>
</q-form>
</q-card>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { useStore } from 'vuex';
export default defineComponent({
name: 'Drink',
setup() {
const store = useStore();
const drink = ref({
name: '',
volume: '',
cost_price: '',
discount: '',
extra_charge: '',
prices: [],
ingredients: [],
});
const emptyPrice = {
volume: '',
price: '',
description: '',
no_auto: false,
round_step: '',
public: true,
};
function addPrice() {
drink.value.prices.unshift({ ...emptyPrice });
}
function save() {
console.log(drink);
drink.value.prices.forEach((price: FG.DrinkPrice) => {
price.no_auto = !price.no_auto;
});
void store.dispatch('drink/createDrink', drink.value);
}
return { drink, addPrice, save };
},
});
</script>
<style scoped></style>

View File

@ -6,7 +6,7 @@
<div class="text-h6">Editere Getränkeart {{ actualDrinkType.name }}</div> <div class="text-h6">Editere Getränkeart {{ actualDrinkType.name }}</div>
</q-card-section> </q-card-section>
<q-card-section> <q-card-section>
<q-input dense label="name" filled v-model="newDrinkTypeName" /> <q-input v-model="newDrinkTypeName" dense label="name" filled />
</q-card-section> </q-card-section>
<q-card-actions> <q-card-actions>
<q-btn flat color="danger" label="Abbrechen" @click="discardChanges()" /> <q-btn flat color="danger" label="Abbrechen" @click="discardChanges()" />
@ -17,11 +17,11 @@
<q-page padding> <q-page padding>
<q-table title="Getränkearten" :data="rows" :row-key="(row) => row.id" :columns="columns"> <q-table title="Getränkearten" :data="rows" :row-key="(row) => row.id" :columns="columns">
<template v-slot:top-right> <template #top-right>
<q-input <q-input
v-model="newDrinkType"
class="q-px-sm" class="q-px-sm"
dense dense
v-model="newDrinkType"
placeholder="Neue Getränkeart" placeholder="Neue Getränkeart"
filled filled
/> />
@ -29,7 +29,7 @@
<div></div> <div></div>
<q-btn color="primary" icon="mdi-plus" label="Hinzufügen" @click="addType" /> <q-btn color="primary" icon="mdi-plus" label="Hinzufügen" @click="addType" />
</template> </template>
<template v-slot:body-cell-actions="props"> <template #body-cell-actions="props">
<q-td :props="props" align="right" :auto-width="true"> <q-td :props="props" align="right" :auto-width="true">
<q-btn <q-btn
round round
@ -46,15 +46,14 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, onBeforeMount, ref } from '@vue/composition-api'; import { computed, defineComponent, onBeforeMount, ref } from 'vue';
import { Store } from 'vuex'; import { useStore } from 'vuex';
import { StateInterface } from 'src/store';
import { DrinkInterface } from 'src/plugins/pricelist/store/drinks'; import { DrinkInterface } from 'src/plugins/pricelist/store/drinks';
export default defineComponent({ export default defineComponent({
name: 'DrinkTypes', name: 'DrinkTypes',
setup(_, { root }) { setup() {
const store = <Store<StateInterface>>root.$store; const store = useStore();
const state = <DrinkInterface>store.state.drink; const state = <DrinkInterface>store.state.drink;
const newDrinkType = ref(''); const newDrinkType = ref('');
const newDrinkTypeName = ref(''); const newDrinkTypeName = ref('');

View File

@ -1,14 +1,14 @@
<template> <template>
<div> <div>
<q-table title="Getränke" :columns="columns" :data="drinks" row-key="name"> <q-table title="Getränke" :columns="columns" :data="drinks" row-key="name">
<template v-slot:body-cell-prices="{ row: { prices } }"> <template #body-cell-prices="{ row: { prices } }">
<q-td> <q-td>
<div v-for="price in prices" :key="price.id" class="row"> <div v-for="price in prices" :key="price.id" class="row">
<div class="col"> <div class="col">
{{ price.volume | setVolume }} {{ setVolume(price.volume) }}
</div> </div>
<div class="col"> <div class="col">
{{ price.price | setCurrency }} {{ setCurrency(price.price) }}
</div> </div>
<div class="col"> <div class="col">
{{ price.description }} {{ price.description }}
@ -20,10 +20,11 @@
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, onBeforeMount, ref } from '@vue/composition-api'; import { defineComponent, onBeforeMount, ref } from 'vue';
import { StateInterface } from 'src/store'; import { StateInterface } from 'src/store';
import { DrinkInterface } from '../store/drinks'; import { DrinkInterface } from '../store/drinks';
import { Store } from 'vuex'; import { useStore } from 'vuex';
export default defineComponent({ export default defineComponent({
name: 'Pricelist', name: 'Pricelist',
filters: { filters: {
@ -37,8 +38,8 @@ export default defineComponent({
return `${price.toFixed(2)}`; return `${price.toFixed(2)}`;
}, },
}, },
setup(_, { root }) { setup() {
const store = <Store<StateInterface>>root.$store; const store = useStore();
const state = <DrinkInterface>store.state.drink; const state = <DrinkInterface>store.state.drink;
const drinks = ref(state.drinks); const drinks = ref(state.drinks);
onBeforeMount(() => { onBeforeMount(() => {

View File

@ -1,44 +0,0 @@
<template>
<div>
<q-page padding v-if="checkMain">
<q-card>
<q-card-section>
<q-list v-for="(mainRoute, index) in mainRoutes" :key="'mainRoute' + index">
<essential-link
v-for="(route, index2) in mainRoute.children"
:key="'route' + index2"
:title="route.title"
:icon="route.icon"
:link="route.name"
:permissions="route.meta.permissions"
/>
</q-list>
</q-card-section>
</q-card>
</q-page>
<router-view />
</div>
</template>
<script lang="ts">
import { computed, defineComponent, onBeforeMount } from '@vue/composition-api';
import EssentialLink from 'src/components/navigation/EssentialLink.vue';
import mainRoutes from '../routes';
import store from '../store/altStore';
export default defineComponent({
// name: 'PageName'
components: { EssentialLink },
setup(_, { root }) {
const checkMain = computed(() => {
return root.$route.matched.length == 2;
});
onBeforeMount(() => {
store.actions.getDrinks();
store.actions.getExtraIngredients();
store.actions.getDrinkTypes();
});
return { checkMain, mainRoutes };
},
});
</script>

View File

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<q-tabs v-model="tab" v-if="$q.screen.gt.sm"> <q-tabs v-if="$q.screen.gt.sm" v-model="tab">
<q-tab <q-tab
v-for="(tabindex, index) in tabs" v-for="(tabindex, index) in tabs"
:key="'tab' + index" :key="'tab' + index"
@ -8,10 +8,10 @@
:label="tabindex.label" :label="tabindex.label"
/> />
</q-tabs> </q-tabs>
<div class="fit row justify-end" v-else> <div v-else class="fit row justify-end">
<q-btn flat round icon="mdi-menu" @click="showDrawer = !showDrawer"></q-btn> <q-btn flat round icon="mdi-menu" @click="showDrawer = !showDrawer"></q-btn>
</div> </div>
<q-drawer side="right" v-model="showDrawer" @click="showDrawer = !showDrawer" behavior="mobile"> <q-drawer v-model="showDrawer" side="right" behavior="mobile" @click="showDrawer = !showDrawer">
<q-list v-model="tab"> <q-list v-model="tab">
<q-item <q-item
v-for="(tabindex, index) in tabs" v-for="(tabindex, index) in tabs"
@ -46,7 +46,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, ref } from '@vue/composition-api'; import { computed, defineComponent, ref } from 'vue';
import { Screen } from 'quasar'; import { Screen } from 'quasar';
import DrinkTypes from 'src/plugins/pricelist/components/DrinkTypes.vue'; import DrinkTypes from 'src/plugins/pricelist/components/DrinkTypes.vue';
import CalculationTable from 'src/plugins/pricelist/components/CalculationTable.vue'; import CalculationTable from 'src/plugins/pricelist/components/CalculationTable.vue';

View File

@ -3,29 +3,35 @@ const mainRoutes: FG_Plugin.PluginRouteConfig[] = [
{ {
title: 'Getränke', title: 'Getränke',
icon: 'mdi-glass-mug-variant', icon: 'mdi-glass-mug-variant',
route: {
path: 'drinks', path: 'drinks',
name: 'drinks', name: 'drinks',
component: () => import('../pages/MainPage.vue'), redirect: { name: 'drinks-pricelist' },
meta: { permissions: ['user'] }, },
permissions: ['user'],
children: [ children: [
{ {
title: 'Preisliste', title: 'Preisliste',
icon: 'mdi-cash-100', icon: 'mdi-cash-100',
shortcut: true,
permissions: ['user'],
route: {
path: 'pricelist', path: 'pricelist',
name: 'drinks-pricelist', name: 'drinks-pricelist',
shortcut: true,
meta: { permissions: ['user'] },
component: () => import('../pages/Pricelist.vue'), component: () => import('../pages/Pricelist.vue'),
}, },
},
{ {
title: 'Einstellungen', title: 'Einstellungen',
icon: 'mdi-coffee-to-go', icon: 'mdi-coffee-to-go',
shortcut: false,
permissions: ['pricelist_settings'],
route: {
path: 'settings', path: 'settings',
name: 'drinks-settings', name: 'drinks-settings',
shortcut: false,
meta: { permissions: ['users_edit_other'] },
component: () => import('../pages/Settings.vue'), component: () => import('../pages/Settings.vue'),
}, },
},
], ],
}, },
]; ];