[pricelist] fixed prices. first steps for volume

This commit is contained in:
Tim Gröger 2021-03-15 23:52:40 +01:00
parent f6951bdf0b
commit e4851bd178
3 changed files with 338 additions and 193 deletions

View File

@ -48,120 +48,53 @@
color="accent"
round
dense
@click="props.expand = !props.expand"
@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-td>
<q-td v-for="col in props.cols" :key="col.name" :props="props">
<div v-if="col.name == 'min_prices'">
<div
v-for="(min_price, index) in col.value"
:key="col.name + 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) }}
</div>
<q-td key="volume" :props="props">
{{ props.row.volume }}L
<q-popup-edit
buttons
label-cancel="Abbrechen"
label-set="Speichern"
v-model="props.row.volume"
@save="updateVolume(props.row, volumes.row)"
>
<q-input
dense
filled
v-model.number="props.row.volume"
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) }}
</div>
</div>
<div v-else-if="col.name == 'prices'">
<q-table
dense
hide-header
:columns="column_prices"
:data="col.value"
:visible-columns="visibleColumn"
flat
>
<template v-slot:body="prices_props">
<q-tr :props="prices_props">
<q-td
v-for="col in prices_props.cols"
:key="col.name"
:props="prices_props"
>
<div v-if="col.name == 'public'" class="full-width">
<q-toggle
v-model="col.value"
dense
@input="updatePrice(prices_props.row, props.row)"
/>
</div>
<div v-else class="full-width">
{{ col.value }}
</div>
</q-td>
<q-td>
<q-btn
color="negative"
padding="xs"
round
size="xs"
icon="mdi-delete"
@click="deletePrice(prices_props.row, props.row)"
/>
</q-td>
</q-tr>
</template>
<template v-slot: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"
/>
<q-input
v-model="newPrice.description"
dense
filled
class="q-px-sm"
label="Beschreibung"
/>
<q-toggle
v-model="newPrice.public"
dense
class="q-px-sm"
label="Öffentlich"
/>
</div>
<div class="row justify-between q-pa-sm">
<q-btn label="Abbrechen" @click="cancelAddPrice" v-close-popup />
<q-btn
label="Speichern"
color="primary"
@click="addPrice(props.row)"
v-close-popup
/>
</div>
</q-menu>
</q-btn>
</div>
</template>
<template v-slot: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"
@click="addPrice(col.value)"
/>
</div>
</template>
</q-table>
</div>
<div v-else>
{{ col.value }}
</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">
@ -221,8 +154,23 @@
icon-right="add"
label="Abgabe hinzufügen"
size="xs"
@click="addVolume(volumes.value)"
/>
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 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(volumes.row)"
v-close-popup
/>
</div>
</q-menu>
</q-btn>
</div>
</template>
</q-table>
@ -235,8 +183,10 @@
import { defineComponent, onBeforeMount, ref, computed } from '@vue/composition-api';
import store, { calc_min_prices } from '../store/altStore';
import { v4 } from 'uuid';
import PriceTable from 'src/plugins/pricelist/components/CalculationTable/PriceTable.vue';
export default defineComponent({
name: 'CalculationTable',
components: { PriceTable },
setup(_, { root }) {
onBeforeMount(() => {
store.actions.getDrinks();
@ -246,82 +196,82 @@ export default defineComponent({
{
name: 'name',
label: 'Getränkename',
field: 'name',
field: 'name'
},
{
name: 'article_id',
label: 'Artikelnummer',
field: 'article_id',
field: 'article_id'
},
{
name: 'drink_type',
label: 'Kategorie',
field: 'type',
format: (val: FG.DrinkType) => `${val.name}`,
format: (val: FG.DrinkType) => `${val.name}`
},
{
name: 'volume_package',
label: 'Inhalt in l des Gebinde',
field: 'volume',
field: 'volume'
},
{
name: 'package_size',
label: 'Gebindegröße',
field: 'package_size',
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)}` : ''),
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)}` : ''),
format: (val: number | null) => (val ? `${val.toFixed(3)}` : '')
},
{
name: 'volumes',
label: 'Preiskalkulation',
field: 'volumes',
},
field: 'volumes'
}
];
const column_calc = [
{
name: 'volume',
label: 'Abgabe in l',
field: 'volume',
format: (val: number) => `${val} L`,
format: (val: number) => `${val} L`
},
{
name: 'min_prices',
label: 'Minimal Preise',
field: 'min_prices',
field: 'min_prices'
},
{
name: 'prices',
label: 'Preise',
field: 'prices',
},
field: 'prices'
}
];
const column_prices = [
{
name: 'price',
label: 'Preis',
field: 'price',
format: (val: number) => `${val.toFixed(2)}`,
format: (val: number) => `${val.toFixed(2)}`
},
{
name: 'description',
label: 'Beschreibung',
field: 'description',
field: 'description'
},
{
name: 'public',
label: 'Öffentlich',
field: 'public',
},
field: 'public'
}
];
const visibleColumn = ref([
'name',
@ -333,56 +283,32 @@ export default defineComponent({
'prices',
'price',
'description',
'public',
'public'
]);
function deletePrice(row: FG.DrinkPrice) {
console.log(row);
}
const emptyPrice = {
price: 0,
description: '',
public: true,
const emptyVolume = {
id: -1,
volume: 0,
min_prices: [
{ percentage: 100, price: 0 },
{ percentage: 250, price: 0 },
{ percentage: 300, price: 0 }
],
prices: [],
ingredients: []
};
const newPrice = ref(emptyPrice);
function addPrice(volume: FG.DrinkPriceVolume) {
store.actions.setPrice({ ...newPrice.value }, volume);
cancelAddPrice();
const newVolume = ref(emptyVolume);
function addVolume(drink: FG.Drink) {
store.actions.setVolume(newVolume.value, drink);
cancelAddVolume();
}
function cancelAddPrice() {
function cancelAddVolume() {
setTimeout(() => {
addPrice.value = emptyPrice;
newVolume.value = emptyVolume;
}, 200);
}
function updatePrice(price: FG.DrinkPrice, volume: FG.DrinkPriceVolume) {
store.actions.updatePrice(price, volume);
}
function deletePrice(price: FG.DrinkPrice, volume: FG.DrinkPriceVolume) {
console.log(price, volume);
store.actions.deletePrice(price, volume);
}
function addVolume(table: FG.DrinkPriceVolume[]) {
table.push({
id: v4(),
volume: null,
min_prices: [
{
percentage: 100,
price: 0,
},
{
percentage: 250,
price: 0,
},
{
percentage: 300,
price: 0,
},
],
prices: [],
ingredients: [],
});
function updateVolume(volume: FG.DrinkPriceVolume, drink: FG.Drink) {
console.log(volume);
store.actions.updateVolume(volume, drink);
}
function addIngredient(ingredients: FG.Ingredient[]) {
@ -390,23 +316,20 @@ export default defineComponent({
}
return {
drinks: computed({ get: () => store.state.drinks, set: (val) => console.log(val) }),
drinks: computed(() => store.state.drinks),
columns,
column_calc,
column_prices,
visibleColumn,
deletePrice,
newPrice,
addPrice,
updatePrice,
deletePrice,
cancelAddPrice,
addVolume,
cancelAddVolume,
newVolume,
updateVolume,
addIngredient,
calc_min_prices,
console,
console
};
},
}
});
</script>

View File

@ -0,0 +1,199 @@
<template>
<q-table
style="max-height: 130px"
dense
hide-header
:columns="columns"
:data="data"
:visible-columns="visibleColumns"
flat
virtual-scroll
:pagination.sync="pagination"
:rows-per-page-options="[0]"
>
<template v-slot: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-model="prices_props.row.price"
buttons
label-cancel="Abbrechen"
label-set="Speichern"
@save="updatePrice(prices_props.row, row)"
>
<q-input
v-model.number="prices_props.row.price"
type="number"
label="Preis"
dense
filled
autofocus
suffix="€"
/> </q-popup-edit
></q-td>
<q-td key="description" :props="prices_props">
{{ prices_props.row.description }}
<q-popup-edit
v-model="prices_props.row.description"
buttons
label="Beschreibung"
label-cancel="Abbrechen"
label-set="Speichern"
@save="updatePrice(prices_props.row, row)"
>
<q-input v-model="prices_props.row.description" dense autofocus filled />
</q-popup-edit>
</q-td>
<q-td key="public" :props="prices_props">
<q-toggle
v-model="prices_props.row.public"
dense
@input="updatePrice(prices_props.row, row)"
/>
</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 v-slot: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="€"
/>
<q-input
v-model="newPrice.description"
dense
filled
class="q-px-sm"
label="Beschreibung"
/>
<q-toggle v-model="newPrice.public" dense class="q-px-sm" label="Öffentlich" />
</div>
<div class="row justify-between q-pa-sm">
<q-btn label="Abbrechen" @click="cancelAddPrice" v-close-popup />
<q-btn label="Speichern" color="primary" @click="addPrice(row)" v-close-popup />
</div>
</q-menu>
</q-btn>
</div>
</template>
<template v-slot: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="€"
/>
<q-input
v-model="newPrice.description"
dense
filled
class="q-px-sm"
label="Beschreibung"
/>
<q-toggle v-model="newPrice.public" dense class="q-px-sm" label="Öffentlich" />
</div>
<div class="row justify-between q-pa-sm">
<q-btn label="Abbrechen" @click="cancelAddPrice" v-close-popup />
<q-btn label="Speichern" color="primary" @click="addPrice(row)" v-close-popup />
</div>
</q-menu>
</q-btn>
</div>
</template>
</q-table>
</template>
<script lang="ts">
import { computed, defineComponent, ref } from '@vue/composition-api';
import store from '../../store/altStore';
export default defineComponent({
name: 'PriceTable',
props: {
columns: {
type: Array,
required: true
},
data: {
type: Array,
required: true
},
row: {
type: Object as () => FG.DrinkPriceVolume,
required: true
},
visibleColumns: {
type: Array,
required: true
}
},
setup(props) {
const emptyPrice = {
id: -1,
price: 0,
description: '',
public: true
};
const newPrice = ref(emptyPrice);
function addPrice(volume: FG.DrinkPriceVolume) {
store.actions.setPrice({ ...newPrice.value }, volume);
cancelAddPrice();
}
function cancelAddPrice() {
setTimeout(() => {
newPrice.value = emptyPrice;
}, 200);
}
function updatePrice(price: FG.DrinkPrice, volume: FG.DrinkPriceVolume) {
console.log(price);
store.actions.updatePrice(price, volume);
}
function deletePrice(price: FG.DrinkPrice, volume: FG.DrinkPriceVolume) {
console.log(price, volume);
store.actions.deletePrice(price, volume);
}
const pagination = computed(() => {
rowsPerPage: props.data.length;
});
return {
newPrice,
addPrice,
cancelAddPrice,
updatePrice,
deletePrice,
pagination,
console
};
}
});
</script>
<style scoped></style>

View File

@ -6,7 +6,7 @@ import ExtraIngredient = FG.ExtraIngredient;
const state = reactive<{ drinks: FG.Drink[]; tags: FG.Tag[]; drinkTypes: FG.DrinkType[] }>({
drinks: [],
tags: [],
drinkTypes: [],
drinkTypes: []
});
const actions = {
@ -21,7 +21,7 @@ const actions = {
volume.min_prices = [
{ percentage: 100, price: (drink.cost_price_pro_volume || 0) * volume.volume * 1 },
{ percentage: 250, price: (drink.cost_price_pro_volume || 0) * volume.volume * 2.5 },
{ percentage: 300, price: (drink.cost_price_pro_volume || 0) * volume.volume * 3 },
{ percentage: 300, price: (drink.cost_price_pro_volume || 0) * volume.volume * 3 }
];
if (volume.ingredients.length > 0) {
calc_min_prices(volume);
@ -30,16 +30,16 @@ const actions = {
});
});
})
.catch((err) => console.warn(err));
.catch(err => console.warn(err));
},
setPrice(price: FG.DrinkPrice, volume: FG.DrinkPriceVolume) {
axios
.post(`pricelist/prices/volumes/${volume.id}`, price)
.post(`pricelist/volumes/${volume.id}/prices`, price)
.then((response: AxiosResponse<FG.DrinkPrice>) => {
volume.prices.push(response.data);
this.sortPrices(volume);
})
.catch((err) => console.warn(err));
.catch(err => console.warn(err));
},
sortPrices(volume: FG.DrinkPriceVolume) {
volume.prices.sort((a, b) => {
@ -52,29 +52,52 @@ const actions = {
axios
.delete(`pricelist/prices/${price.id}`)
.then(() => {
const index = volume.prices.findIndex((a) => a.id == price.id);
const index = volume.prices.findIndex(a => a.id == price.id);
if (index > -1) {
volume.prices.splice(index, 1);
}
})
.catch((err) => console.warn(err));
.catch(err => console.warn(err));
},
updatePrice(price: FG.DrinkPrice, volume: FG.DrinkPriceVolume) {
axios
.put(`pricelist/prices/${price.id}`, price)
.then((response: AxiosResponse<FG.DrinkPrice>) => {
const index = volume.prices.findIndex((a) => a.id === price.id);
const index = volume.prices.findIndex(a => a.id === price.id);
if (index > -1) {
volume.prices[index] = response.data;
//volume.prices[index] = response.data;
this.sortPrices(volume);
}
})
.catch((err) => console.log(err));
.catch(err => console.log(err));
},
setVolume(volume: FG.DrinkPriceVolume, drink: FG.Drink) {
axios
.post(`pricelist/drinks/${drink.id}/volumes`, volume)
.then((response: AxiosResponse<FG.DrinkPriceVolume>) => {
drink.volumes.push(response.data);
})
.catch(err => console.warn(err));
},
updateVolume(volume: FG.DrinkPriceVolume, drink: FG.Drink) {
axios
.put(`pricelist/volumes/${volume.id}`, volume)
.then(() => {
//calc_min_prices_by_drink(volume, drink);
})
.catch(err => console.warn(err));
}
};
const getters = {};
function calc_min_prices_by_drink(volume: FG.DrinkPriceVolume, drink: FG.Drink) {
volume.min_prices.forEach(min_price => {
min_price.price =
((drink.cost_price_pro_volume || 0) * volume.volume * min_price.percentage) / 100;
});
}
function calc_min_prices(row: FG.DrinkPriceVolume) {
row.volume = 0;
let cost_price = 0;
@ -87,7 +110,7 @@ function calc_min_prices(row: FG.DrinkPriceVolume) {
cost_price += ingredient.price;
}
});
row.min_prices.forEach((min_price) => {
row.min_prices.forEach(min_price => {
min_price.price = (cost_price * min_price.percentage) / 100;
});
}
@ -96,5 +119,5 @@ export { calc_min_prices };
export default {
state,
actions,
getters,
getters
};