<template> <q-table v-model:pagination="pagination" title="Kalkulationstabelle" :columns="columns" :rows="drinks" :visible-columns="visibleColumn" :dense="$q.screen.lt.md" row-key="id" virtual-scroll :rows-per-page-options="[0]" > <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 #top-right> <div class="row justify-end q-gutter-sm"> <q-btn label="Aufpreise"> <q-menu anchor="center middle" self="center middle"> <min-price-setting /> </q-menu> </q-btn> <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 v-model="newDrink.name" class="col-sm-4 col-xs-6 q-pa-sm" filled label="Getränkname" /> <q-input v-model="newDrink.article_id" class="col-sm-4 col-xs-6 q-pa-sm" filled label="Artikelnummer" /> <q-select v-model="newDrink.type" class="col-sm-4 col-xs-6 q-pa-sm" filled label="Kategorie" :options="drinkTypes" option-label="name" /> <q-input v-model.number="newDrink.volume" class="col-sm-4 col-xs-6 q-pa-sm" filled label="Inhalt in L/Gebinde" type="number" /> <q-input v-model.number="newDrink.package_size" class="col-sm-4 col-xs-6 q-pa-sm" filled label="Gebindegröße" type="number" /> <q-input v-model.number="newDrink.cost_price_package_netto" class="col-sm-4 col-xs-6 q-pa-sm" filled label="Preis Netto/Gebinde" type="number" /> <q-input v-model="cost_price_pro_volume" class="col-sm-4 col-xs-6 q-pa-sm" filled label="Preis mit 19%/Liter" :disable="calc_price_pro_volume" /> </div> <div class="row justify-between"> <q-btn v-close-popup label="Abbrechen" @click="cancelAddDrink" /> <q-btn v-close-popup label="Speichern" 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 #body="drinks_props"> <q-tr :props="drinks_props"> <q-td auto-width> <q-btn v-if="drinks_props.row.volumes.length === 0" size="xs" color="negative" round dense icon="mdi-delete" class="q-mx-sm" @click="deleteDrink(drinks_props.row)" /> </q-td> <q-td key="name" :props="drinks_props"> {{ drinks_props.row.name }} <q-popup-edit v-slot="scope" v-model="drinks_props.row.name" buttons label-cancel="Abbrechen" label-set="Speichern" @update:modelValue="updateDrink(drinks_props.row)" > <q-input v-model="scope.value" filled dense autofocus clearable @keyup.enter="scope.set" /> </q-popup-edit> </q-td> <q-td key="drink_type" :props="drinks_props"> {{ drinks_props.row.type.name }} <q-popup-edit v-slot="scope" v-model="drinks_props.row.type" buttons label-cancel="Abbrechen" label-set="Speichern" @update:modelValue="updateDrink(drinks_props.row)" > <q-select v-model="scope.value" :options="drinkTypes" option-label="name" filled dense autofocus @keyup.enter="scope.set" /> </q-popup-edit> </q-td> <q-td key="article_id" :props="drinks_props"> {{ drinks_props.row.article_id || 'o.A.' }} <q-popup-edit v-slot="scope" v-model="drinks_props.row.article_id" buttons label-cancel="Abbrechen" label-set="Speichern" @update:modelValue="updateDrink(drinks_props.row)" > <q-input v-model="scope.value" filled dense autofocus clearable @keyup.enter="scope.set" /> </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-slot="scope" v-model.number="drinks_props.row.volume" buttons label-cancel="Abbrechen" label-set="Speichern" @update:modelValue="updateDrink(drinks_props.row)" > <q-input v-model.number="scope.value" filled dense autofocus type="number" clearable step="0.01" min="0" suffix="L" @keyup.enter="scope.set" /> </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-slot="scope" v-model="drinks_props.row.package_size" buttons label-cancel="Abbrechen" label-set="Speichern" @update:modelValue="updateDrink(drinks_props.row)" > <q-input v-model.number="scope.value" filled dense autofocus type="number" min="0" @keyup.enter="scope.set" /> </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.toFixed(2)}€` : 'o.A.' }} <q-popup-edit v-if=" !drinks_props.row.volumes.some((volume) => volume.ingredients.some((ingredient) => ingredient.drink_ingredient) ) " v-slot="scope" v-model="drinks_props.row.cost_price_package_netto" buttons label-cancel="Abbrechen" label-set="Speichern" @update:modelValue="updateDrink(drinks_props.row)" > <q-input v-model.number="scope.value" filled dense autofocus type="number" step="0.01" min="0" suffix="€" @keyup.enter="scope.set" /> </q-popup-edit> </q-td> <q-td key="cost_price_pro_volume" :props="drinks_props"> {{ drinks_props.row.cost_price_pro_volume ? `${drinks_props.row.cost_price_pro_volume.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-slot="scope" v-model="drinks_props.row.cost_price_pro_volume" buttons label-cancel="Abbrechen" label-set="Speichern" @update:modelValue="updateDrink(drinks_props.row)" > <q-input v-model.number="scope.value" filled dense autofocus type="number" min="0" step="0.1" suffix="€" @keyup.enter="scope.set" /> </q-popup-edit> </q-td> <q-td key="volumes" :props="drinks_props"> <drink-price-volumes-table :rows="drinks_props.row.volumes" :visible-columns="visibleColumn" :columns="column_calc" :drink="drinks_props.row" @updateDrink="updateDrink(drinks_props.row)" /> </q-td> </q-tr> </template> </q-table> </template> <script lang="ts"> import { defineComponent, onBeforeMount, ref, ComputedRef, computed } from 'vue'; import DrinkPriceVolumesTable from 'src/plugins/pricelist/components/CalculationTable/DrinkPriceVolumesTable.vue'; import { useMainStore } from 'src/store'; import { Drink, usePricelistStore } from 'src/plugins/pricelist/store'; import MinPriceSetting from 'src/plugins/pricelist/components/MinPriceSetting.vue'; function sort(a: string | number, b: string | number) { if (a > b) return 1; if (b > a) return -1; return 0; } export default defineComponent({ name: 'CalculationTable', components: { MinPriceSetting, DrinkPriceVolumesTable }, setup() { const mainStore = useMainStore(); const store = usePricelistStore(); onBeforeMount(() => { store.getPriceCalcColumn(user); }); const user = mainStore.currentUser.userid; const columns = [ { name: 'name', label: 'Getränkename', field: 'name', sortable: true, sort, }, { name: 'article_id', label: 'Artikelnummer', field: 'article_id', sortable: true, sort, }, { name: 'drink_type', label: 'Kategorie', field: 'type', format: (val: FG.DrinkType) => `${val.name}`, sortable: true, sort: (a: FG.DrinkType, b: FG.DrinkType) => sort(a.name, b.name), }, { name: 'volume_package', label: 'Inhalt in l des Gebinde', field: 'volume', sortable: true, sort, }, { name: 'package_size', label: 'Gebindegröße', field: 'package_size', sortable: true, sort, }, { name: 'cost_price_package_netto', label: 'Preis Netto/Gebinde', field: 'cost_price_package_netto', format: (val: number | null) => (val ? `${val.toFixed(3)}€` : ''), sortable: true, sort, }, { name: 'cost_price_pro_volume', label: 'Preis mit 19%/Liter', field: 'cost_price_pro_volume', format: (val: number | null) => (val ? `${val.toFixed(3)}€` : ''), sortable: true, sort: (a: ComputedRef, b: ComputedRef) => sort(a.value, b.value), }, { name: 'volumes', label: 'Preiskalkulation', field: 'volumes', }, ]; const column_calc = [ { name: 'volume', label: 'Abgabe in l', field: 'volume', }, { 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 = computed({ get: () => store.pricecalc_columns, set: (val) => { store.updatePriceCalcColumn(user, val); }, }); // eslint-disable-next-line vue/return-in-computed-property const pagination = computed(() => { rowsPerPage: store.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; // eslint-disable-next-line vue/no-side-effects-in-computed-properties 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.drinkTypes); function addDrink() { void store.setDrink(newDrink.value); cancelAddDrink(); } function cancelAddDrink() { setTimeout(() => (newDrink.value = emptyDrink), 200); } function updateDrink(drink: Drink) { void store.updateDrink(drink); } function deleteDrink(drink: Drink) { store.deleteDrink(drink); } return { drinks: computed(() => store.drinks), pagination, columns, column_calc, column_prices, visibleColumn, newDrink, cost_price_pro_volume, calc_price_pro_volume, drinkTypes, addDrink, cancelAddDrink, updateDrink, deleteDrink, console, }; }, }); </script> <style scoped></style>