[pricelist] fixed some computed values, new interfaces

This commit is contained in:
Tim Gröger 2021-03-16 18:10:37 +01:00
parent e4851bd178
commit 7e01ffc507
4 changed files with 197 additions and 66 deletions

View File

@ -61,11 +61,13 @@ declare namespace FG {
} }
interface DrinkMinPrice { interface DrinkMinPrice {
percentage: number; percentage: number;
//price: ComputedRef<number>;
price: number; price: number;
} }
interface DrinkPriceVolume { interface DrinkPriceVolume {
id: number; id: number;
volume: number; volume: number;
//computed_volume: ComputedRef<number>;
min_prices: DrinkMinPrice[]; min_prices: DrinkMinPrice[];
prices: Array<DrinkPrice>; prices: Array<DrinkPrice>;
ingredients: Array<DrinkIngredient & ExtraIngredient>; ingredients: Array<DrinkIngredient & ExtraIngredient>;

View File

@ -55,20 +55,32 @@
:icon="props.expand ? 'mdi-chevron-up' : 'mdi-chevron-down'" :icon="props.expand ? 'mdi-chevron-up' : 'mdi-chevron-down'"
v-if="volumes.row.cost_price_pro_volume == null" 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>
<q-td key="volume" :props="props"> <q-td key="volume" :props="props">
{{ props.row.volume }}L {{ parseFloat(props.row.volume.value).toFixed(3) }}L
<!--{{ props.row.volume }}-->
<q-popup-edit <q-popup-edit
buttons buttons
label-cancel="Abbrechen" label-cancel="Abbrechen"
label-set="Speichern" label-set="Speichern"
v-model="props.row.volume" v-model="props.row.volume.value"
v-if="volumes.row.cost_price_pro_volume"
@save="updateVolume(props.row, volumes.row)" @save="updateVolume(props.row, volumes.row)"
> >
<q-input <q-input
dense dense
filled filled
v-model.number="props.row.volume" v-model.number="props.row.volume.value"
type="number" type="number"
suffix="L" suffix="L"
/> />
@ -84,7 +96,8 @@
<q-badge color="primary">{{ min_price.percentage }}%</q-badge> <q-badge color="primary">{{ min_price.percentage }}%</q-badge>
</div> </div>
<div class="col" style="text-align: end"> <div class="col" style="text-align: end">
{{ parseFloat(min_price.price).toFixed(3) }} <!--{{ parseFloat(min_price.price).toFixed(3) }}-->
{{ min_price.price.value.toFixed(3) }}
</div> </div>
</div> </div>
</q-td> </q-td>
@ -116,7 +129,6 @@
:options="drinks" :options="drinks"
option-label="name" option-label="name"
v-model="ingredient.drink_ingredient" v-model="ingredient.drink_ingredient"
@input="calc_min_prices(props.row)"
/> />
<q-input <q-input
class="col q-px-sm" class="col q-px-sm"
@ -125,7 +137,6 @@
filled filled
dense dense
v-model.number="ingredient.volume" v-model.number="ingredient.volume"
@change="calc_min_prices(props.row)"
step="0.01" step="0.01"
min="0" min="0"
/> />
@ -141,7 +152,6 @@
color="positive" color="positive"
label="Zutat hinzufügen" label="Zutat hinzufügen"
@click="addIngredient(props.row.ingredients, props.row.id)" @click="addIngredient(props.row.ingredients, props.row.id)"
@change="calc_min_prices(props.row)"
/> />
</div> </div>
</q-td> </q-td>
@ -180,9 +190,15 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, onBeforeMount, ref, computed } from '@vue/composition-api'; import {
import store, { calc_min_prices } from '../store/altStore'; defineComponent,
onBeforeMount,
ref,
computed,
WritableComputedRef
} from '@vue/composition-api';
import { v4 } from 'uuid'; import { v4 } from 'uuid';
import store, { create_volume, DrinkPriceVolume, Drink } from '../store/altStore';
import PriceTable from 'src/plugins/pricelist/components/CalculationTable/PriceTable.vue'; import PriceTable from 'src/plugins/pricelist/components/CalculationTable/PriceTable.vue';
export default defineComponent({ export default defineComponent({
name: 'CalculationTable', name: 'CalculationTable',
@ -285,31 +301,37 @@ export default defineComponent({
'description', 'description',
'public' 'public'
]); ]);
const emptyVolume = { const emptyVolume: DrinkPriceVolume = {
id: -1, id: -1,
volume: 0, _volume: 0,
volume: null,
min_prices: [ min_prices: [
{ percentage: 100, price: 0 }, { percentage: 100, price: null },
{ percentage: 250, price: 0 }, { percentage: 250, price: null },
{ percentage: 300, price: 0 } { percentage: 300, price: null }
], ],
prices: [], prices: [],
ingredients: [] ingredients: []
}; };
const newVolume = ref(emptyVolume); const newVolume = ref<DrinkPriceVolume>(emptyVolume);
function addVolume(drink: FG.Drink) { function addVolume(drink: Drink) {
store.actions.setVolume(newVolume.value, drink); store.actions.setVolume(<DrinkPriceVolume>newVolume.value, drink);
cancelAddVolume(); cancelAddVolume();
} }
function cancelAddVolume() { function cancelAddVolume() {
setTimeout(() => { setTimeout(() => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
newVolume.value = emptyVolume; newVolume.value = emptyVolume;
}, 200); }, 200);
} }
function updateVolume(volume: FG.DrinkPriceVolume, drink: FG.Drink) { function updateVolume(volume: DrinkPriceVolume, drink: Drink) {
console.log(volume); console.log(volume);
store.actions.updateVolume(volume, drink); store.actions.updateVolume(volume, drink);
} }
function deleteVolume(volume: FG.DrinkPriceVolume, drink: FG.Drink) {
store.actions.deleteVolume(volume, drink);
}
function addIngredient(ingredients: FG.Ingredient[]) { function addIngredient(ingredients: FG.Ingredient[]) {
ingredients.push({ id: -1, volume_id: 0, drink_ingredient: null, extra_ingredient: null }); ingredients.push({ id: -1, volume_id: 0, drink_ingredient: null, extra_ingredient: null });
@ -325,8 +347,8 @@ export default defineComponent({
cancelAddVolume, cancelAddVolume,
newVolume, newVolume,
updateVolume, updateVolume,
deleteVolume,
addIngredient, addIngredient,
calc_min_prices,
console console
}; };
} }

View File

@ -132,7 +132,7 @@
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, ref } from '@vue/composition-api'; import { computed, defineComponent, ref } from '@vue/composition-api';
import store from '../../store/altStore'; import store, { DrinkPriceVolume } from '../../store/altStore';
export default defineComponent({ export default defineComponent({
name: 'PriceTable', name: 'PriceTable',
props: { props: {
@ -145,7 +145,7 @@ export default defineComponent({
required: true required: true
}, },
row: { row: {
type: Object as () => FG.DrinkPriceVolume, type: Object as () => DrinkPriceVolume,
required: true required: true
}, },
visibleColumns: { visibleColumns: {
@ -161,7 +161,7 @@ export default defineComponent({
public: true public: true
}; };
const newPrice = ref(emptyPrice); const newPrice = ref(emptyPrice);
function addPrice(volume: FG.DrinkPriceVolume) { function addPrice(volume: DrinkPriceVolume) {
store.actions.setPrice({ ...newPrice.value }, volume); store.actions.setPrice({ ...newPrice.value }, volume);
cancelAddPrice(); cancelAddPrice();
} }
@ -170,7 +170,7 @@ export default defineComponent({
newPrice.value = emptyPrice; newPrice.value = emptyPrice;
}, 200); }, 200);
} }
function updatePrice(price: FG.DrinkPrice, volume: FG.DrinkPriceVolume) { function updatePrice(price: FG.DrinkPrice, volume: DrinkPriceVolume) {
console.log(price); console.log(price);
store.actions.updatePrice(price, volume); store.actions.updatePrice(price, volume);
} }

View File

@ -1,38 +1,87 @@
import { reactive } from '@vue/composition-api'; import { reactive, computed, ComputedRef, WritableComputedRef } from '@vue/composition-api';
import { axios } from 'src/boot/axios'; import { axios } from 'src/boot/axios';
import { AxiosResponse } from 'axios'; import { AxiosResponse } from 'axios';
import ExtraIngredient = FG.ExtraIngredient;
const state = reactive<{ drinks: FG.Drink[]; tags: FG.Tag[]; drinkTypes: FG.DrinkType[] }>({ const state = reactive<{ drinks: Drink[]; tags: FG.Tag[]; drinkTypes: FG.DrinkType[] }>({
drinks: [], drinks: [],
tags: [], tags: [],
drinkTypes: [] drinkTypes: []
}); });
interface MinPrice extends Omit<FG.DrinkMinPrice, 'price'> {
price: WritableComputedRef<number> | null;
}
interface DrinkPriceVolume extends Omit<Omit<FG.DrinkPriceVolume, 'volume'>, 'min_prices'> {
_volume: number;
volume: WritableComputedRef<number> | null;
min_prices: MinPrice[];
}
interface Drink extends Omit<FG.Drink, 'volumes'> {
volumes: DrinkPriceVolume[];
}
const actions = { const actions = {
getDrinks() { getDrinks() {
axios axios
.get('pricelist/drinks') .get('pricelist/drinks')
.then((response: AxiosResponse<FG.Drink[]>) => { .then((response: AxiosResponse<FG.Drink[]>) => {
state.drinks = response.data; //state.drinks = [...response.data];
console.log(state.drinks); /*state.drinks.forEach((drink: Drink) => {
state.drinks.forEach((drink: FG.Drink) => { drink.volumes.forEach((volume: DrinkPriceVolume) => {
drink.volumes.forEach((volume: FG.DrinkPriceVolume) => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
volume._volume = volume.volume;
volume.volume = create_volume(drink, volume);
volume.min_prices = [ 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: 100,
{ percentage: 300, price: (drink.cost_price_pro_volume || 0) * volume.volume * 3 } price: create_min_prices(drink, volume, 100)
},
{
percentage: 250,
price: create_min_prices(drink, volume, 250)
},
{
percentage: 300,
price: create_min_prices(drink, volume, 300)
}
]; ];
if (volume.ingredients.length > 0) {
calc_min_prices(volume);
}
this.sortPrices(volume); this.sortPrices(volume);
}); });
});*/
response.data.forEach(drink => {
const newDrink: Drink = { ...drink, volumes: [] };
drink.volumes.forEach(volume => {
const newVolume: DrinkPriceVolume = {
...volume,
_volume: volume.volume,
min_prices: [],
volume: null
};
newVolume.volume = create_volume(newDrink, newVolume);
newVolume.min_prices = [
{
percentage: 100,
price: create_min_prices(newDrink, newVolume, 100)
},
{
percentage: 250,
price: create_min_prices(newDrink, newVolume, 250)
},
{
percentage: 300,
price: create_min_prices(newDrink, newVolume, 300)
}
];
console.log(newVolume);
newDrink.volumes.push(newVolume);
});
state.drinks.push(newDrink);
}); });
}) })
.catch(err => console.warn(err)); .catch(err => console.warn(err));
}, },
setPrice(price: FG.DrinkPrice, volume: FG.DrinkPriceVolume) { setPrice(price: FG.DrinkPrice, volume: DrinkPriceVolume) {
axios axios
.post(`pricelist/volumes/${volume.id}/prices`, price) .post(`pricelist/volumes/${volume.id}/prices`, price)
.then((response: AxiosResponse<FG.DrinkPrice>) => { .then((response: AxiosResponse<FG.DrinkPrice>) => {
@ -41,7 +90,7 @@ const actions = {
}) })
.catch(err => console.warn(err)); .catch(err => console.warn(err));
}, },
sortPrices(volume: FG.DrinkPriceVolume) { sortPrices(volume: DrinkPriceVolume) {
volume.prices.sort((a, b) => { volume.prices.sort((a, b) => {
if (a.price > b.price) return 1; if (a.price > b.price) return 1;
if (b.price > a.price) return -1; if (b.price > a.price) return -1;
@ -59,31 +108,61 @@ const actions = {
}) })
.catch(err => console.warn(err)); .catch(err => console.warn(err));
}, },
updatePrice(price: FG.DrinkPrice, volume: FG.DrinkPriceVolume) { updatePrice(price: FG.DrinkPrice, volume: DrinkPriceVolume) {
axios axios
.put(`pricelist/prices/${price.id}`, price) .put(`pricelist/prices/${price.id}`, price)
.then((response: AxiosResponse<FG.DrinkPrice>) => { .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) { if (index > -1) {
//volume.prices[index] = response.data;
this.sortPrices(volume); this.sortPrices(volume);
} }
}) })
.catch(err => console.log(err)); .catch(err => console.log(err));
}, },
setVolume(volume: FG.DrinkPriceVolume, drink: FG.Drink) { setVolume(volume: DrinkPriceVolume, drink: Drink) {
console.log(volume);
axios axios
.post(`pricelist/drinks/${drink.id}/volumes`, volume) .post(`pricelist/drinks/${drink.id}/volumes`, { ...volume, volume: volume.volume })
.then((response: AxiosResponse<FG.DrinkPriceVolume>) => { .then((response: AxiosResponse<FG.DrinkPriceVolume>) => {
drink.volumes.push(response.data); const a: DrinkPriceVolume = {
...response.data,
min_prices: [],
_volume: response.data.volume,
volume: null
};
a.volume = create_volume(drink, a);
a.min_prices = [
{
percentage: 100,
price: create_min_prices(drink, a, 100)
},
{
percentage: 250,
price: create_min_prices(drink, a, 250)
},
{
percentage: 300,
price: create_min_prices(drink, a, 300)
}
];
console.log(a);
drink.volumes.push(a);
}) })
.catch(err => console.warn(err)); .catch(err => console.warn(err));
}, },
updateVolume(volume: FG.DrinkPriceVolume, drink: FG.Drink) { updateVolume(volume: DrinkPriceVolume, drink: Drink) {
axios axios
.put(`pricelist/volumes/${volume.id}`, volume) .put(`pricelist/volumes/${volume.id}`, { ...volume, volume: volume.volume?.value })
.catch(err => console.warn(err));
},
deleteVolume(volume: FG.DrinkPriceVolume, drink: FG.Drink) {
axios
.delete(`pricelist/volumes/${volume.id}`)
.then(() => { .then(() => {
//calc_min_prices_by_drink(volume, drink); const index = drink.volumes.findIndex(a => a.id === volume.id);
if (index > -1) {
drink.volumes.splice(index, 1);
}
}) })
.catch(err => console.warn(err)); .catch(err => console.warn(err));
} }
@ -91,31 +170,59 @@ const actions = {
const getters = {}; const getters = {};
function calc_min_prices_by_drink(volume: FG.DrinkPriceVolume, drink: FG.Drink) { function create_volume(drink: Drink, volume: DrinkPriceVolume) {
volume.min_prices.forEach(min_price => { return computed<number>({
min_price.price = get: () => {
((drink.cost_price_pro_volume || 0) * volume.volume * min_price.percentage) / 100; if (volume.ingredients.some(ingredient => !!ingredient.drink_ingredient)) {
let retVal = 0;
volume.ingredients.forEach(ingredient => {
if (ingredient.volume) {
retVal += ingredient.volume;
}
});
volume._volume = retVal;
return retVal;
} else {
return volume._volume;
}
},
set: val => (volume._volume = val)
}); });
} }
function calc_min_prices(row: FG.DrinkPriceVolume) { function create_min_prices(drink: Drink, volume: DrinkPriceVolume, percentage: number) {
row.volume = 0; if (drink.cost_price_pro_volume) {
let cost_price = 0; if (volume.ingredients.every(ingredient => !!ingredient.drink_ingredient)) {
row.ingredients.forEach((ingredient: FG.DrinkIngredient & FG.ExtraIngredient) => { return computed<number>(() => {
console.log(ingredient); let retVal = (drink.cost_price_pro_volume || 0) * (volume.volume?.value || 0);
if (ingredient.drink_ingredient) { volume.ingredients.forEach((ingredient: FG.ExtraIngredient) => {
row.volume = row.volume + ingredient.volume; retVal += ingredient.price;
cost_price += ingredient.volume * (ingredient.drink_ingredient.cost_price_pro_volume || 0); });
} else if (ingredient.name && ingredient.price) { retVal = (retVal * percentage) / 100;
cost_price += ingredient.price; return retVal;
});
} else {
return computed<number>(
() => ((drink.cost_price_pro_volume || 0) * (volume.volume?.value || 0) * percentage) / 100
);
} }
}); } else {
row.min_prices.forEach(min_price => { return computed<number>(() => {
min_price.price = (cost_price * min_price.percentage) / 100; let retVal = 0;
}); volume.ingredients.forEach(ingredient => {
if (ingredient.drink_ingredient) {
retVal += ingredient.volume * (ingredient.drink_ingredient.cost_price_pro_volume || 0);
}
if (ingredient.name) {
retVal += ingredient.price;
}
});
console.log(volume);
return (retVal * percentage) / 100;
});
}
} }
export { create_min_prices, create_volume, DrinkPriceVolume, MinPrice, Drink };
export { calc_min_prices };
export default { export default {
state, state,
actions, actions,