release v2.0.0 #4

Merged
crimsen merged 481 commits from develop into master 2024-01-18 15:15:08 +00:00
7 changed files with 421 additions and 396 deletions
Showing only changes of commit e2d2a5cf9d - Show all commits

View File

@ -3,7 +3,7 @@
v-model:pagination="pagination" v-model:pagination="pagination"
title="Kalkulationstabelle" title="Kalkulationstabelle"
:columns="columns" :columns="columns"
:data="drinks" :rows="drinks"
:visible-columns="visibleColumn" :visible-columns="visibleColumn"
:dense="$q.screen.lt.md" :dense="$q.screen.lt.md"
row-key="id" row-key="id"
@ -237,8 +237,8 @@
</q-td> </q-td>
<q-td key="cost_price_pro_volume" :props="drinks_props"> <q-td key="cost_price_pro_volume" :props="drinks_props">
{{ {{
drinks_props.row.cost_price_pro_volume.value drinks_props.row.cost_price_pro_volume
? `${drinks_props.row.cost_price_pro_volume.value.toFixed(3)}` ? `${drinks_props.row.cost_price_pro_volume.toFixed(3)}`
: 'o.A.' : 'o.A.'
}} }}
<q-popup-edit <q-popup-edit
@ -252,14 +252,14 @@
volume.ingredients.some((ingredient) => ingredient.drink_ingredient) volume.ingredients.some((ingredient) => ingredient.drink_ingredient)
) )
" "
v-model="drinks_props.row.cost_price_pro_volume.value" v-model="drinks_props.row.cost_price_pro_volume"
buttons buttons
label-cancel="Abbrechen" label-cancel="Abbrechen"
label-set="Speichern" label-set="Speichern"
@save="updateDrink(drinks_props.row)" @save="updateDrink(drinks_props.row)"
> >
<q-input <q-input
v-model.number="drinks_props.row.cost_price_pro_volume.value" v-model.number="drinks_props.row.cost_price_pro_volume"
filled filled
dense dense
autofocus autofocus
@ -273,7 +273,7 @@
<q-td key="volumes" :props="drinks_props"> <q-td key="volumes" :props="drinks_props">
<q-table <q-table
:columns="column_calc" :columns="column_calc"
:data="drinks_props.row.volumes" :rows="drinks_props.row.volumes"
dense dense
:visible-columns="visibleColumn" :visible-columns="visibleColumn"
row-key="id" row-key="id"
@ -292,7 +292,7 @@
<q-tr :props="props"> <q-tr :props="props">
<q-td auto-width> <q-td auto-width>
<q-btn <q-btn
v-if="!drinks_props.row.cost_price_pro_volume.value" v-if="!drinks_props.row.cost_price_pro_volume"
size="sm" size="sm"
color="accent" color="accent"
round round
@ -312,17 +312,17 @@
/> />
</q-td> </q-td>
<q-td key="volume" :props="props"> <q-td key="volume" :props="props">
{{ parseFloat(props.row.volume.value).toFixed(3) }}L {{ parseFloat(props.row.volume).toFixed(3) }}L
<q-popup-edit <q-popup-edit
v-if="drinks_props.row.cost_price_pro_volume" v-if="drinks_props.row.cost_price_pro_volume"
v-model="props.row.volume.value" v-model="props.row.volume"
buttons buttons
label-cancel="Abbrechen" label-cancel="Abbrechen"
label-set="Speichern" label-set="Speichern"
@save="updateVolume(props.row, drinks_props.row)" @save="updateVolume(props.row, drinks_props.row)"
> >
<q-input <q-input
v-model.number="props.row.volume.value" v-model.number="props.row.volume"
dense dense
filled filled
type="number" type="number"
@ -340,14 +340,14 @@
<q-badge color="primary">{{ min_price.percentage }}%</q-badge> <q-badge color="primary">{{ min_price.percentage }}%</q-badge>
</div> </div>
<div class="col" style="text-align: end"> <div class="col" style="text-align: end">
{{ min_price.price.value.toFixed(3) }} {{ min_price.price.toFixed(3) }}
</div> </div>
</div> </div>
</q-td> </q-td>
<q-td key="prices" :props="props"> <q-td key="prices" :props="props">
<price-table <price-table
:columns="column_prices" :columns="column_prices"
:data="props.row.prices" :rows="props.row.prices"
:row="props.row" :row="props.row"
:visible-columns="visibleColumn" :visible-columns="visibleColumn"
/> />
@ -427,172 +427,6 @@
</q-td> </q-td>
</q-tr> </q-tr>
</template> </template>
<template #body-cell-volumes="volumes">
<q-table
:columns="column_calc"
:data="volumes.value"
dense
:visible-columns="visibleColumn"
row-key="id"
flat
>
<template #header="props">
<q-tr :props="props">
<q-th auto-width />
<q-th v-for="col in props.cols" :key="col.name" :props="props">
{{ col.label }}
</q-th>
</q-tr>
</template>
<template #body="props">
<q-tr :props="props">
<q-td auto-width>
<q-btn
v-if="volumes.row.cost_price_pro_volume == null"
size="sm"
color="accent"
round
dense
:icon="props.expand ? 'mdi-chevron-up' : 'mdi-chevron-down'"
@click="
props.expand = !props.expand;
console.log(volumes);
"
/>
<q-btn
v-if="props.row.ingredients.length === 0 && props.row.prices.length === 0"
size="xs"
color="negative"
round
dense
icon="mdi-delete"
class="q-mx-sm"
@click="deleteVolume(props.row, volumes.row)"
/>
</q-td>
<q-td key="volume" :props="props">
{{ parseFloat(props.row.volume.value).toFixed(3) }}L
<!--{{ props.row.volume }}-->
<q-popup-edit
v-if="volumes.row.cost_price_pro_volume"
v-model="props.row.volume.value"
buttons
label-cancel="Abbrechen"
label-set="Speichern"
@save="updateVolume(props.row, volumes.row)"
>
<q-input
v-model.number="props.row.volume.value"
dense
filled
type="number"
suffix="L"
/>
</q-popup-edit>
</q-td>
<q-td key="min_prices" :props="props">
<div
v-for="(min_price, index) in props.row.min_prices"
:key="`min_prices` + index"
class="row justify-between"
>
<div class="col">
<q-badge color="primary">{{ min_price.percentage }}%</q-badge>
</div>
<div class="col" style="text-align: end">
<!--{{ parseFloat(min_price.price).toFixed(3) }}-->
{{ min_price.price.value.toFixed(3) }}
</div>
</div>
</q-td>
<q-td key="prices" :props="props">
<price-table
:columns="column_prices"
:data="props.row.prices"
:row="props.row"
:visible-columns="visibleColumn"
/>
</q-td>
</q-tr>
<q-tr v-show="props.expand" :props="props">
<q-td colspan="100%">
<ingredients :ingredients="props.row.ingredients" :volume="props.row" />
</q-td>
</q-tr>
</template>
<template #bottom>
<div class="full-width row justify-end">
<q-btn
v-if="volumes.row.cost_price_pro_volume"
color="positive"
icon-right="add"
label="Abgabe hinzufügen"
size="xs"
>
<q-menu anchor="center middle" self="center middle">
<div class="row justify-around q-pa-sm">
<q-input
v-model.number="newVolume.volume"
filled
dense
label="Liter"
type="number"
min="0"
step="0.01"
suffix="L"
/>
</div>
<div class="row justify-between q-pa-sm">
<q-btn v-close-popup label="Abbrechen" @click="cancelAddVolume" />
<q-btn
v-close-popup
label="Speichern"
color="primary"
@click="addVolume(volumes.row)"
/>
</div>
</q-menu>
</q-btn>
</div>
</template>
<template #no-data>
<div class="full-width row justify-end">
<q-btn
v-if="volumes.row.cost_price_pro_volume"
color="positive"
icon-right="add"
label="Abgabe hinzufügen"
size="xs"
>
<q-menu anchor="center middle" self="center middle">
<div class="row justify-around q-pa-sm">
<q-input
v-model.number="newVolume.volume"
filled
dense
label="Liter"
type="number"
min="0"
step="0.01"
suffix="L"
/>
</div>
<div class="row justify-between q-pa-sm">
<q-btn v-close-popup label="Abbrechen" @click="cancelAddVolume" />
<q-btn
v-close-popup
label="Speichern"
color="primary"
@click="addVolume(volumes.row)"
/>
</div>
</q-menu>
</q-btn>
</div>
</template>
</q-table>
</template>
</q-table> </q-table>
</template> </template>
@ -600,8 +434,9 @@
import { defineComponent, onBeforeMount, ref, ComputedRef, computed } from 'vue'; import { defineComponent, onBeforeMount, ref, ComputedRef, computed } from 'vue';
import PriceTable from 'src/plugins/pricelist/components/CalculationTable/PriceTable.vue'; import PriceTable from 'src/plugins/pricelist/components/CalculationTable/PriceTable.vue';
import Ingredients from 'src/plugins/pricelist/components/CalculationTable/Ingredients.vue'; import Ingredients from 'src/plugins/pricelist/components/CalculationTable/Ingredients.vue';
import { StateInterface, useMainStore } from 'src/store'; import { useMainStore } from 'src/store';
import { Store } from 'vuex'; import { Drink, DrinkPriceVolume, usePricelistStore } from 'src/plugins/pricelist/store';
function sort(a: string | number, b: string | number) { function sort(a: string | number, b: string | number) {
if (a > b) return 1; if (a > b) return 1;
@ -613,9 +448,10 @@ export default defineComponent({
components: { PriceTable, Ingredients }, components: { PriceTable, Ingredients },
setup() { setup() {
const mainStore = useMainStore(); const mainStore = useMainStore();
const store = usePricelistStore();
onBeforeMount(() => { onBeforeMount(() => {
//store.actions.getPriceCalcColumn(user); store.getPriceCalcColumn(user);
}); });
const user = mainStore.currentUser.userid; const user = mainStore.currentUser.userid;
@ -684,7 +520,6 @@ export default defineComponent({
name: 'volume', name: 'volume',
label: 'Abgabe in l', label: 'Abgabe in l',
field: 'volume', field: 'volume',
format: (val: number) => `${val} L`,
}, },
{ {
name: 'min_prices', name: 'min_prices',
@ -715,11 +550,10 @@ export default defineComponent({
field: 'public', field: 'public',
}, },
]; ];
/*
const visibleColumn = computed({ const visibleColumn = computed({
get: () => store.state.pricecalc_columns, get: () => store.pricecalc_columns,
set: (val) => { set: (val) => {
store.actions.updatePriceCalcColumn(user, val); store.updatePriceCalcColumn(user, val);
}, },
}); });
const emptyVolume: DrinkPriceVolume = { const emptyVolume: DrinkPriceVolume = {
@ -736,7 +570,7 @@ export default defineComponent({
}; };
const newVolume = ref<DrinkPriceVolume>(emptyVolume); const newVolume = ref<DrinkPriceVolume>(emptyVolume);
function addVolume(drink: Drink) { function addVolume(drink: Drink) {
store.actions.setVolume(>newVolume.value, drink); store.setVolume(<DrinkPriceVolume>newVolume.value, drink);
cancelAddVolume(); cancelAddVolume();
} }
function cancelAddVolume() { function cancelAddVolume() {
@ -748,14 +582,15 @@ export default defineComponent({
} }
function updateVolume(volume: DrinkPriceVolume, drink: Drink) { function updateVolume(volume: DrinkPriceVolume, drink: Drink) {
console.log(volume); console.log(volume);
store.actions.updateVolume(volume, drink); store.updateVolume(volume, drink);
} }
function deleteVolume(volume: FG.DrinkPriceVolume, drink: FG.Drink) { function deleteVolume(volume: FG.DrinkPriceVolume, drink: FG.Drink) {
store.actions.deleteVolume(volume, drink); store.deleteVolume(volume, drink);
} }
// eslint-disable-next-line vue/return-in-computed-property
const pagination = computed(() => { const pagination = computed(() => {
rowsPerPage: store.state.drinks.length; rowsPerPage: store.drinks.length;
}); });
const emptyDrink: FG.Drink = { const emptyDrink: FG.Drink = {
@ -785,6 +620,7 @@ export default defineComponent({
((newDrink.value.cost_price_package_netto || 0) / ((newDrink.value.cost_price_package_netto || 0) /
((newDrink.value.volume || 0) * (newDrink.value.package_size || 0))) * ((newDrink.value.volume || 0) * (newDrink.value.package_size || 0))) *
1.19; 1.19;
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
newDrink.value.cost_price_pro_volume = Math.round(retVal * 1000) / 1000; newDrink.value.cost_price_pro_volume = Math.round(retVal * 1000) / 1000;
} }
return newDrink.value.cost_price_pro_volume; return newDrink.value.cost_price_pro_volume;
@ -794,24 +630,24 @@ export default defineComponent({
}, },
}); });
const drinkTypes = computed(() => store.state.drinkTypes); const drinkTypes = computed(() => store.drinkTypes);
function addDrink() { function addDrink() {
store.actions.setDrink(newDrink.value); store.setDrink(newDrink.value);
cancelAddDrink(); cancelAddDrink();
} }
function cancelAddDrink() { function cancelAddDrink() {
setTimeout(() => (newDrink.value = emptyDrink), 200); setTimeout(() => (newDrink.value = emptyDrink), 200);
} }
function updateDrink(drink: Drink) { function updateDrink(drink: Drink) {
store.actions.updateDrink(drink); store.updateDrink(drink);
} }
function deleteDrink(drink: Drink) { function deleteDrink(drink: Drink) {
store.actions.deleteDrink(drink); store.deleteDrink(drink);
} }
return { return {
drinks: computed(() => store.state.drinks), drinks: computed(() => store.drinks),
pagination, pagination,
columns, columns,
column_calc, column_calc,
@ -831,7 +667,7 @@ export default defineComponent({
updateDrink, updateDrink,
deleteDrink, deleteDrink,
console, console,
};*/ };
}, },
}); });
</script> </script>

View File

@ -146,7 +146,7 @@
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, PropType, ref } from 'vue'; import { computed, defineComponent, PropType, ref } from 'vue';
import { usePricelistStore } from '../../store'; import { DrinkPriceVolume, usePricelistStore } from '../../store';
export default defineComponent({ export default defineComponent({
name: 'Ingredients', name: 'Ingredients',
@ -162,7 +162,7 @@ export default defineComponent({
}, },
setup() { setup() {
const store = usePricelistStore(); const store = usePricelistStore();
/*
const emptyIngredient: FG.Ingredient = { const emptyIngredient: FG.Ingredient = {
id: -1, id: -1,
drink_ingredient: undefined, drink_ingredient: undefined,
@ -172,7 +172,7 @@ export default defineComponent({
const newIngredientVolume = ref<number>(0); const newIngredientVolume = ref<number>(0);
function addIngredient(volume: DrinkPriceVolume) { function addIngredient(volume: DrinkPriceVolume) {
if ((<FG.Drink>newIngredient.value)?.volume && newIngredient.value) { if ((<FG.Drink>newIngredient.value)?.volume && newIngredient.value) {
store.actions.setIngredient( store.setIngredient(
{ {
id: -1, id: -1,
drink_ingredient: { drink_ingredient: {
@ -185,11 +185,11 @@ export default defineComponent({
volume volume
); );
} else if (newIngredient.value) { } else if (newIngredient.value) {
store.actions.setIngredient( store.setIngredient(
{ {
id: -1, id: -1,
drink_ingredient: undefined, drink_ingredient: undefined,
extra_ingredient: >newIngredient.value, extra_ingredient: <FG.ExtraIngredient>newIngredient.value,
}, },
volume volume
); );
@ -202,10 +202,10 @@ export default defineComponent({
}, 200); }, 200);
} }
function updateIngredient(ingredient: FG.Ingredient, volume: DrinkPriceVolume) { function updateIngredient(ingredient: FG.Ingredient, volume: DrinkPriceVolume) {
store.actions.updateIngredient(ingredient, volume); store.updateIngredient(ingredient, volume);
} }
function deleteIngredient(ingredient: FG.Ingredient, volume: DrinkPriceVolume) { function deleteIngredient(ingredient: FG.Ingredient, volume: DrinkPriceVolume) {
store.actions.deleteIngredient(ingredient, volume); store.deleteIngredient(ingredient, volume);
} }
const drinks = computed(() => const drinks = computed(() =>
store.drinks.filter((drink) => { store.drinks.filter((drink) => {
@ -228,7 +228,7 @@ export default defineComponent({
updateIngredient, updateIngredient,
deleteIngredient, deleteIngredient,
get_drink_ingredient_name, get_drink_ingredient_name,
};*/ };
}, },
}); });
</script> </script>

View File

@ -1,160 +0,0 @@
<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 { usePricelistStore } from '../store';
export default defineComponent({
name: 'Drink',
setup() {
const store = usePricelistStore();
const drink = ref<FG.Drink>({
id: -1,
name: '',
volume: -1,
tags: [],
volumes: [],
});
const emptyPrice = {
volume: '',
price: '',
description: '',
no_auto: false,
round_step: '',
public: true,
};
function addPrice() {
//drink.value.prices.unshift({ ...emptyPrice });
}
async function save() {
console.log(drink);
/*drink.value.prices.forEach((price: FG.DrinkPrice) => {
price.no_auto = !price.no_auto;
});*/
await store.createDrink(drink.value);
}
return { drink, addPrice, save };
},
});
</script>
<style scoped></style>

View File

@ -26,7 +26,7 @@
</q-dialog> </q-dialog>
<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" :rows="rows" :row-key="(row) => row.id" :columns="columns">
<template #top-right> <template #top-right>
<q-input <q-input
v-model="newExtraIngredient.name" v-model="newExtraIngredient.name"
@ -62,23 +62,24 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, ref } from 'vue'; import {computed, ComputedRef, defineComponent, ref} from 'vue';
import {usePricelistStore} from 'src/plugins/pricelist/store';
export default defineComponent({ export default defineComponent({
name: 'DrinkTypes', name: 'DrinkTypes',
setup() { setup() {
/* const store = usePricelistStore()
const emptyExtraIngredient: FG.ExtraIngredient = { const emptyExtraIngredient: FG.ExtraIngredient = {
name: '', name: '',
price: 0, price: 0,
id: -1, id: -1,
}; };
const newExtraIngredient = ref(emptyExtraIngredient); const newExtraIngredient = ref<FG.ExtraIngredient>(emptyExtraIngredient);
const newDrinkTypeName = ref(''); const newDrinkTypeName = ref<string>('');
const edittype = ref(false); const edittype = ref(false);
const actualExtraIngredient = ref(emptyExtraIngredient); const actualExtraIngredient = ref(emptyExtraIngredient);
const rows = computed(() => store.state.extraIngredients); const rows = computed(() => store.extraIngredients);
const columns = [ const columns = [
{ {
name: 'name', name: 'name',
@ -102,8 +103,8 @@ export default defineComponent({
}, },
]; ];
function addExtraIngredient() { async function addExtraIngredient() {
store.actions.setExtraIngredient(newExtraIngredient.value); await store.setExtraIngredient((<ComputedRef>newExtraIngredient).value);
newExtraIngredient.value = emptyExtraIngredient; newExtraIngredient.value = emptyExtraIngredient;
} }
@ -112,8 +113,8 @@ export default defineComponent({
actualExtraIngredient.value = extraIngredient; actualExtraIngredient.value = extraIngredient;
} }
function saveChanges() { async function saveChanges() {
store.actions.updateExtraIngredient(actualExtraIngredient.value); await store.updateExtraIngredient(actualExtraIngredient.value);
setTimeout(() => discardChanges(), 200); setTimeout(() => discardChanges(), 200);
} }
@ -124,7 +125,7 @@ export default defineComponent({
} }
function deleteType(extraIngredient: FG.ExtraIngredient) { function deleteType(extraIngredient: FG.ExtraIngredient) {
store.actions.deleteExtraIngredient(extraIngredient); void store.deleteExtraIngredient(extraIngredient);
} }
return { return {
@ -138,7 +139,7 @@ export default defineComponent({
actualExtraIngredient, actualExtraIngredient,
discardChanges, discardChanges,
saveChanges, saveChanges,
};*/ };
}, },
}); });
</script> </script>

View File

@ -32,13 +32,13 @@
class="q-ma-none q-pa-none fit row justify-center content-start items-start" class="q-ma-none q-pa-none fit row justify-center content-start items-start"
> >
<q-tab-panel name="pricelist"> <q-tab-panel name="pricelist">
<CalculationTable /> <calculation-table />
</q-tab-panel> </q-tab-panel>
<q-tab-panel name="extra_ingredients"> <q-tab-panel name="extra_ingredients">
<extra-ingredients /> <extra-ingredients />
</q-tab-panel> </q-tab-panel>
<q-tab-panel name="drink_types"> <q-tab-panel name="drink_types">
<DrinkTypes /> <drink-types />
</q-tab-panel> </q-tab-panel>
</q-tab-panels> </q-tab-panels>
</q-page> </q-page>
@ -46,19 +46,29 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, ref } from 'vue'; import { computed, defineComponent, onBeforeMount, 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';
import ExtraIngredients from 'src/plugins/pricelist/components/ExtraIngredients.vue'; import ExtraIngredients from 'src/plugins/pricelist/components/ExtraIngredients.vue';
import { usePricelistStore } from 'src/plugins/pricelist/store';
export default defineComponent({ export default defineComponent({
name: 'Settings', name: 'Settings',
components: { DrinkTypes, ExtraIngredients, CalculationTable }, //components: { DrinkTypes, ExtraIngredients, CalculationTable },
components: { ExtraIngredients, DrinkTypes, CalculationTable },
setup(_) { setup(_) {
interface Tab { interface Tab {
name: string; name: string;
label: string; label: string;
} }
const store = usePricelistStore()
onBeforeMount(() => {
store.getExtraIngredients().then(() => {
console.log(store.extraIngredients)
}).catch(err => console.log(err))
void store.getDrinkTypes()
store.getDrinks()
})
const drawer = ref<boolean>(false); const drawer = ref<boolean>(false);
@ -68,19 +78,19 @@ export default defineComponent({
}, },
set: (val: boolean) => { set: (val: boolean) => {
drawer.value = val; drawer.value = val;
}, }
}); });
const tabs: Tab[] = [ const tabs: Tab[] = [
{ name: 'pricelist', label: 'Getränke' }, { name: 'pricelist', label: 'Getränke' },
{ name: 'extra_ingredients', label: 'Zutaten' }, { name: 'extra_ingredients', label: 'Zutaten' },
{ name: 'drink_types', label: 'Getränketypen' }, { name: 'drink_types', label: 'Getränketypen' }
]; ];
const tab = ref<string>('pricelist'); const tab = ref<string>('pricelist');
return { tabs, tab, showDrawer }; return { tabs, tab, showDrawer };
}, }
}); });
</script> </script>

View File

@ -6,7 +6,7 @@ const mainRoutes: FG_Plugin.PluginRouteConfig[] = [
route: { route: {
path: 'drinks', path: 'drinks',
name: 'drinks', name: 'drinks',
redirect: { name: 'drinks-pricelist' }, redirect: { name: 'drinks-pricelist' }
}, },
permissions: ['user'], permissions: ['user'],
children: [ children: [
@ -18,22 +18,22 @@ const mainRoutes: FG_Plugin.PluginRouteConfig[] = [
route: { route: {
path: 'pricelist', path: 'pricelist',
name: 'drinks-pricelist', name: 'drinks-pricelist',
component: () => import('../pages/PricelistP.vue'), component: () => import('../pages/PricelistP.vue')
}, }
}, },
{ {
title: 'Einstellungen', title: 'Einstellungen',
icon: 'mdi-coffee-to-go', icon: 'mdi-coffee-to-go',
shortcut: false, shortcut: false,
permissions: ['pricelist_settings'], permissions: ['user'],
route: { route: {
path: 'settings', path: 'settings',
name: 'drinks-settings', name: 'drinks-settings',
component: () => import('../pages/Settings.vue'), component: () => import('../pages/Settings.vue')
}, }
}, }
], ]
}, }
]; ];
export default mainRoutes; export default mainRoutes;

View File

@ -1,12 +1,154 @@
import { api } from 'src/boot/axios'; import { api } from 'src/boot/axios';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { AxiosResponse } from 'axios';
import { computed, WritableComputedRef } from 'vue';
interface MinPrice extends Omit<FG.MinPrices, 'price'> {
price: WritableComputedRef<number> | null;
}
interface DrinkPriceVolume extends Omit<Omit<FG.DrinkPriceVolume, 'volume'>, 'min_prices'> {
_volume: number;
volume: WritableComputedRef<number> | null;
min_prices: MinPrice[];
}
interface Drink extends Omit<Omit<FG.Drink, 'cost_price_pro_volume'>, 'volumes'> {
volumes: DrinkPriceVolume[];
cost_price_pro_volume: WritableComputedRef<number | undefined>;
_cost_price_pro_volume?: number;
}
class DrinkPriceVolume {
constructor({ id, volume, min_prices, prices, ingredients }: FG.DrinkPriceVolume, drink: Drink) {
this.id = id;
this._volume = volume;
this.prices = prices;
this.ingredients = ingredients;
this.min_prices = [
{
percentage: 100,
price: create_min_prices(drink, this, 100),
},
{
percentage: 250,
price: create_min_prices(drink, this, 250),
},
{
percentage: 300,
price: create_min_prices(drink, this, 300),
},
];
this.volume = computed<number>({
get: () => {
if (this.ingredients.some((ingredient) => !!ingredient.drink_ingredient)) {
let retVal = 0;
this.ingredients.forEach((ingredient) => {
if (ingredient.drink_ingredient?.volume) {
retVal += ingredient.drink_ingredient.volume;
}
});
this._volume = retVal;
return retVal;
} else {
return this._volume;
}
},
set: (val) => (this._volume = val),
});
}
}
class Drink {
constructor({
id,
article_id,
package_size,
name,
volume,
cost_price_pro_volume,
cost_price_package_netto,
tags,
type,
volumes,
}: FG.Drink) {
this.id = id;
this.article_id = article_id;
this.package_size = package_size;
this.name = name;
this.volume = volume;
this.cost_price_package_netto = cost_price_package_netto;
this._cost_price_pro_volume = cost_price_pro_volume;
this.cost_price_pro_volume = computed({
get: () => {
if (!!this.volume && !!this.package_size && !!this.cost_price_package_netto) {
const retVal =
((this.cost_price_package_netto || 0) /
((this.volume || 0) * (this.package_size || 0))) *
1.19;
this._cost_price_pro_volume = Math.round(retVal * 1000) / 1000;
}
return this._cost_price_pro_volume;
},
set: (val) => (this._cost_price_pro_volume = val),
});
this.tags = tags;
this.type = type;
this.volumes = [];
//volumes.forEach(volume => {
// this.volumes.push(new DrinkPriceVolume(volume, this));
//});
}
}
function create_min_prices(drink: Drink, volume: DrinkPriceVolume, percentage: number) {
if (drink.cost_price_pro_volume?.value) {
if (volume.ingredients.every((ingredient) => !!ingredient.drink_ingredient)) {
return computed<number>(() => {
let retVal = (drink.cost_price_pro_volume?.value || 0) * (volume.volume?.value || 0);
volume.ingredients.forEach((ingredient) => {
if (ingredient.extra_ingredient) {
retVal += ingredient.extra_ingredient.price;
}
});
retVal = (retVal * percentage) / 100;
return retVal;
});
} else {
return computed<number>(
() =>
((drink.cost_price_pro_volume?.value || 0) * (volume.volume?.value || 0) * percentage) /
100
);
}
} else {
return computed<number>(() => {
let retVal = 0;
volume.ingredients.forEach((ingredient) => {
if (ingredient.drink_ingredient) {
const _drink = usePricelistStore().drinks.find(
(a) => a.id === ingredient.drink_ingredient?.drink_ingredient_id
);
retVal +=
ingredient.drink_ingredient.volume * (_drink?.cost_price_pro_volume?.value || 0);
}
if (ingredient.extra_ingredient) {
retVal += ingredient.extra_ingredient.price;
}
});
console.log(volume);
return (retVal * percentage) / 100;
});
}
}
export const usePricelistStore = defineStore({ export const usePricelistStore = defineStore({
id: 'pricelist', id: 'pricelist',
state: () => ({ state: () => ({
drinkTypes: [] as Array<FG.DrinkType>, drinkTypes: [] as Array<FG.DrinkType>,
drinks: [] as Array<FG.Drink>, drinks: [] as Array<Drink>,
extraIngredients: [] as Array<FG.ExtraIngredient>,
pricecalc_columns: [] as Array<string>,
}), }),
actions: { actions: {
@ -31,16 +173,212 @@ export const usePricelistStore = defineStore({
const itm = this.drinkTypes.filter((val) => val.id == drinkType.id); const itm = this.drinkTypes.filter((val) => val.id == drinkType.id);
if (itm.length > 0) itm[0].name = drinkType.name; if (itm.length > 0) itm[0].name = drinkType.name;
}, },
async getDrinks(force = false) { async getExtraIngredients() {
const { data } = await api.get<Array<FG.ExtraIngredient>>(
'pricelist/ingredients/extraIngredients'
);
this.extraIngredients = data;
},
async setExtraIngredient(ingredient: FG.ExtraIngredient) {
const { data } = await api.post<FG.ExtraIngredient>(
'pricelist/ingredients/extraIngredients',
ingredient
);
this.extraIngredients.push(data);
},
async updateExtraIngredient(ingredient: FG.ExtraIngredient) {
const { data } = await api.put<FG.ExtraIngredient>(
`pricelist/ingredients/extraIngredients/${ingredient.id}`,
ingredient
);
const index = this.extraIngredients.findIndex((a) => a.id === ingredient.id);
if (index > -1) {
this.extraIngredients[index] = data;
} else {
this.extraIngredients.push(data);
}
},
async deleteExtraIngredient(ingredient: FG.ExtraIngredient) {
await api.delete(`pricelist/ingredients/extraIngredients/${ingredient.id}`);
const index = this.extraIngredients.findIndex((a) => a.id === ingredient.id);
if (index > -1) {
this.extraIngredients.splice(index, 1);
}
},
/*async getDrinks(force = false) {
if (force || this.drinks.length == 0) { if (force || this.drinks.length == 0) {
const { data } = await api.get<Array<FG.Drink>>('/pricelist/drinks'); const { data } = await api.get<Array<FG.Drink>>('/pricelist/drinks');
this.drinks = data; this.drinks = data;
} }
return this.drinks; return this.drinks;
},*/
getDrinks() {
api
.get('pricelist/drinks')
.then((response: AxiosResponse<FG.Drink[]>) => {
this.drinks = [];
response.data.forEach((drink) => {
this.drinks.push(new Drink(drink));
});
this.drinks.forEach((drink) => {
const _drink = response.data.find((a) => a.id === drink.id);
_drink?.volumes.forEach((volume) => {
drink.volumes.push(new DrinkPriceVolume(volume, drink));
});
});
console.log(this.drinks);
})
.catch((err) => console.warn(err));
}, },
async createDrink(drink: FG.Drink) { setPrice(price: FG.DrinkPrice, volume: DrinkPriceVolume) {
await api.post('/pricelist/drinks', drink); api
this.drinks.push(drink); .post(`pricelist/volumes/${volume.id}/prices`, price)
.then((response: AxiosResponse<FG.DrinkPrice>) => {
volume.prices.push(response.data);
this.sortPrices(volume);
})
.catch((err) => console.warn(err));
},
sortPrices(volume: DrinkPriceVolume) {
volume.prices.sort((a, b) => {
if (a.price > b.price) return 1;
if (b.price > a.price) return -1;
return 0;
});
},
deletePrice(price: FG.DrinkPrice, volume: FG.DrinkPriceVolume) {
api
.delete(`pricelist/prices/${price.id}`)
.then(() => {
const index = volume.prices.findIndex((a) => a.id == price.id);
if (index > -1) {
volume.prices.splice(index, 1);
}
})
.catch((err) => console.warn(err));
},
updatePrice(price: FG.DrinkPrice, volume: DrinkPriceVolume) {
api
.put(`pricelist/prices/${price.id}`, price)
.then((response: AxiosResponse<FG.DrinkPrice>) => {
const index = volume.prices.findIndex((a) => a.id === price.id);
if (index > -1) {
this.sortPrices(volume);
}
})
.catch((err) => console.log(err));
},
setVolume(volume: DrinkPriceVolume, drink: Drink) {
console.log(volume);
api
.post(`pricelist/drinks/${drink.id}/volumes`, {
...volume,
volume: volume.volume,
})
.then((response: AxiosResponse<FG.DrinkPriceVolume>) => {
drink.volumes.push(new DrinkPriceVolume(response.data, drink));
})
.catch((err) => console.warn(err));
},
updateVolume(volume: DrinkPriceVolume, drink: Drink) {
api
.put(`pricelist/volumes/${volume.id}`, {
...volume,
volume: volume.volume?.value,
})
.catch((err) => console.warn(err));
},
deleteVolume(volume: FG.DrinkPriceVolume, drink: FG.Drink) {
api
.delete(`pricelist/volumes/${volume.id}`)
.then(() => {
const index = drink.volumes.findIndex((a) => a.id === volume.id);
if (index > -1) {
drink.volumes.splice(index, 1);
}
})
.catch((err) => console.warn(err));
},
setIngredient(ingredient: FG.Ingredient, volume: DrinkPriceVolume) {
api
.post(`pricelist/volumes/${volume.id}/ingredients`, ingredient)
.then((response: AxiosResponse<FG.Ingredient>) => {
volume.ingredients.push(response.data);
})
.catch((err) => console.warn(err));
},
updateIngredient(ingredient: FG.Ingredient, volume: DrinkPriceVolume) {
api
.put(`pricelist/ingredients/${ingredient.id}`, ingredient)
.then((response: AxiosResponse<FG.Ingredient>) => {
//const index = volume.ingredients.findIndex(a => a.id === response.data.id);
//if (index > -1) {
// volume.ingredients[index] = response.data;
//}
})
.catch((err) => console.warn(err));
},
deleteIngredient(ingredient: FG.Ingredient, volume: DrinkPriceVolume) {
api
.delete(`pricelist/ingredients/${ingredient.id}`)
.then(() => {
const index = volume.ingredients.findIndex((a) => a.id === ingredient.id);
if (index > -1) {
volume.ingredients.splice(index, 1);
}
})
.catch((err) => console.warn(err));
},
setDrink(drink: FG.Drink) {
api
.post('pricelist/drinks', drink)
.then((response: AxiosResponse<FG.Drink>) => {
this.drinks.push(new Drink(response.data));
const drink = this.drinks.find((a) => a.id === response.data.id);
response.data.volumes.forEach((volume) => {
drink?.volumes.push(new DrinkPriceVolume(volume, drink));
});
})
.catch((err) => console.warn(err));
},
updateDrink(drink: Drink) {
api
.put(`pricelist/drinks/${drink.id}`, {
...drink,
cost_price_pro_volume: drink.cost_price_pro_volume?.value,
})
.catch((err) => console.warn(err));
},
deleteDrink(drink: Drink) {
api
.delete(`pricelist/drinks/${drink.id}`)
.then(() => {
const index = this.drinks.findIndex((a) => a.id === drink.id);
if (index > -1) {
this.drinks.splice(index, 1);
}
})
.catch((err) => console.warn(err));
},
getPriceCalcColumn(userid: string) {
api
.get(`pricelist/users/${userid}/pricecalc_columns`)
.then(({ data }: AxiosResponse<Array<string>>) => {
if (data.length > 0) {
this.pricecalc_columns = data;
}
})
.catch((err) => console.log(err));
},
updatePriceCalcColumn(userid: string, data: Array<string>) {
api
.put(`pricelist/users/${userid}/pricecalc_columns`, data)
.then(() => {
this.pricecalc_columns = data;
})
.catch((err) => console.log(err));
}, },
}, },
}); });
export { create_min_prices, DrinkPriceVolume, MinPrice, Drink };