Browse Source

[ADD] Iint Store

adrielso 7 years ago
parent
commit
71d48b77ce

+ 10 - 4
src/App.vue

@@ -6,8 +6,10 @@
             tab-content(title="Que cuota es ?")
             tab-content(title="Como vas a pagar ?")
 </template>
-<script>
 
+<script>
+    import { INIT_PAYMENTS_PURCHASES } from '@/constants/actionTypes'
+    import { mapActions, mapGetters } from 'vuex'
     import { FormWizard, TabContent} from 'vue-form-wizard'
     import 'vue-form-wizard/dist/vue-form-wizard.min.css'
     import Supplier from '@@/steps/Supplier'
@@ -17,9 +19,13 @@
             TabContent,
             FormWizard,
         },
-        // mounted() {
-        //     this
-        // }
+        methods: mapActions([
+            INIT_PAYMENTS_PURCHASES
+        ]),
+
+        mounted() {
+            this.initPaymentsPurchases()
+        }
     }
 </script>
 <style lang="sass">

+ 38 - 0
src/components/common/AddCard.vue

@@ -0,0 +1,38 @@
+<template lang="pug">
+    .add-card(@click='onClick')
+        i.fa.fa-plus(aria-hidden='true')
+</template>
+
+<script>
+    export default {
+        methods: {
+            onClick(e) {
+                this.$emit('onClickAdd')
+            }
+        }
+    }
+</script>
+
+<style lang="sass">
+    @import '../../assets/variables'
+
+    .add-card
+        width: 130px
+        height: 160px
+        margin: 5px
+        border: 1px solid $app-border-color
+        display: inline-block
+        position: relative
+        &:hover
+            cursor: pointer
+        i
+            font-size: 36pt
+            margin: 0
+            border: none
+            position: absolute;
+            top: 50%
+            left: 50%
+            margin-right: -50%
+            transform: translate(-50%, -50%)
+            color: $app-main-color
+</style>

+ 100 - 0
src/components/common/Card.vue

@@ -0,0 +1,100 @@
+<template lang="pug">
+    .card(@click='onClick' :class="{ 'selected-card': isSelected }")
+        h2.card-title {{ title }}
+        img.card-image(:src="'data:image/png;base64,' + (image || 'iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAAAAACPAi4CAAAACXZwQWcAAABAAAAAQADq8/hgAAAEWklEQVRYw9WX6XKjRhCAef8HiySQvGt5vfZuEselOUAcEpe4GdI9MAgQOjb5k3SVyzY1801PX9OtNf9StP80QJR5miRpXtb/AFCnvmMySgmhlJn2Mal+BSBSj1NCGeNSGAMOd0/iQYCI95TAXnm+FCr/I2ZYPwJILEJhPaGm7flBFIW+Z5sUvwEivguovG7pMR0cV2e+BbYArF3cBqQclKfEvryvSB2KaHa6BYhgDSP7ZN7gmUNQCf86wCdgcBaKq04/cTzAuwbA/czKb8VdZYMSI8IAEOJ+XjTiFkF4SDjOARIIHLiBK+4E/xHOIdEloMSAAwZx7hEOBKIquwA4lFPbR/3uEhzCqSUmgBiwrGgeIlQm5b0zO0CN3yKw34QgQC4JKZqrGAFC0MpWvuwJ3V6hWD3BI5wchoDaBAumzYQgmsrd7ewZx5bosHIAAAtQp4+nXUuA+2yXy9Xyi4OsIorjauBLZQWtd0Gqrt3EvCXQlb4BMZYfsPP7cr0gvS4FaNw6Qus0ovtez8DZcYyHt8Wmk9XWdF+Mjf570Ke4q46UgAgUCtX55mKl/wSbsD83hrEE0VGJ1RrEWHz2aaXuIAEe7b3SNG/601oSzL/W20/T2r2uDNACARvjWelZQTTaCiCg2vSR1bzrsFgSQMk8SbPi8FWX+0GFbX2OXMarDoAmOGfo+wpXt7cwj4Hv+1n+rSMYW3HOfS4TAgHZIDIVYG38wNzchyB+kj4ZUwB4npw6ABokmgA2qz9kfbIkoWDLzQSQ0tbw2gA20kA/nmyqCHG8nmqQd2prbSKQZAIwnk5B5PSE/EWfACCUZGFSgHQKeE6DsCcExfc5wKEDRLMaJHBwTwA/zFzhOLBBPGODoCfEyYUb0XVBB1AGHXvho/SVDsSjF15QrtMG1xlpsDbCrCewj7UxAWAJSjsAlJOuHI0AX9Mi8IMgsJnMC2MMOJA2f7RhXI8AG/2LVxZZVlQWmKElnAFiT5nMH62L67Mb3lTmbIzVK3Uc9r6GvJAEyMa6d0KXP1oXliqbRPPzN0NvBcrBAmSpr37wlrB8GeRS6zkJECZVNRKeuLfty1C+wc/zp7TD9jVQN7DUDq2vkUEzfAymIl9uZ5iL1B0U1Rw7surmc4SE/sUBE3KaDB8Wd1QS7hJQga4Kayow2aAsXiV0L458HE/jx9UbPi33CIf+ITwDSnxM/IcIcAGIrHzaH+BX8Ky4awdq41nBZYsjG4/kEQLjg9Q5A9A1jJ7u3CJEa1OzmuvSKgubwPA24IT7WT7fJ5YmEtwbASWO2AkP94871WpPOCc8vmYHaORhv5lf75VrV3bD+9nZIrUJamhXN9v9kMlu3wonYVlGe9msU1/cGTgKpx0YmO2fsrKq66rMk8Bh7dd99sDIk+xxxsE5icqhqfsLflkz1pkbukSCBzI5bqG0EGrPGvfK2FeGDseRi1I5eVFuB8WvDp51FvsH13Fcz4+y6n86Oz8kfwPMD02INEiadQAAAABJRU5ErkJggg==')")
+        .card-details(v-if='details.length > 0')
+            span(v-for='detail in details') {{ computeDetail(detail) }}
+</template>
+
+<script>
+    export default {
+        props: {
+            title: {
+                type: String,
+                default: 'Sin título'
+            },
+            image: {
+                type: String,
+                default: 'iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAAAAACPAi4CAAAACXZwQWcAAABAAAAAQADq8/hgAAAEWklEQVRYw9WX6XKjRhCAef8HiySQvGt5vfZuEselOUAcEpe4GdI9MAgQOjb5k3SVyzY1801PX9OtNf9StP80QJR5miRpXtb/AFCnvmMySgmhlJn2Mal+BSBSj1NCGeNSGAMOd0/iQYCI95TAXnm+FCr/I2ZYPwJILEJhPaGm7flBFIW+Z5sUvwEivguovG7pMR0cV2e+BbYArF3cBqQclKfEvryvSB2KaHa6BYhgDSP7ZN7gmUNQCf86wCdgcBaKq04/cTzAuwbA/czKb8VdZYMSI8IAEOJ+XjTiFkF4SDjOARIIHLiBK+4E/xHOIdEloMSAAwZx7hEOBKIquwA4lFPbR/3uEhzCqSUmgBiwrGgeIlQm5b0zO0CN3yKw34QgQC4JKZqrGAFC0MpWvuwJ3V6hWD3BI5wchoDaBAumzYQgmsrd7ewZx5bosHIAAAtQp4+nXUuA+2yXy9Xyi4OsIorjauBLZQWtd0Gqrt3EvCXQlb4BMZYfsPP7cr0gvS4FaNw6Qus0ovtez8DZcYyHt8Wmk9XWdF+Mjf570Ke4q46UgAgUCtX55mKl/wSbsD83hrEE0VGJ1RrEWHz2aaXuIAEe7b3SNG/601oSzL/W20/T2r2uDNACARvjWelZQTTaCiCg2vSR1bzrsFgSQMk8SbPi8FWX+0GFbX2OXMarDoAmOGfo+wpXt7cwj4Hv+1n+rSMYW3HOfS4TAgHZIDIVYG38wNzchyB+kj4ZUwB4npw6ABokmgA2qz9kfbIkoWDLzQSQ0tbw2gA20kA/nmyqCHG8nmqQd2prbSKQZAIwnk5B5PSE/EWfACCUZGFSgHQKeE6DsCcExfc5wKEDRLMaJHBwTwA/zFzhOLBBPGODoCfEyYUb0XVBB1AGHXvho/SVDsSjF15QrtMG1xlpsDbCrCewj7UxAWAJSjsAlJOuHI0AX9Mi8IMgsJnMC2MMOJA2f7RhXI8AG/2LVxZZVlQWmKElnAFiT5nMH62L67Mb3lTmbIzVK3Uc9r6GvJAEyMa6d0KXP1oXliqbRPPzN0NvBcrBAmSpr37wlrB8GeRS6zkJECZVNRKeuLfty1C+wc/zp7TD9jVQN7DUDq2vkUEzfAymIl9uZ5iL1B0U1Rw7surmc4SE/sUBE3KaDB8Wd1QS7hJQga4Kayow2aAsXiV0L458HE/jx9UbPi33CIf+ITwDSnxM/IcIcAGIrHzaH+BX8Ky4awdq41nBZYsjG4/kEQLjg9Q5A9A1jJ7u3CJEa1OzmuvSKgubwPA24IT7WT7fJ5YmEtwbASWO2AkP94871WpPOCc8vmYHaORhv5lf75VrV3bD+9nZIrUJamhXN9v9kMlu3wonYVlGe9msU1/cGTgKpx0YmO2fsrKq66rMk8Bh7dd99sDIk+xxxsE5icqhqfsLflkz1pkbukSCBzI5bqG0EGrPGvfK2FeGDseRi1I5eVFuB8WvDp51FvsH13Fcz4+y6n86Oz8kfwPMD02INEiadQAAAABJRU5ErkJggg=='
+            },
+            details: {
+                type: Array,
+                default: []
+            },
+            isSelected: {
+                type: Boolean,
+                default: true
+            },
+            options: {
+                type: Object,
+                default: {}
+            }
+        },
+        methods: {
+            computeDetail(detail) {
+                if (detail.format === 'currency') {
+                    return this.$options.filters.currency(detail.value, {...this.options})
+                }
+
+                return detail.value
+            },
+            onClick() {
+                this.$emit('onClick')
+            }
+        } 
+    }
+</script>
+
+<style lang="sass">
+    @import '../../assets/variables'
+
+    .card
+        width: 130px
+        height: 160px
+        margin: 5px
+        border: 1px solid $app-border-color
+        display: inline-block
+        position: relative
+        &.selected-card
+            transition-duration: 300ms
+            border-bottom: 3px solid $app-main-color
+        &:hover
+            cursor: pointer
+        .card-title
+            width: 100%
+            height: 30px
+            font-size: 9pt
+            text-align: center
+            margin-top: 10px
+            position: absolute
+            top: 0
+        .card-image
+            width: 80px
+            height: 80px;
+            margin: 0
+            border: none
+            position: absolute
+            top: 50%
+            left: 50%
+            margin-right: -50%
+            transform: translate(-50%, -50%)
+        .card-details
+            width: 100%
+            height: 30px
+            padding-top: 5px
+            text-align: center
+            font-size: 10pt
+            font-weight: bold
+            background: $app-main-color
+            color: $app-bg-color
+            position: absolute
+            bottom: 0
+
+        @keyframes card-bubble
+            30%
+                transform: scaleX(0.75) scaleY(1.25)
+            40%
+                transform: scaleX(1.25) scaleY(0.75)
+            60%
+                transform: scaleX(0.85) scaleY(1.15)
+</style>

+ 142 - 0
src/components/common/CardGrid.vue

@@ -0,0 +1,142 @@
+<template lang="pug">
+    .card-grid-wrapper
+        .card-grid-loading(v-if='loading')
+            spinner(type='wave')
+        .card-grid(v-else)
+            add-card(v-if='canAdd' @onClickAdd='onClickAdd')
+            card(v-for='item in items' :key='item.id' :title='item.name' :image='item.imageMedium' :isSelected='item.id === selectedId' :details='computeDetails(item)' :options='defaultOptions.currency' @onClick='onClickCard(item)')
+</template>
+
+<script>
+    import AddCard from '@/components/common/AddCard'
+    import Card from '@/components/common/Card'
+    import Spinner from '@/components/common/Spinner'
+
+    export default {
+        props: {
+            items: {
+                type: Array,
+                default: []
+            },
+            canAdd: {
+                type: Boolean,
+                default: false
+            },
+            details: {
+                type: Array,
+                default: []
+            },
+            loading: {
+                type: Boolean,
+                default: false
+            },
+            options: {
+                type: Object,
+                default: {}
+            }
+        },
+        components: {
+            AddCard,
+            Card,
+            Spinner
+        },
+        watch: {
+            options(value) {
+                this.computeOptions(value)
+            }
+        },
+        methods: {
+            computeDetails(item) {
+                if (!this.details) {
+                    return []
+                }
+
+                if (this.details.length === 0) {
+                    return []
+                }
+
+                let results = []
+                let computableDetails = this.details.map(item => item.split(/:/))
+
+                for (let detail of computableDetails) {
+                    for (let field in item) {
+                        if (field === detail[0]) {
+                            results.push({
+                                value: item[field],
+                                format: (() => {
+                                    if (!detail[1] || detail[1] === 's') {
+                                        return 'string'
+                                    }
+                                    
+                                    if (detail[1] === 'c') {
+                                        return 'currency'
+                                    }
+
+                                    if (detail[1] === 'd') {
+                                        return 'date'
+                                    }
+
+                                    return 'string'
+                                })()
+                            })
+
+                            break
+                        }
+                    }
+                }
+
+                return results
+            },
+            computeOptions(value) {
+                if (!value) {
+                    return
+                }
+
+                for(let key in value) {
+                    if(!this.defaultOptions.currency[key]) {
+                        continue
+                    }
+
+                    this.defaultOptions.currency[key] = value[key]
+                }
+            },
+            onClickAdd() {
+                this.$emit('onAdd')
+            },
+            onClickCard(item) {
+                this.selectedId = item.id
+                this.$emit('onSelect', item)
+            }
+        },
+        data() {
+            return {
+                selectedId: -1,
+                defaultOptions: {
+                    currency: {
+                        symbol: '$',
+                        position: 'before',
+                        thousandsSeparator: '.',
+                        decimalPlaces: 2,
+                        decimalSeparator: ',' 
+                    },
+                } 
+            }
+        }
+    }
+</script>
+
+<style lang="sass">
+    .card-grid-wrapper
+        width: 100%
+        height: calc(100% - 50px)
+        margin-top: 10px
+        overflow-y: auto
+        .card-grid-loading
+            width: 100%
+            height: 100%
+            display: flex
+            align-items: center
+            justify-content: center
+        .card-grid
+            width: 100%
+</style>

+ 130 - 0
src/components/common/Cart.vue

@@ -0,0 +1,130 @@
+<template lang="pug">
+    .cart(:style='{ width: defaultOptions.layout.width, height: defaultOptions.layout.height }')
+        .cart-total
+            h2.currency-cart-total {{ total | currency(...defaultOptions.currency) }}
+        .cart-items-wrapper
+            transition-group(name='list' tag='ul' class='cart-items')
+                cart-item(v-for='(item, index) in items' :key='index' :index='index' :item='item' @onChange='onItemChanged' @onClickIncrement='onIncrementQty' @onClickDecrement='onDecrementQty' @onClickMoney='onChangePrice' @onClickUndo='onUndoPrice' @onClickDelete='onDeleteItem' :options='defaultOptions.currency')
+</template>
+
+<script>
+    import CartItem from './CartItem'
+
+    export default {
+        components: {
+            CartItem
+        },
+        props: {
+            items: {
+                type: Array,
+                default: [],
+                required: true
+            },
+            options: {
+                type: Object || String,
+                default: null
+            }
+        },
+        methods: {
+            computeOptions(value) {
+                if (!value) {
+                    return
+                }
+
+                for(let key in value) {
+                    if(!this.defaultOptions.currency[key]) {
+                        continue
+                    }
+
+                    this.defaultOptions.currency[key] = value[key]
+                }
+            },
+            computeTotal() {
+                let sum = 0
+
+                for (let item of this.items) {
+                    sum = sum + ((item.price || 0) * (item.quantity || 0))
+                }
+
+                this.total = sum
+
+                this.$emit('onTotalComputed', this.total)
+            },
+            onItemChanged(item) {
+                this.computeTotal();
+            },
+            onIncrementQty(item) {
+                this.$emit('onIncrementQty', item)
+            },
+            onDecrementQty(item) {
+                this.$emit('onDecrementQty', item)
+            },
+            onChangePrice(item) {
+                this.$emit('onChangePrice', item)
+            },
+            onUndoPrice(item) {
+                this.$emit('onUndoPrice', item)
+            },
+            onDeleteItem(item) {
+                this.$emit('onDeleteItem', item)
+            }
+        },
+        watch: {
+            items() {
+                this.computeTotal()
+            },
+            options(value) {
+                this.computeOptions(value)
+            }
+        },
+        data() {
+            return {
+                total: 0,
+                defaultOptions: {
+                    currency: {
+                        symbol: '$',
+                        position: 'before',
+                        thousandsSeparator: '.',
+                        decimalPlaces: 2,
+                        decimalSeparator: ',' 
+                    },
+                    layout: {
+                        width: '300px',
+                        height: '100%'
+                    }
+                }
+            }
+        }
+    }
+</script>
+
+<style lang="sass">
+    @import '../../assets/variables'
+    .cart
+        border-left: 1px solid $app-border-color
+        padding-left: 10px
+        display: inline-block
+        vertical-align: top
+        .cart-total
+            width: 100%
+            height: 50px
+            .currency-cart-total
+                width: 100%
+                height: 50px
+                margin: 0
+                font-size: 30pt
+        .cart-items-wrapper
+            width: 100%
+            height: calc(100% - 100px)
+            overflow-y: auto
+            overflow-x: hidden
+            .cart-items
+                width: 100%
+                padding: 0
+                margin: 0
+            .list-enter-active, .list-leave-active
+                transition: all 0.3s
+            .list-enter, .list-leave-to
+                opacity: 0
+                transform: translateX(300px)
+</style>

+ 168 - 0
src/components/common/CartItem.vue

@@ -0,0 +1,168 @@
+<template lang="pug">
+    li.cart-item(:class="{'cart-item-invalid': !isValid()}")
+        h3.item-name {{ item.displayName }}
+        input.item-quantity(type='number' min='1' :value='item.quantity' readonly)
+        span.item-x x
+        span.item-price {{ item.price | currency(...options) }}
+        span.item-equals =
+        span.item-subtotal {{ (item.price * (item.quantity || 1)) | currency(...options) }}
+        .cart-item-options-wrapper
+            .cart-item-options
+                .cart-item-option(class='fa fa-plus' @click='onClickIncrement')
+                .cart-item-option(class='fa fa-minus' @click='onClickDecrement')
+                .cart-item-option(class='fa fa-money' @click='onClickMoney')
+                .cart-item-option(class='fa fa-undo' @click='onClickUndo')
+                .cart-item-option(class='fa fa-trash' @click='onClickDelete')
+</template>
+
+<script>
+    export default {
+        props: {
+            index: {
+                type: Number,
+                default: -1,
+                required: true
+            },
+            item: {
+                type: Object,
+                default: null
+            },
+            options: {
+                type: Object,
+                default: {
+                    symbol: '$',
+                    position: 'before',
+                    thousandsSeparator: '.',
+                    decimalPlaces: 2,
+                    decimalSeparator: ',' 
+                }
+            }
+        },
+        watch: {
+            item: {
+                handler(value) {
+                    this.onChange(value)
+                },
+                deep: true,
+                immediate: true
+            }
+        },
+        methods: {
+            onChange(item) {
+                this.$emit('onChange', item)
+            },
+            onClickIncrement() {
+                this.$emit('onClickIncrement', this.item)
+            },
+            onClickDecrement() {
+                this.$emit('onClickDecrement', this.item)
+            },
+            onClickMoney() {
+                this.$emit('onClickMoney', this.item)
+            },
+            onClickUndo() {
+                this.$emit('onClickUndo', this.item)
+            },
+            onClickDelete() {
+                this.$emit('onClickDelete', this.item)
+            },
+            isValid() {
+                return this.item.price > 0
+            }
+        }
+    }
+</script>
+
+<style lang="sass">
+    @import '../../assets/variables'
+    .cart-item
+        width: 100%
+        height: 90px
+        list-style: none outside none
+        border-bottom: 1px solid $app-border-color
+        box-sizing: border-box
+        position: relative
+        &.cart-item-invalid
+            border-bottom: 2px solid $app-error-color
+        &:nth-child(1)
+            border-top: 1px solid $app-border-color
+        &:hover
+            transition-duration: 1000ms
+            border-bottom: 2px solid $app-main-color
+        .item-name
+            width: 100%
+            height: 20px
+            margin: 10px 0 5px 0
+            float: left
+            font-size: 8pt
+            display: inline-block
+        .item-quantity
+            width: 50px
+            height: 28px
+            margin-top: 6px
+            text-align: right
+            float: left
+            display: inline-block
+        .item-x
+            width: 20px
+            height: 20px
+            margin-top: 12px
+            text-align: right
+            float: left
+            display: inline-block
+        .item-price
+            width: 80px
+            height: 20px
+            margin-top: 12px
+            text-align: right
+            float: left
+            display: inline-block
+        .item-equals
+            width: 20px
+            height: 20px
+            margin-top: 12px
+            text-align: center
+            float: left
+            display: inline-block
+        .item-subtotal
+            width: 100px
+            height: 20px
+            margin-top: 12px
+            text-align: right
+            font-weight: bold
+            display: inline-block
+        .cart-item-options-wrapper
+            width: 100%
+            height: 20px
+            position: absolute
+            bottom: 0
+            display: flex
+            justify-content: center
+            .cart-item-options
+                width: 120px
+                height: 20px
+                border: 1px solid #d3d3d3
+                border-bottom: none
+                display: flex
+                justify-content: center
+                .cart-item-option
+                    width: 18px
+                    height: 18px
+                    margin: 0 5px
+                    color: #666
+                    &:hover
+                        cursor: pointer
+                    &.fa
+                        padding-left: 2px
+                        line-height: 20px
+                        &.fa-plus:hover
+                            color: #2196f3
+                        &.fa-minus:hover
+                            color: #ffc107
+                        &.fa-money:hover
+                            color: #4caf50
+                        &.fa-undo:hover
+                            color: #3f51b5
+                        &.fa-trash:hover
+                            color: #f44336
+</style>

+ 143 - 0
src/components/common/Searcher.vue

@@ -0,0 +1,143 @@
+<template lang="pug">
+    input.searcher(type='search' :placeholder='placeholder' :style='{ width, height }' v-model='search')
+</template>
+
+<script>
+    import Fuse from 'fuse.js'
+
+    export default {
+        props: {
+            items: {
+                type: Array,
+                default: [],
+                required: true
+            },
+            placeholder: {
+                type: String,
+                default: 'Buscar...'
+            },
+            width: {
+                type: String,
+                default: '100%'
+            },
+            height: {
+                type: String,
+                default: '35px'
+            },
+            shouldSort: {
+                type: Boolean,
+                default: true
+            },
+            threshold: {
+                type: Number,
+                default: 0.4,
+            },
+            location: {
+                type: Number,
+                default: 0
+            },
+            distance: {
+                type: Number,
+                default: 100
+            },
+            maxPatternLength: {
+                type: Number,
+                default: 32
+            },
+            minMatchCharLength: {
+                type: Number,
+                default: 1
+            },
+            keys: {
+                type: Array,
+                default: [],
+                required: true
+            },
+            mode: {
+                type: String,
+                default: 'fuzzy'
+            }
+        },
+        watch: {
+            items(values) {
+                if (this.mode !== 'fuzzy') {
+                    return
+                }
+
+                this.fuse.setCollection(values)
+            },
+            search(value) {
+                this.performSearch(value.trim())
+            },
+            results(values) {
+                this.$emit('onSearch', values)
+            }
+        },
+        methods: {
+            initFuse() {
+                this.fuse = new Fuse(this.items, {
+                    shouldSort: this.shouldSort,
+                    threshold: this.threshold,
+                    location: this.location,
+                    distance: this.distance,
+                    maxPatternLength: this.maxPatternLength,
+                    minMatchCharLength: this.minMatchCharLength,
+                    keys: this.keys
+                })
+            },
+            performSearch(value) {
+                if(!value) {
+                    this.results = []
+                    return
+                }
+
+                if (this.mode === 'fuzzy') {
+                    this.results = this.fuse.search(value)
+                } else {
+                    this.results = []
+
+                    for (let item of this.items) {
+                        for (let field in item) {
+                            if (typeof item[field] !== 'string') {
+                                continue
+                            }
+
+                            if (this.keys.length !== 0 && this.keys.indexOf(field) === -1) {
+                                continue
+                            }
+
+                            if (item[field].toLowerCase().indexOf(value.toLowerCase()) !== -1) {
+                                this.results.push(item)
+                                break
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        data() {
+            return {
+                fuse: null,
+                search: '',
+                results: []
+            }
+        },
+        mounted() {
+            if (this.mode !== 'fuzzy') {
+                return
+            }
+            
+            this.initFuse()
+        }
+    }
+</script>
+
+<style lang="sass">
+    .searcher
+        text-align: center
+        border-radius: 0 !important
+        font:
+            size: 11pt
+            style: normal
+            weight: bold
+</style>

+ 102 - 0
src/components/common/Spinner.vue

@@ -0,0 +1,102 @@
+<template lang="pug">
+    .spinner
+        .spinner-wave(v-if="type === 'wave'")
+            .spinner-rect.spinner-rect-1
+            .spinner-rect.spinner-rect-2
+            .spinner-rect.spinner-rect-3
+            .spinner-rect.spinner-rect-4
+            .spinner-rect.spinner-rect-5
+        .spinner-circle(v-if="type === 'circle'")
+            .spinner-circle-1.spinner-child
+            .spinner-circle-2.spinner-child
+            .spinner-circle-3.spinner-child
+            .spinner-circle-4.spinner-child
+            .spinner-circle-5.spinner-child
+            .spinner-circle-6.spinner-child
+            .spinner-circle-7.spinner-child
+            .spinner-circle-8.spinner-child
+            .spinner-circle-9.spinner-child
+            .spinner-circle-10.spinner-child
+            .spinner-circle-11.spinner-child
+            .spinner-circle-12.spinner-child
+</template>
+
+<script>
+    export default {
+        props: {
+            type: String,
+            default: 'wave'
+        }
+    }
+</script>
+
+<style lang="sass">
+    @import '../../assets/variables'
+    .spinner
+        .spinner-wave
+            $rect-count: 5
+            $animation-duration: 1000ms
+            $delay-range: 400ms
+
+            width: 50px
+            height: 40px
+            text-align: center
+            font-size: 10px
+            margin: 40px auto
+
+            .spinner-rect
+                width: 5px
+                height: 100%
+                background: $app-main-color
+                margin: 0 3px 0 0
+                display: inline-block
+                animation: spinner-rect-wave $animation-duration infinite ease-in-out
+
+            @for $i from 1 through $rect-count
+                .spinner-rect-#{$i}
+                    animation-delay: - $animation-duration + $delay-range / ($rect-count - 1) * ($i - 1)
+
+        .spinner-circle
+            $circle-count: 12
+            $animation-duration: 1200ms
+
+            margin: 40px auto
+            width: 40px
+            height: 40px
+            position: relative
+
+            .spinner-child
+                width: 100%
+                height: 100%
+                position: absolute
+                left: 0
+                top: 0
+            
+            .spinner-child:before
+                content: ''
+                display: block
+                margin: 0 auto
+                width: 15%
+                height: 15%
+                background: $app-main-color
+                border-radius: 100%
+                animation: spinner-circle-bounce $animation-duration infinite ease-in-out both
+            
+            @for $i from 2 through $circle-count
+                .spinner-circle#{$i}
+                    transform: rotate(360deg / $circle-count * ($i - 1))
+                .spinner-circle#{$i}:before
+                    animation-delay: - $animation-duration + $animation-duration / $circle-count * ($i - 1)
+    
+    @keyframes spinner-rect-wave
+        0%, 40%, 100%
+            transform: scaleY(0.4)
+        20%
+            transform: scaleY(1.0)
+
+    @keyframes spinner-circle-bounce
+        0%, 80%, 100%
+            transform: scale(0)
+        40%
+            transform: scale(1.0)
+</style>

+ 162 - 0
src/components/common/Ticket.vue

@@ -0,0 +1,162 @@
+<template lang="pug">
+    .ticket
+        .ticket-summary
+            .ticket-summary-header
+                h3 {{ companyName }}
+                table
+                    tbody
+                        tr
+                            td Producto
+                            td Precio
+                            td Cant
+                            td Subtotal
+            .ticket-items-wrapper
+                table
+                    tbody
+                        tr(v-for='item in items' :key='item.id')
+                            td {{ item.name }}
+                            td {{ item.price }}
+                            td {{ item.quantity }}
+                            td {{ (item.price || 0) * (item.quantity || 0) }}
+            .ticket-summary-footer
+                table
+                    tbody
+                        tr
+                            td Total:
+                            td {{ total | currency(...defaultCurrency) }}
+                        tr
+                            td Cliente:
+                            td {{ customerName }}
+</template>
+
+<script>
+    export default {
+        props: {
+            companyName: {
+                type: String,
+                default: ''
+            },
+            customerName: {
+                type: String,
+                default: ''
+            },
+            defaultCurrency: {
+                type: Object,
+                default: {
+                    symbol: '$',
+                    position: 'before',
+                    thousandsSeparator: '.',
+                    decimalPlaces: 2,
+                    decimalSeparator: ',' 
+                }
+            },
+            total: {
+                type: Number,
+                default: 0
+            },
+            items: {
+                type: [],
+                default: []
+            }
+        }
+    }
+</script>
+
+<style lang="sass">
+    @import '../../assets/variables'
+    .ticket
+        width: 500px
+        height: 100%
+        .ticket-summary
+            width: 350px
+            height: 450px
+            border: 1px solid $app-border-color
+            margin: auto
+            box-shadow: -2px 2px 5pc $app-border-color, 2px 2px 5px $app-border-color
+            position: relative
+            .ticket-summary-header, .ticket-summary-footer
+                width: 100%
+                position: absolute
+            .ticket-summary-header
+                height: 65px
+                top: 0
+                h3
+                    text-align: center
+                    font-size: 14pt
+                    margin: 0 15px
+                    padding: 30px 0 15px 0
+                    color: $app-dark-color
+                table
+                    width: 308px
+                    height: 30px
+                    margin: 0 20px
+                    font-size: 7.5pt
+                    font-weight: bold
+                    tbody
+                        tr
+                            line-height: 30px
+                            border-top: 1px solid $app-border-color
+                            border-bottom: 1px solid $app-border-color
+                            td
+                                &:nth-child(1)
+                                    width: 180px
+                                    text-align: left
+                    
+                                &:nth-child(2)
+                                    width: 50px
+                                    text-align: right
+                    
+                                &:nth-child(3)
+                                    width: 30px
+                                    text-align: right
+
+                                &:nth-child(4)
+                                    width: 50px
+                                    text-align: right
+            .ticket-items-wrapper
+                width: 310px
+                height: 280px
+                margin: 95px 20px 75px 20px
+                padding-top: 5px
+                overflow-y: auto
+                table
+                    width: 100%
+                    font-size: 7.5pt
+                    tbody
+                        tr
+                            height: 28px
+                            line-height: 30px
+
+                            td
+                                &:nth-child(1)
+                                    width: 180px
+                    
+                                &:nth-child(2)
+                                    width: 50px
+                                    text-align: right
+                    
+                                &:nth-child(3)
+                                    width: 30px
+                                    text-align: right
+
+                                &:nth-child(4)
+                                    width: 50px
+                                    text-align: right
+            .ticket-summary-footer
+                width: 348px
+                height: 75px
+                bottom: 0
+                padding: 15px 25px
+                background: $app-bg-color
+                table
+                    width: 100%
+                    tbody
+                        tr
+                            height: 25px
+                            line-height: 20px
+                            td
+                                &:nth-child(1)
+                                    font-weight: bold
+                                &:nth-child(2)
+                                    text-align: right
+</style>

+ 13 - 0
src/components/common/index.js

@@ -0,0 +1,13 @@
+import CardGrid from './CardGrid'
+import Cart from './Cart'
+import Searcher from './Searcher'
+import Ticket from './Ticket'
+import Spinner from './Spinner'
+
+export {
+    CardGrid,
+    Cart,
+    Searcher,
+    Ticket,
+    Spinner
+}

+ 36 - 0
src/constants/actionTypes.js

@@ -0,0 +1,36 @@
+/**
+ * [Initial]
+ */
+const INIT_PAYMENTS_PURCHASES = 'initPaymentsPurchases'
+const PAYMENTS_PURCHASES_NOTIFY = 'paymentsPurchasesNotify'
+const EXPLODE_DATA = 'explodeData'
+/**
+ * [ Date]
+ */
+const INIT_PAYMENTS_PURCHASES_DATE = 'initPaymentsPurchasesDate'
+/**
+ * [ User ]
+ */
+const INIT_PAYMENTS_PURCHASES_USER = 'initPaymentsPurchasesUser'
+/**
+ * [ supplier ]
+ */
+const INIT_PAYMENTS_PURCHASES_SUPPLIER = 'initPaymentsPurchasesSupplier'
+/**
+ * [ Journal]
+ */
+const INIT_PAYMENTS_PURCHASES_JOURNALS = 'initPaymentsPurchasesJournals'
+/**
+ * [ Currency ]
+ */
+const INIT_PAYMENTS_PURCHASES_CURRENCIES = 'initPaymentsPurchasesCurrencies'
+
+
+export {
+    INIT_PAYMENTS_PURCHASES, PAYMENTS_PURCHASES_NOTIFY, EXPLODE_DATA, //INIT
+    INIT_PAYMENTS_PURCHASES_DATE, //DATE
+    INIT_PAYMENTS_PURCHASES_USER, //USER
+    INIT_PAYMENTS_PURCHASES_SUPPLIER, //SUPPLIER
+    INIT_PAYMENTS_PURCHASES_JOURNALS, //JOURNAL
+    INIT_PAYMENTS_PURCHASES_CURRENCIES //CURRENCIES
+}

+ 34 - 0
src/constants/mutationTypes.js

@@ -0,0 +1,34 @@
+/**
+ * [ Date]
+ */
+const SET_DATE = 'setDate'
+const SET_LOADING_DATE = 'setLoadingDate'
+/**
+ * [ User ]
+ */
+const SET_USER = 'setUser'
+const SET_LOADING_USER = 'setLoadingUser'
+/**
+ * [ supplier ]
+ */
+const SET_SUPPLIER = 'setSupplier'
+const SET_LOADING_SUPPLIER = 'setLoadingSupplier'
+/**
+ * [ Journal]
+ */
+const SET_JOURNALS = 'setJournals'
+const SET_LOADING_JOURNALS = 'setLoadingJournals'
+/**
+ * [ Currency ]
+ */
+const SET_CURRENCIES = 'setCurrencies'
+const SET_LOADING_CURRENCIES = 'setLoadingCurrencies'
+
+
+export {
+    SET_DATE, SET_LOADING_DATE, //DATE
+    SET_USER,  SET_LOADING_USER, //USER
+    SET_SUPPLIER, SET_LOADING_SUPPLIER, //SUPPLIER
+    SET_JOURNALS, SET_LOADING_JOURNALS, //JOURNALS
+    SET_CURRENCIES, SET_LOADING_CURRENCIES //CURRENCIES
+}

+ 7 - 0
src/constants/resourcePaths.js

@@ -0,0 +1,7 @@
+const BASE_URL = '/eiru_payments_purchases'
+const INIT_PAYMENTS_PURCHASES_URL = `${BASE_URL}/init`
+// const PAYMENTS_PURCHASES_PROCESS_URL = `${BASE_URL}/purchases_process`
+
+export {
+    INIT_PAYMENTS_PURCHASES_URL
+}

+ 4 - 2
src/index.js

@@ -1,17 +1,19 @@
 import Vue from 'vue'
 import App from '@/App'
+import store from '@/store'
 
 /*config*/
 Vue.config.productionTip = false
 Vue.config.silent = true
-Vue.config.devtools = false
+Vue.config.devTools = false
+
 
 openerp.eiru_payments_purchases = (instance, local) => {
     local.PaymentsPurchasesWidget = instance.Widget.extend({
         start() {
             this.sidebarFold()
             this.vm = new Vue({
-
+                store,
                 el: this.el,
                 template: '<App />',
                 components: {

+ 40 - 0
src/store/actions.js

@@ -0,0 +1,40 @@
+import axios from 'axios'
+import {
+    INIT_PAYMENTS_PURCHASES_URL
+} from '@/constants/resourcePaths'
+
+import {
+    INIT_PAYMENTS_PURCHASES,
+    PAYMENTS_PURCHASES_NOTIFY,
+    EXPLODE_DATA
+} from '@/constants/actionTypes'
+const actions = {
+    /**
+     * [PAYMENTS_PURCHASES_NOTIFY]
+     */
+    [PAYMENTS_PURCHASES_NOTIFY] ({ commit }, paylod) {
+        opererp.web.notification.do_warn('Atencion', paylod)
+        return false
+    },
+    /**
+     * [response description]
+     * @type {[type]}
+     */
+    [INIT_PAYMENTS_PURCHASES] ({ dispatch }) {
+        return axios.get(INIT_PAYMENTS_PURCHASES_URL).then(response => {
+            dispatch(EXPLODE_DATA, response.data)
+        }).catch(error => {
+            console.log(error);
+        } )
+    },
+    /**
+     *  [EXPLODE_DATA]
+     */
+    [EXPLODE_DATA] ({ dispatch },paylod) {
+        for (let value in paylod) {
+            dispatch(`initPaymentsPurchases${value[0].toUpperCase()}${value.slice(1)}`, paylod[value])
+        }
+    }
+}
+
+export default actions

+ 0 - 0
src/store/getters.js


+ 34 - 0
src/store/index.js

@@ -0,0 +1,34 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+/**
+ * [ Store ]
+ */
+import actions from '@/store/actions'
+// import getters from '@/store/getters'
+// import mutations from '@/store/mutations'
+// import state from '@/store/state'
+
+/**
+ * [ Modules]
+ */
+import paymentsPurchasesDate from '@/store/modules/PaymentsPurchasesDate'
+import paymentsPurchasesUser from '@/store/modules/PaymentsPurchasesUser'
+import paymentsPurchasesSupplier from '@/store/modules/PaymentsPurchasesSupplier'
+import paymentsPurchasesJournals from '@/store/modules/PaymentsPurchasesJournals'
+import paymentsPurchasesCurrencies from '@/store/modules/PaymentsPurchasesCurrencies'
+
+
+Vue.use(Vuex)
+
+const store = new Vuex.Store({
+    actions,
+    modules: {
+        paymentsPurchasesDate,
+        paymentsPurchasesUser,
+        paymentsPurchasesSupplier,
+        paymentsPurchasesJournals,
+        paymentsPurchasesCurrencies
+    }
+})
+
+export default store

+ 54 - 0
src/store/modules/PaymentsPurchasesCurrencies.js

@@ -0,0 +1,54 @@
+import { INIT_PAYMENTS_PURCHASES_CURRENCIES } from '@/constants/actionTypes'
+import { SET_CURRENCIES,  SET_LOADING_CURRENCIES }from '@/constants/mutationTypes'
+
+const initialState = {
+    currency: [],
+    loadingCurrency: false
+}
+
+const state = {
+    currency: initialState.currency,
+    loadingCurrency: initialState.loadingCurrency
+}
+
+const getters = {
+    /**
+     * [currency description]
+     * @param  {[type]} state [description]
+     * @return {[type]}       [description]
+     */
+    currency ( state ) {
+        return state.currency
+    },
+    /**
+     * [loadingCurrency description]
+     * @param  {[type]} state [description]
+     * @return {[type]}       [description]
+     */
+    loadingCurrency ( state ) {
+        return state.loadingCurrency
+    }
+}
+
+const mutations = {
+    [SET_CURRENCIES] ( state, paylod ){
+        state.currency = paylod
+    },
+    [SET_LOADING_CURRENCIES] ( state, paylod ) {
+        state.loadingCurrency = !!paylod
+    }
+}
+
+const actions = {
+    [INIT_PAYMENTS_PURCHASES_CURRENCIES] ({ commit }, paylod) {
+        commit(SET_CURRENCIES, paylod)
+        commit(SET_LOADING_CURRENCIES, paylod)
+    }
+}
+
+export default {
+    state,
+    getters,
+    mutations,
+    actions
+}

+ 65 - 0
src/store/modules/PaymentsPurchasesDate.js

@@ -0,0 +1,65 @@
+import { INIT_PAYMENTS_PURCHASES_DATE } from '@/constants/actionTypes'
+import { SET_DATE, SET_LOADING_DATE } from '@/constants/mutationTypes'
+
+const initialState = {
+    date: null,
+    loadingDate: false
+}
+
+const state = {
+    date: initialState.date,
+    loadingDate: initialState.loadingDate
+}
+
+const getters = {
+    /**
+     * [date description]
+     * @param  {[type]} state [description]
+     * @return {[type]}       [description]
+     */
+    date (state) {
+        return state.date
+    },
+    /**
+     * [loadingDate description]
+     * @param  {[type]} state [description]
+     * @return {[type]}       [description]
+     */
+    loadingDate (state) {
+        return state.loadingDate
+    }
+}
+
+const mutations = {
+    /**
+     * [date description]
+     * @type {[type]}
+     */
+    [SET_DATE] (state, paylod) {
+        state.date = paylod
+    },
+    /**
+     * [loadingDate description]
+     * @type {[type]}
+     */
+    [SET_LOADING_DATE] (state, paylod) {
+        state.loadingDate = !!paylod
+    }
+}
+
+const actions = {
+    /**
+     * [INIT_PAYMENTS_PURCHASES_DATE]
+     */
+    [INIT_PAYMENTS_PURCHASES_DATE] ({ commit }, paylod) {
+        commit(SET_DATE, paylod)
+        commit(SET_LOADING_DATE, paylod)
+    }
+}
+
+export default {
+    state,
+    getters,
+    mutations,
+    actions
+}

+ 65 - 0
src/store/modules/PaymentsPurchasesJournals.js

@@ -0,0 +1,65 @@
+import { INIT_PAYMENTS_PURCHASES_JOURNALS } from '@/constants/actionTypes'
+import { SET_JOURNALS ,SET_LOADING_JOURNALS } from '@/constants/mutationTypes'
+
+const initialState = {
+    journals : null,
+    loadingJournals: false
+}
+
+const state = {
+    journals: initialState.journals,
+    loadingJournals: initialState.loadingJournals
+}
+
+const getters  = {
+    /**
+     * [journals description]
+     * @param  {[type]} state [description]
+     * @return {[type]}       [description]
+     */
+    journals ( state ) {
+        return state.journals
+    },
+    /**
+     * [loadingJournals description]
+     * @param  {[type]} state [description]
+     * @return {[type]}       [description]
+     */
+    loadingJournals( state ) {
+        return state.loadingJournals
+    }
+}
+
+const mutations = {
+    /**
+     * [journals description]
+     * @type {[type]}
+     */
+    [SET_JOURNALS] (state, paylod ) {
+        state.journals = paylod
+    },
+    /**
+     * [loadingJournals description]
+     * @type {[type]}
+     */
+    [SET_LOADING_JOURNALS] (state, paylod) {
+        state.loadingJournals = !!paylod
+    }
+}
+
+ const actions = {
+     /**
+      * [INIT_PAYMENTS_PURCHASES_JOURNALS]
+      */
+     [INIT_PAYMENTS_PURCHASES_JOURNALS] ({ commit }, paylod) {
+         commit(SET_JOURNALS, paylod)
+         commit(SET_LOADING_JOURNALS, paylod)
+     }
+ }
+
+ export default {
+     state,
+     getters,
+     mutations,
+     actions
+ }

+ 65 - 0
src/store/modules/PaymentsPurchasesSupplier.js

@@ -0,0 +1,65 @@
+import { INIT_PAYMENTS_PURCHASES_SUPPLIER } from '@/constants/actionTypes'
+import { SET_SUPPLIER, SET_LOADING_SUPPLIER }from '@/constants/mutationTypes'
+
+const initialState = {
+    supplier: [],
+    loadingSupplier: false
+}
+
+const state = {
+    supplier: initialState.supplier,
+    loadingSupplier: initialState.loadingSupplier
+}
+
+const getters = {
+    /**
+     * [supplier description]
+     * @param  {[type]} state [description]
+     * @return {[type]}       [description]
+     */
+    supplier ( state ) {
+        return state.supplier
+    },
+    /**
+     * [loadingSupplier description]
+     * @param  {[type]} state [description]
+     * @return {[type]}       [description]
+     */
+    loadingSupplier ( state ) {
+        return state.loadingSupplier
+    }
+}
+
+const mutations = {
+    /**
+     * [supplier description]
+     * @type {[type]}
+     */
+    [SET_SUPPLIER] (state, paylod) {
+        state.supplier = paylod
+    },
+    /**
+     * [loadingSupplier description]
+     * @type {[type]}
+     */
+    [SET_LOADING_SUPPLIER] (state, paylod) {
+        state.loadingSupplier = paylod
+    }
+}
+
+const actions = {
+    /**
+     * [INIT_PAYMENTS_PURCHASES_SUPPLIER]
+     */
+    [INIT_PAYMENTS_PURCHASES_SUPPLIER] ({ commit },paylod) {
+        commit(SET_SUPPLIER, paylod)
+        commit(SET_LOADING_SUPPLIER, paylod)
+    }
+}
+
+export default {
+    state,
+    getters,
+    mutations,
+    actions
+}

+ 62 - 0
src/store/modules/PaymentsPurchasesUser.js

@@ -0,0 +1,62 @@
+import { INIT_PAYMENTS_PURCHASES_USER } from '@/constants/actionTypes'
+import { SET_USER, SET_LOADING_USER } from '@/constants/mutationTypes'
+
+const initialState = {
+    user: [],
+    loadingUser: false
+}
+
+const state = {
+    user: initialState.user,
+    loadingUser: initialState.loadingUser
+}
+
+const getters = {
+    /**
+     * [user description]
+     * @param  {[type]} state [description]
+     * @return {[type]}       [description]
+     */
+    user (state) {
+        return state.user
+    },
+    /**
+     * [loadingUser description]
+     * @param  {[type]} state [description]
+     * @return {[type]}       [description]
+     */
+    loadingUser (state) {
+        return state.loadingUser
+    }
+}
+
+const mutations = {
+    /**
+     * [user description]
+     * @type {[type]}
+     */
+    [SET_USER] (state, paylod) {
+        state.user = paylod
+    },
+    /**
+     * [loadingUser description]
+     * @type {[type]}
+     */
+    [SET_LOADING_USER] (state, paylod) {
+        state.loadingUser = !!paylod
+    }
+}
+
+const actions = {
+    [INIT_PAYMENTS_PURCHASES_USER] ({ commit }, paylod) {
+        commit(SET_USER, paylod)
+        commit(SET_LOADING_USER, paylod)
+    }
+}
+
+export default {
+    state,
+    getters,
+    mutations,
+    actions
+}

+ 0 - 0
src/store/mutations.js


+ 0 - 0
src/store/state.js