[pricelist] add, modify, delete ingredients of volume
This commit is contained in:
parent
7e01ffc507
commit
c272c9e4a5
|
@ -49,9 +49,7 @@ declare namespace FG {
|
||||||
interface DrinkIngredient {
|
interface DrinkIngredient {
|
||||||
id: number;
|
id: number;
|
||||||
volume: number;
|
volume: number;
|
||||||
drink_ingredient_id: number;
|
|
||||||
drink_ingredient?: Drink;
|
drink_ingredient?: Drink;
|
||||||
price: number;
|
|
||||||
}
|
}
|
||||||
interface DrinkPrice {
|
interface DrinkPrice {
|
||||||
id: number;
|
id: number;
|
||||||
|
@ -70,7 +68,7 @@ declare namespace FG {
|
||||||
//computed_volume: ComputedRef<number>;
|
//computed_volume: ComputedRef<number>;
|
||||||
min_prices: DrinkMinPrice[];
|
min_prices: DrinkMinPrice[];
|
||||||
prices: Array<DrinkPrice>;
|
prices: Array<DrinkPrice>;
|
||||||
ingredients: Array<DrinkIngredient & ExtraIngredient>;
|
ingredients: Array<Ingredient>;
|
||||||
}
|
}
|
||||||
interface DrinkType {
|
interface DrinkType {
|
||||||
id: number;
|
id: number;
|
||||||
|
@ -83,7 +81,6 @@ declare namespace FG {
|
||||||
}
|
}
|
||||||
interface Ingredient {
|
interface Ingredient {
|
||||||
id: number;
|
id: number;
|
||||||
volume_id: number;
|
|
||||||
drink_ingredient: DrinkIngredient | null;
|
drink_ingredient: DrinkIngredient | null;
|
||||||
extra_ingredient: ExtraIngredient | null;
|
extra_ingredient: ExtraIngredient | null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,48 +112,7 @@
|
||||||
</q-tr>
|
</q-tr>
|
||||||
<q-tr v-show="props.expand" :props="props">
|
<q-tr v-show="props.expand" :props="props">
|
||||||
<q-td colspan="100%">
|
<q-td colspan="100%">
|
||||||
<div
|
<ingredients :ingredients="props.row.ingredients" :volume="props.row" />
|
||||||
v-for="ingredient in props.row.ingredients"
|
|
||||||
:key="`volume:${props.row.id},ingredient:${ingredient.id}`"
|
|
||||||
class="full-width row justify-evenly q-py-xs"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="full-width row justify-evenly q-py-xs"
|
|
||||||
v-if="ingredient.drink_ingredient"
|
|
||||||
>
|
|
||||||
<q-select
|
|
||||||
class="col q-px-sm"
|
|
||||||
label="Getränk"
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
:options="drinks"
|
|
||||||
option-label="name"
|
|
||||||
v-model="ingredient.drink_ingredient"
|
|
||||||
/>
|
|
||||||
<q-input
|
|
||||||
class="col q-px-sm"
|
|
||||||
label="Volume in L"
|
|
||||||
type="number"
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
v-model.number="ingredient.volume"
|
|
||||||
step="0.01"
|
|
||||||
min="0"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="ingredient.name">
|
|
||||||
{{ ingredient.name }} {{ ingredient.price }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="full-width row justify-end q-py-xs">
|
|
||||||
<q-btn
|
|
||||||
size="sm"
|
|
||||||
icon-right="add"
|
|
||||||
color="positive"
|
|
||||||
label="Zutat hinzufügen"
|
|
||||||
@click="addIngredient(props.row.ingredients, props.row.id)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</q-td>
|
</q-td>
|
||||||
</q-tr>
|
</q-tr>
|
||||||
</template>
|
</template>
|
||||||
|
@ -168,7 +127,13 @@
|
||||||
>
|
>
|
||||||
<q-menu anchor="center middle" self="center middle">
|
<q-menu anchor="center middle" self="center middle">
|
||||||
<div class="row justify-around q-pa-sm">
|
<div class="row justify-around q-pa-sm">
|
||||||
<q-input dense label="Liter" type="number" v-model.number="newVolume.volume" />
|
<q-input
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
label="Liter"
|
||||||
|
type="number"
|
||||||
|
v-model.number="newVolume.volume"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-between q-pa-sm">
|
<div class="row justify-between q-pa-sm">
|
||||||
<q-btn label="Abbrechen" @click="cancelAddVolume" v-close-popup />
|
<q-btn label="Abbrechen" @click="cancelAddVolume" v-close-popup />
|
||||||
|
@ -200,12 +165,14 @@ import {
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
import store, { create_volume, DrinkPriceVolume, Drink } from '../store/altStore';
|
import store, { create_volume, DrinkPriceVolume, Drink } from '../store/altStore';
|
||||||
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';
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'CalculationTable',
|
name: 'CalculationTable',
|
||||||
components: { PriceTable },
|
components: { PriceTable, Ingredients },
|
||||||
setup(_, { root }) {
|
setup(_, { root }) {
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
store.actions.getDrinks();
|
store.actions.getDrinks();
|
||||||
|
store.actions.getExtraIngredients();
|
||||||
});
|
});
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
|
@ -334,7 +301,7 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
function addIngredient(ingredients: FG.Ingredient[]) {
|
function addIngredient(ingredients: FG.Ingredient[]) {
|
||||||
ingredients.push({ id: -1, volume_id: 0, drink_ingredient: null, extra_ingredient: null });
|
ingredients.push({ id: -1, drink_ingredient: null, extra_ingredient: null });
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -0,0 +1,219 @@
|
||||||
|
<template>
|
||||||
|
<div class="full-width">
|
||||||
|
<div
|
||||||
|
v-for="ingredient in ingredients"
|
||||||
|
:key="`volume:${volume.id},ingredient:${ingredient.id}`"
|
||||||
|
class="full-width row justify-evenly q-py-xs"
|
||||||
|
>
|
||||||
|
<div class="full-width row justify-evenly">
|
||||||
|
<div class="col" v-if="ingredient.drink_ingredient">
|
||||||
|
<div class="full-width row justify-evenly q-py-xs">
|
||||||
|
<div class="col">
|
||||||
|
{{ ingredient.drink_ingredient.drink_ingredient.name }}
|
||||||
|
<q-popup-edit
|
||||||
|
buttons
|
||||||
|
label-cancel="Abbrechen"
|
||||||
|
label-set="Speichern"
|
||||||
|
v-model="ingredient.drink_ingredient.drink_ingredient"
|
||||||
|
@save="updateIngredient(ingredient, volume)"
|
||||||
|
>
|
||||||
|
<q-select
|
||||||
|
class="col q-px-sm"
|
||||||
|
label="Getränk"
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
:options="drinks"
|
||||||
|
option-label="name"
|
||||||
|
v-model="ingredient.drink_ingredient.drink_ingredient"
|
||||||
|
/>
|
||||||
|
</q-popup-edit>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
{{ ingredient.drink_ingredient.volume.toFixed(3) }}L
|
||||||
|
<q-popup-edit
|
||||||
|
buttons
|
||||||
|
label-cancel="Abbrechen"
|
||||||
|
label-set="Speichern"
|
||||||
|
v-model="ingredient.drink_ingredient.volume"
|
||||||
|
@save="updateIngredient(ingredient, volume)"
|
||||||
|
>
|
||||||
|
<q-input
|
||||||
|
class="col q-px-sm"
|
||||||
|
label="Volume"
|
||||||
|
type="number"
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
suffix="L"
|
||||||
|
v-model.number="ingredient.drink_ingredient.volume"
|
||||||
|
step="0.01"
|
||||||
|
min="0"
|
||||||
|
/>
|
||||||
|
</q-popup-edit>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="ingredient.extra_ingredient" class="col">
|
||||||
|
<div class="full-width row justify-evenly q-py-xs">
|
||||||
|
<div class="col">
|
||||||
|
{{ ingredient.extra_ingredient.name }}
|
||||||
|
</div>
|
||||||
|
<div class="col">{{ ingredient.extra_ingredient.price.toFixed(3) }}€</div>
|
||||||
|
</div>
|
||||||
|
<q-popup-edit
|
||||||
|
v-model="ingredient.extra_ingredient"
|
||||||
|
buttons
|
||||||
|
label-cancel="Abbrechen"
|
||||||
|
label-set="Speichern"
|
||||||
|
@save="updateIngredient(ingredient, volume)"
|
||||||
|
>
|
||||||
|
<q-select
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model="ingredient.extra_ingredient"
|
||||||
|
:options="extra_ingredients"
|
||||||
|
option-label="name"
|
||||||
|
/>
|
||||||
|
</q-popup-edit>
|
||||||
|
</div>
|
||||||
|
<div class="col-1 row justify-end q-pa-xs">
|
||||||
|
<q-btn
|
||||||
|
icon="mdi-delete"
|
||||||
|
round
|
||||||
|
size="xs"
|
||||||
|
color="negative"
|
||||||
|
@click="deleteIngredient(ingredient, volume)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<q-separator />
|
||||||
|
</div>
|
||||||
|
<div class="full-width row justify-end q-py-xs">
|
||||||
|
<q-btn size="sm" icon-right="add" color="positive" label="Zutat hinzufügen">
|
||||||
|
<q-menu anchor="center middle" self="center middle">
|
||||||
|
<div class="full-width row justify-around q-gutter-sm q-pa-sm">
|
||||||
|
<div class="col">
|
||||||
|
<q-select
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
label="Zutat"
|
||||||
|
:options="[...drinks, ...extra_ingredients]"
|
||||||
|
option-label="name"
|
||||||
|
v-model="newIngredient"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
label="Volume"
|
||||||
|
type="number"
|
||||||
|
steps="0.1"
|
||||||
|
v-model.number="newIngredientVolume"
|
||||||
|
v-if="newIngredient && newIngredient.volume"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
v-else-if="newIngredient && newIngredient.price"
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
label="Preis"
|
||||||
|
disable
|
||||||
|
:value="newIngredient.price.toFixed(3)"
|
||||||
|
suffix="€"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="full-width row jusitfy-between q-gutter-sm q-pa-sm">
|
||||||
|
<q-btn label="Abbrechen" @click="cancelAddIngredient" v-close-popup />
|
||||||
|
<q-btn
|
||||||
|
label="Speichern"
|
||||||
|
color="positive"
|
||||||
|
@click="addIngredient(volume)"
|
||||||
|
v-close-popup
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</q-menu>
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, ref } from '@vue/composition-api';
|
||||||
|
import store, { DrinkPriceVolume } from '../../store/altStore';
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'Ingredients',
|
||||||
|
props: {
|
||||||
|
ingredients: {
|
||||||
|
type: Array as () => Array<FG.Ingredient>,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
volume: {
|
||||||
|
type: Object as () => DrinkPriceVolume,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const emptyIngredient: FG.Ingredient = {
|
||||||
|
id: -1,
|
||||||
|
drink_ingredient: null,
|
||||||
|
extra_ingredient: null
|
||||||
|
};
|
||||||
|
const newIngredient = ref<FG.Drink | FG.ExtraIngredient>();
|
||||||
|
const newIngredientVolume = ref<number>(0);
|
||||||
|
function addIngredient(volume: DrinkPriceVolume) {
|
||||||
|
if ((<FG.Drink>newIngredient.value)?.volume) {
|
||||||
|
store.actions.setIngredient(
|
||||||
|
{
|
||||||
|
id: -1,
|
||||||
|
drink_ingredient: {
|
||||||
|
id: -1,
|
||||||
|
drink_ingredient: <FG.Drink>newIngredient.value,
|
||||||
|
volume: newIngredientVolume.value
|
||||||
|
},
|
||||||
|
extra_ingredient: null
|
||||||
|
},
|
||||||
|
volume
|
||||||
|
);
|
||||||
|
} else if (newIngredient) {
|
||||||
|
store.actions.setIngredient(
|
||||||
|
{
|
||||||
|
id: -1,
|
||||||
|
drink_ingredient: null,
|
||||||
|
extra_ingredient: <FG.ExtraIngredient>newIngredient.value
|
||||||
|
},
|
||||||
|
volume
|
||||||
|
);
|
||||||
|
}
|
||||||
|
cancelAddIngredient();
|
||||||
|
}
|
||||||
|
function cancelAddIngredient() {
|
||||||
|
setTimeout(() => {
|
||||||
|
(newIngredient.value = undefined), (newIngredientVolume.value = 0);
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
function updateIngredient(ingredient: FG.Ingredient, volume: DrinkPriceVolume) {
|
||||||
|
store.actions.updateIngredient(ingredient, volume);
|
||||||
|
}
|
||||||
|
function deleteIngredient(ingredient: FG.Ingredient, volume: DrinkPriceVolume) {
|
||||||
|
store.actions.deleteIngredient(ingredient, volume);
|
||||||
|
}
|
||||||
|
const drinks = computed(() =>
|
||||||
|
store.state.drinks.filter(drink => !!drink.cost_price_pro_volume)
|
||||||
|
);
|
||||||
|
const extra_ingredients = computed(() => store.state.extraIngredients);
|
||||||
|
|
||||||
|
return {
|
||||||
|
addIngredient,
|
||||||
|
drinks,
|
||||||
|
extra_ingredients,
|
||||||
|
newIngredient,
|
||||||
|
newIngredientVolume,
|
||||||
|
cancelAddIngredient,
|
||||||
|
updateIngredient,
|
||||||
|
deleteIngredient
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
|
@ -2,10 +2,16 @@ import { reactive, computed, ComputedRef, WritableComputedRef } from '@vue/compo
|
||||||
import { axios } from 'src/boot/axios';
|
import { axios } from 'src/boot/axios';
|
||||||
import { AxiosResponse } from 'axios';
|
import { AxiosResponse } from 'axios';
|
||||||
|
|
||||||
const state = reactive<{ drinks: Drink[]; tags: FG.Tag[]; drinkTypes: FG.DrinkType[] }>({
|
const state = reactive<{
|
||||||
|
drinks: Drink[];
|
||||||
|
tags: FG.Tag[];
|
||||||
|
drinkTypes: FG.DrinkType[];
|
||||||
|
extraIngredients: FG.ExtraIngredient[];
|
||||||
|
}>({
|
||||||
drinks: [],
|
drinks: [],
|
||||||
tags: [],
|
tags: [],
|
||||||
drinkTypes: []
|
drinkTypes: [],
|
||||||
|
extraIngredients: []
|
||||||
});
|
});
|
||||||
|
|
||||||
interface MinPrice extends Omit<FG.DrinkMinPrice, 'price'> {
|
interface MinPrice extends Omit<FG.DrinkMinPrice, 'price'> {
|
||||||
|
@ -165,6 +171,44 @@ const actions = {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(err => console.warn(err));
|
.catch(err => console.warn(err));
|
||||||
|
},
|
||||||
|
getExtraIngredients() {
|
||||||
|
axios
|
||||||
|
.get('pricelist/ingredients/extraIngredients')
|
||||||
|
.then((response: AxiosResponse<FG.ExtraIngredient[]>) => {
|
||||||
|
state.extraIngredients = response.data;
|
||||||
|
})
|
||||||
|
.catch(err => console.log(err));
|
||||||
|
},
|
||||||
|
setIngredient(ingredient: FG.Ingredient, volume: DrinkPriceVolume) {
|
||||||
|
axios
|
||||||
|
.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) {
|
||||||
|
axios
|
||||||
|
.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) {
|
||||||
|
axios
|
||||||
|
.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));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -176,8 +220,8 @@ function create_volume(drink: Drink, volume: DrinkPriceVolume) {
|
||||||
if (volume.ingredients.some(ingredient => !!ingredient.drink_ingredient)) {
|
if (volume.ingredients.some(ingredient => !!ingredient.drink_ingredient)) {
|
||||||
let retVal = 0;
|
let retVal = 0;
|
||||||
volume.ingredients.forEach(ingredient => {
|
volume.ingredients.forEach(ingredient => {
|
||||||
if (ingredient.volume) {
|
if (ingredient.drink_ingredient?.volume) {
|
||||||
retVal += ingredient.volume;
|
retVal += ingredient.drink_ingredient.volume;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
volume._volume = retVal;
|
volume._volume = retVal;
|
||||||
|
@ -195,8 +239,10 @@ function create_min_prices(drink: Drink, volume: DrinkPriceVolume, percentage: n
|
||||||
if (volume.ingredients.every(ingredient => !!ingredient.drink_ingredient)) {
|
if (volume.ingredients.every(ingredient => !!ingredient.drink_ingredient)) {
|
||||||
return computed<number>(() => {
|
return computed<number>(() => {
|
||||||
let retVal = (drink.cost_price_pro_volume || 0) * (volume.volume?.value || 0);
|
let retVal = (drink.cost_price_pro_volume || 0) * (volume.volume?.value || 0);
|
||||||
volume.ingredients.forEach((ingredient: FG.ExtraIngredient) => {
|
volume.ingredients.forEach(ingredient => {
|
||||||
retVal += ingredient.price;
|
if (ingredient.extra_ingredient) {
|
||||||
|
retVal += ingredient.extra_ingredient.price;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
retVal = (retVal * percentage) / 100;
|
retVal = (retVal * percentage) / 100;
|
||||||
return retVal;
|
return retVal;
|
||||||
|
@ -211,10 +257,12 @@ function create_min_prices(drink: Drink, volume: DrinkPriceVolume, percentage: n
|
||||||
let retVal = 0;
|
let retVal = 0;
|
||||||
volume.ingredients.forEach(ingredient => {
|
volume.ingredients.forEach(ingredient => {
|
||||||
if (ingredient.drink_ingredient) {
|
if (ingredient.drink_ingredient) {
|
||||||
retVal += ingredient.volume * (ingredient.drink_ingredient.cost_price_pro_volume || 0);
|
retVal +=
|
||||||
|
ingredient.drink_ingredient.volume *
|
||||||
|
(ingredient.drink_ingredient.drink_ingredient?.cost_price_pro_volume || 0);
|
||||||
}
|
}
|
||||||
if (ingredient.name) {
|
if (ingredient.extra_ingredient) {
|
||||||
retVal += ingredient.price;
|
retVal += ingredient.extra_ingredient.price;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
console.log(volume);
|
console.log(volume);
|
||||||
|
|
Loading…
Reference in New Issue