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

516 lines
13 KiB
Vue
Raw Normal View History

2021-03-14 19:37:41 +00:00
<template>
<q-table
v-model:pagination="pagination"
2021-04-13 14:20:32 +00:00
title="Preistabelle"
:columns="columns"
:rows="drinks"
dense
2021-03-31 12:18:55 +00:00
:filter="search"
:filter-method="filter"
grid
:rows-per-page-options="[0]"
>
<template #top-right>
<div class="row justify-end q-gutter-sm">
2021-04-02 17:32:08 +00:00
<search-input v-model="search" :keys="search_keys" />
2021-04-15 20:04:33 +00:00
<slot></slot>
2021-04-13 14:20:32 +00:00
<q-btn v-if="!public && !nodetails" label="Aufpreise">
2021-03-21 21:07:12 +00:00
<q-menu anchor="center middle" self="center middle">
<min-price-setting />
</q-menu>
</q-btn>
2021-04-13 14:20:32 +00:00
<q-btn
2021-04-14 20:42:09 +00:00
v-if="!public && !nodetails && editable && hasPermission(PERMISSIONS.CREATE)"
2021-04-13 14:20:32 +00:00
color="primary"
round
icon="mdi-plus"
@click="newDrink"
>
<q-tooltip> Neues Getränk </q-tooltip>
</q-btn>
</div>
</template>
<template #item="props">
<div class="q-pa-xs col-xs-12 col-sm-6 col-md-4">
<q-card>
2021-04-17 11:40:03 +00:00
<q-img style="max-height: 256px" :src="image(props.row.uuid)">
2021-04-13 14:20:32 +00:00
<div
v-if="!public && !nodetails && editable"
class="absolute-top-right justify-end"
style="background-color: transparent"
>
2021-04-09 21:49:49 +00:00
<q-btn
round
icon="mdi-pencil"
style="background-color: rgba(0, 0, 0, 0.5)"
@click="editDrink = props.row"
/>
</div>
<div class="absolute-bottom-right justify-end">
<div class="text-subtitle1 text-right">
{{ props.row.name }}
</div>
<div class="text-caption text-right">
{{ props.row.type.name }}
</div>
</div>
</q-img>
<q-card-section>
<q-badge
v-for="tag in props.row.tags"
:key="`${props.row.id}-${tag.id}`"
class="text-caption"
rounded
:style="`background-color: ${tag.color}`"
>
{{ tag.name }}
</q-badge>
</q-card-section>
2021-04-13 14:20:32 +00:00
<q-card-section v-if="!public && !nodetails">
<div class="fit row">
<q-input
v-if="props.row.article_id"
class="col-xs-12 col-sm-6 q-pa-sm"
:model-value="props.row.article_id"
outlined
readonly
label="Artikelnummer"
dense
/>
<q-input
v-if="props.row.volume"
class="col-xs-12 col-sm-6 q-pa-sm"
:model-value="props.row.volume"
outlined
readonly
label="Inhalt"
dense
suffix="L"
/>
<q-input
v-if="props.row.package_size"
class="col-xs-12 col-sm-6 q-pa-sm"
:model-value="props.row.package_size"
outlined
readonly
label="Gebindegröße"
dense
/>
<q-input
v-if="props.row.cost_per_package"
class="col-xs-12 col-sm-6 q-pa-sm"
:model-value="props.row.cost_per_package"
outlined
readonly
label="Preis Gebinde"
suffix="€"
dense
/>
<q-input
v-if="props.row.cost_per_volume"
class="col-xs-12 col-sm-6 q-pa-sm q-pb-lg"
:model-value="props.row.cost_per_volume"
outlined
readonly
label="Preis pro L"
hint="Inkl. 19% Mehrwertsteuer"
suffix="€"
dense
/>
</div>
</q-card-section>
<q-card-section v-if="props.row.volumes.length > 0 && notLoading">
<drink-price-volumes
:model-value="props.row.volumes"
:public="public"
:nodetails="nodetails"
/>
</q-card-section>
</q-card>
</div>
</template>
</q-table>
2021-04-09 21:49:49 +00:00
<q-dialog :model-value="editDrink !== undefined" persistent>
<drink-modify
:drink="editDrink"
@save="editing_drink"
@cancel="editDrink = undefined"
@delete="deleteDrink"
/>
2021-04-09 21:49:49 +00:00
</q-dialog>
2021-03-14 19:37:41 +00:00
</template>
<script lang="ts">
import { defineComponent, onBeforeMount, ComputedRef, computed, ref } from 'vue';
import { Drink, usePricelistStore, DrinkPriceVolume } from 'src/plugins/pricelist/store';
2021-03-21 21:07:12 +00:00
import MinPriceSetting from 'src/plugins/pricelist/components/MinPriceSetting.vue';
2021-03-31 12:18:55 +00:00
import SearchInput from './SearchInput.vue';
import DrinkPriceVolumes from 'src/plugins/pricelist/components/CalculationTable/DrinkPriceVolumes.vue';
2021-04-09 21:49:49 +00:00
import DrinkModify from './DrinkModify.vue';
2021-04-01 09:15:21 +00:00
import { filter, Search } from '../utils/filter';
2021-03-22 22:18:22 +00:00
import { Notify } from 'quasar';
2021-04-01 09:15:21 +00:00
import { sort } from '../utils/sort';
import { DeleteObjects } from 'src/plugins/pricelist/utils/utils';
2021-04-14 20:42:09 +00:00
import { hasPermission } from 'src/utils/permission';
import { PERMISSIONS } from 'src/plugins/pricelist/permissions';
import { baseURL } from 'src/config';
2021-03-14 19:37:41 +00:00
export default defineComponent({
name: 'CalculationTable',
components: {
SearchInput,
MinPriceSetting,
DrinkPriceVolumes,
2021-04-09 21:49:49 +00:00
DrinkModify,
},
2021-04-13 14:20:32 +00:00
props: {
public: {
type: Boolean,
default: false,
},
editable: {
type: Boolean,
default: false,
},
nodetails: {
type: Boolean,
default: false,
},
},
setup(props) {
const store = usePricelistStore();
onBeforeMount(() => {
2021-04-13 14:20:32 +00:00
void store.getDrinks();
});
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',
2021-04-02 17:32:08 +00:00
label: 'Name',
field: 'name',
2021-03-18 20:10:54 +00:00
sortable: true,
sort,
2021-04-02 17:32:08 +00:00
filterable: true,
2021-04-13 14:20:32 +00:00
public: true,
2021-03-14 19:37:41 +00:00
},
2021-04-01 20:39:54 +00:00
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-04-02 17:32:08 +00:00
filterable: true,
2021-04-13 14:20:32 +00:00
public: true,
2021-03-14 19:37:41 +00:00
},
2021-04-01 20:39:54 +00:00
{
name: 'tags',
label: 'Tags',
field: 'tags',
format: (val: Array<FG.Tag>) => {
let retVal = '';
val.forEach((tag, index) => {
if (index > 0) {
retVal += ', ';
}
retVal += tag.name;
});
return retVal;
},
2021-04-02 17:32:08 +00:00
filterable: true,
2021-04-13 14:20:32 +00:00
public: true,
2021-04-01 20:39:54 +00:00
},
{
name: 'article_id',
label: 'Artikelnummer',
field: 'article_id',
sortable: true,
sort,
2021-04-02 17:32:08 +00:00
filterable: true,
2021-04-13 14:20:32 +00:00
public: false,
2021-04-01 20:39:54 +00:00
},
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-04-13 14:20:32 +00:00
public: false,
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,
public: false,
2021-03-14 19:37:41 +00:00
},
{
name: 'cost_per_package',
2021-03-14 19:37:41 +00:00
label: 'Preis Netto/Gebinde',
field: 'cost_per_package',
format: (val: number | null) => (val ? `${val.toFixed(3)}` : ''),
2021-03-18 20:10:54 +00:00
sortable: true,
sort,
2021-04-13 14:20:32 +00:00
public: false,
2021-03-14 19:37:41 +00:00
},
{
name: 'cost_per_volume',
2021-03-14 19:37:41 +00:00
label: 'Preis mit 19%/Liter',
field: 'cost_per_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',
format: (val: Array<DrinkPriceVolume>) => {
let retVal = '';
val.forEach((val, index) => {
if (index > 0) {
retVal += ', ';
}
retVal += val.id;
});
return retVal;
},
sortable: false,
},
2021-03-29 10:50:50 +00:00
{
name: 'receipt',
label: 'Bauanleitung',
field: 'receipt',
format: (val: Array<string>) => {
let retVal = '';
val.forEach((value, index) => {
if (index > 0) {
retVal += ', ';
}
retVal += value;
});
return retVal;
},
2021-04-02 17:32:08 +00:00
filterable: true,
sortable: false,
public: false,
2021-03-29 10:50:50 +00:00
},
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',
},
];
2021-03-14 19:37:41 +00:00
const search_keys = computed(() =>
columns.filter(
(column) => column.filterable && (props.public || props.nodetails ? column.public : true)
)
);
2021-04-02 17:32:08 +00:00
const pagination = ref({
sortBy: 'name',
descending: false,
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() {
if (editDrink.value) {
store.deleteDrink(editDrink.value);
}
editDrink.value = undefined;
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;
}
async function savePicture(drinkPic: File) {
if (editDrink.value) {
await store.upload_drink_picture(editDrink.value, drinkPic).catch((response: Response) => {
if (response && response.status == 400) {
onPictureRejected();
}
});
2021-03-25 22:14:49 +00:00
}
}
async function deletePicture() {
if (editDrink.value) {
await store.delete_drink_picture(editDrink.value);
}
2021-03-22 22:18:22 +00:00
}
2021-04-01 09:15:21 +00:00
const search = ref<Search>({
2021-03-31 12:18:55 +00:00
value: '',
key: '',
label: '',
});
const emptyDrink: Drink = {
id: -1,
article_id: undefined,
package_size: undefined,
name: '',
volume: undefined,
cost_per_volume: undefined,
cost_per_package: undefined,
tags: [],
type: undefined,
volumes: [],
uuid: '',
};
function newDrink() {
editDrink.value = Object.assign({}, emptyDrink);
}
const editDrink = ref<Drink>();
async function editing_drink(
drink: Drink,
toDeleteObjects: DeleteObjects,
drinkPic: File | undefined,
deletePic: boolean
) {
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);
if (drink.id > 0) {
await store.updateDrink(drink);
} else {
const _drink = await store.setDrink(drink);
if (editDrink.value) {
editDrink.value.id = _drink.id;
}
}
if (deletePic) {
await deletePicture();
}
if (drinkPic instanceof File) {
await savePicture(drinkPic);
}
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);
const imageloading = ref<Array<{ id: number; loading: boolean }>>([]);
function getImageLoading(id: number) {
const loading = imageloading.value.find((a) => a.id === id);
if (loading) {
return loading.loading;
}
return false;
}
function image(uuid: string | undefined) {
if (uuid) {
2021-04-17 11:40:03 +00:00
console.log(baseURL.value);
return `${baseURL.value}/pricelist/picture/${uuid}?size=256`;
}
return 'no-image.svg';
}
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,
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,
console,
2021-03-31 12:18:55 +00:00
search,
filter,
2021-04-02 17:32:08 +00:00
search_keys,
2021-04-01 20:39:54 +00:00
tags: computed(() => store.tags),
2021-04-09 21:49:49 +00:00
editDrink,
editing_drink,
get_volumes,
notLoading,
getImageLoading,
newDrink,
2021-04-14 20:42:09 +00:00
hasPermission,
PERMISSIONS,
2021-04-17 11:40:03 +00:00
image,
};
},
2021-03-14 19:37:41 +00:00
});
</script>
<style scoped></style>