flaschengeist-frontend/src/plugins/pricelist/components/CalculationTable.vue

561 lines
16 KiB
Vue
Raw Normal View History

2021-03-14 19:37:41 +00:00
<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]"
2021-03-31 12:18:55 +00:00
:filter="search"
:filter-method="filter"
>
<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">
2021-03-31 12:18:55 +00:00
<search-input v-model="search" :keys="[...columns, ...column_calc, ...column_prices]" />
2021-03-21 21:07:12 +00:00
<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">
2021-03-21 22:02:25 +00:00
<q-menu v-model="showNewDrink" anchor="center middle" self="center middle">
<new-drink @close="showNewDrink = false" />
</q-menu>
</q-btn>
2021-03-14 19:37:41 +00:00
<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>
2021-03-25 22:14:49 +00:00
<q-td key="picture" :props="drinks_props" style="min-width: 256px">
2021-03-22 22:18:22 +00:00
<q-img
loading="lazy"
:src="
drinks_props.row.uuid
? `/api/pricelist/picture/${drinks_props.row.uuid}?size=256`
: 'no-image.svg'
"
placeholder-src="no-image.svg"
>
<template #error>
<div class="absolute-full flex flex-center bg-negative text-white">
Cannot load image
</div>
</template>
</q-img>
2021-03-25 22:14:49 +00:00
<q-popup-edit v-model="drinkPic" @update:modelValue="savePicture(drinks_props.row)">
<template #default="scope">
<div class="full-width row">
<q-file v-model="scope.value" filled>
<template #prepend>
<q-icon name="attach_file" />
</template>
</q-file>
<div class="full-width row justify-between">
<q-btn label="Abbrechen" flat color="primary" @click="scope.cancel" />
<q-btn
label="Löschen"
flat
color="primary"
@click="
scope.cancel();
deletePicture(drinks_props.row);
"
/>
<q-btn label="Speichern" flat color="primary" @click="scope.set" />
</div>
</div>
</template>
2021-03-22 22:18:22 +00:00
</q-popup-edit>
</q-td>
<q-td key="name" :props="drinks_props">
{{ drinks_props.row.name }}
<q-popup-edit
2021-03-21 21:07:12 +00:00
v-slot="scope"
v-model="drinks_props.row.name"
buttons
label-cancel="Abbrechen"
label-set="Speichern"
2021-03-21 21:07:12 +00:00
@update:modelValue="updateDrink(drinks_props.row)"
>
2021-03-21 21:07:12 +00:00
<q-input
v-model="scope.value"
filled
dense
autofocus
clearable
@keyup.enter="scope.set"
/>
</q-popup-edit>
</q-td>
2021-03-29 19:29:04 +00:00
<q-td key="article_id" :props="drinks_props">
{{ drinks_props.row.article_id || 'o.A.' }}
<q-popup-edit
2021-03-21 21:07:12 +00:00
v-slot="scope"
2021-03-29 19:29:04 +00:00
v-model="drinks_props.row.article_id"
buttons
label-cancel="Abbrechen"
label-set="Speichern"
2021-03-21 21:07:12 +00:00
@update:modelValue="updateDrink(drinks_props.row)"
>
2021-03-29 19:29:04 +00:00
<q-input
2021-03-21 21:07:12 +00:00
v-model="scope.value"
filled
dense
autofocus
2021-03-29 19:29:04 +00:00
clearable
2021-03-21 21:07:12 +00:00
@keyup.enter="scope.set"
/>
</q-popup-edit>
</q-td>
2021-03-29 19:29:04 +00:00
<q-td key="drink_type" :props="drinks_props">
{{ drinks_props.row.type.name }}
<q-popup-edit
2021-03-21 21:07:12 +00:00
v-slot="scope"
2021-03-29 19:29:04 +00:00
v-model="drinks_props.row.type"
buttons
label-cancel="Abbrechen"
label-set="Speichern"
2021-03-21 21:07:12 +00:00
@update:modelValue="updateDrink(drinks_props.row)"
>
2021-03-29 19:29:04 +00:00
<q-select
2021-03-21 21:07:12 +00:00
v-model="scope.value"
2021-03-29 19:29:04 +00:00
:options="drinkTypes"
option-label="name"
option-value="id"
2021-03-21 21:07:12 +00:00
filled
dense
autofocus
@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)
)
"
2021-03-21 21:07:12 +00:00
v-slot="scope"
v-model.number="drinks_props.row.volume"
buttons
label-cancel="Abbrechen"
label-set="Speichern"
2021-03-21 21:07:12 +00:00
@update:modelValue="updateDrink(drinks_props.row)"
>
<q-input
2021-03-21 21:07:12 +00:00
v-model.number="scope.value"
filled
dense
autofocus
type="number"
clearable
step="0.01"
min="0"
suffix="L"
2021-03-21 21:07:12 +00:00
@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)
)
"
2021-03-21 21:07:12 +00:00
v-slot="scope"
v-model="drinks_props.row.package_size"
buttons
label-cancel="Abbrechen"
label-set="Speichern"
2021-03-21 21:07:12 +00:00
@update:modelValue="updateDrink(drinks_props.row)"
>
<q-input
2021-03-21 21:07:12 +00:00
v-model.number="scope.value"
filled
dense
autofocus
type="number"
min="0"
2021-03-21 21:07:12 +00:00
@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
2021-03-21 21:07:12 +00:00
? `${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)
)
"
2021-03-21 21:07:12 +00:00
v-slot="scope"
v-model="drinks_props.row.cost_price_package_netto"
buttons
label-cancel="Abbrechen"
label-set="Speichern"
2021-03-21 21:07:12 +00:00
@update:modelValue="updateDrink(drinks_props.row)"
>
<q-input
2021-03-21 21:07:12 +00:00
v-model.number="scope.value"
filled
dense
autofocus
type="number"
step="0.01"
min="0"
suffix="€"
2021-03-21 21:07:12 +00:00
@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)
)
"
2021-03-21 21:07:12 +00:00
v-slot="scope"
v-model="drinks_props.row.cost_price_pro_volume"
buttons
label-cancel="Abbrechen"
label-set="Speichern"
2021-03-21 21:07:12 +00:00
@update:modelValue="updateDrink(drinks_props.row)"
>
<q-input
2021-03-21 21:07:12 +00:00
v-model.number="scope.value"
filled
dense
autofocus
type="number"
min="0"
step="0.1"
suffix="€"
2021-03-21 21:07:12 +00:00
@keyup.enter="scope.set"
/>
</q-popup-edit>
</q-td>
<q-td key="volumes" :props="drinks_props">
2021-03-21 21:07:12 +00:00
<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>
2021-03-29 10:50:50 +00:00
<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-popup-edit
2021-03-29 10:50:50 +00:00
v-slot="scope"
v-model="drinks_props.row.receipt"
buttons
label-cancel="Abbrechen"
label-set="Speichern"
@update:modelValue="updateDrink(drinks_props.row)"
>
<q-input
v-model="scope.value"
2021-03-29 19:29:04 +00:00
type="textarea"
2021-03-29 10:50:50 +00:00
autofocus
counter
@keyup.enter.stop
/>
</q-popup-edit>-->
2021-03-29 10:50:50 +00:00
</q-td>
</q-tr>
</template>
</q-table>
2021-03-14 19:37:41 +00:00
</template>
<script lang="ts">
import { defineComponent, onBeforeMount, ComputedRef, computed, ref } from 'vue';
import DrinkPriceVolumesTable from 'src/plugins/pricelist/components/CalculationTable/DrinkPriceVolumesTable.vue';
import { useMainStore } from 'src/stores';
import { Drink, usePricelistStore } from 'src/plugins/pricelist/store';
2021-03-21 21:07:12 +00:00
import MinPriceSetting from 'src/plugins/pricelist/components/MinPriceSetting.vue';
2021-03-21 22:02:25 +00:00
import NewDrink from 'src/plugins/pricelist/components/CalculationTable/NewDrink.vue';
import BuildManual from 'src/plugins/pricelist/components/CalculationTable/BuildManual.vue';
2021-03-31 12:18:55 +00:00
import SearchInput from './SearchInput.vue';
import { filter } from '../utils/filter';
2021-03-22 22:18:22 +00:00
import { Notify } from 'quasar';
2021-03-18 20:10:54 +00:00
function sort(a: string | number, b: string | number) {
if (a > b) return 1;
if (b > a) return -1;
return 0;
}
2021-03-14 19:37:41 +00:00
export default defineComponent({
name: 'CalculationTable',
2021-03-31 12:18:55 +00:00
components: { SearchInput, BuildManual, MinPriceSetting, DrinkPriceVolumesTable, NewDrink },
setup() {
const mainStore = useMainStore();
const store = usePricelistStore();
onBeforeMount(() => {
void store.getDrinkTypes();
void store.getExtraIngredients();
void store.get_min_prices();
store.getPriceCalcColumn(user);
});
const user = mainStore.currentUser.userid;
2021-03-14 19:37:41 +00:00
const columns = [
2021-03-22 22:18:22 +00:00
{
name: 'picture',
label: 'Bild',
},
2021-03-14 19:37:41 +00:00
{
name: 'name',
label: 'Getränkename',
field: 'name',
2021-03-18 20:10:54 +00:00
sortable: true,
sort,
2021-03-14 19:37:41 +00:00
},
{
name: 'article_id',
label: 'Artikelnummer',
field: 'article_id',
2021-03-18 20:10:54 +00:00
sortable: true,
sort,
2021-03-14 19:37:41 +00:00
},
{
name: 'drink_type',
2021-03-14 19:37:41 +00:00
label: 'Kategorie',
field: 'type',
format: (val: FG.DrinkType) => `${val.name}`,
2021-03-18 20:10:54 +00:00
sortable: true,
sort: (a: FG.DrinkType, b: FG.DrinkType) => sort(a.name, b.name),
2021-03-14 19:37:41 +00:00
},
{
name: 'volume_package',
label: 'Inhalt in l des Gebinde',
field: 'volume',
2021-03-18 20:10:54 +00:00
sortable: true,
sort,
2021-03-14 19:37:41 +00:00
},
{
name: 'package_size',
label: 'Gebindegröße',
field: 'package_size',
2021-03-18 20:10:54 +00:00
sortable: true,
sort,
2021-03-14 19:37:41 +00:00
},
{
name: 'cost_price_package_netto',
label: 'Preis Netto/Gebinde',
field: 'cost_price_package_netto',
format: (val: number | null) => (val ? `${val.toFixed(3)}` : ''),
2021-03-18 20:10:54 +00:00
sortable: true,
sort,
2021-03-14 19:37:41 +00:00
},
{
name: 'cost_price_pro_volume',
label: 'Preis mit 19%/Liter',
field: 'cost_price_pro_volume',
format: (val: number | null) => (val ? `${val.toFixed(3)}` : ''),
2021-03-18 20:10:54 +00:00
sortable: true,
sort: (a: ComputedRef, b: ComputedRef) => sort(a.value, b.value),
2021-03-14 19:37:41 +00:00
},
{
name: 'volumes',
2021-03-14 19:37:41 +00:00
label: 'Preiskalkulation',
field: 'volumes',
},
2021-03-29 10:50:50 +00:00
{
name: 'receipt',
label: 'Bauanleitung',
field: 'receipt',
},
2021-03-14 19:37:41 +00:00
];
const column_calc = [
{
name: 'volume',
label: 'Abgabe in l',
field: 'volume',
},
{
name: 'min_prices',
label: 'Minimal Preise',
field: 'min_prices',
2021-03-14 19:37:41 +00:00
},
{
name: 'prices',
label: 'Preise',
field: 'prices',
},
2021-03-14 19:37:41 +00:00
];
const column_prices = [
{
name: 'price',
label: 'Preis',
field: 'price',
format: (val: number) => `${val.toFixed(2)}`,
2021-03-14 19:37:41 +00:00
},
{
name: 'description',
label: 'Beschreibung',
field: 'description',
2021-03-14 19:37:41 +00:00
},
{
name: 'public',
label: 'Öffentlich',
field: 'public',
},
];
const visibleColumn = computed({
get: () => store.pricecalc_columns,
set: (val) => {
store.updatePriceCalcColumn(user, val);
},
});
2021-03-14 19:37:41 +00:00
// eslint-disable-next-line vue/return-in-computed-property
const pagination = computed(() => {
rowsPerPage: store.drinks.length;
});
const drinkTypes = computed(() => store.drinkTypes);
function updateDrink(drink: Drink) {
2021-03-21 21:07:12 +00:00
void store.updateDrink(drink);
}
function deleteDrink(drink: Drink) {
store.deleteDrink(drink);
2021-03-14 19:37:41 +00:00
}
2021-03-21 22:02:25 +00:00
const showNewDrink = ref(false);
2021-03-22 22:18:22 +00:00
const drinkPic = ref<File>();
function onPictureRejected() {
Notify.create({
group: false,
type: 'negative',
message: 'Datei zu groß oder keine gültige Bilddatei.',
timeout: 10000,
progress: true,
actions: [{ icon: 'mdi-close', color: 'white' }],
});
drinkPic.value = undefined;
}
2021-03-22 22:18:22 +00:00
function savePicture(drink: Drink) {
console.log('hier bin ich!!!', drinkPic.value);
2021-03-25 22:14:49 +00:00
if (drinkPic.value && drinkPic.value instanceof File) {
store.upload_drink_picture(drink, drinkPic.value).catch((response: Response) => {
if (response && response.status == 400) {
onPictureRejected();
}
});
2021-03-25 22:14:49 +00:00
}
}
function deletePicture(drink: Drink) {
void store.delete_drink_picture(drink);
2021-03-22 22:18:22 +00:00
}
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);
}
2021-03-31 12:18:55 +00:00
const search = ref({
value: '',
key: '',
label: '',
});
2021-03-14 19:37:41 +00:00
return {
drinks: computed(() => store.drinks),
pagination,
2021-03-14 19:37:41 +00:00
columns,
column_calc,
column_prices,
visibleColumn,
drinkTypes,
updateDrink,
deleteDrink,
2021-03-21 22:02:25 +00:00
showNewDrink,
2021-03-22 22:18:22 +00:00
drinkPic,
savePicture,
2021-03-25 22:14:49 +00:00
deletePicture,
addStep,
deleteStep,
console,
2021-03-31 12:18:55 +00:00
search,
filter,
};
},
2021-03-14 19:37:41 +00:00
});
</script>
<style scoped></style>