update logic

This commit is contained in:
2021-07-18 01:04:13 +02:00
parent 20490ef9b6
commit 8d18e5838e
36 changed files with 7103 additions and 3695 deletions
+56 -142
View File
@@ -23,6 +23,8 @@
<InventoryTable
ref="inventory"
:articles="articles"
:groups="groups"
:dimensions="dimensions"
/>
</div>
<div
@@ -37,120 +39,11 @@
id="article"
class="col s12"
>
<div
v-for="(a, index) in articles"
:key="'article_'+index"
class="card"
>
<div class="card-content row">
<span class="card-title">{{ a.name }}</span>
<div class="input-field inline col s8">
<input
:id="'a_name_'+index"
v-model="a.name"
placeholder="Artikelname"
class="validate"
>
<label
:for="'a_name_'+index"
class="active"
>Name</label>
</div>
<div class="input-field inline col s4">
<input
:id="'a_short_'+index"
v-model="a.short"
placeholder="Kurzname"
class="validate"
>
<label
:for="'a_short_'+index"
class="active"
>Kürzel</label>
</div>
<div class="input-field col s8">
<input
:id="'a_csize_'+index"
v-model.number="a.content.size"
placeholder="Gesamtinhalt"
type="number"
class="validate"
step="0.01"
>
<label
:for="'a_csize_'+index"
class="active"
>Gesamtinhalt</label>
</div>
<div class="input-field col s4">
<input
:id="'a_dim_'+index"
v-model="a.dimension"
placeholder="Dimension"
class="validate"
max="5"
>
<label
:for="'a_dim_'+index"
class="active"
>Dimension</label>
<span class="helper-text">z.B. Liter(l), Stück(Stk.)</span>
</div>
<div class="input-field col s4">
<input
:id="'a_psize_'+index"
v-model.number="a.portion.size"
placeholder="Gesamtinhalt"
type="number"
class="validate"
step="0.01"
max="5"
>
<label
:for="'a_psize_'+index"
class="active"
>Portionsinhalt</label>
</div>
<div class="input-field col s4">
<input
:id="'a_ptype_'+index"
v-model="a.portion.type"
placeholder="Art"
class="validate"
max="5"
>
<label
:for="'a_ptype_'+index"
class="active"
>Portionsbezeichnung</label>
</div>
<div class="input-field col s4">
<input
:id="'a_pprice_'+index"
v-model.number="a.portion.price"
placeholder="Preis"
type="number"
step="0.01"
class="validate"
>
<label
:for="'a_pprice_'+index"
class="active"
>Portionspreis in </label>
</div>
<div class="col s12 right">
<span class="right">Gesamtpreis {{ a.ContentPrice }}</span>
</div>
</div>
<div class="card-action">
<a
class
href="#"
>Artikel löschen</a>
</div>
</div>
<ArticlesView
:articles="articles"
:groups="groups"
:dimensions="dimensions"
/>
</div>
</transition>
</div>
@@ -165,17 +58,6 @@
<i class="large material-icons">more_vert</i>
</a>
<ul>
<li v-show="view == 'article'">
<a
href="#"
class="btn-floating tooltipped"
data-position="left"
data-tooltip="Artikelliste speichern"
@click="storeArticles"
>
<i class="material-icons">save</i>
</a>
</li>
<li v-show="view == 'article'">
<a
href="#"
@@ -217,20 +99,23 @@
<script>
import Calculator from './components/Calculator';
import InventoryTable from './components/InventoryTable';
import ArticlesView from './components/ArticlesView';
import NavigationBar from './components/NavigationBar';
import {Article, thawArticle} from './model/article';
export default {
name: "App",
components: { InventoryTable, NavigationBar, Calculator },
components: { InventoryTable, NavigationBar, Calculator, ArticlesView },
data() {
return {
articles: [],
dimensions: [],
groups: [],
bon: [],
ready: false,
caretPosition: 0,
fab: null,
view: "inventory",
view: "article",
location: 'fairwertbar'
};
},
computed: {
@@ -245,9 +130,14 @@ export default {
default:
return "red";
}
},
ready() {
return this.dimensions.length && this.groups.length;
}
},
created: function() {
this.loadDimensions();
this.loadGroups();
this.loadArticles();
},
mounted() {
@@ -261,35 +151,59 @@ export default {
addArticle: function() {
this.articles.push(new Article());
},
storeArticles: function() {
loadDimensions: function() {
var app = this;
this.$http
.post(
"/api/artikel",
JSON.stringify(this.articles)
)
.get("api/dimension")
.then(response => {
this.$M.toast({ html: response.body });
return response.data;
})
.then(json => {
if (json != null) {
json.forEach(element => {
app.dimensions.push(element);
});
}
})
.then(() => {
this.$M.toast({ html: "Dimensionen wurden geladen." });
});
},
loadGroups: function() {
var app = this;
this.$http
.get("api/group")
.then(response => {
return response.data;
})
.then(json => {
if (json != null) {
json.forEach(element => {
app.groups.push(element);
});
}
})
.then(() => {
this.$M.toast({ html: "Artikelgruppen wurden geladen." });
});
},
loadArticles: function() {
var app = this;
this.$http
.get("api/artikel/theater")
.get("api/artikel")
.then(response => {
return response.data;
})
.then(json => {
console.log(json);
json.forEach(element => {
app.articles.push(thawArticle(element));
});
if (json != null) {
json.forEach(element => {
app.articles.push(thawArticle(element));
});
}
})
.then(() => {
this.$M.toast({ html: "Artikel wurden geladen." });
})
.then(() => {
this.$M.updateTextFields();
app.ready = true;
});
},
exportInventur() {
+315
View File
@@ -0,0 +1,315 @@
<template>
<div
class="card"
>
<div class="card-content">
<span class="card-title">
{{ article.name }}
</span>
</div>
<div class="card-tabs">
<ul class="tabs tabs-fixed-width">
<li class="tab">
<a
:href="'#' + identifier + '_info'"
class="active"
>Info</a>
</li>
<li class="tab">
<a :href="'#' + identifier + '_variants'">Varianten</a>
</li>
</ul>
</div>
<div class="card-content">
<div
:id="identifier + '_info'"
class="row"
>
<div class="input-field inline col s8">
<input
:id="'a_name_' + article.id"
v-model="article.name"
placeholder="Artikelname"
class="validate"
>
<label
:for="'a_name_' + article.id"
class="active"
>Name</label>
</div>
<div class="input-field inline col s4">
<input
:id="'a_short_' + article.id"
v-model="article.short"
placeholder="Kurzname"
class="validate"
>
<label
:for="'a_short_' + article.id"
class="active"
>Kürzel</label>
</div>
<div class="input-field col s4">
<materialize-select
:id="'a_group_' + article.id"
v-model.number="article.group"
:value="article.group"
placeholder="Artikelgruppe"
>
<option
v-for="group in groups"
:key="group.id"
:value="group.id"
>
{{ group.name }}
</option>
</materialize-select>
<label :for="'a_group_' + article.id">Artikelgruppe</label>
</div>
<div class="input-field col s4">
<input
:id="'a_csize_' + article.id"
v-model.number="article.content.size"
placeholder="Gesamtinhalt"
type="number"
class="validate"
step="0.01"
>
<label
:for="'a_csize_' + article.id"
class="active"
>Gesamtinhalt</label>
</div>
<div class="input-field col s4">
<materialize-select
:id="'a_dim_' + article.id"
v-model.number="article.dimension"
:value="article.dimension"
placeholder="Dimension"
>
<option
v-for="dim in dimensions"
:key="dim.id"
:value="dim.id"
>
{{ dim.name }}
</option>
</materialize-select>
<label :for="'a_dim_' + article.id">Dimension</label>
</div>
<div class="input-field col s4">
<input
:id="'a_psize_' + article.id"
v-model.number="article.portion.size"
placeholder="Gesamtinhalt"
type="number"
class="validate"
step="0.01"
max="5"
>
<label
:for="'a_psize_' + article.id"
class="active"
>Portionsinhalt</label>
</div>
<div class="input-field col s4">
<MaterializeSelect
:id="'a_ptype_' + article.id"
v-model.number="article.portion.type"
:value="article.portion.type"
placeholder="Art"
>
<option
v-for="dim in dimensions"
:key="dim.id"
:value="dim.id"
>
{{ dim.name }}
</option>
</MaterializeSelect>
<label :for="'a_ptype_' + article.id">Portionsbezeichnung</label>
</div>
<div class="input-field col s4">
<input
:id="'a_pprice_' + article.id"
v-model.number="article.portion.price"
placeholder="Preis"
type="number"
step="0.01"
class="validate"
>
<label
:for="'a_pprice_' + article.id"
class="active"
>Portionspreis in </label>
</div>
<div class="col s12 right">
<span
class="right"
>Gesamtpreis
<TweenedNumber
:wert="article.ContentPrice"
:einheit="'€'"
:precision="2"
/></span>
</div>
</div>
<div
:id="identifier + '_variants'"
class="row"
>
<div class="col s12">
<ArticleVariant
v-for="(variant, index) in variants"
:key="'article_' + article.id + '_variant_' + index"
:identifier="'article_' + article.id + '_variant_' + index"
:variant="variant"
:article="article"
/>
<a
href="#"
@click="addVariant(article)"
>Variante hinzufügen</a>
</div>
</div>
</div>
<div class="card-action">
<a
href="#"
@click="deleteArticle(article)"
>Artikel löschen</a>
<a
v-if="changed"
href="#"
@click="storeArticle(article)"
>Artikel speichern</a>
</div>
</div>
</template>
<script>
import { Article, thawArticle } from '../model/article';
import MaterializeSelect from "./MaterializeSelect";
import TweenedNumber from "./TweenedNumber";
import ArticleVariant from "./ArticleVariant";
import { thawVariant, Variant } from '../model/variant';
export default {
name: "Article",
components: {MaterializeSelect, TweenedNumber, ArticleVariant},
props: {
a: {
type: Object,
default() { return new Article(); }
},
dimensions: {
type: Array,
default() { return []; }
},
groups: {
type: Array,
default() { return []; }
},
identifier: {
type: String,
}
},
data() {
return {
changed: false,
article: null,
original: null,
variants: [],
tabs: null
}
},
computed: {
},
watch: {
article: {
handler(newValue, oldVal) {
this.changed = JSON.stringify(newValue) != this.original;
},
deep: true
}
},
created() {
this.article = this.a;
this.original = JSON.stringify(this.a);
this.loadVariants(this.article);
},
mounted() {
var tabs = this.$el.getElementsByClassName("tabs")[0];
M.Tabs.init(tabs, {});
this.tabs = M.Tabs.getInstance(tabs);
this.$M.updateTextFields();
},
methods: {
storeArticle: function(article) {
this.$http
.post(
"/api/artikel" + (article.id > 0 ? '/' + article.id : ''),
JSON.stringify(article),
{
headers: {
'Content-Type': 'application/json'
}
}
)
.then(response => {
this.$M.toast({ html: response.data + " Artikel aktualisiert." });
this.article = thawArticle(response.data);
})
.catch(error => {
this.$M.toast({ html: response });
});
},
deleteArticle: function(article) {
this.$http
.delete(
"/api/artikel/" + article.id,
)
.then(response => {
this.$emit('delete-article', article);
this.$M.toast({ html: response.data + " Artikel gelöscht." });
})
.catch(error => {
this.$M.toast({ html: response });
});
},
addVariant(article) {
var variant = new Variant();
variant.ArticleId = article.id;
this.variants.push(variant);
},
loadVariants: function(article) {
this.$http
.get(
"/api/artikel/" + article.id + '/varianten',
)
.then(response => {
return response.data;
})
.then(json => {
if (json != null) {
json.forEach(element => {
this.variants.push(thawVariant(element));
});
}
})
.then(() => {
this.$M.updateTextFields();
})
.catch(error => {
this.$M.toast({ html: error });
});
},
}
};
</script>
+100
View File
@@ -0,0 +1,100 @@
<template>
<div class="row">
<div class="input-field col s4">
<input
:id="identifier + '_name'"
v-model="variant.name"
placeholder="Variantenbezeichnung"
type="text"
class="validate"
>
<label
:for="identifier + '_name'"
class="active"
>Variantenbezeichnung</label>
</div>
<div class="input-field col s4">
<input
:id="identifier + '_price'"
v-model.number="variant.price"
placeholder="Preis"
type="number"
class="validate"
step="0.01"
>
<label
:for="identifier + '_price'"
class="active"
>Preis</label>
</div>
<div class="input-field col s2">
<input
:value="article.PortionPrice + variant.Price"
class="validate"
disabled
>
</div>
<div class="input-field col s2">
<a
href="#"
@click="storeVariant(variant)"
>Variante speichern</a>
</div>
</div>
</template>
<script>
import { Article } from '../model/article';
import { thawVariant, Variant } from '../model/variant'
export default {
name: 'Variant',
props: {
variant: {
type: Object,
default() { return new Variant(); }
},
article: {
type: Object,
default() { return new Article(); }
},
identifier: {
type: String
}
},
methods: {
storeVariant: function(variant) {
variant.ArticleId = this.article.id;
this.$http
.post(
"/api/varianten" + (variant.id > 0 ? '/' + variant.id : ''),
JSON.stringify(variant),
{
headers: {
'Content-Type': 'application/json'
}
}
)
.then(response => {
this.$M.toast({ html: response.data + " Variante aktualisiert." });
this.variant = thawVariant(response.data);
})
.catch(error => {
this.$M.toast({ html: error });
});
},
deleteVariant: function(variant) {
this.$http
.delete(
"/api/varianten/" + variant.id,
)
.then(response => {
this.$emit('delete-variant', variant);
this.$M.toast({ html: response.data + " Varianten gelöscht." });
})
.catch(error => {
this.$M.toast({ html: response });
});
},
}
}
</script>
+51
View File
@@ -0,0 +1,51 @@
<template>
<div>
<ArticleCard
v-for="(a, index) in articles"
:key="'article_' + index"
:identifier="'article_' + index"
:a="a"
:dimensions="dimensions"
:groups="groups"
@delete-article="removeFromList(a)"
/>
</div>
</template>
<script>
import ArticleCard from "./ArticleCard";
export default {
name: "ArticlesView",
components: {ArticleCard},
props: {
articles: {
type: Array,
default() { return []; }
},
groups: {
type: Array,
default() { return []; }
},
dimensions: {
type: Array,
default() { return []; }
}
},
data() {
return {
}
},
computed: {
},
mounted() {
},
methods: {
removeFromList(article) {
this.articles.splice(this.articles.indexOf(article),1);
}
}
};
</script>
+20 -11
View File
@@ -2,7 +2,7 @@
<tr
style="border: 0;"
class="inventory-item"
:class="{
:class="{
'teal lighten-5' : item.Sale > 0,
'orange darken-2 white-text' : item.Sold %1 !=0,
'red darken-2 white-text' : item.Sale < 0,
@@ -26,20 +26,20 @@
>
{{ item[prop] }}
</td>
<td
class="right-align border-bottom border-diagram"
>
<TweenedNumber
:style="{
'border-image': 'linear-gradient(to right , rgba(0,150,136,.01) 0%, rgba(0,150,136,.3) '
+ item.Sold*100/total
:style="{
'border-image': 'linear-gradient(to right , rgba(0,150,136,.01) 0%, rgba(0,150,136,.3) '
+ item.Sold*100/total
+ '%, transparent '
+ item.Sold*100/total
+'%,transparent 100%) 1'
}"
:wert="item.Sold"
:einheit="item.article.PortionType"
:einheit="Dimension.short"
:precision="item.PortionPrecision"
/>
</td>
@@ -74,6 +74,12 @@ export default {
total: {
type: Number,
default: 0
},
dimensions: {
type: Array,
default() {
return [];
}
}
},
data() {
@@ -89,10 +95,13 @@ export default {
},
SaleAnimated() {
return this.tweenedSale.toFixed(2);
},
Dimension() {
return this.dimensions.find(element => element.id == this.item.article.PortionType)
}
},
watch: {
},
methods: {
setCaretPosition(el){
@@ -139,15 +148,15 @@ export default {
if (event.data.match(regex)){
var value = event.target.innerText;
this.item[property] = Number(value);
}
}
}
this.setCaretPosition(event.target);
this.setCaretPosition(event.target);
},
}
}
</script>
<style>
</style>
</style>
+14 -1
View File
@@ -29,6 +29,7 @@
<InventoryItem
v-for="(item,index) in inventory"
:key="'item-'+index"
:dimensions="dimensions"
:item="item"
:total="sold"
/>
@@ -68,6 +69,18 @@ export default {
default() {
return [];
}
},
groups: {
type: Array,
default() {
return [];
}
},
dimensions: {
type: Array,
default() {
return [];
}
}
},
data() {
@@ -148,4 +161,4 @@ export default {
</script>
<style>
</style>
</style>
+75
View File
@@ -0,0 +1,75 @@
<template>
<select :value="value">
<option
value=""
disabled
selected
>
Bitte wählen
</option>
<slot />
</select>
</template>
<script>
export default {
name: "MaterializeSelect",
props: {
value: {
type: [Number, String],
default: "",
},
},
/**
* @description Component local variables
* @return {Object} data
* @return {undefined|FormSelect} data.instance
*/
data() {
return {
instance: undefined,
};
},
watch: {
value() {
this.instance.destroy();
this.$nextTick(() => (this.instance = this.initializeSelect()));
},
},
mounted() {
this.instance = this.initializeSelect();
this.$el.addEventListener("change", this.changeHandler);
},
destroyed() {
this.$el.removeEventListener("change", this.changeHandler);
},
methods: {
/**
* @description Initialize a new Materialize select component
* @param {Object} options
* @return {FormSelect}
* @see https://materializecss.com/select.html#options
*/
initializeSelect(options = {}) {
return M.FormSelect.init(this.$el, options);
},
/**
* @description Send the proper input event to the parents components
* @param {Event} event
* @param {HTMLSelectElement} target
* @see https://developer.mozilla.org/fr/docs/Web/API/Event/target
*/
changeHandler({ target }) {
this.$emit("input", target.value);
},
},
};
</script>
+4 -3
View File
@@ -8,12 +8,13 @@
<ul class="tabs tabs-transparent">
<li class="tab">
<a
class="active"
@click.stop.prevent="changeTab('inventory', $event)"
>Inventur</a>
</li>
<li class="tab">
<a
class="active"
TweenedNumber
@click.stop.prevent="changeTab('article', $event)"
>Artikelliste</a>
</li>
@@ -39,7 +40,7 @@ export default {
},
data() {
return {
tabs: []
tabs: []
}
},
mounted() {
@@ -60,4 +61,4 @@ export default {
<style>
</style>
</style>
+10 -11
View File
@@ -1,12 +1,11 @@
<template>
<div class="tweened">
<div>{{ intAnimated }}</div>
<div v-if="precision>0">
<span class="tweened">
{{ intAnimated }}
<span v-if="precision>0">
,{{ deciAnimated }}
</div>
<div v-else />
<div>{{ einheit }}</div>
</div>
</span>
{{ einheit }}
</span>
</template>
<script>
@@ -31,15 +30,15 @@ export default {
},
data(){
return {
tweenedValue: 0,
tweenedValue: this.wert,
}
},
computed: {
valueAnimated() {
return this.tweenedValue.toLocaleString(
"de-DE",
{
maximumFractionDigits: 2,
"de-DE",
{
maximumFractionDigits: 2,
minimumFractionDigits: this.precision
}
);
+18 -8
View File
@@ -1,8 +1,10 @@
export class Article{
constructor() {
this.id = 0;
this.name = "";
this.short = "";
this.dimension = "";
this.dimension = 0;
this.group = 0;
this.content = {
size : 0,
price: 0
@@ -10,7 +12,7 @@ export class Article{
this.portion = {
size : 0,
price : 0,
type : ""
type : 0
};
}
get Name() {
@@ -55,6 +57,12 @@ export class Article{
set PortionPrice(value) {
this.portion.price = value;
}
get Group() {
return this.group;
}
set Group(value) {
this.group = value;
}
get ContentPrice() {
return this.Portions * this.portion.price;
}
@@ -68,13 +76,15 @@ Article.thaw = function (json) {
article.id = json.id;
article.Name = json.name;
article.Short = json.short;
article.Dimension = json.dimension;
article.ContentSize = json.content.size;
article.PortionPrice = json.portion.price;
article.PortionSize = json.portion.size;
article.PortionType = json.portion.type;
article.Dimension = json.content_dimension;
article.ContentSize = json.content_size;
article.PortionPrice = json.portion_price;
article.PortionSize = json.portion_size;
article.PortionType = json.portion_dimension;
article.Group = json.group_id;
article.variants = json.variants;
return article;
};
export const thawArticle = Article.thaw;
export const thawArticle = Article.thaw;
+38
View File
@@ -0,0 +1,38 @@
export class Variant{
constructor() {
this.id = 0;
this.name = "";
this.article_id = 0;
this.price = 0;
}
get Name() {
return this.name;
}
set Name(value) {
this.name = value;
}
get ArticleId() {
return this.article_id;
}
set ArticleId(value) {
this.article_id = value;
}
get Price() {
return this.price;
}
set Price(value) {
this.price = value;
}
}
Variant.thaw = function (json) {
var variant = new Variant();
variant.id = json.id;
variant.Name = json.name;
variant.ArticleId = json.article_id;
variant.Price = json.price;
return variant;
};
export const thawVariant = Variant.thaw;