release v2.0.0 #4

Merged
crimsen merged 481 commits from develop into master 2024-01-18 15:15:08 +00:00
9 changed files with 392 additions and 322 deletions
Showing only changes of commit e90dc4c306 - Show all commits

View File

@ -8,9 +8,7 @@
dense dense
:filter="search" :filter="search"
:filter-method="filter" :filter-method="filter"
:grid="grid" grid
:style="grid ? '' : 'max-height: 80vh;'"
:virtual-scroll="!grid"
:rows-per-page-options="[0]" :rows-per-page-options="[0]"
> >
<template #header="props"> <template #header="props">
@ -47,10 +45,9 @@
option-value="name" option-value="name"
options-cover options-cover
/> />
<q-btn label="grid" @click="grid = !grid" />
</div> </div>
</template> </template>
<template #body="drinks_props"> <!--<template #body="drinks_props">
<q-tr :props="drinks_props"> <q-tr :props="drinks_props">
<q-td auto-width> <q-td auto-width>
<q-btn <q-btn
@ -354,14 +351,10 @@
/> />
</q-td> </q-td>
<q-td key="receipt" :props="drinks_props"> <q-td key="receipt" :props="drinks_props">
<build-manual
:steps="drinks_props.row.receipt"
@deleteStep="deleteStep($event, drinks_props.row)"
@addStep="addStep($event, drinks_props.row)"
/>
</q-td> </q-td>
</q-tr> </q-tr>
</template> </template>-->
<template #item="props"> <template #item="props">
<div class="q-pa-xs col-xs-12 col-sm-6 col-md-4"> <div class="q-pa-xs col-xs-12 col-sm-6 col-md-4">
<q-card> <q-card>
@ -452,19 +445,15 @@
/> />
</div> </div>
</q-card-section> </q-card-section>
<q-card-section v-if="props.row.volumes.length > 0"> <q-card-section v-if="props.row.volumes.length > 0 && notLoading">
<drink-price-volumes v-model="props.row.volumes" /> <drink-price-volumes :model-value="props.row.volumes" />
</q-card-section> </q-card-section>
</q-card> </q-card>
</div> </div>
</template> </template>
</q-table> </q-table>
<q-dialog :model-value="editDrink !== undefined" persistent> <q-dialog :model-value="editDrink !== undefined" persistent>
<drink-modify <drink-modify :drink="editDrink" @save="editing_drink" @cancel="editDrink = undefined" />
:drink="editDrink"
@save="editDrink = undefined"
@cancel="editDrink = undefined"
/>
</q-dialog> </q-dialog>
</template> </template>
@ -482,6 +471,7 @@ import DrinkModify from './DrinkModify.vue';
import { filter, Search } from '../utils/filter'; import { filter, Search } from '../utils/filter';
import { Notify } from 'quasar'; import { Notify } from 'quasar';
import { sort } from '../utils/sort'; import { sort } from '../utils/sort';
import { DeleteObjects } from 'src/plugins/pricelist/utils/utils';
export default defineComponent({ export default defineComponent({
name: 'CalculationTable', name: 'CalculationTable',
@ -489,8 +479,6 @@ export default defineComponent({
SearchInput, SearchInput,
MinPriceSetting, MinPriceSetting,
NewDrink, NewDrink,
BuildManual,
DrinkPriceVolumesTable,
DrinkPriceVolumes, DrinkPriceVolumes,
DrinkModify, DrinkModify,
}, },
@ -710,25 +698,36 @@ export default defineComponent({
void store.delete_drink_picture(drink); void store.delete_drink_picture(drink);
} }
function addStep(event: string, drink: Drink) {
console.log(event, drink.receipt);
drink.receipt?.push(event);
updateDrink(drink);
}
function deleteStep(event: number, drink: Drink) {
console.log(event, drink.receipt);
drink.receipt?.splice(event, 1);
updateDrink(drink);
}
const search = ref<Search>({ const search = ref<Search>({
value: '', value: '',
key: '', key: '',
label: '', label: '',
}); });
const grid = ref(true);
const editDrink = ref(); const editDrink = ref();
async function editing_drink(drink: Drink, toDeleteObjects: DeleteObjects) {
notLoading.value = false;
for (const ingredient of toDeleteObjects.ingredients) {
await store.deleteIngredient(ingredient);
}
for (const price of toDeleteObjects.prices) {
await store.deletePrice(price);
}
for (const volume of toDeleteObjects.volumes) {
await store.deleteVolume(volume, drink);
}
console.log(drink);
await store.updateDrink(drink);
editDrink.value = undefined;
notLoading.value = true;
}
function get_volumes(drink_id: number) {
return store.drinks.find((a) => a.id === drink_id)?.volumes;
}
const notLoading = ref(true);
return { return {
drinks: computed(() => store.drinks), drinks: computed(() => store.drinks),
pagination, pagination,
@ -743,15 +742,15 @@ export default defineComponent({
drinkPic, drinkPic,
savePicture, savePicture,
deletePicture, deletePicture,
addStep,
deleteStep,
console, console,
search, search,
filter, filter,
search_keys, search_keys,
grid,
tags: computed(() => store.tags), tags: computed(() => store.tags),
editDrink, editDrink,
editing_drink,
get_volumes,
notLoading,
}; };
}, },
}); });

View File

@ -6,16 +6,16 @@
animated animated
swipeable swipeable
control-color="primary" control-color="primary"
navigation
arrows arrows
> >
<q-carousel-slide v-for="volume in volumes" :key="volume.id" :name="volume.id"> <q-carousel-slide v-for="volume in volumes" :key="volume.id" :name="volume.id">
<div class="full-width row">
<q-input <q-input
v-model.number="volume.volume" v-model.number="volume.volume"
class="q-pa-sm" class="q-pa-sm col-10"
:outlined="!editable" :outlined="!editable || !volume_can_edit"
:filled="editable" :filled="editable && volume_can_edit"
:readonly="!editable" :readonly="!editable || !volume_can_edit"
dense dense
label="Inhalt" label="Inhalt"
mask="#.###" mask="#.###"
@ -24,6 +24,12 @@
min="0" min="0"
step="0.001" step="0.001"
/> />
<div v-if="deleteable && editable" class="q-pa-sm col-2 text-right">
<q-btn round icon="mdi-delete" size="sm" color="negative" @click="deleteVolume">
<q-tooltip> Abgabe entfernen </q-tooltip>
</q-btn>
</div>
</div>
<div class="full-width row q-gutter-sm q-pa-sm justify-around"> <div class="full-width row q-gutter-sm q-pa-sm justify-around">
<div v-for="(min_price, index) in volume.min_prices" :key="index"> <div v-for="(min_price, index) in volume.min_prices" :key="index">
<q-badge class="text-body1" color="primary"> {{ min_price.percentage }}% </q-badge> <q-badge class="text-body1" color="primary"> {{ min_price.percentage }}% </q-badge>
@ -33,13 +39,11 @@
<div class="q-pa-sm"> <div class="q-pa-sm">
<div v-for="(price, index) in volume.prices" :key="price.id"> <div v-for="(price, index) in volume.prices" :key="price.id">
<div class="fit row justify-around q-py-sm"> <div class="fit row justify-around q-py-sm">
<div v-if="!editable" class="text-body1 col-xs-12 col-md-3"> <div v-if="!editable" class="text-body1 col-3">{{ price.price.toFixed(2) }}</div>
{{ price.price.toFixed(2) }}
</div>
<q-input <q-input
v-else v-else
v-model.number="price.price" v-model.number="price.price"
class="col-xs-12 col-md-3" class="col-3"
type="number" type="number"
min="0" min="0"
step="0.01" step="0.01"
@ -47,50 +51,77 @@
filled filled
dense dense
label="Preis" label="Preis"
@update:model-value="change"
/> />
<div class="text-body1 col-xs-12 col-md-2"> <div class="text-body1 col-2">
<q-toggle <q-toggle
v-model="price.public" v-model="price.public"
:disable="!editable" :disable="!editable"
checked-icon="mdi-earth" checked-icon="mdi-earth"
unchecked-icon="mdi-earth-off" unchecked-icon="mdi-earth-off"
@update:model-value="change"
/> />
</div> </div>
<div v-if="!editable" class="text-body1 col-xs-12 col-md-5"> <div v-if="!editable" class="text-body1 col-5">
{{ price.description }} {{ price.description }}
</div> </div>
<q-input <q-input
v-else v-else
v-model="price.description" v-model="price.description"
class="col-xs-12 col-md-5" class="col-5"
filled filled
dense dense
label="Beschreibung" label="Beschreibung"
@update:model-value="change"
/> />
<div v-if="editable" class="col-1">
<q-btn round icon="mdi-delete" color="negative" size="xs" @click="deletePrice(price)">
<q-tooltip> Preis entfernen </q-tooltip>
</q-btn>
</div>
</div> </div>
<q-separator v-if="index < volume.prices.length - 1" /> <q-separator v-if="index < volume.prices.length - 1" />
</div> </div>
<div v-if="editable" class="full-width row justify-end text-right">
<q-btn round icon="mdi-plus" size="sm" color="primary">
<q-tooltip> Preis hinzufügen </q-tooltip>
<q-menu anchor="center middle" self="center middle">
<new-price @save="addPrice" />
</q-menu>
</q-btn>
</div>
</div> </div>
<div class="q-pa-sm"> <div class="q-pa-sm">
<ingredients <ingredients
v-model="volume.ingredients" v-model="volume.ingredients"
:editable="editable" :editable="editable"
@update="updateVolume(volume)" @update="updateVolume(volume)"
@delete-ingredient="deleteIngredient"
/> />
</div> </div>
</q-carousel-slide> </q-carousel-slide>
</q-carousel> </q-carousel>
<div class="full-width row justify-center q-pa-sm">
<div class="q-px-sm">
<q-btn-toggle v-model="volume" :options="options" /> <q-btn-toggle v-model="volume" :options="options" />
</div>
<div v-if="editable" class="q-px-sm">
<q-btn class="q-px-sm" round icon="mdi-plus" color="primary" size="sm" @click="newVolume">
<q-tooltip> Abgabe hinzufügen </q-tooltip>
</q-btn>
</div>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, PropType, ref, onBeforeMount, unref } from 'vue'; import { computed, defineComponent, PropType, ref, onBeforeMount } from 'vue';
import { DrinkPriceVolume } from 'src/plugins/pricelist/store'; import { DrinkPriceVolume } from 'src/plugins/pricelist/store';
import Ingredients from 'src/plugins/pricelist/components/CalculationTable/Ingredients.vue'; import Ingredients from 'src/plugins/pricelist/components/CalculationTable/Ingredients.vue';
import NewPrice from 'src/plugins/pricelist/components/CalculationTable/NewPrice.vue';
import { calc_volume, clone } from '../../utils/utils'; import { calc_volume, clone } from '../../utils/utils';
export default defineComponent({ export default defineComponent({
name: 'DrinkPriceVolume', name: 'DrinkPriceVolume',
components: { Ingredients }, components: { Ingredients, NewPrice },
props: { props: {
modelValue: { modelValue: {
type: Array as PropType<Array<DrinkPriceVolume>>, type: Array as PropType<Array<DrinkPriceVolume>>,
@ -103,6 +134,10 @@ export default defineComponent({
}, },
emits: { emits: {
'update:modelValue': (val: Array<DrinkPriceVolume>) => val, 'update:modelValue': (val: Array<DrinkPriceVolume>) => val,
update: (val: number) => val,
'delete-volume': (val: DrinkPriceVolume) => val,
'delete-price': (val: FG.DrinkPrice) => val,
'delete-ingredient': (val: FG.Ingredient) => val,
}, },
setup(props, { emit }) { setup(props, { emit }) {
onBeforeMount(() => { onBeforeMount(() => {
@ -123,10 +158,13 @@ export default defineComponent({
}, },
set: (val: number | undefined) => (_volume.value = val), set: (val: number | undefined) => (_volume.value = val),
}); });
const edit_volume = computed(() => {
return volumes.value.find((a) => a.id === volume.value);
});
const options = computed<Array<{ label: string; value: number }>>(() => { const options = computed<Array<{ label: string; value: number }>>(() => {
const retVal: Array<{ label: string; value: number }> = []; const retVal: Array<{ label: string; value: number }> = [];
volumes.value.forEach((volume: DrinkPriceVolume) => { volumes.value.forEach((volume: DrinkPriceVolume) => {
retVal.push({ label: `${<number>volume.volume}L`, value: volume.id }); retVal.push({ label: `${(<number>volume.volume).toFixed(3)}L`, value: volume.id });
}); });
return retVal; return retVal;
}); });
@ -138,10 +176,101 @@ export default defineComponent({
console.log('updateVolume old', volumes.value[index]); console.log('updateVolume old', volumes.value[index]);
volumes.value[index].volume = calc_volume(_volume); volumes.value[index].volume = calc_volume(_volume);
} }
emit('update:modelValue', unref(volumes)); change();
setTimeout(() => {
emit('update', index);
}, 50);
} }
return { volumes, volume, options, updateVolume }; const volume_can_edit = computed(() => {
if (edit_volume.value) {
return !edit_volume.value.ingredients.some((ingredient) => ingredient.drink_ingredient);
}
return true;
});
const newVolumeId = ref(-1);
function newVolume() {
const new_volume: DrinkPriceVolume = {
id: newVolumeId.value,
_volume: 0,
volume: 0,
prices: [],
ingredients: [],
min_prices: [],
};
newVolumeId.value--;
volumes.value.push(new_volume);
change();
_volume.value = volumes.value[volumes.value.length - 1].id;
}
function deleteVolume() {
if (edit_volume.value) {
if (edit_volume.value.id > 0) {
emit('delete-volume', edit_volume.value);
}
const index = volumes.value.findIndex((a) => a.id === edit_volume.value?.id);
if (index > -1) {
_volume.value = volumes.value[0].id;
volumes.value.splice(index, 1);
}
}
}
const deleteable = computed(() => {
if (edit_volume.value) {
const has_ingredients = edit_volume.value.ingredients.length > 0;
const has_prices = edit_volume.value.prices.length > 0;
return !(has_ingredients || has_prices);
}
return true;
});
function addPrice(price: FG.DrinkPrice) {
if (edit_volume.value) {
edit_volume.value.prices.push(price);
change();
}
}
function deletePrice(price: FG.DrinkPrice) {
if (edit_volume.value) {
const index = edit_volume.value.prices.findIndex((a) => a.id === price.id);
if (index > -1) {
if (edit_volume.value.prices[index].id > 0) {
emit('delete-price', edit_volume.value.prices[index]);
change();
}
edit_volume.value.prices.splice(index, 1);
}
}
}
function deleteIngredient(ingredient: FG.Ingredient) {
emit('delete-ingredient', ingredient);
}
function change() {
emit('update:modelValue', volumes.value);
}
return {
volumes,
volume,
options,
updateVolume,
volume_can_edit,
newVolume,
deleteable,
addPrice,
deletePrice,
deleteVolume,
deleteIngredient,
change,
};
}, },
}); });
</script> </script>

View File

@ -67,13 +67,13 @@
</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"
:rows="props.row.prices" :rows="props.row.prices"
:row="props.row" :row="props.row"
:visible-columns="visibleColumns" :visible-columns="visibleColumns"
@updateDrink="updateDrink" @updateDrink="updateDrink"
/> />-->
</q-td> </q-td>
</q-tr> </q-tr>
<q-tr v-show="props.expand" :props="props"> <q-tr v-show="props.expand" :props="props">
@ -108,8 +108,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { Drink, DrinkPriceVolume, usePricelistStore } from '../../store'; import { Drink, DrinkPriceVolume } from '../../store';
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 NewVolume from 'src/plugins/pricelist/components/CalculationTable/DrinkPriceVolumeTable/NewVolume.vue'; import NewVolume from 'src/plugins/pricelist/components/CalculationTable/DrinkPriceVolumeTable/NewVolume.vue';
import { PropType, defineComponent } from 'vue'; import { PropType, defineComponent } from 'vue';
@ -134,7 +133,7 @@ const columns = [
export default defineComponent({ export default defineComponent({
name: 'DrinkPriceVolumsTable', name: 'DrinkPriceVolumsTable',
components: { PriceTable, Ingredients, NewVolume }, components: { Ingredients, NewVolume },
props: { props: {
visibleColumns: { visibleColumns: {
type: Array, type: Array,
@ -155,7 +154,7 @@ export default defineComponent({
}, },
emits: { updateDrink: () => true }, emits: { updateDrink: () => true },
setup(_, { emit }) { setup(_, { emit }) {
const store = usePricelistStore(); //const store = usePricelistStore();
function addVolume(volume: DrinkPriceVolume, drink: Drink) { function addVolume(volume: DrinkPriceVolume, drink: Drink) {
drink.volumes.push(volume); drink.volumes.push(volume);
@ -165,8 +164,9 @@ export default defineComponent({
function updateDrink() { function updateDrink() {
emit('updateDrink'); emit('updateDrink');
} }
function deleteVolume(volume: FG.DrinkPriceVolume, drink: FG.Drink) { function deleteVolume() {
store.deleteVolume(volume, drink); //store.deleteVolume(volume, drink);
return;
} }
const column_prices = [ const column_prices = [
{ {
@ -191,8 +191,9 @@ export default defineComponent({
volume.ingredients.push(ingredient); volume.ingredients.push(ingredient);
updateDrink(); updateDrink();
} }
function deleteIngredient(ingredient: FG.Ingredient, volume: DrinkPriceVolume) { function deleteIngredient() {
store.deleteIngredient(ingredient, volume); //store.deleteIngredient(ingredient, volume);
return;
} }
return { addVolume, updateDrink, deleteVolume, column_prices, addIngredient, deleteIngredient }; return { addVolume, updateDrink, deleteVolume, column_prices, addIngredient, deleteIngredient };

View File

@ -90,13 +90,16 @@
size="xs" size="xs"
color="negative" color="negative"
@click="deleteIngredient(ingredient)" @click="deleteIngredient(ingredient)"
/> >
<q-tooltip> Zutat entfernen </q-tooltip>
</q-btn>
</div> </div>
</div> </div>
<q-separator /> <q-separator />
</div> </div>
<div v-if="editable" class="full-width row justify-end q-py-xs"> <div v-if="editable" class="full-width row justify-end q-py-xs">
<q-btn size="sm" icon-right="mdi-plus" color="positive" label="Zutat hinzufügen"> <q-btn size="sm" round icon="mdi-plus" color="primary">
<q-tooltip> Neue Zutat hinzufügen </q-tooltip>
<q-menu anchor="center middle" self="center middle"> <q-menu anchor="center middle" self="center middle">
<div class="full-width row justify-around q-gutter-sm q-pa-sm"> <div class="full-width row justify-around q-gutter-sm q-pa-sm">
<div class="col"> <div class="col">
@ -166,6 +169,7 @@ export default defineComponent({
emits: { emits: {
'update:modelValue': (val: Array<FG.Ingredient>) => val, 'update:modelValue': (val: Array<FG.Ingredient>) => val,
update: () => true, update: () => true,
'delete-ingredient': (val: FG.Ingredient) => val,
}, },
setup(props, { emit }) { setup(props, { emit }) {
onBeforeMount(() => { onBeforeMount(() => {
@ -218,6 +222,9 @@ export default defineComponent({
function deleteIngredient(ingredient: FG.Ingredient) { function deleteIngredient(ingredient: FG.Ingredient) {
const index = edit_ingredients.value.findIndex((a) => a.id === ingredient.id); const index = edit_ingredients.value.findIndex((a) => a.id === ingredient.id);
if (index > -1) { if (index > -1) {
if (edit_ingredients.value[index].id > 0) {
emit('delete-ingredient', edit_ingredients.value[index]);
}
edit_ingredients.value.splice(index, 1); edit_ingredients.value.splice(index, 1);
} }
emit('update:modelValue', unref(edit_ingredients)); emit('update:modelValue', unref(edit_ingredients));
@ -238,7 +245,7 @@ export default defineComponent({
function update() { function update() {
setTimeout(() => { setTimeout(() => {
emit('update'); emit('update');
}, 500); }, 50);
} }
return { return {

View File

@ -0,0 +1,59 @@
<template>
<div class="row justify-around q-pa-sm">
<q-input
v-model.number="newPrice.price"
dense
filled
class="q-px-sm"
type="number"
label="Preis"
suffix="€"
min="0"
step="0.1"
/>
<q-input
v-model="newPrice.description"
dense
filled
class="q-px-sm"
label="Beschreibung"
clearable
/>
<q-toggle v-model="newPrice.public" dense class="q-px-sm" label="Öffentlich" />
</div>
<div class="row justify-between q-pa-sm">
<q-btn v-close-popup label="Abbrechen" @click="cancelAddPrice" />
<q-btn v-close-popup label="Speichern" color="primary" @click="addPrice(row)" />
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
export default defineComponent({
name: 'NewPrice',
emits: {
save: (val: FG.DrinkPrice) => val,
},
setup(_, { emit }) {
const emptyPrice: FG.DrinkPrice = {
id: -1,
price: 0,
description: '',
public: true,
};
const newPrice = ref(emptyPrice);
function addPrice() {
emit('save', newPrice.value);
cancelAddPrice();
}
function cancelAddPrice() {
setTimeout(() => {
newPrice.value = emptyPrice;
}, 200);
}
return { newPrice, addPrice, cancelAddPrice };
},
});
</script>
<style scoped></style>

View File

@ -1,218 +0,0 @@
<template>
<q-table
v-model:pagination="pagination"
dense
hide-header
:columns="columns"
:rows="rows"
:visible-columns="visibleColumns"
flat
virtual-scroll
:rows-per-page-options="[0]"
>
<!--
style="max-height: 130px" -->
<template #body="prices_props">
<q-tr :props="prices_props">
<q-td key="price" :props="prices_props">
{{ prices_props.row.price.toFixed(2) }}
<q-popup-edit
v-slot="scope"
v-model="prices_props.row.price"
buttons
label-cancel="Abbrechen"
label-set="Speichern"
@update:modelValue="updateDrink"
>
<q-input
v-model.number="scope.value"
type="number"
label="Preis"
dense
filled
autofocus
min="0"
step="0.1"
suffix="€"
@keyup.enter="scope.set"
/> </q-popup-edit
></q-td>
<q-td key="description" :props="prices_props">
{{ prices_props.row.description }}
<q-popup-edit
v-slot="scope"
v-model="prices_props.row.description"
buttons
label="Beschreibung"
label-cancel="Abbrechen"
label-set="Speichern"
@update:modelValue="updateDrink"
>
<q-input
v-model="scope.value"
dense
autofocus
filled
clearable
@keyup.enter="scope.set"
/>
</q-popup-edit>
</q-td>
<q-td key="public" :props="prices_props">
<q-toggle v-model="prices_props.row.public" dense @update:modelValue="updateDrink" />
</q-td>
<q-td>
<q-btn
color="negative"
padding="xs"
round
size="xs"
icon="mdi-delete"
@click="deletePrice(prices_props.row, row)"
/>
</q-td>
</q-tr>
</template>
<template #bottom>
<div class="full-width row justify-end">
<q-btn size="xs" icon-right="add" color="positive" label="Preis hinzufügen">
<q-menu anchor="center middle" self="center middle">
<div class="row justify-around q-pa-sm">
<q-input
v-model.number="newPrice.price"
dense
filled
class="q-px-sm"
type="number"
label="Preis"
suffix="€"
min="0"
step="0.1"
/>
<q-input
v-model="newPrice.description"
dense
filled
class="q-px-sm"
label="Beschreibung"
clearable
/>
<q-toggle v-model="newPrice.public" dense class="q-px-sm" label="Öffentlich" />
</div>
<div class="row justify-between q-pa-sm">
<q-btn v-close-popup label="Abbrechen" @click="cancelAddPrice" />
<q-btn v-close-popup label="Speichern" color="primary" @click="addPrice(row)" />
</div>
</q-menu>
</q-btn>
</div>
</template>
<template #no-data class="justify-end">
<div class="full-width row justify-end">
<q-btn size="xs" icon-right="add" color="positive" label="Preis hinzufügen">
<q-menu anchor="center middle" self="center middle">
<div class="row justify-around q-pa-sm">
<q-input
v-model.number="newPrice.price"
dense
filled
class="q-px-sm"
type="number"
label="Preis"
suffix="€"
min="0"
step="0.1"
/>
<q-input
v-model="newPrice.description"
dense
filled
class="q-px-sm"
label="Beschreibung"
clearable
/>
<q-toggle v-model="newPrice.public" dense class="q-px-sm" label="Öffentlich" />
</div>
<div class="row justify-between q-pa-sm">
<q-btn v-close-popup label="Abbrechen" @click="cancelAddPrice" />
<q-btn v-close-popup label="Speichern" color="primary" @click="addPrice(row)" />
</div>
</q-menu>
</q-btn>
</div>
</template>
</q-table>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { DrinkPriceVolume, usePricelistStore } from '../../store';
export default defineComponent({
name: 'PriceTable',
props: {
columns: {
type: Array,
required: true,
},
rows: {
type: Array,
required: true,
},
row: {
type: Object /*as PropType<DrinkPriceVolume>*/,
required: true,
},
visibleColumns: {
type: Array,
required: true,
},
},
emits: { updateDrink: () => true },
setup(props, { emit }) {
const store = usePricelistStore();
const emptyPrice: FG.DrinkPrice = {
id: -1,
price: 0,
description: '',
public: true,
};
const newPrice = ref(emptyPrice);
function addPrice(volume: DrinkPriceVolume) {
volume.prices.push(newPrice.value);
updateDrink();
cancelAddPrice();
}
function updateDrink() {
emit('updateDrink');
}
function cancelAddPrice() {
setTimeout(() => {
newPrice.value = emptyPrice;
}, 200);
}
function deletePrice(price: FG.DrinkPrice, volume: FG.DrinkPriceVolume) {
console.log(price, volume);
store.deletePrice(price, volume);
}
const pagination = ref({
rowsPerPage: (<DrinkPriceVolume>props.row).prices.length,
});
return {
newPrice,
addPrice,
cancelAddPrice,
updateDrink,
deletePrice,
pagination,
console,
};
},
});
</script>
<style scoped></style>

View File

@ -3,6 +3,36 @@
<q-card-section> <q-card-section>
<div class="text-h6">Getränk Bearbeiten</div> <div class="text-h6">Getränk Bearbeiten</div>
</q-card-section> </q-card-section>
<q-card-section>
<q-select
v-model="edit_drink.tags"
multiple
:options="tags"
label="Tags"
option-label="name"
filled
dense
>
<template #selected-item="item">
<q-chip
removable
:tabindex="item.tabindex"
:style="`background-color: ${item.opt.color}`"
@remove="item.removeAtIndex(item.index)"
>
{{ item.opt.name }}
</q-chip>
</template>
<template #option="item">
<q-item v-bind="item.itemProps" v-on="item.itemEvents">
<q-chip :style="`background-color: ${item.opt.color}`">
<q-avatar v-if="item.selected" icon="mdi-check" color="positive" text-color="white" />
{{ item.opt.name }}
</q-chip>
</q-item>
</template>
</q-select>
</q-card-section>
<q-card-section> <q-card-section>
<div class="fit row"> <div class="fit row">
<q-input <q-input
@ -50,9 +80,15 @@
<drink-price-volumes <drink-price-volumes
v-model="edit_drink.volumes" v-model="edit_drink.volumes"
editable editable
@update:modelValue="updateVolume" @update="updateVolume"
@delete-volume="deleteVolume"
@delete-price="deletePrice"
@delete-ingredient="deleteIngredient"
/> />
</q-card-section> </q-card-section>
<q-card-section>
<build-manual :steps="edit_drink.receipt" @deleteStep="deleteStep" @addStep="addStep" />
</q-card-section>
<q-card-actions class="justify-around"> <q-card-actions class="justify-around">
<q-btn label="Abbrechen" @click="cancel" /> <q-btn label="Abbrechen" @click="cancel" />
<q-btn label="Speichern" color="primary" @click="save" /> <q-btn label="Speichern" color="primary" @click="save" />
@ -60,37 +96,90 @@
</q-card> </q-card>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, PropType, ref, onBeforeMount } from 'vue'; import { defineComponent, PropType, ref, onBeforeMount, computed } from 'vue';
import { Drink } from '../store'; import { Drink, DrinkPriceVolume, usePricelistStore } from '../store';
import DrinkPriceVolumes from './CalculationTable/DrinkPriceVolumes.vue'; import DrinkPriceVolumes from './CalculationTable/DrinkPriceVolumes.vue';
import { clone } from '../utils/utils'; import { clone, calc_min_prices, DeleteObjects } from '../utils/utils';
import BuildManual from 'src/plugins/pricelist/components/CalculationTable/BuildManual.vue';
export default defineComponent({ export default defineComponent({
name: 'DrinkModify', name: 'DrinkModify',
components: { DrinkPriceVolumes }, components: { BuildManual, DrinkPriceVolumes },
props: { props: {
drink: { drink: {
type: Object as PropType<Drink>, type: Object as PropType<Drink>,
required: true, required: true,
}, },
}, },
emits: { save: () => true, cancel: () => true }, emits: {
save: (drink: Drink, toDeleteObjects: DeleteObjects) => drink && toDeleteObjects,
cancel: () => true,
},
setup(props, { emit }) { setup(props, { emit }) {
onBeforeMount(() => { onBeforeMount(() => {
//edit_drink.value = <Drink>JSON.parse(JSON.stringify(props.drink)); //edit_drink.value = <Drink>JSON.parse(JSON.stringify(props.drink));
edit_drink.value = clone(props.drink); edit_drink.value = clone(props.drink);
}); });
const store = usePricelistStore();
const toDeleteObjects = ref<DeleteObjects>({
prices: [],
volumes: [],
ingredients: [],
});
const edit_drink = ref<Drink>(); const edit_drink = ref<Drink>();
function save() { function save() {
emit('save'); emit('save', <Drink>edit_drink.value, toDeleteObjects.value);
} }
function cancel() { function cancel() {
emit('cancel'); emit('cancel');
} }
function updateVolume(test: Drink) { function updateVolume(index: number) {
console.log(test); if (index > -1 && edit_drink.value) {
edit_drink.value.volumes[index].min_prices = calc_min_prices(
edit_drink.value.volumes[index],
edit_drink.value.cost_per_volume,
store.min_prices
);
} }
return { edit_drink, save, cancel, updateVolume }; }
function deletePrice(price: FG.DrinkPrice) {
toDeleteObjects.value.prices.push(price);
console.log('toDelete', toDeleteObjects.value);
}
function deleteVolume(volume: DrinkPriceVolume) {
toDeleteObjects.value.volumes.push(volume);
console.log('toDelete', toDeleteObjects.value);
}
function deleteIngredient(ingredient: FG.Ingredient) {
toDeleteObjects.value.ingredients.push(ingredient);
console.log('toDelete', toDeleteObjects.value);
}
function addStep(event: string) {
edit_drink.value?.receipt?.push(event);
}
function deleteStep(event: number) {
edit_drink.value?.receipt?.splice(event, 1);
}
return {
edit_drink,
save,
cancel,
updateVolume,
deletePrice,
deleteIngredient,
deleteVolume,
addStep,
deleteStep,
tags: computed(() => store.tags),
};
}, },
}); });
</script> </script>

View File

@ -146,8 +146,8 @@ export const usePricelistStore = defineStore({
return 0; return 0;
}); });
}, },
deletePrice(price: FG.DrinkPrice, volume: FG.DrinkPriceVolume) { async deletePrice(price: FG.DrinkPrice) {
api /*api
.delete(`pricelist/prices/${price.id}`) .delete(`pricelist/prices/${price.id}`)
.then(() => { .then(() => {
const index = volume.prices.findIndex((a) => a.id == price.id); const index = volume.prices.findIndex((a) => a.id == price.id);
@ -155,21 +155,18 @@ export const usePricelistStore = defineStore({
volume.prices.splice(index, 1); volume.prices.splice(index, 1);
} }
}) })
.catch((err) => console.warn(err)); .catch((err) => console.warn(err));*/
await api.delete(`pricelist/prices/${price.id}`);
}, },
deleteVolume(volume: FG.DrinkPriceVolume, drink: FG.Drink) { async deleteVolume(volume: DrinkPriceVolume, drink: Drink) {
api await api.delete(`pricelist/volumes/${volume.id}`);
.delete(`pricelist/volumes/${volume.id}`)
.then(() => {
const index = drink.volumes.findIndex((a) => a.id === volume.id); const index = drink.volumes.findIndex((a) => a.id === volume.id);
if (index > -1) { if (index > -1) {
drink.volumes.splice(index, 1); drink.volumes.splice(index, 1);
} }
})
.catch((err) => console.warn(err));
}, },
deleteIngredient(ingredient: FG.Ingredient, volume: DrinkPriceVolume) { async deleteIngredient(ingredient: FG.Ingredient) {
api /*api
.delete(`pricelist/ingredients/${ingredient.id}`) .delete(`pricelist/ingredients/${ingredient.id}`)
.then(() => { .then(() => {
const index = volume.ingredients.findIndex((a) => a.id === ingredient.id); const index = volume.ingredients.findIndex((a) => a.id === ingredient.id);
@ -178,6 +175,8 @@ export const usePricelistStore = defineStore({
} }
}) })
.catch((err) => console.warn(err)); .catch((err) => console.warn(err));
*/
await api.delete(`pricelist/ingredients/${ingredient.id}`);
}, },
async setDrink(drink: FG.Drink) { async setDrink(drink: FG.Drink) {
const { data } = await api.post<FG.Drink>('pricelist/drinks', { const { data } = await api.post<FG.Drink>('pricelist/drinks', {
@ -192,10 +191,8 @@ export const usePricelistStore = defineStore({
calc_all_min_prices(this.drinks, this.min_prices); calc_all_min_prices(this.drinks, this.min_prices);
}, },
async updateDrink(drink: Drink) { async updateDrink(drink: Drink) {
console.log(drink);
const { data } = await api.put<FG.Drink>(`pricelist/drinks/${drink.id}`, { const { data } = await api.put<FG.Drink>(`pricelist/drinks/${drink.id}`, {
...drink, ...drink,
cost_per_volume: drink._cost_per_volume,
}); });
const index = this.drinks.findIndex((a) => a.id === data.id); const index = this.drinks.findIndex((a) => a.id === data.id);
if (index > -1) { if (index > -1) {

View File

@ -72,4 +72,11 @@ function clone<T>(o: T): T {
return <T>JSON.parse(JSON.stringify(o)); return <T>JSON.parse(JSON.stringify(o));
} }
interface DeleteObjects {
prices: Array<FG.DrinkPrice>;
volumes: Array<DrinkPriceVolume>;
ingredients: Array<FG.Ingredient>;
}
export { DeleteObjects };
export { calc_volume, calc_cost_per_volume, calc_all_min_prices, calc_min_prices, clone }; export { calc_volume, calc_cost_per_volume, calc_all_min_prices, calc_min_prices, clone };