[Pricelist] break no backend new view

This commit is contained in:
Tim Gröger 2021-03-14 20:37:41 +01:00
parent c0d57c6a71
commit 724ae66dd7
5 changed files with 2283 additions and 99 deletions

View File

@ -16,12 +16,14 @@
"cordova": "^10.0.0", "cordova": "^10.0.0",
"core-js": "^3.7.0", "core-js": "^3.7.0",
"quasar": "^1.14.5", "quasar": "^1.14.5",
"uuid": "^8.3.2",
"vue-router": "3.3.2" "vue-router": "3.3.2"
}, },
"devDependencies": { "devDependencies": {
"@quasar/app": "^2.1.8", "@quasar/app": "^2.1.8",
"@quasar/quasar-app-extension-qcalendar": "^3.3.4", "@quasar/quasar-app-extension-qcalendar": "^3.3.4",
"@types/node": "^12.19.6", "@types/node": "^12.19.6",
"@types/uuid": "^8.3.0",
"@types/webpack": "^4.41.25", "@types/webpack": "^4.41.25",
"@types/webpack-env": "^1.16.0", "@types/webpack-env": "^1.16.0",
"@typescript-eslint/eslint-plugin": "^4.8.2", "@typescript-eslint/eslint-plugin": "^4.8.2",

View File

@ -0,0 +1,467 @@
<template>
<div>
<q-table
title="Kalkulationstabelle"
:columns="columns"
:data="test"
:visible-columns="visibleColumn"
:dense="$q.screen.lt.md"
>
<template v-slot:top-right>
<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
/>
</template>
<template v-slot:body-cell-price_calc="price_calc">
<q-table
:columns="column_calc"
:data="price_calc.value"
dense
:visible-columns="visibleColumn"
row-key="id"
flat
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width />
<q-th v-for="(col, index) in props.cols" :key="col + index" :props="props">
{{ col }}
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props">
<q-td auto-width>
<q-btn
size="sm"
color="accent"
round
dense
@click="props.expand = !props.expand"
:icon="props.expand ? 'mdi-chevron-up' : 'mdi-chevron-down'"
/>
</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>
</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"
>
{{ col.value }}
</q-td>
<q-td>
<q-btn
color="negative"
round
size="xs"
icon="mdi-delete"
@click="deletePrice(prices_props.row)"
v-if="!prices_props.row.to_delete"
/>
<q-btn color="positive" size="xs" label="Speichern" v-else />
</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"
@click="addPrice(col.value)"
/>
</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-tr>
<q-tr v-show="props.expand" :props="props">
<q-td colspan="100%">
<div
v-for="(ingredient, index) in props.row.ingredients"
:key="`${props.key}_${index}`"
class="full-width row justify-evenly q-py-xs"
>
<q-select
class="col q-px-sm"
label="Getränk"
filled
dense
:options="test"
option-label="name"
v-model="ingredient.drink"
@input="calc_min_prices(props.row)"
/>
<q-input
class="col q-px-sm"
label="Volume in L"
type="number"
filled
dense
v-model="ingredient.volume"
@change="calc_min_prices(props.row)"
step="0.01"
min="0"
/>
</div>
<div class="full-width row justify-end q-py-xs">
<q-btn
size="sm"
icon-right="add"
color="positive"
label="Zutat hinzufügen"
@click="addIngredient(props.row.ingredients)"
@change="calc_min_prices(props.row)"
/>
</div>
</q-td>
</q-tr>
</template>
<template v-slot:bottom>
<div class="full-width row justify-end">
<q-btn
color="positive"
icon-right="add"
label="Abgabe hinzufügen"
size="xs"
@click="addVolume(price_calc.value)"
/>
</div>
</template>
</q-table>
</template>
</q-table>
</div>
</template>
<script lang="ts">
import { defineComponent, onBeforeMount, ref } from '@vue/composition-api';
import { Store } from 'vuex';
import { StateInterface } from '../../../store';
import { DrinkInterface } from '../store/drinks';
import { v4 } from 'uuid';
export default defineComponent({
name: 'CalculationTable',
setup(_, { root }) {
const store = <Store<StateInterface>>root.$store;
const state = <DrinkInterface>store.state.drink;
const drinks = ref();
onBeforeMount(() => {
void store
.dispatch('drink/getDrinks')
.then(() => (drinks.value = drinks.value = state.drinks));
});
const columns = [
{
name: 'name',
label: 'Getränkename',
field: 'name'
},
{
name: 'article_id',
label: 'Artikelnummer',
field: 'article_id'
},
{
name: 'drink_kind',
label: 'Kategorie',
field: 'drink_kind'
},
{
name: 'volume_package',
label: 'Inhalt in l des Gebinde',
field: 'volume_package'
},
{
name: 'package_size',
label: 'Gebindegröße',
field: 'package_size'
},
{
name: 'cost_price_package_netto',
label: 'Preis Netto/Gebinde',
field: 'cost_price_package_netto',
format: (val: number) => `${val.toFixed(3)}`
},
{
name: 'cost_price_pro_volume',
label: 'Preis mit 19%/Liter',
field: 'cost_price_pro_volume',
format: (val: number) => `${val.toFixed(3)}`
},
{
name: 'price_calc',
label: 'Preiskalkulation',
field: 'price_calc'
}
];
const column_calc = [
{
name: 'volume',
label: 'Abgabe in l',
field: 'volume',
format: (val: number) => `${val} L`
},
{
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'
}
];
const test = ref([
{
name: 'Elbhang Rot',
article_id: 41807,
drink_kind: 'Bier',
volume_package: 50,
package_size: 1,
cost_price_package_netto: 97,
cost_price_pro_volume: 2.309,
price_calc: [
{
id: v4(),
volume: 0.5,
min_prices: [
{
percentage: 100,
price: 1.15
},
{
percentage: 250,
price: 2.875
},
{
percentage: 300,
price: 3.45
}
],
prices: [
{ price: 2, description: '', to_delete: false },
{ price: 1.4, description: 'Club intern', to_delete: false },
{
price: 1.6,
description: 'Club extern',
to_delete: false
}
]
}
]
},
{
name: 'Sinalco Cola',
article_id: 443,
drink_kind: 'AFG',
volume_package: 1,
package_size: 12,
cost_price_package_netto: 7.28,
cost_price_pro_volume: 0.722,
price_calc: [
{
id: v4(),
volume: 0.2,
min_prices: [
{
percentage: 100,
price: 0.14
},
{
percentage: 250,
price: 0.35
},
{
percentage: 300,
price: 0.42
}
],
prices: [
{
price: 1,
description: 'klein',
to_delete: false
},
{ price: 0.5, description: 'klein club intern', to_delete: false }
]
},
{
id: v4(),
volume: 0.4,
min_prices: [
{
percentage: 100,
price: 0.289
},
{
percentage: 250,
price: 0.722
},
{
percentage: 300,
price: 0.866
}
],
prices: [
{
price: 1.8,
description: 'groß',
to_delet: false
},
{ price: 1, description: 'groß club intern', to_delete: false }
]
}
]
}
]);
const visibleColumn = ref([
'name',
'drink_kind',
'cost_price_pro_volumne',
'price_calc',
'volume',
'min_prices',
'prices',
'price',
'description'
]);
function deletePrice(row) {
console.log(row);
row.to_delete = true;
}
function addPrice(table) {
console.log(table);
table.push({
price: 20,
description: 'test',
to_delete: false
});
}
function addVolume(table) {
table.push({
id: v4(),
volume: null,
min_prices: [
{
percentage: 100,
price: null
},
{
percentage: 250,
price: null
},
{
percentage: 300,
price: null
}
],
prices: [],
ingredients: []
});
}
function addIngredient(ingredients) {
ingredients.push({ drink: null, volume: null });
}
function calc_min_prices(row) {
console.log(row.ingredients);
row.volume = 0;
let cost_price = 0;
row.ingredients.forEach(ingredient => {
row.volume = row.volume + Number.parseFloat(ingredient.volume);
cost_price = Number.parseFloat(ingredient.volume) * ingredient.drink.cost_price_pro_volume;
});
console.log(row.volume, cost_price);
row.min_prices.forEach(min_price => {
min_price.price = (cost_price * min_price.percentage) / 100;
});
}
return {
drinks,
columns,
test,
column_calc,
column_prices,
visibleColumn,
deletePrice,
addPrice,
addVolume,
addIngredient,
calc_min_prices
};
}
});
</script>
<style scoped></style>

View File

@ -8,7 +8,12 @@
<q-card-section> <q-card-section>
<div class="text-h5">Getränkinformationen</div> <div class="text-h5">Getränkinformationen</div>
<div class="row"> <div class="row">
<q-input class="col-12 col-sm-6 q-px-sm q-py-md" v-model="drink.name" filled label="Name" /> <q-input
class="col-12 col-sm-6 q-px-sm q-py-md"
v-model="drink.name"
filled
label="Name"
/>
<q-input <q-input
class="col-12 col-sm-6 q-px-sm q-py-md" class="col-12 col-sm-6 q-px-sm q-py-md"
v-model="drink.volume" v-model="drink.volume"
@ -53,14 +58,51 @@
</div> </div>
<q-btn round icon="mdi-plus" @click="addPrice" color="primary" /> <q-btn round icon="mdi-plus" @click="addPrice" color="primary" />
</div> </div>
<q-card class="q-ma-sm" v-for="(price,index) in drink.prices" :key="index"> <q-card class="q-ma-sm" v-for="(price, index) in drink.prices" :key="index">
<div class="row"> <div class="row">
<q-input class="col-12 col-sm-6 q-px-sm q-py-md" v-model="price.volume" label="Inhalt in Liter" filled type="number" step="0.01" /> <q-input
<q-input class="col-12 col-sm-6 q-px-sm q-py-md" v-model="price.price" label="Preis in €" filled :disable="price.no_auto" type="number" step="0.1"/> class="col-12 col-sm-6 q-px-sm q-py-md"
<q-toggle class="col-12 col-sm-6 q-px-sm q-py-md" v-model="price.no_auto" label="Automatische Preiskalkulation" color="primary" /> v-model="price.volume"
<q-input class="col-12 col-sm-6 q-px-sm q-py-md" v-model="price.round_step" label="Rundungsschritt" type="number" filled step="0.1"/> label="Inhalt in Liter"
<q-toggle class="col-12 col-sm-6 q-px-sm q-py-md" v-model="price.public" label="Öffentlich" color="primary" /> filled
<q-input class="col-12 col-sm-6 q-px-sm q-py-md" v-model="price.description" label="Beschreibung" filled /> type="number"
step="0.01"
/>
<q-input
class="col-12 col-sm-6 q-px-sm q-py-md"
v-model="price.price"
label="Preis in €"
filled
:disable="price.no_auto"
type="number"
step="0.1"
/>
<q-toggle
class="col-12 col-sm-6 q-px-sm q-py-md"
v-model="price.no_auto"
label="Automatische Preiskalkulation"
color="primary"
/>
<q-input
class="col-12 col-sm-6 q-px-sm q-py-md"
v-model="price.round_step"
label="Rundungsschritt"
type="number"
filled
step="0.1"
/>
<q-toggle
class="col-12 col-sm-6 q-px-sm q-py-md"
v-model="price.public"
label="Öffentlich"
color="primary"
/>
<q-input
class="col-12 col-sm-6 q-px-sm q-py-md"
v-model="price.description"
label="Beschreibung"
filled
/>
</div> </div>
</q-card> </q-card>
</q-card-section> </q-card-section>
@ -87,37 +129,57 @@ import { Store } from 'vuex';
export default defineComponent({ export default defineComponent({
name: 'Drink', name: 'Drink',
setup(_, {root}) { setup(_, { root }) {
const store = <Store<StateInterface>>root.$store; const store = <Store<StateInterface>>root.$store;
const drink = ref({ interface EmptyDrink {
name: string;
volume: number | null;
cost_price: number | null;
discount: number | null;
extra_charge: number | null;
prices: EmptyPrice[];
ingredients: [];
}
const emptyDrink: EmptyDrink = {
name: '', name: '',
volume: '', volume: null,
cost_price: '', cost_price: null,
discount: '', discount: null,
extra_charge: '', extra_charge: null,
prices: [], prices: [],
ingredients: [] ingredients: []
}) };
const emptyPrice = { const drink = ref<EmptyDrink>(emptyDrink);
interface EmptyPrice {
volume: string;
price: number | null;
description: string;
no_auto: boolean;
round_step: number | null;
public: boolean;
}
const emptyPrice: EmptyPrice = {
volume: '', volume: '',
price: '', price: null,
description: '', description: '',
no_auto: false, no_auto: false,
round_step: '', round_step: null,
public: true public: true
} };
function addPrice() { function addPrice() {
drink.value.prices.unshift({...emptyPrice}) const test = { ...emptyPrice };
drink.value.prices.unshift(test);
} }
function save() { function save() {
console.log(drink) console.log(drink);
drink.value.prices.forEach((price: FG.DrinkPrice) => {
price.no_auto = !price.no_auto drink.value.prices.forEach((price: EmptyPrice) => {
}) price.no_auto = !price.no_auto;
void store.dispatch('drink/createDrink', drink.value) });
void store.dispatch('drink/createDrink', drink.value);
} }
return {drink, addPrice, save}; return { drink, addPrice, save };
} }
}); });
</script> </script>

View File

@ -32,7 +32,7 @@
class="q-ma-none q-pa-none fit row justify-center content-start items-start" class="q-ma-none q-pa-none fit row justify-center content-start items-start"
> >
<q-tab-panel name="pricelist"> <q-tab-panel name="pricelist">
<h1>preisliste</h1> <CalculationTable />
</q-tab-panel> </q-tab-panel>
<q-tab-panel name="new_drink"> <q-tab-panel name="new_drink">
<Drink /> <Drink />
@ -50,9 +50,10 @@ import { computed, defineComponent, ref } from '@vue/composition-api';
import { Screen } from 'quasar'; import { Screen } from 'quasar';
import DrinkTypes from 'src/plugins/pricelist/components/DrinkTypes.vue'; import DrinkTypes from 'src/plugins/pricelist/components/DrinkTypes.vue';
import Drink from 'src/plugins/pricelist/components/Drink.vue'; import Drink from 'src/plugins/pricelist/components/Drink.vue';
import CalculationTable from 'src/plugins/pricelist/components/CalculationTable.vue';
export default defineComponent({ export default defineComponent({
name: 'Settings', name: 'Settings',
components: { DrinkTypes, Drink }, components: { DrinkTypes, Drink, CalculationTable },
setup(_) { setup(_) {
interface Tab { interface Tab {
name: string; name: string;

1748
yarn.lock

File diff suppressed because it is too large Load Diff