<template> <q-table title="Kalkulationstabelle" :columns="columns" :data="drinks" :visible-columns="visibleColumn" :dense="$q.screen.lt.md" row-key="id" virtual-scroll :pagination.sync="pagination" :rows-per-page-options="[0]" > <template v-slot: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 v-slot:top-right> <div class="row justify-end q-gutter-sm"> <q-btn label="neues Getränk" color="positive" icon-right="add"> <q-menu anchor="center middle" self="center middle"> <div class="q-pa-sm"> <div class="q-table__title q-pa-sm">Neues Getränk</div> <div class="row"> <q-input class="col-sm-4 col-xs-6 q-pa-sm" filled label="Getränkname" v-model="newDrink.name" /> <q-input class="col-sm-4 col-xs-6 q-pa-sm" filled label="Artikelnummer" v-model="newDrink.article_id" /> <q-select class="col-sm-4 col-xs-6 q-pa-sm" filled label="Kategorie" :options="drinkTypes" option-label="name" v-model="newDrink.type" /> <q-input class="col-sm-4 col-xs-6 q-pa-sm" filled label="Inhalt in L/Gebinde" type="number" v-model.number="newDrink.volume" /> <q-input class="col-sm-4 col-xs-6 q-pa-sm" filled label="Gebindegröße" type="number" v-model.number="newDrink.package_size" /> <q-input class="col-sm-4 col-xs-6 q-pa-sm" filled label="Preis Netto/Gebinde" type="number" v-model.number="newDrink.cost_price_package_netto" /> <q-input class="col-sm-4 col-xs-6 q-pa-sm" filled label="Preis mit 19%/Liter" v-model="cost_price_pro_volume" :disable="calc_price_pro_volume" /> </div> <div class="row justify-between"> <q-btn label="Abbrechen" v-close-popup @click="cancelAddDrink" /> <q-btn label="Speichern" v-close-popup color="primary" @click="addDrink" /> </div> </div> </q-menu> </q-btn> <q-select v-model="visibleColumn" multiple filled dense options-dense display-value="Sichtbarkeit" emit-value map-options :options="[...columns, ...column_calc, ...column_prices]" option-value="name" options-cover /> </div> </template> <template v-slot:body="drinks_props"> <q-tr :props="drinks_props"> <q-td auto-width> <q-btn size="xs" color="negative" round dense icon="mdi-delete" class="q-mx-sm" v-if="drinks_props.row.volumes.length === 0" @click="deleteDrink(drinks_props.row)" /> </q-td> <q-td key="name" :props="drinks_props"> {{ drinks_props.row.name }} <q-popup-edit v-model="drinks_props.row.name" buttons label-cancel="Abbrechen" label-set="Speichern" @save="updateDrink(drinks_props.row)" > <q-input v-model="drinks_props.row.name" filled dense autofocus clearable /> </q-popup-edit> </q-td> <q-td key="drink_type" :props="drinks_props"> {{ drinks_props.row.type.name }} <q-popup-edit v-model="drinks_props.row.type" buttons label-cancel="Abbrechen" label-set="Speichern" @save="updateDrink(drinks_props.row)" > <q-select v-model="drinks_props.row.type" :options="drinkTypes" option-label="name" filled dense autofocus /> </q-popup-edit> </q-td> <q-td key="article_id" :props="drinks_props"> {{ drinks_props.row.article_id || 'o.A.' }} <q-popup-edit v-model="drinks_props.row.article_id" buttons label-cancel="Abbrechen" label-set="Speichern" @save="updateDrink(drinks_props.row)" > <q-input v-model="drinks_props.row.article_id" filled dense autofocus clearable /> </q-popup-edit> </q-td> <q-td key="volume_package" :props="drinks_props"> {{ drinks_props.row.volume ? `${drinks_props.row.volume} L` : 'o.A.' }} <q-popup-edit v-if=" !drinks_props.row.volumes.some((volume) => volume.ingredients.some((ingredient) => ingredient.drink_ingredient) ) " v-model.number="drinks_props.row.volume" buttons label-cancel="Abbrechen" label-set="Speichern" @save="updateDrink(drinks_props.row)" > <q-input v-model.number="drinks_props.row.volume" filled dense autofocus type="number" clearable step="0.01" min="0" suffix="L" /> </q-popup-edit> </q-td> <q-td key="package_size" :props="drinks_props"> {{ drinks_props.row.package_size || 'o.A.' }} <q-popup-edit v-if=" !drinks_props.row.volumes.some((volume) => volume.ingredients.some((ingredient) => ingredient.drink_ingredient) ) " v-model="drinks_props.row.package_size" buttons label-cancel="Abbrechen" label-set="Speichern" @save="updateDrink(drinks_props.row)" > <q-input v-model.number="drinks_props.row.package_size" filled dense autofocus type="number" min="0" /> </q-popup-edit> </q-td> <q-td key="cost_price_package_netto" :props="drinks_props"> {{ drinks_props.row.cost_price_package_netto ? `${drinks_props.row.cost_price_package_netto}€` : 'o.A.' }} <q-popup-edit v-if=" !drinks_props.row.volumes.some((volume) => volume.ingredients.some((ingredient) => ingredient.drink_ingredient) ) " v-model="drinks_props.row.cost_price_package_netto" buttons label-cancel="Abbrechen" label-set="Speichern" @save="updateDrink(drinks_props.row)" > <q-input v-model.number="drinks_props.row.cost_price_package_netto" filled dense autofocus type="number" step="0.01" min="0" suffix="€" @change="console.log(drinks_props.row)" /> </q-popup-edit> </q-td> <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.value.toFixed(3)}€` : 'o.A.' }} <q-popup-edit v-if=" !( !!drinks_props.row.cost_price_package_netto && !!drinks_props.row.volume && !!drinks_props.row.package_size ) && !drinks_props.row.volumes.some((volume) => volume.ingredients.some((ingredient) => ingredient.drink_ingredient) ) " v-model="drinks_props.row.cost_price_pro_volume.value" buttons label-cancel="Abbrechen" label-set="Speichern" @save="updateDrink(drinks_props.row)" > <q-input v-model.number="drinks_props.row.cost_price_pro_volume.value" filled dense autofocus type="number" min="0" step="0.1" suffix="€" /> </q-popup-edit> </q-td> <q-td key="volumes" :props="drinks_props"> <q-table :columns="column_calc" :data="drinks_props.row.volumes" dense :visible-columns="visibleColumn" row-key="id" flat > <template v-slot: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 v-slot:body="props"> <q-tr :props="props"> <q-td auto-width> <q-btn size="sm" color="accent" round dense @click="props.expand = !props.expand" :icon="props.expand ? 'mdi-chevron-up' : 'mdi-chevron-down'" v-if="!drinks_props.row.cost_price_pro_volume.value" /> <q-btn size="xs" color="negative" round dense icon="mdi-delete" class="q-mx-sm" v-if="props.row.ingredients.length === 0 && props.row.prices.length === 0" @click="deleteVolume(props.row, drinks_props.row)" /> </q-td> <q-td key="volume" :props="props"> {{ parseFloat(props.row.volume.value).toFixed(3) }}L <q-popup-edit buttons label-cancel="Abbrechen" label-set="Speichern" v-model="props.row.volume.value" v-if="drinks_props.row.cost_price_pro_volume" @save="updateVolume(props.row, drinks_props.row)" > <q-input dense filled v-model.number="props.row.volume.value" 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"> {{ 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 v-slot:bottom> <div class="full-width row justify-end"> <q-btn color="positive" icon-right="add" label="Abgabe hinzufügen" size="xs" v-if="drinks_props.row.cost_price_pro_volume" > <q-menu anchor="center middle" self="center middle"> <div class="row justify-around q-pa-sm"> <q-input filled dense label="Liter" type="number" v-model.number="newVolume.volume" /> </div> <div class="row justify-between q-pa-sm"> <q-btn label="Abbrechen" @click="cancelAddVolume" v-close-popup /> <q-btn label="Speichern" color="primary" @click="addVolume(drinks_props.row)" v-close-popup /> </div> </q-menu> </q-btn> </div> </template> <template v-slot:no-data> <div class="full-width row justify-end"> <q-btn color="positive" icon-right="add" label="Abgabe hinzufügen" size="xs" v-if="drinks_props.row.cost_price_pro_volume" > <q-menu anchor="center middle" self="center middle"> <div class="row justify-around q-pa-sm"> <q-input filled dense label="Liter" type="number" v-model.number="newVolume.volume" /> </div> <div class="row justify-between q-pa-sm"> <q-btn label="Abbrechen" @click="cancelAddVolume" v-close-popup /> <q-btn label="Speichern" color="primary" @click="addVolume(drinks_props.row)" v-close-popup /> </div> </q-menu> </q-btn> </div> </template> </q-table> </q-td> </q-tr> </template> <template v-slot:body-cell-volumes="volumes"> <q-table :columns="column_calc" :data="volumes.value" dense :visible-columns="visibleColumn" row-key="id" flat > <template v-slot: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 v-slot:body="props"> <q-tr :props="props"> <q-td auto-width> <q-btn size="sm" color="accent" round dense @click=" props.expand = !props.expand; console.log(volumes); " :icon="props.expand ? 'mdi-chevron-up' : 'mdi-chevron-down'" v-if="volumes.row.cost_price_pro_volume == null" /> <q-btn size="xs" color="negative" round dense icon="mdi-delete" class="q-mx-sm" v-if="props.row.ingredients.length === 0 && props.row.prices.length === 0" @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 buttons label-cancel="Abbrechen" label-set="Speichern" v-model="props.row.volume.value" v-if="volumes.row.cost_price_pro_volume" @save="updateVolume(props.row, volumes.row)" > <q-input dense filled v-model.number="props.row.volume.value" 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 v-slot:bottom> <div class="full-width row justify-end"> <q-btn color="positive" icon-right="add" label="Abgabe hinzufügen" size="xs" v-if="volumes.row.cost_price_pro_volume" > <q-menu anchor="center middle" self="center middle"> <div class="row justify-around q-pa-sm"> <q-input filled dense label="Liter" type="number" min="0" step="0.01" suffix="L" v-model.number="newVolume.volume" /> </div> <div class="row justify-between q-pa-sm"> <q-btn label="Abbrechen" @click="cancelAddVolume" v-close-popup /> <q-btn label="Speichern" color="primary" @click="addVolume(volumes.row)" v-close-popup /> </div> </q-menu> </q-btn> </div> </template> <template v-slot:no-data> <div class="full-width row justify-end"> <q-btn color="positive" icon-right="add" label="Abgabe hinzufügen" size="xs" v-if="volumes.row.cost_price_pro_volume" > <q-menu anchor="center middle" self="center middle"> <div class="row justify-around q-pa-sm"> <q-input filled dense label="Liter" type="number" min="0" step="0.01" suffix="L" v-model.number="newVolume.volume" /> </div> <div class="row justify-between q-pa-sm"> <q-btn label="Abbrechen" @click="cancelAddVolume" v-close-popup /> <q-btn label="Speichern" color="primary" @click="addVolume(volumes.row)" v-close-popup /> </div> </q-menu> </q-btn> </div> </template> </q-table> </template> </q-table> </template> <script lang="ts"> import { defineComponent, onBeforeMount, ref, computed } from '@vue/composition-api'; import store, { DrinkPriceVolume, Drink } from '../store/altStore'; import PriceTable from 'src/plugins/pricelist/components/CalculationTable/PriceTable.vue'; import Ingredients from 'src/plugins/pricelist/components/CalculationTable/Ingredients.vue'; export default defineComponent({ name: 'CalculationTable', components: { PriceTable, Ingredients }, setup() { const columns = [ { name: 'name', label: 'Getränkename', field: 'name', }, { name: 'article_id', label: 'Artikelnummer', field: 'article_id', }, { name: 'drink_type', label: 'Kategorie', field: 'type', format: (val: FG.DrinkType) => `${val.name}`, }, { name: 'volume_package', label: 'Inhalt in l des Gebinde', field: 'volume', }, { name: 'package_size', label: 'Gebindegröße', field: 'package_size', }, { name: 'cost_price_package_netto', label: 'Preis Netto/Gebinde', field: 'cost_price_package_netto', format: (val: number | null) => (val ? `${val.toFixed(3)}€` : ''), }, { name: 'cost_price_pro_volume', label: 'Preis mit 19%/Liter', field: 'cost_price_pro_volume', format: (val: number | null) => (val ? `${val.toFixed(3)}€` : ''), }, { name: 'volumes', label: 'Preiskalkulation', field: 'volumes', }, ]; const column_calc = [ { name: 'volume', label: 'Abgabe in l', field: 'volume', format: (val: number) => `${val} L`, }, { name: 'min_prices', label: 'Minimal Preise', field: 'min_prices', }, { name: 'prices', label: 'Preise', field: 'prices', }, ]; const column_prices = [ { name: 'price', label: 'Preis', field: 'price', format: (val: number) => `${val.toFixed(2)}€`, }, { name: 'description', label: 'Beschreibung', field: 'description', }, { name: 'public', label: 'Öffentlich', field: 'public', }, ]; const visibleColumn = ref([ 'name', 'drink_type', 'volume_package', 'cost_price_package_netto', 'package_size', 'cost_price_pro_volume', 'volumes', 'volume', 'min_prices', 'prices', 'price', 'description', 'public', ]); const emptyVolume: DrinkPriceVolume = { id: -1, _volume: 0, volume: null, min_prices: [ { percentage: 100, price: null }, { percentage: 250, price: null }, { percentage: 300, price: null }, ], prices: [], ingredients: [], }; const newVolume = ref<DrinkPriceVolume>(emptyVolume); function addVolume(drink: Drink) { store.actions.setVolume(<DrinkPriceVolume>newVolume.value, drink); cancelAddVolume(); } function cancelAddVolume() { setTimeout(() => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore newVolume.value = emptyVolume; }, 200); } function updateVolume(volume: DrinkPriceVolume, drink: Drink) { console.log(volume); store.actions.updateVolume(volume, drink); } function deleteVolume(volume: FG.DrinkPriceVolume, drink: FG.Drink) { store.actions.deleteVolume(volume, drink); } const pagination = computed(() => { rowsPerPage: store.state.drinks.length; }); const emptyDrink: FG.Drink = { id: -1, article_id: undefined, package_size: undefined, name: '', volume: undefined, cost_price_pro_volume: undefined, cost_price_package_netto: undefined, tags: [], type: undefined, volumes: [], }; const newDrink = ref<FG.Drink>(emptyDrink); const calc_price_pro_volume = computed( () => !!newDrink.value.cost_price_package_netto && !!newDrink.value.volume && !!newDrink.value.package_size ); const cost_price_pro_volume = computed({ get: () => { if (calc_price_pro_volume.value) { const retVal = ((newDrink.value.cost_price_package_netto || 0) / ((newDrink.value.volume || 0) * (newDrink.value.package_size || 0))) * 1.19; newDrink.value.cost_price_pro_volume = Math.round(retVal * 1000) / 1000; } return newDrink.value.cost_price_pro_volume; }, set: (val) => { newDrink.value.cost_price_pro_volume = val; }, }); const drinkTypes = computed(() => store.state.drinkTypes); function addDrink() { store.actions.setDrink(newDrink.value); cancelAddDrink(); } function cancelAddDrink() { setTimeout(() => (newDrink.value = emptyDrink), 200); } function updateDrink(drink: Drink) { store.actions.updateDrink(drink); } function deleteDrink(drink: Drink) { store.actions.deleteDrink(drink); } return { drinks: computed(() => store.state.drinks), pagination, columns, column_calc, column_prices, visibleColumn, addVolume, cancelAddVolume, newVolume, updateVolume, deleteVolume, newDrink, cost_price_pro_volume, calc_price_pro_volume, drinkTypes, addDrink, cancelAddDrink, updateDrink, deleteDrink, console, }; }, }); </script> <style scoped></style>