release v2.0.0 #4

Merged
crimsen merged 481 commits from develop into master 2024-01-18 15:15:08 +00:00
4 changed files with 289 additions and 58 deletions
Showing only changes of commit c272c9e4a5 - Show all commits

View File

@ -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;
} }

View File

@ -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 {

View File

@ -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>

View File

@ -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);