소스 검색

[ADD] first store data

Gogs 7 년 전
부모
커밋
1d639f42f7
60개의 변경된 파일922개의 추가작업 그리고 2797개의 파일을 삭제
  1. 1 1
      controllers/main.py
  2. 1 0
      package.json
  3. 11 51
      src/App.vue
  4. 4 0
      src/assets/_variables.sass
  5. 0 49
      src/components/Loader.vue
  6. 0 27
      src/components/cart/CartContainer.vue
  7. 0 179
      src/components/cart/CartItem.vue
  8. 0 43
      src/components/cart/CartItems.vue
  9. 0 21
      src/components/cart/CartStep.vue
  10. 0 36
      src/components/cart/CartTotal.vue
  11. 0 79
      src/components/cart/ProductCard.vue
  12. 0 146
      src/components/cart/ProductDiscount.vue
  13. 0 77
      src/components/cart/ProductSearcher.vue
  14. 0 102
      src/components/cart/ProductSelector.vue
  15. 0 53
      src/components/cart/ProductVariant.vue
  16. 0 26
      src/components/cart/ProductsContainer.vue
  17. 0 40
      src/components/cart/ProductsGrid.vue
  18. 13 14
      src/components/common/AddCard.vue
  19. 80 0
      src/components/common/Card.vue
  20. 73 0
      src/components/common/CardGrid.vue
  21. 105 0
      src/components/common/Searcher.vue
  22. 46 0
      src/components/common/Spinner.vue
  23. 0 69
      src/components/customer/CustomerCard.vue
  24. 0 78
      src/components/customer/CustomerDetails.vue
  25. 0 99
      src/components/customer/CustomerForm.vue
  26. 0 79
      src/components/customer/CustomerSearcher.vue
  27. 0 59
      src/components/customer/CustomerStep.vue
  28. 0 36
      src/components/customer/CustomersGrid.vue
  29. 0 175
      src/components/payment/Ticket.vue
  30. 0 118
      src/components/payment/cash/PaymentAmount.vue
  31. 0 27
      src/components/payment/cash/PaymentCashStep.vue
  32. 0 172
      src/components/payment/credit/PaymentCreditAmount.vue
  33. 0 27
      src/components/payment/credit/PaymentCreditStep.vue
  34. 0 134
      src/components/payment/method/PaymentDetails.vue
  35. 0 27
      src/components/payment/method/PaymentMethodStep.vue
  36. 13 0
      src/constants/actionTypes.js
  37. 28 0
      src/constants/mutationTypes.js
  38. 7 0
      src/constants/resourcePaths.js
  39. 4 5
      src/index.js
  40. 30 80
      src/store/actions.js
  41. 0 10
      src/store/getters.js
  42. 18 23
      src/store/index.js
  43. 0 136
      src/store/modules/account.js
  44. 0 120
      src/store/modules/cart.js
  45. 0 51
      src/store/modules/company.js
  46. 0 44
      src/store/modules/currencies.js
  47. 67 0
      src/store/modules/currency.js
  48. 67 0
      src/store/modules/customer.js
  49. 0 97
      src/store/modules/customers.js
  50. 67 0
      src/store/modules/date.js
  51. 67 0
      src/store/modules/journal.js
  52. 0 57
      src/store/modules/loader.js
  53. 66 0
      src/store/modules/payment.js
  54. 68 0
      src/store/modules/product.js
  55. 0 93
      src/store/modules/products.js
  56. 0 21
      src/store/modules/sessions.js
  57. 67 0
      src/store/modules/user.js
  58. 0 10
      src/store/mutations.js
  59. 0 6
      src/store/state.js
  60. 19 0
      yarn.lock

+ 1 - 1
controllers/main.py

@@ -183,7 +183,7 @@ class Sales(http.Controller):
     '''
         New purchase resource route
     '''
-    @http.route('/eiru_sales/new', auth='user', methods=['GET'], cors='*')
+    @http.route('/eiru_sales/init', auth='user', methods=['GET'], cors='*')
     def init_sale(self, **kw):
         self.make_info_log('Sending JSON response')
     

+ 1 - 0
package.json

@@ -27,6 +27,7 @@
 		"webpack-livereload-plugin": "^0.11.0"
 	},
 	"dependencies": {
+		"axios": "^0.17.0",
 		"fuse.js": "^3.0.5",
 		"vue": "^2.4.1",
 		"vue-form-wizard": "^0.6.1",

+ 11 - 51
src/App.vue

@@ -1,109 +1,69 @@
 <template lang="pug">
     .pos
-        form-wizard(title="" subtitle="" finishButtonText="Finalizar" color="#7c7bad" nextButtonText="Continuar" backButtonText="Volver" @on-complete="completeSale()")
-            tab-content(title="Qué productos necesita?" :before-change="checkCart")
-                cart-step
-            tab-content(title="Quién es el cliente?" :before-change="checkCustomer")
-                customer-step
-            tab-content(title="Cómo quieres pagar?")
-                payment-method-step
-            tab-content(v-if="payment === 'cash'" title="Qué monto quieres pagar?" :before-change="checkAmountPaid")
-                payment-cash-step
-            tab-content(v-else title="Quieres hacer alguna entrega?")
-                payment-credit-step
-        loader
+        form-wizard(title='' subtitle='' finishButtonText='Finalizar' color='#7c7bad' nextButtonText='Continuar' backButtonText='Volver' @on-complete='createSale')
+            tab-content(title="Qué productos necesita?") Step 1
+            tab-content(title="Quién es el cliente?") Step 2
+            tab-content(title="Cómo quieres pagar?") Step 3
 </template>
 
 <script>
-    import { FormWizard, TabContent, WizardButton } from 'vue-form-wizard'
-    import 'vue-form-wizard/dist/vue-form-wizard.min.css'
+    import { mapActions } from 'vuex'
 
-    import CartStep from '@/components/cart/CartStep'
-    import CustomerStep from '@/components/customer/CustomerStep'
-    import PaymentMethodStep from '@/components/payment/method/PaymentMethodStep'
-    import PaymentCashStep from '@/components/payment/cash/PaymentCashStep'
-    import PaymentCreditStep from '@/components/payment/credit/PaymentCreditStep'
-
-    import Loader from '@/components/Loader'
-
-    import { mapActions, mapGetters } from 'vuex'
-    import Vuex from 'vuex'
+    import { FormWizard, TabContent } from 'vue-form-wizard'
 
     export default {
         components: {
-            TabContent,
             FormWizard,
-            WizardButton,
-            CartStep,
-            CustomerStep,
-            PaymentMethodStep,
-            PaymentCashStep,
-            PaymentCreditStep,
-            Loader,
+            TabContent
         },
-        computed: mapGetters([
-            'payment'
-        ]),
         methods: mapActions([
             'initSale',
-            'checkCart',
-            'checkCustomer',
-            'checkAmountPaid',
-            'completeSale'
+            'createSale'
         ]),
         mounted() {
-            this.initSale(this.$root.pos_instance)
+            this.initSale()
         }
     }
 </script>
 
 <style lang="sass">
+    @import './assets/variables'
     .pos
         width: 100%
         height: 100%
         position: absolute
-
         .vue-form-wizard
             width: 100%
             height: 100%
             padding-bottom: 0
-
             .wizard-header
                 display: none
-
             .wizard-navigation
                 width: 100%
                 height: 100%
-
                 .wizard-progress-with-circle
                     top: 35px
-
                 .wizard-icon-circle
                     width: 60px
                     height: 60px
-
                 .wizard-tab-content
                     width: 100%
                     height: calc(100% - 82px)
                     padding: 0
                     overflow: hidden
-
                     .wizard-tab-container
                         width: calc(100% - 20px)
                         height: calc(100% - 20px)
                         margin: 10px
-
                         .pos-step
                             width: 100%
                             height: 100%
                             background: #fff
-
             .wizard-card-footer
                 width: 100%
                 height: 50px
                 position: absolute
                 bottom: 0
-
                 .wizard-btn
                     width: 160px
                     height: 40px
@@ -111,5 +71,5 @@
                     box-shadow: none
                     border: none
                     &:hover, &:focus
-                        background: #7c7bad
+                        background: $app-main-color
 </style>

+ 4 - 0
src/assets/_variables.sass

@@ -0,0 +1,4 @@
+$app-main-color: #7c7bad
+$app-bg-color: #fff
+$app-border-color: #d3d3d3
+$app-title-color: #d3d3d3

+ 0 - 49
src/components/Loader.vue

@@ -1,49 +0,0 @@
-<template lang="pug">
-    modal(name="pos-loader" transition="nice-modal-fade" @before-close="beforeClose" :classes="['v--modal', 'pos-loader']")
-        simple-spinner(size="large" message="Cargando, espere...")
-</template>
-
-<script>
-    import { mapGetters, mapActions } from 'vuex'
-    import Spinner from 'vue-simple-spinner'
-
-    export default {
-        components: {
-            'simple-spinner': Spinner
-        },
-        computed: mapGetters({
-            loaded: 'isLoaded'
-        }),
-        watch: {
-            loaded(completed) {
-                if (completed) {
-                    this.$modal.hide("pos-loader")
-                } else {
-                    this.$modal.show("pos-loader")
-                }
-            }
-        },
-        methods: {
-            beforeClose(e) {
-                if (!this.loaded) {
-                    e.stop()
-                }
-            },
-            ...mapActions([
-                'clear'
-            ])
-        },
-        mounted() {
-            this.clear()
-            this.$modal.show("pos-loader")
-        }
-    }
-</script>
-
-<style lang="sass">
-    .pos-loader
-        display: flex
-        align-items: center
-        justify-content: center
-
-</style>

+ 0 - 27
src/components/cart/CartContainer.vue

@@ -1,27 +0,0 @@
-<template lang="pug">
-    .cart-container
-        cart-total
-        cart-items
-</template>
-
-<script>
-    import CartTotal from '@/components/cart/CartTotal'
-    import CartItems from '@/components/cart/CartItems'
-
-    export default {
-        components: {
-            CartTotal,
-            CartItems
-        }
-    }
-</script>
-
-<style lang="sass">
-    .cart-container
-        width: 300px
-        height: 100%
-        border-left: 1px solid #d3d3d3
-        padding-left: 10px
-        display: inline-block
-        vertical-align: top
-</style>

+ 0 - 179
src/components/cart/CartItem.vue

@@ -1,179 +0,0 @@
-<template lang="pug">
-        li.cart-item
-            h3.item-name {{ item.display_name }}
-            input.item-quantity(:value="quantity" v-model="quantity" number)
-            span.item-x x
-            span.item-price {{ formatPrice() }}
-            span.item-equals =
-            span.item-subtotal {{ formatSubtotal() }}
-
-            .cart-item-options-wrapper
-                .cart-item-options
-                    .cart-item-option(class="fa fa-plus" @click="add(item)")
-                    .cart-item-option(class="fa fa-minus" @click="subtract(item)")
-                    .cart-item-option(class="fa fa-money" @click="discount(item)")
-                    .cart-item-option(class="fa fa-trash" @click="remove(item)")
-</template>
-
-<script>
-    import { mapGetters, mapActions } from 'vuex'
-
-    export default {
-        props: {
-            item: {
-                type: Object,
-                required: true,
-                default: () => {
-                    return {}
-                }
-            }
-        },
-        computed: {
-            quantity: {
-                get() {
-                    return this.item.qty || 1
-                },
-                set(value) {
-                    this.item.qty = value || 1
-                }
-            },
-            ...mapGetters([
-                'currencySymbol'
-            ])
-        },
-        methods: {
-            add(item) {
-                this.addToCart(item)
-            },
-            subtract(item) {
-                this.subtractFromCart(item)
-            },
-            discount(item) {
-                this.discountFromCart(item)
-            },
-            remove(item) {
-                this.removeFromCart(item)
-            },
-            formatPrice() {
-                return accounting.formatMoney(this.item.price, this.currencySymbol, 0, '.', ',')
-            },
-            formatSubtotal() {
-                return accounting.formatMoney(this.item.price * this.item.qty, this.currencySymbol, 0, '.', ',')
-            },
-            ...mapActions([
-                'addToCart',
-                'subtractFromCart',
-                'discountFromCart',
-                'removeFromCart'
-            ])
-        },
-        mounted() {
-            this.$set(this.item, 'qty', 1)
-            this.$set(this.item, 'price', this.item.list_price)
-            this.$set(this.item, 'discount', 0)
-        }
-    }
-</script>
-
-<style lang="sass">
-    .cart-item
-        width: 100%
-        height: 90px
-        list-style: none outside none
-        border-bottom: 1px solid #d3d3d3
-        box-sizing: border-box
-        position: relative
-
-        &:nth-child(1)
-            border-top: 1px solid #d3d3d3
-
-        &:hover
-            transition-duration: 1s
-            border-bottom: 2px solid #7c7bad
-
-        .item-name
-            width: 100%
-            height: 20px
-            margin: 10px 0 5px 0
-            float: left
-            font:
-                size: 8pt
-
-        .item-quantity
-            width: 50px
-            height: 28px
-            margin-top: 6px
-            text-align: right
-            float: left
-
-        .item-x
-            width: 20px
-            height: 20px
-            margin-top: 12px
-            text-align: center
-            float: left
-
-        .item-price
-            width: 80px
-            height: 20px
-            margin-top: 12px
-            text-align: right
-            float: left
-
-        .item-equals
-            width: 20px
-            height: 20px
-            margin-top: 12px
-            text-align: center
-            float: left
-
-        .item-subtotal
-            width: 100px
-            height: 20px
-            margin-top: 12px
-            padding-right: 15px
-            text-align: right
-            float: right
-            font-weight: bold
-
-        .cart-item-options-wrapper
-            width: 100%
-            height: 20px
-            position: absolute
-            bottom: 0
-            display: flex
-            justify-content: center
-
-            .cart-item-options
-                width: 100px
-                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-trash:hover
-                            color: #f44336
-</style>

+ 0 - 43
src/components/cart/CartItems.vue

@@ -1,43 +0,0 @@
-<template lang="pug">
-    .cart-items-wrapper
-        transition-group(name="list" tag="ul" class="cart-items")
-            cart-item(v-for="item in cartItems" :key="item" :item="item")
-</template>
-
-<script>
-    import CartItem from '@/components/cart/CartItem'
-    import { mapGetters, mapActions } from 'vuex'
-
-    export default {
-        components: {
-            CartItem
-        },
-        computed: mapGetters([
-            'cartItems'
-        ]),
-        methods: mapActions([
-            'addToCart',
-            'removeFromCart'
-        ])
-    }
-</script>
-
-<style lang="sass">
-    .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>

+ 0 - 21
src/components/cart/CartStep.vue

@@ -1,21 +0,0 @@
-<template lang="pug">
-    .pos-step
-        products-container
-        cart-container
-</template>
-
-<script>
-    import ProductsContainer from '@/components/cart/ProductsContainer'
-    import CartContainer from '@/components/cart/CartContainer'
-    
-    export default {
-        components: {
-            ProductsContainer,
-            CartContainer
-        }
-    }
-</script>
-
-<style lang="sass">
-
-</style>

+ 0 - 36
src/components/cart/CartTotal.vue

@@ -1,36 +0,0 @@
-<template lang="pug">
-    .cart-total
-        h2
-            small.cart-total-currency {{ currencySymbol }}
-            |
-            | {{ formatTotal() }}
-</template>
-
-<script>
-    import { mapGetters } from 'vuex'
-
-    export default {
-        computed: mapGetters([
-            'total',
-            'currencySymbol'
-        ]),
-        methods: {
-            formatTotal() {
-                return accounting.format(this.total, 0, '.', ',')
-            }
-        }
-    }
-
-</script>
-
-<style lang="sass">
-    .cart-total
-        width: 100%
-        height: 50px
-
-        h2
-            width: 100%
-            height: inherit
-            margin: 0
-            font-size: 30pt
-</style>

+ 0 - 79
src/components/cart/ProductCard.vue

@@ -1,79 +0,0 @@
-<template lang="pug">
-    .product-card(@click="selectProduct(item)")
-        h2.product-title {{ item.name }}
-        img.product-image(:src="this.item.image_medium ? 'data:image/png;base64,' + this.item.image_medium : '/web/static/src/img/placeholder.png'")
-        .product-price
-            span {{ getPrice() }}
-        .product-qty
-</template>
-
-<script>
-    import { mapGetters, mapActions } from 'vuex'
-
-    export default {
-        props: {
-            item: {
-                type: Object,
-                default: () => {
-                    return {}
-                }
-            }
-        },
-        computed: mapGetters([
-            'currencySymbol'
-        ]),
-        methods: {
-            getPrice() {
-                return accounting.formatMoney(this.item.list_price, this.currencySymbol, 0, '.', ',')
-            },
-            ...mapActions([
-                'selectProduct'
-            ])
-        }
-    }
-</script>
-
-<style lang="sass">
-    .product-card
-        width: 130px
-        height: 160px
-        margin: 5px
-        border: 1px solid #d3d3d3
-        display: inline-block
-        position: relative
-
-        &:hover
-            cursor: pointer
-
-        .product-title
-            width: 100%
-            height: 30px
-            font-size: 9pt
-            text-align: center
-            margin-top: 10px
-            position: absolute
-            top: 0
-
-        .product-image
-            width: 80px
-            height: 80px
-            margin: 0
-            border: none
-            position: absolute;
-            top: 50%
-            left: 50%
-            margin-right: -50%
-            transform: translate(-50%, -50%)
-
-        .product-price
-            width: 100%
-            height: 30px
-            padding-top: 5px
-            text-align: center
-            font-size: 10pt
-            font-weight: bold
-            background: #7c7bad
-            color: #fff
-            position: absolute
-            bottom: 0
-</style>

+ 0 - 146
src/components/cart/ProductDiscount.vue

@@ -1,146 +0,0 @@
-<template lang="pug">
-    modal(name="product-discount" transition="nice-modal-fade" @before-close="beforeClose" :classes="['v--modal', 'product-discount']")
-        form.discount-form
-            .discount-item
-                label.discount-label Precio unitario
-                input.discount-input(:value="formatPrice()" readonly)
-            .discount-item
-                label.discount-label Precio mínimo
-                input.discount-input(:value="formatMinPrice()" readonly)
-            .discount-item
-                label.discount-label Precio máximo
-                input.discount-input(:value="formatMaxPrice()" readonly)
-            hr
-            .discount-item
-                label.discount-label Precio aplicado
-                input.discount-input(:value="priceApply" v-model="priceApply" autofocus)
-            .discount-item
-                label.discount-label Descuento
-                input.discount-input(:value="formatDiscount()" readonly)
-        .discount-options
-            button.discount-button(@click="applyDiscount({ apply: true })") Aceptar
-            button.discount-button(@click="applyDiscount({ apply: false })") Cancelar
-</template>
-
-<script>
-    import { mapGetters, mapActions } from 'vuex'
-
-    export default {
-        data() {
-            return {
-                discount: 0
-            }
-        },
-        computed: {
-            price() {
-                return this.productToDiscount ? this.productToDiscount.list_price : 0
-            },
-            minimumPrice() {
-                return this.productToDiscount ? this.productToDiscount.minimum_price : 0
-            },
-            maximumPrice() {
-                return this.productToDiscount ? this.productToDiscount.maximum_price : 0
-            },
-            priceApply: {
-                get() {
-                    let value = this.productToDiscount ? this.productToDiscount.discount : this.minimumPrice
-                    return accounting.formatMoney(value, this.currencySymbol, 0, '.', ',')
-                },
-                set(value) {
-                    value = accounting.unformat(value, ',')
-
-                    this.discount = this.price - value
-                    this.productToDiscount.discount = value
-                }
-            },
-            ...mapGetters([
-                'productToDiscount',
-                'currencySymbol'
-            ])
-        },
-        watch: {
-            productToDiscount(value) {
-                if (value) {
-                    this.$modal.show('product-discount')
-                } else {
-                    this.$modal.hide('product-discount')
-                }
-            }
-        },
-        methods: {
-            formatPrice() {
-                return accounting.formatMoney(this.price, this.currencySymbol, 0, '.', ',')
-            },
-            formatMinPrice() {
-                return accounting.formatMoney(this.minimumPrice, this.currencySymbol, 0, '.', ',')
-            },
-            formatMaxPrice() {
-                return accounting.formatMoney(this.maximumPrice, this.currencySymbol, 0, '.', ',')
-            },
-            formatDiscount() {
-                return accounting.formatMoney(this.discount, this.currencySymbol, 0, '.', ',')
-            },
-            beforeClose(e) {
-                if (this.productToDiscount) {
-                    e.stop()
-                }
-            },
-            ...mapActions([
-                'discountFromCart',
-                'applyDiscount'
-            ])
-        }
-    }
-</script>
-
-<style lang="sass">
-    .product-discount
-        width: 600px
-        height: 340px !important
-        .discount-form
-            width: 100%
-            height: 290px
-            padding: 15px
-            .discount-item
-                width: 100%
-                height: 45px
-                margin-bottom: 10px
-                &:nth-child(1)
-                    .discount-input
-                        border: none
-                &:nth-child(2), &:nth-child(3), &:nth-child(6) 
-                    height: 35px
-                    margin-bottom: 5px
-                    .discount-label
-                        width: 30%
-                        height: 35px
-                        font-size: 10pt
-                        color: #666
-                    .discount-input
-                        width: 70%
-                        height: 35px
-                        font-size: 18pt
-                        text-align: right
-                        border: none
-                .discount-label
-                    width: 30%
-                    height: 45px
-                    font-size: 14pt
-                .discount-input
-                    width: 70%
-                    height: 45px
-                    font-size: 28pt
-                    text-align: right
-                    border-radius: 0
-        .discount-options
-            float: right
-            .discount-button
-                width: 160px
-                height: 40px
-                border: none
-                box-shadow: none
-                border-radius: 0
-                margin-right: 5px
-                background: #0288d1
-                color: #fff
-</style>

+ 0 - 77
src/components/cart/ProductSearcher.vue

@@ -1,77 +0,0 @@
-<template lang="pug">
-    .products-searcher
-        input(type="search" placeholder="Buscar un producto" v-model="search")
-</template>
-
-<script>
-    import { mapGetters, mapActions } from 'vuex'
-    import Fuse from 'fuse.js'
-
-    export default {
-        computed: mapGetters([
-            'products'
-        ]),
-        methods: {
-            initFuse() {
-                if (this.fuse) {
-                    this.fuse.setCollection(this.products)
-                    return
-                }
-
-                this.fuse = new Fuse(this.products, {
-                    shouldSort: true,
-                    threshold: 0.4,
-                    location: 0,
-                    distance: 100,
-                    maxPatternLength: 32,
-                    minMatchCharLength: 1,
-                    keys: [
-                        'name',
-                        'display_name',
-                        'ean13',
-                    ]
-                })
-            },
-            fuzzySearch() {
-                this.results = this.fuse.search(this.search)
-            },
-            ...mapActions([
-                'filterProducts'
-            ])
-        },
-        watch: {
-            products() {
-                this.initFuse()
-            },
-            search() {
-                this.fuzzySearch()
-            },
-            results() {
-                this.filterProducts(this.results)
-            }
-        },
-        data() {
-            return {
-                search: '',
-                results: [],
-                fuse: null,
-            }
-        },
-    }
-</script>
-
-<style lang="sass">
-    .products-searcher
-        width: 100%
-        height: 35px
-
-        input
-            width: inherit
-            height: inherit
-            text-align: center
-            border-radius: 0
-            font: 
-                size: 11pt
-                style: normal
-                weight: bold
-</style>

+ 0 - 102
src/components/cart/ProductSelector.vue

@@ -1,102 +0,0 @@
-<template lang="pug">
-    .product-selector
-        modal(name="product-selector" transition="nice-modal-fade" height="500" @closed="handleClosed")
-            input.variant-searcher(type="search" placeholder="Buscar una variante de producto" v-model="search")
-            .product-variants
-                product-variant(v-for="variant in variants" :key="variant.id" :item="variant")
-</template>
-
-<script>
-    import { mapGetters, mapActions } from 'vuex'
-    import Fuse from 'fuse.js'
-    import ProductVariant from '@/components/cart/ProductVariant'
-
-    export default {
-        components: {
-            ProductVariant
-        },
-        computed: mapGetters([
-            'variants'
-        ]),
-        methods: {
-            initFuse() {
-                if (this.fuse) {
-                    this.fuse.setCollection(this.variants)
-                    return
-                } 
-
-                this.fuse = new Fuse(this.variants, {
-                    shouldSort: true,
-                    threshold: 0.4,
-                    location: 0,
-                    distance: 100,
-                    maxPatternLength: 32,
-                    minMatchCharLength: 1,
-                    keys: [
-                        'name',
-                        'attributes.name',
-                    ]
-                })
-            },
-            fuzzySearch() {
-                this.results = this.fuse.search(this.search)
-            },
-            handleClosed() {
-                this.search = ''
-                this.selectVariant()
-            },
-            ...mapActions([
-                'filterVariants',
-                'selectVariant'
-            ])
-        },
-        watch: {
-            variants() {
-                this.initFuse()
-            },
-            search() {
-                this.fuzzySearch()
-            },
-            results() {
-                this.filterVariants(this.results)
-            }
-        },
-        data() {
-            return {
-                search: '',
-                results: [],
-                fuse: null,
-            }
-        },
-        mounted() {
-            this.$store.watch(state => {
-                return state.products.selectedProduct
-            }, value => {
-                if (value) {                    
-                    this.$modal.show('product-selector')
-                } else {
-                    this.$modal.hide('product-selector')                                
-                }
-            })
-        }
-    }
-</script>
-
-<style lang="sass">
-    .product-selector
-        .variant-searcher
-            width: calc(100% - 30px)
-            height: 35px
-            text-align: center
-            margin: 10px 15px
-            border-radius: 0
-            font: 
-                size: 10pt
-                style: normal
-                weight: bold
-
-        .product-variants
-            width: 100%;
-            height: calc(100% - 70px)
-            overflow-y: auto
-</style>

+ 0 - 53
src/components/cart/ProductVariant.vue

@@ -1,53 +0,0 @@
-<template lang="pug">
-    .product-variant(@click="selectVariant(item)")
-        img.variant-image(:src="this.item.image_medium ? 'data:image/png;base64,' + this.item.image_medium : '/web/static/src/img/placeholder.png'")
-        .variant-details
-            h2.variant-name {{ item.display_name }}
-</template>
-
-<script>
-    import { mapActions } from 'vuex'
-
-    export default {
-        props: {
-            item: {
-                type: Object,
-                default: () => {
-                    return {}
-                }
-            }
-        },
-        methods: mapActions([
-            'selectVariant'
-        ])
-    }
-</script>
-
-
-<style lang="sass">
-    .product-variant
-        width: calc(100% - 20px)
-        height: 84px
-        margin: 5px 10px
-        
-        display: flex
-
-        &:hover
-            cursor: pointer
-            .variant-details
-                transition-duration: 0.5s
-                border-bottom: 2px solid #7c7bad
-
-        .variant-image
-            width: 80px
-            height: 80px
-            margin: 0
-            border: none
-        
-        .variant-details
-            flex-grow: 1
-            border-bottom: 1px solid #d3d3d3
-
-            .variant-name
-                font-size: 10pt
-</style>

+ 0 - 26
src/components/cart/ProductsContainer.vue

@@ -1,26 +0,0 @@
-<template lang="pug">
-    .products-container
-        product-searcher
-        products-grid
-</template>
-
-<script>
-    import ProductSearcher from '@/components/cart/ProductSearcher'
-    import ProductsGrid from '@/components/cart/ProductsGrid'
-
-    export default {
-        components: {
-            ProductSearcher,
-            ProductsGrid
-        }
-    }
-</script>
-
-<style lang="sass">
-    .products-container
-        width: calc(100% - 300px)
-        height: 100%
-        padding-right: 5px
-        display: inline-block
-</style>
-

+ 0 - 40
src/components/cart/ProductsGrid.vue

@@ -1,40 +0,0 @@
-<template lang="pug">
-    .products-grid-wrapper
-        .products-grid
-            product-card(v-for="product in products" :key="product" :item="product")
-            product-selector
-            product-discount
-</template>
-
-<script>
-    import ProductCard from '@/components/cart/ProductCard'
-    import ProductSelector from '@/components/cart/ProductSelector'
-    import ProductDiscount from '@/components/cart/ProductDiscount'
-
-    import { mapGetters, mapActions } from 'vuex'
-
-    export default {
-        components: {
-            ProductCard,
-            ProductSelector,
-            ProductDiscount
-        },
-        computed: mapGetters([
-            'products',
-        ]),
-        methods: mapActions([
-            'selectProduct'
-        ])
-    }
-</script>
-
-<style lang="sass">
-    .products-grid-wrapper
-        width: 100%
-        height: calc(100% - 50px)
-        margin-top: 15px
-        overflow-y: auto
-
-        .products-grid
-            width: 100%        
-</style>

+ 13 - 14
src/components/customer/CustomerAdd.vue → src/components/common/AddCard.vue

@@ -1,30 +1,30 @@
 <template lang="pug">
-    .customer-add(@click="addCustomer()")
-        i.fa.fa-plus(aria-hidden="true")
+    .add-card(@click='onClick')
+        i.fa.fa-plus(aria-hidden='true')
 </template>
 
 <script>
-    import { mapActions } from 'vuex'
-
     export default {
-        methods: mapActions([
-            'addCustomer'
-        ])
+        methods: {
+            onClick(e) {
+                this.$emit('onClick')
+            }
+        }
     }
 </script>
 
 <style lang="sass">
-    .customer-add
+    @import '../../assets/variables'
+
+    .add-card
         width: 130px
         height: 160px
         margin: 5px
-        border: 1px solid #d3d3d3
+        border: 1px solid $app-border-color
         display: inline-block
         position: relative
-
         &:hover
             cursor: pointer
-
         i
             font-size: 36pt
             margin: 0
@@ -34,6 +34,5 @@
             left: 50%
             margin-right: -50%
             transform: translate(-50%, -50%)
-            color: #0288d1
-            
-</style>
+            color: $app-main-color
+</style>

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

@@ -0,0 +1,80 @@
+<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-description(v-if='!!description')
+            span {{ description }}
+</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=='
+            },
+            description: {
+                type: String,
+                default: ''
+            },
+            isSelected: {
+                type: Boolean,
+                default: true
+            }
+        },
+        methods: {
+            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-description
+            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
+</style>

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

@@ -0,0 +1,73 @@
+<template lang="pug">
+    .card-grid-wrapper
+        .card-grid-loading(v-if='loading')
+            spinner
+        .card-grid(v-else)
+            add-card(v-if='canAdd' @onClick='onClickAdd')
+            card(v-for='item in items' :key='item.id' :title='item.name' :image='item.imageMedium' :isSelected='item.id === selectedId' :description='getDescription(item)' @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
+            },
+            description: {
+                type: String,
+                default: ''
+            },
+            loading: {
+                type: Boolean,
+                default: false
+            }
+        },
+        components: {
+            AddCard,
+            Card,
+            Spinner
+        },
+        methods: {
+            getDescription(item) {
+                return (!!this.description && item[this.description]) || ''
+            },
+            onClickAdd() {
+                this.$emit('onAdd')
+            },
+            onClickCard(item) {
+                this.selectedId = item.id
+                this.$emit('onSelect', item)
+            }
+        },
+        data() {
+            return {
+                selectedId: -1
+            }
+        }
+    }
+</script>
+
+<style lang="sass">
+    .card-grid-wrapper
+        width: 100%
+        height: calc(100% - 100px)
+        margin-top: 15px
+        overflow-y: auto
+        .card-grid-loading
+            width: 100%
+            height: 100%
+            display: flex
+            align-items: center
+            justify-content: center
+        .card-grid
+            width: 100%
+</style>

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

@@ -0,0 +1,105 @@
+<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
+            }
+        },
+        watch: {
+            items(values) {
+                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) {
+                this.results = this.fuse.search(value)
+            }
+        },
+        data() {
+            return {
+                fuse: null,
+                search: '',
+                results: []
+            }
+        },
+        mounted() {
+            this.initFuse()
+        }
+    }
+</script>
+
+<style lang="sass">
+    .searcher
+        text-align: center
+        border-radius: 0 !important
+        font:
+            size: 11pt
+            style: normal
+            weight: bold
+</style>

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

@@ -0,0 +1,46 @@
+<template lang="pug">
+    .spinner
+        .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
+</template>
+
+<script>
+    export default {
+    
+    }
+</script>
+
+<style lang="sass">
+    @import '../../assets/variables'
+    .spinner 
+        $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-bubbling $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)
+
+        @keyframes spinner-bubbling
+            0%, 40%, 100%
+                transform: scaleY(0.4)
+            20%
+                transform: scaleY(1.0)
+</style>

+ 0 - 69
src/components/customer/CustomerCard.vue

@@ -1,69 +0,0 @@
-<template lang="pug">
-    .customer-card(@click="selectCustomer(item)" v-bind:class="{ 'selected-customer': isSelectedCustomer() }")
-        h2.customer-name {{ item.name }}
-        img.customer-photo(:src="this.item.image_medium ? 'data:image/png;base64,' + this.item.image_medium : '/base/static/src/img/avatar.png'")
-</template>
-
-<script>
-    import { mapActions, mapGetters } from 'vuex'
-
-    export default {
-        props: {
-            item: {
-                type: Object,
-                default: () => {
-                    return {}
-                }
-            }
-        },
-        computed: mapGetters([
-            'selectedCustomer',
-            'hasSelectedCustomer'
-        ]),
-        methods: {
-            isSelectedCustomer() {
-                return this.hasSelectedCustomer ? this.item.id === this.selectedCustomer.id : false
-            },
-            ...mapActions([
-                'selectCustomer'
-            ])
-        }
-    }
-</script>
-
-<style lang="sass">
-    .customer-card
-        width: 130px
-        height: 160px
-        margin: 5px
-        border: 1px solid #d3d3d3
-        display: inline-block
-        position: relative
-
-        &.selected-customer
-            transition-duration: 0.3s
-            border-bottom: 3px solid #7c7bad
-
-        &:hover
-            cursor: pointer
-
-        .customer-name
-            width: 100%
-            height: 30px
-            font-size: 9pt
-            text-align: center
-            margin-top: 10px
-            position: absolute
-            top: 0
-        
-        .customer-photo
-            width: 80px
-            height: 80px
-            margin: 0
-            border: none
-            position: absolute;
-            top: 50%
-            left: 50%
-            margin-right: -50%
-            transform: translate(-50%, -50%)
-</style>

+ 0 - 78
src/components/customer/CustomerDetails.vue

@@ -1,78 +0,0 @@
-<template lang="pug">
-    .customer-details
-        form.customer-form
-            .form-separator
-                h3 Detalles generales
-                hr
-            .customer-form-item
-                label.form-label Nombre
-                input.form-input(readonly :value="selectedCustomer.name")
-            .customer-form-item
-                label.form-label RUC/CIN
-                input.form-input(readonly :value="getRuc()")
-            .customer-form-item
-                label.form-label Celular
-                input.form-input(readonly :value="selectedCustomer.mobile")
-            .customer-form-item
-                label.form-label Teléfono
-                input.form-input(readonly :value="selectedCustomer.phone")
-            .customer-form-item
-                label.form-label Email
-                input.form-input(readonly :value="selectedCustomer.email")
-            .form-separator
-                h3 Créditos
-                hr
-            .customer-form-item
-                label.form-label Crédito
-                input.form-input(readonly :value="getCredit()")
-</template>
-
-<script>
-    import { mapGetters } from 'vuex'
-
-    export default {
-        computed: mapGetters([
-            'hasSelectedCustomer',
-            'selectedCustomer'
-        ]),
-        methods: {
-            getRuc() {
-                return this.hasSelectedCustomer && !!this.selectedCustomer.ruc ? this.selectedCustomer.ruc : ''
-            },
-            getCredit() {
-                return this.hasSelectedCustomer && this.selectedCustomer.credit_limit >= this.selectedCustomer.credit ? this.selectedCustomer.credit_limit - this.selectedCustomer.credit : 0
-            }
-        }
-    }
-</script>
-
-<style lang="sass">
-    .customer-details
-        width: 100%
-        height: 100%
-
-        .customer-form
-            width: 100%
-            height: 100%
-
-            .form-separator
-                h3
-                    color: #9e9e9e
-                    font-size: 8pt
-                hr
-                    margin-top: 5px
-
-            .customer-form-item
-                width: 100%
-                height: 35px
-                margin-bottom: 5px
-
-                .form-label
-                    width: 65px
-                    height: 35px
-
-                .form-input
-                    width: 210px
-                    height: 35px
-                    border-radius: 0
-</style>

+ 0 - 99
src/components/customer/CustomerForm.vue

@@ -1,99 +0,0 @@
-<template lang="pug">
-    modal(name="customer-modal" transition="nice-modal-fade" @before-close="beforeClose" :classes="['v--modal', 'customer-modal']")
-        h2 Nuevo cliente
-        hr
-        form.customer-form
-            .customer-form-item
-                label.customer-form-label Nombre
-                input.customer-form-input(v-model="customer.name" autofocus)
-            .customer-form-item
-                label.customer-form-label R.U.C/C.I.N
-                input.customer-form-input(v-model="customer.ruc")
-            .customer-form-item
-                label.customer-form-label Teléfono
-                input.customer-form-input(v-model="customer.phone")
-        .customer-form-options
-            button.customer-button(@click="saveCustomer(customer)") Aceptar
-            button.customer-button(@click="saveCustomer()") Cancelar
-</template>
-
-<script>
-    import { mapGetters, mapActions } from 'vuex'
-
-    export default {
-        data() {
-            return {
-                customer: {
-                    name: '',
-                    ruc: '',
-                    phone: ''    
-                }
-            }
-        },
-        computed: mapGetters([
-            'addCustomer'
-        ]),
-        watch: {
-            addCustomer(value) {
-                if (value) {
-                    this.$modal.show('customer-modal')
-
-                    this.customer.name = ''
-                    this.customer.ruc = ''
-                    this.phone = ''
-                } else {
-                    this.$modal.hide('customer-modal')
-                }
-            }
-        },
-        methods: {
-            beforeClose(e) {
-                if (this.addCustomer) {
-                    e.stop()
-                }                    
-            },
-            ...mapActions([
-                'saveCustomer'
-            ])
-        }
-    }
-</script>
-
-<style lang="sass">
-    .customer-modal
-        h2
-            font-size: 10pt
-            color: #d3d3d3
-            margin-left: 15px
-        hr
-            margin: 0 15px
-        .customer-form
-            width: 100%
-            height: 200px
-            padding: 15px
-            .customer-form-item
-                width: 100%
-                height: 45px
-                margin-bottom: 10px
-                .customer-form-label
-                    width: 30%
-                    height: 45px
-                    font-size: 14pt
-                .customer-form-input
-                    width: 70%
-                    height: 45px
-                    font-size: 18pt
-                    border-radius: 0
-        .customer-form-options
-            float: right
-            .customer-button
-                width: 160px
-                height: 40px
-                border: none
-                box-shadow: none
-                border-radius: 0
-                margin-right: 5px
-                background: #0288d1
-                color: #fff
-</style>
-

+ 0 - 79
src/components/customer/CustomerSearcher.vue

@@ -1,79 +0,0 @@
-<template lang="pug">
-    .customer-searcher
-        input(type="text" placeholder="Buscar un cliente" v-model="search")
-</template>
-
-<script>
-    import Fuse from 'fuse.js'
-    import { mapGetters, mapActions } from 'vuex'
-
-    export default {
-        computed: mapGetters([
-            'customers'
-        ]),
-        methods: {
-            initFuse() {
-                if (this.fuse) {
-                    this.fuse.setCollection(this.customers)
-                    return
-                }
-
-                this.fuse = new Fuse(this.customers, {
-                    shouldSort: true,
-                    threshold: 0.4,
-                    location: 0,
-                    distance: 100,
-                    maxPatternLength: 32,
-                    minMatchCharLength: 1,
-                    keys: [
-                        'name',
-                        'phone',
-                        'mobile',
-                        'email',
-                        'categories.name'
-                    ]
-                })
-            },
-            fuzzySearch() {
-                this.results = this.fuse.search(this.search)
-            },
-            ...mapActions([
-                'filterCustomers'
-            ])
-        },
-        watch: {
-            customers() {
-                this.initFuse()
-            },
-            search() {
-                this.fuzzySearch()
-            },
-            results() {
-                this.filterCustomers(this.results)
-            }
-        },
-        data() {
-            return {
-                search: '',
-                fuse: null,
-                results: []
-            }
-        }
-    }
-</script>
-
-<style lang="sass">
-    .customer-searcher
-        width: 100%
-        height: 35px
-
-        input
-            width: 100%
-            height: inherit
-            text-align: center
-            border-radius: 0
-            font: 
-                size: 11pt
-                style: normal
-                weight: bold
-</style>

+ 0 - 59
src/components/customer/CustomerStep.vue

@@ -1,59 +0,0 @@
-<template lang="pug">
-    .pos-step
-        .customers-step
-            .customers-left
-                customer-searcher
-                customers-grid
-            transition(name="slide-fade")
-                .customers-right(v-if="hasSelectedCustomer")
-                    customer-details
-</template>
-
-<script>
-    import { mapGetters } from 'vuex'
-
-    import CustomerSearcher from '@/components/customer/CustomerSearcher'
-    import CustomersGrid from '@/components/customer/CustomersGrid'
-    import CustomerDetails from '@/components/customer/CustomerDetails'
-
-    export default {
-        components: {
-            CustomerSearcher,
-            CustomersGrid,
-            CustomerDetails
-        },
-        computed: mapGetters([
-            'hasSelectedCustomer'
-        ])
-    }
-</script>
-
-<style lang="sass">
-    .pos-step
-        .customers-step
-            width: 100%
-            height: 100%
-            display: flex
-
-            .customers-left, .customers-right
-                height: 100%
-
-            .customers-left
-                width: 100%
-
-            .customers-right
-                width: 360px
-                margin: 0 15px
-
-            .slide-fade-enter-active
-                transition: all .3s ease;
-
-            .slide-fade-leave-active
-                transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0)
-
-            .slide-fade-enter, .slide-fade-leave-to
-                transform: translateX(10px)
-                opacity: 0
-</style>
-
-

+ 0 - 36
src/components/customer/CustomersGrid.vue

@@ -1,36 +0,0 @@
-<template lang="pug">
-    .customers-grid-wrapper
-        .customers-grid
-            customer-add
-            customer-card(v-for="customer in customers" :key="customer" :item="customer")
-        customer-form
-</template>
-
-<script>
-    import { mapGetters } from 'vuex'
-    import CustomerAdd from '@/components/customer/CustomerAdd'
-    import CustomerCard from '@/components/customer/CustomerCard'
-    import CustomerForm from '@/components/customer/CustomerForm'
-
-    export default {
-        components: {
-            CustomerAdd,
-            CustomerCard,
-            CustomerForm
-        },
-        computed: mapGetters([
-            'customers'
-        ])
-    }
-</script>
-
-<style lang="sass">
-    .customers-grid-wrapper
-        width: 100%
-        height: calc(100% - 100px)
-        margin-top: 15px
-        overflow-y: auto
-
-        .customers-grid
-            width: 100%
-</style>

+ 0 - 175
src/components/payment/Ticket.vue

@@ -1,175 +0,0 @@
-<template lang="pug">
-    .ticket
-        .ticket-summary
-            .ticket-summary-header
-                h3 {{ getCompanyName() }}
-
-                table
-                    tbody
-                        tr
-                            td Producto
-                            td Precio
-                            td Cant
-                            td Subtotal
-
-            .ticket-items-wrapper
-                table
-                    tbody
-                        tr(v-for="item in cartItems" :key="item")
-                            td {{ item.name }}
-                            td {{ item.price }}
-                            td {{ item.qty }}
-                            td {{ item.qty * item.price }}
-            .ticket-summary-footer
-                table
-                    tbody
-                        tr
-                            td Total:
-                            td {{ formatTotal() }}
-                        tr
-                            td Cliente:
-                            td {{ getCustomerName() }}
-</template>
-
-<script>
-    import { mapGetters } from 'vuex'
-
-    export default {
-        computed: mapGetters([
-            'company',
-            'selectedCustomer',
-            'hasSelectedCustomer',
-            'cartItems',
-            'total',
-        ]),
-        methods: {
-            getCompanyName() {
-                return !!this.company ? this.company.name : ''
-            },
-            formatTotal() {
-                return accounting.formatMoney(this.total, '₲', 0, '.', ',')
-            },
-            getCustomerName() {
-                return this.hasSelectedCustomer ? this.selectedCustomer.name : ''
-            }
-        }
-    
-    }
-</script>
-
-<style lang="sass">
-    .ticket
-        width: 500px
-        height: 100%
-        font-size: 8pt
-        display: flex
-
-        .ticket-summary
-            width: 350px
-            height: 450px
-            border: 1px solid #d3d3d3
-            margin: auto
-            box-shadow: -2px 2px 5px #d3d3d3, 2px 2px 5px #d3d3d3
-            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: #666
-
-                table
-                    width: 308px
-                    height: 30px
-                    margin: 0 20px
-                    font-size: 7.5pt
-                    font-weight: bold
-
-                    tbody
-                        tr
-                            line-height: 30px
-                            border-top: 1px solid #d3d3d3
-                            border-bottom: 1px solid #d3d3d3
-
-                            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: #fff
-
-                table
-                    width: 100%
-
-                    tbody
-                        tr
-                            height: 25px
-                            line-height: 20px
-
-                            td
-                                &:nth-child(1)
-                                    font-weight: bold
-
-                                &:nth-child(2)
-                                    text-align: right
-
-</style>

+ 0 - 118
src/components/payment/cash/PaymentAmount.vue

@@ -1,118 +0,0 @@
-<template lang="pug">
-    form.payment-amount-form
-        div(v-show="selectedJournal && selectedJournal.type === 'bank'")
-            .payment-form-separator
-                h3 Referencia de Pago
-                hr
-            .payment-amount-item
-                label.payment-label Referencia
-                input.payment-input(:value="reference" v-model="reference")
-        .payment-form-separator
-            h3 Detalles del Pago
-            hr
-        .payment-amount-item
-            label.payment-label Monto a pagar
-            input.payment-input(readonly :value="formatTotal()")
-        .payment-amount-item
-            label.payment-label Monto recibido
-            input.payment-input(:value="paid" v-model="paid" autofocus)
-        hr.result-separator
-        .payment-amount-item
-            label.payment-label Monto a devolver
-            input.payment-input(readonly :value="formatResidual()")
-</template>
-
-<script>
-    import { mapGetters, mapActions } from 'vuex'
-
-    export default {
-        computed: {
-            reference: {
-                get() {
-                    return this.paymentReference
-                },
-                set(value) {
-                    this.setPaymentReference(value)
-                }
-            },
-            paid: {
-                get() {
-                    return accounting.formatMoney(this.amountPaid, this.currencySymbol, 0, '.', ',')
-                },
-                set(value) {
-                    value = accounting.unformat(value, ',')
-
-                    this.changeAmountPaid(value)
-                    this.calculateResidual()
-                }
-            },
-            ...mapGetters([
-                'total',
-                'currencySymbol',
-                'selectedJournal',
-                'paymentReference',
-                'amountPaid'
-            ])
-        },
-        methods: {
-            formatTotal() {
-                return accounting.formatMoney(this.total, this.currencySymbol, 0, '.', ',')
-            },
-            formatResidual() {
-                return accounting.formatMoney(this.residual, this.currencySymbol, 0, '.', ',')
-            },
-            calculateResidual() {
-                this.residual = this.amountPaid >= this.total ? this.amountPaid - this.total : 0
-            },
-            ...mapActions([
-                'setPaymentReference',
-                'changeAmountPaid',
-            ])
-        },
-        data() {
-            return {
-                residual: 0
-            }
-        },
-        mounted() {
-            this.changeAmountPaid(0)
-        }
-    }
-</script>
-
-<style lang="sass">
-    .payment-amount-form
-        width: calc(100% - 450px)
-        height: 100%
-        margin-right: 50px
-        padding: 35px
-        background: #f5f5f5
-
-        .payment-amount-item
-            width: 100%
-            height: 45px
-            margin-bottom: 15px
-
-            .payment-label
-                width: 250px
-                height: 45px
-                font-size: 14pt
-
-            .payment-input
-                width: 350px
-                height: 45px
-                font-size: 24pt
-                border-radius: 0
-                text-align: right
-
-        .payment-form-separator
-            h3
-                color: #9e9e9e
-                font-size: 8pt
-            hr
-                margin-top: 5px
-
-        .result-separator
-            margin: 30px 165px 30px 250px
-            border-top: 1px solid #bdbdbd
-</style>

+ 0 - 27
src/components/payment/cash/PaymentCashStep.vue

@@ -1,27 +0,0 @@
-<template lang="pug">
-    .pos-step
-        ticket
-        payment-amount
-</template>
-
-<script>
-    import { mapGetters } from 'vuex'
-
-    import Ticket from '@/components/payment/Ticket'
-    import PaymentAmount from '@/components/payment/cash/PaymentAmount'
-
-    export default {
-        components: {
-            Ticket,
-            PaymentAmount
-        }
-    }
-</script>
-
-<style lang="sass" scoped>
-    .pos-step
-        width: 100%
-        height: calc(100% - 50px)
-        padding-bottom: 50px
-        display: flex
-</style>

+ 0 - 172
src/components/payment/credit/PaymentCreditAmount.vue

@@ -1,172 +0,0 @@
-<template lang="pug">
-    form.payment-credit-form
-        .payment-credit-form-separator
-            h3 Detalles de la Entrega
-            hr
-        .payment-amount-item
-            label.payment-label Monto entregado
-            input.payment-input(:value="paid" v-model="paid" autofocus)
-        .payment-credit-form-separator
-            h3 Pagos Pendientes
-            hr
-        .payment-credit-form-table
-            table
-                thead
-                    tr
-                        th Monto a pagar
-                        th Fecha de Pago
-                tbody
-                    tr(v-for="line in totalLines")
-                        td {{ formatAmount(line.total) }}
-                        td {{ line.date }}
-</template>
-
-<script>
-    import { mapGetters, mapActions } from 'vuex'
-
-    export default {
-        computed: {
-            paid: {
-                get() {
-                    return accounting.formatMoney(this.amountPaid, this.currencySymbol, 0, '.', ',')
-                },
-                set(value) {
-                    value = accounting.unformat(value, ',')
-
-                    this.changeAmountPaid(value)
-
-                    this.computeAmounts(this.amountPaid)
-                }
-            },
-            ...mapGetters([
-                'total',
-                'currencySymbol',
-                'amountPaid',
-                'selectedPaymentTerm',
-                'company'
-            ])
-        },
-        methods: {
-            formatAmount(amount) {
-                return accounting.formatMoney(amount, this.currencySymbol, 0, '.', ',')
-            },
-            computeAmounts(value) {
-                this.totalLines = []
-
-                let percentPaid = value / this.total
-                let distributePercent = -(percentPaid / this.selectedPaymentTerm.lines.length)
-                let totals = []
-                let residual = this.total
-
-                for (let line of this.selectedPaymentTerm.lines) {
-                    let dueDate = moment(this.company.today).add(line.days + line.days2, 'days').format('YYYY-MM-DD')
-
-                    if (percentPaid && percentPaid < 1) {
-                        totals.push([this.company.today, percentPaid])
-                        percentPaid = 0
-
-                        if (dueDate === this.company.today) {
-                            distributePercent = -((totals[0][1] - line.value_amount) / (this.selectedPaymentTerm.lines.length - 1))
-                            continue
-                        }
-                    }
-
-                    if (line.value !== 'balance') {
-                        totals.push([dueDate, line.value_amount + distributePercent])
-                        continue
-                    }
-
-                    totals.push([dueDate, line.value_amount])
-
-                    for (let line of totals) {
-                        let currentPrice = (this.total * line[1]).toFixed(2)
-
-                        if (currentPrice < 0) continue
-
-                        residual = residual - currentPrice
-            
-                        this.totalLines.push({
-                            date: line[0], 
-                            total: currentPrice !== parseFloat(0).toFixed(2) ? currentPrice : residual.toFixed(2)
-                        })
-                    }
-
-                    totals = []
-                }
-            },
-            ...mapActions([
-                'changeAmountPaid'
-            ])
-        },
-        data() {
-            return {
-                totalLines: []
-            }
-        },
-        mounted() {
-            this.changeAmountPaid(0)
-            this.computeAmounts(this.total)
-        }
-    }
-</script>
-
-<style lang="sass">
-    .payment-credit-form
-        width: calc(100% - 450px)
-        height: 100%
-        margin-right: 50px
-        padding: 35px
-        background: #f5f5f5
-
-        .payment-credit-form-separator
-            h3
-                color: #9e9e9e
-                font-size: 8pt
-            hr
-                margin-top: 5px
-
-        .result-separator
-            margin: 30px 165px 30px 250px
-            border-top: 1px solid #bdbdbd
-
-        .payment-amount-item
-            width: 100%
-            height: 45px
-            margin-bottom: 15px
-
-            .payment-label
-                width: 250px
-                height: 45px
-                font-size: 14pt
-
-            .payment-input
-                width: 350px
-                height: 45px
-                font-size: 24pt
-                border-radius: 0
-                text-align: right
-
-        .payment-credit-form-table
-                width: 100%
-                height: 250px
-                border: 1px solid #d3d3d3
-                table
-                    width: 100%
-                    thead
-                        th
-                            line-height: 35px
-                            padding-left: 10px
-                        th:nth-child(1)
-                            width: 200px
-                        th:nth-child(2)
-                            width: 200px
-                    tbody
-                        td
-                            height: 35px
-                            padding-left: 10px
-
-                        td:nth-child(1)
-                            width: 200px
-                        td:nth-child(2)
-                            width: 200px
-</style>

+ 0 - 27
src/components/payment/credit/PaymentCreditStep.vue

@@ -1,27 +0,0 @@
-<template lang="pug">
-    .pos-step
-        ticket
-        payment-credit-amount
-</template>
-
-<script>
-    import { mapGetters } from 'vuex'
-
-    import Ticket from '@/components/payment/Ticket'
-    import PaymentCreditAmount from '@/components/payment/credit/PaymentCreditAmount'
-
-    export default {
-        components: {
-            Ticket,
-            PaymentCreditAmount
-        }
-    }
-</script>
-
-<style lang="sass" scoped>
-    .pos-step
-        width: 100%
-        height: calc(100% - 50px)
-        padding-bottom: 50px
-        display: flex
-</style>

+ 0 - 134
src/components/payment/method/PaymentDetails.vue

@@ -1,134 +0,0 @@
-<template lang="pug">
-    form.payment-details
-        .form-separator
-            h3 Detalles del Cliente
-            hr
-        .form-item
-                label.form-label Cliente
-                input.form-input(readonly :value="getCustomerName()")
-        transition(name="fade")
-            .form-item(v-if="payment === 'credit'")
-                    label.form-label Crédito
-                    input.form-input(readonly :value="getCustomerCredit()")
-        .form-separator
-            h3 Detalles del Pago
-            hr
-        .form-item
-            label.form-label Forma de pago
-            .form-item-option
-                input.form-input(type="radio" id="cash" value="cash" v-model="payIn") 
-                label(for="cash") Contado
-            .form-item-option
-                input.form-input(type="radio" id="credit" value="credit" v-model="payIn")
-                label(for="credit") Crédito
-        transition(name="fade")
-            .form-item(v-if="payment === 'credit'")
-                select.form-input.input-only(v-model="term")
-                    option(v-for="term in paymentTerms" :value="term" v-if="term.lines.length > 0 && (term.lines[0].days !== 0 || term.lines[0].value !== 'balance')") {{ term.display_name }}
-            .form-item(v-else)
-                label.form-label Método de Pago
-                select.form-input(v-model="journal")
-                    option(v-for="journal in journals" :value="journal") {{ journal.display_name }}
-</template>
-
-<script>
-    import { mapGetters, mapActions } from 'vuex'
-
-    export default {
-        computed: {
-            payIn: {
-                get() {
-                    return this.payment
-                },
-                set(value) {
-                    this.togglePayment(value)
-                }
-            },
-            term: {
-                get() {
-                    return this.selectedPaymentTerm
-                },
-                set(value) {
-                    this.selectPaymentTerm(value)
-                }
-            },
-            journal: {
-                get() {
-                    return this.selectedJournal
-                },
-                set(value) {
-                    this.selectJournal(value)
-                }
-            },
-            ...mapGetters([
-                'hasSelectedCustomer',
-                'selectedCustomer',
-                'payment',
-                'paymentTerms',
-                'journals',
-                'selectedJournal',
-                'selectedPaymentTerm'
-            ])
-        },
-        methods: {
-            getCustomerName() {
-                return this.hasSelectedCustomer ? this.selectedCustomer.name : ''
-            },
-            getCustomerCredit() {
-                return this.hasSelectedCustomer && this.selectedCustomer.credit_limit >= this.selectedCustomer.credit ? this.selectedCustomer.credit_limit - this.selectedCustomer.credit : 0
-            },
-            ...mapActions([
-                'togglePayment',
-                'selectJournal',
-                'selectPaymentTerm'
-            ]) 
-        }
-    }
-</script>
-
-<style lang="sass">
-    .payment-details
-        width: calc(100% - 450px)
-        height: 100%
-        margin-right: 50px
-        padding: 35px
-        background: #f5f5f5
-
-        .form-separator
-            h3
-                color: #9e9e9e
-                font-size: 8pt
-            hr
-                margin-top: 5px
-
-        .form-item
-            width: 100%
-            height: 45px
-            margin-bottom: 15px
-
-            .form-label
-                width: 250px
-                height: 45px
-                font-size: 14pt
-
-            .form-input
-                width: 350px
-                height: 45px
-                font-size: 14pt
-                border-radius: 0
-
-                &.input-only
-                    margin-left: 250px
-                    margin-bottom: 15px
-
-            .form-item-option
-                display: inline-block
-
-                input
-                    width: 20px
-                    height: 20px
-
-                label
-                    font-size: 12pt
-                    margin: 0 45px 15px 5px
-</style>

+ 0 - 27
src/components/payment/method/PaymentMethodStep.vue

@@ -1,27 +0,0 @@
-<template lang="pug">
-    .pos-step
-        ticket
-        payment-details
-</template>
-
-<script>
-    import { mapActions, mapGetters } from 'vuex'
-
-    import Ticket from '@/components/payment/Ticket'
-    import PaymentDetails from '@/components/payment/method/PaymentDetails'
-
-    export default {
-        components: {
-            Ticket,
-            PaymentDetails
-        }
-    }
-</script>
-
-<style lang="sass" scoped>
-    .pos-step
-        width: 100%
-        height: calc(100% - 50px)
-        padding-bottom: 50px
-        display: flex
-</style>

+ 13 - 0
src/constants/actionTypes.js

@@ -0,0 +1,13 @@
+export const INIT_USER = 'INIT_USER'
+
+export const INIT_PRODUCTS = 'INIT_PRODUCTS'
+
+export const INIT_PAYMENT_TERMS = 'INIT_PAYMENT_TERMS'
+
+export const INIT_JOURNALS = 'INIT_JOURNALS' 
+
+export const INIT_DATE = 'INIT_DATE'
+
+export const INIT_CUSTOMERS = 'INIT_CUSTOMERS'
+
+export const INIT_CURRENCIES = 'INIT_CURRENCIES'

+ 28 - 0
src/constants/mutationTypes.js

@@ -0,0 +1,28 @@
+
+export const SET_USER = 'SET_USER'
+
+export const SET_LOADING_USER = 'SET_LOADING_USER'
+
+export const SET_PRODUCTS = 'SET_PRODUCTS'
+
+export const SET_LOADING_PRODUCTS = 'SET_LOADING_PRODUCTS'
+
+export const SET_PAYMENT_TERMS = 'SET_PAYMENT_TERMS'
+
+export const SET_LOADING_PAYMENT_TERMS = 'SET_LOADING_PAYMENT_TERMS'
+
+export const SET_JOURNALS = 'SET_JOURNALS'
+
+export const SET_LOADING_JOURNALS= 'SET_LOADING_JOURNALS'
+
+export const SET_DATE = 'SET_DATE'
+
+export const SET_LOADING_DATE = 'SET_LOADING_DATE'
+
+export const SET_CUSTOMERS = 'SET_CUSTOMERS'
+
+export const SET_LOADING_CUSTOMERS = 'SET_LOADING_CUSTOMERS'
+
+export const SET_CURRENCIES = 'SET_CURRENCIES'
+
+export const SET_LOADING_CURRENCIES = 'SET_LOADING_CURRENCIES'

+ 7 - 0
src/constants/resourcePaths.js

@@ -0,0 +1,7 @@
+const BASE_URL = '/eiru_sales'
+
+export const INIT_SALE_URL = `${BASE_URL}/init`
+
+export const CREATE_CUSTOMER_URL = `${BASE_URL}/create_customer`
+
+export const PROCESS_SALE_URL = `${BASE_URL}/process`

+ 4 - 5
src/index.js

@@ -1,12 +1,13 @@
 import Vue from 'vue'
 import App from '@/App'
-import VModal from 'vue-js-modal'
+import VueModal from 'vue-js-modal'
 import store from '@/store'
 
-Vue.use(VModal)
+Vue.use(VueModal)
 
 Vue.config.productionTip = false
 Vue.config.silent = true
+Vue.config.devTools = false
 
 openerp.eiru_pos = (instance, local) => {
 
@@ -20,9 +21,6 @@ openerp.eiru_pos = (instance, local) => {
                 template: '<App />',
                 components: {
                     App
-                },
-                data: {
-                    pos_instance: this
                 }
             });
         },
@@ -40,6 +38,7 @@ openerp.eiru_pos = (instance, local) => {
             instance.eiru_sidebar_toggler.sidebar_fold()
         },
         sidebarUnfold() {
+            if (!instance.eiru_sidebar_toggler) return
             instance.eiru_sidebar_toggler.sidebar_unfold()
         }
     })

+ 30 - 80
src/store/actions.js

@@ -1,93 +1,43 @@
+import axios from 'axios'
+import { INIT_SALE_URL, CREATE_CUSTOMER_URL, PROCESS_SALE_URL } from '@/constants/resourcePaths'
+
 const actions = {
+    /**
+     * 
+     * @param {*} param0 
+     * @param {*} payload 
+     */
     notify({ commit }, payload) {
         openerp.web.notification.do_warn('Atención', payload)
         return false
     },
-    initSale({ commit, dispatch }, payload) {
-        commit('setPosInstance', payload)
-
-        let promises = [
-            dispatch('fetchCompany'),
-            dispatch('fetchCurrencies'),
-            dispatch('fetchProducts'),
-            dispatch('fetchCustomers'),
-            dispatch('fetchJournals'),
-            dispatch('fetchPaymentTerms')
-        ]
-
-        Promise.all(promises).then(() => {
-            console.info('loaded')
+    /**
+     * 
+     * @param {*} param0 
+     */
+    initSale({ dispatch }) {
+        return axios.get(INIT_SALE_URL).then(response => {
+            dispatch('explodeData', response.data)
         }).catch(error => {
-            console.error(error)
+            console.log(error)
         })
     },
-    checkCart({ getters, dispatch }) {
-        return getters.cartIsEmpty ||  dispatch('notify', 'Necesitas agregar productos al carrito para continuar')
-    },
-    checkCustomer({ getters, dispatch }) {
-        return getters.hasSelectedCustomer || dispatch('notify', 'Necesitas seleccionar un cliente para continuar')
-    },
-    checkAmountPaid({ getters, dispatch }) {
-        return (getters.payment === 'cash' && getters.amountPaid >= getters.total) || dispatch('notify', 'El monto recibido no puede ser menor al monto a pagar')
-    },
-    completeSale({ getters, dispatch }) {
-        return new Promise((resolve, reject) => {
-            let AccountVoucher = new openerp.web.Model('account.voucher')
-
-            AccountVoucher.call('create_from_pos', [
-                {
-                    customer_id: getters.selectedCustomer.id,
-                    payment_term_id: getters.selectedPaymentTerm.id,
-                    journal_id: getters.selectedJournal.id,
-                    account_id: getters.selectedJournal.default_credit_account.id,
-                    cart_items: getters.cartItems.map(item => {
-                        return {
-                            id: item.id,
-                            qty: item.qty,
-                            price: item.list_price,
-                            discount: item.price >= item.list_price ? 1.0 : item.price / item.list_price
-                        }
-                    }),
-                    cart_total: getters.total,
-                    payment_reference: getters.paymentReference,
-                    amount_paid: getters.amountPaid > getters.total ? getters.total : getters.amountPaid
-                }
-            ], {
-                context: new openerp.web.CompoundContext()
-            }).then(response => {
-                window.location.reload()
-
-                dispatch('printInvoice', response)
-
-                resolve(response)
-            }).fail(error => {
-                reject(error)
-            })
-        })
+    /**
+     * 
+     * @param {*} param0 
+     * @param {*} payload 
+     */
+    explodeData({ dispatch }, payload) {
+        for (let value in payload) {
+            console.log(value)
+        }
     },
-    printInvoice({ getters }, payload) {
-        let self = getters.posInstance
+    /**
+     * 
+     * @param {*} param0 
+     */
+    createSale({ getters , dispatch }) {
 
-        let active_ids = {
-            active_id: payload.invoice_id,
-            active_ids: [payload.invoice_id],
-            active_model: 'account.invoice'
-        }
-            
-        self.rpc('/web/action/load', {
-            action_id: payload.action_id,
-            context: new openerp.web.CompoundContext(active_ids).eval()
-        }).then(result => {
-            result.context = new openerp.web.CompoundContext(result.context || {}, active_ids).eval();
-            result.flags = result.flags || {};
-            result.flags.inline = true;
-    
-            self.do_action(result, {
-                on_close: function () {
-                    self.getParent().reload();
-                }
-            }) 
-        })
     }
 }
 

+ 0 - 10
src/store/getters.js

@@ -1,10 +0,0 @@
-const getters = {
-    isLoading(state) {
-        return state.loading
-    },
-    posInstance(state) {
-        return state.pos_instance
-    }
-}
-
-export default getters

+ 18 - 23
src/store/index.js

@@ -1,35 +1,30 @@
 import Vue from 'vue'
 import Vuex from 'vuex'
 
-import state from '@/store/state'
-import getters from '@/store/getters'
 import actions from '@/store/actions'
-import mutations from '@/store/mutations'
 
-import loader from '@/store/modules/loader'
-import company from '@/store/modules/company'
-import currencies from '@/store/modules/currencies'
-import products from '@/store/modules/products'
-import cart from '@/store/modules/cart'
-import customers from '@/store/modules/customers'
-import account from '@/store/modules/account'
+import currency from '@/store/modules/currency'
+import customer from '@/store/modules/customer'
+import date from '@/store/modules/date'
+import journal from '@/store/modules/journal'
+import payment from '@/store/modules/payment'
+import product from '@/store/modules/product'
+import user from '@/store/modules/user'
 
 Vue.use(Vuex)
 
-const Store = new Vuex.Store({
-    state,
-    getters,
-    mutations,
+const store = new Vuex.Store({
     actions,
     modules: {
-        loader,
-        company,
-        currencies,
-        products,
-        cart,
-        customers,
-        account
-    }
+        currency,
+        customer,
+        date,
+        journal,
+        payment,
+        product,
+        user
+    },
+    strict: false
 })
 
-export default Store
+export default store

+ 0 - 136
src/store/modules/account.js

@@ -1,136 +0,0 @@
-const state = {
-    journals: [],
-    payment: 'cash',
-    paymentTerms: [],
-    paymentReference: '',
-    selectedJournal: null,
-    selectedPaymentTerm: null,
-    amountPaid: 0
-}
-
-const getters = {
-    journals(state) {
-        return state.journals
-    },
-    payment(state) {
-        return state.payment
-    },
-    paymentTerms(state) {
-        return state.paymentTerms
-    },
-    paymentReference(state) {
-        return state.paymentReference
-    },
-    selectedJournal(state) {
-        return state.selectedJournal
-    },
-    selectedPaymentTerm(state) {
-        return state.selectedPaymentTerm
-    },
-    amountPaid(state) {
-        return state.amountPaid
-    }
-}
-
-const mutations = {
-    setJournals(state, payload) {
-        state.journals = payload
-    },
-    setPaymentTerms(state, payload) {
-        state.paymentTerms = payload
-    },
-    setSelectedJournal(state, payload) {
-        state.selectedJournal = payload
-    },
-    setSelectedPaymentTerm(state, payload) {
-        state.selectedPaymentTerm = payload
-    },
-    setPaymentReference(state, payload) {
-        state.paymentReference = payload
-    },
-    togglePayment(state, payload) {
-        state.payment = payload
-    },
-    setAmountPaid(state, payload) {
-        state.amountPaid = payload
-    }
-}
-
-const actions = {
-    fetchJournals({ commit, dispatch }) {
-        return new Promise((resolve, reject) => {
-            let AccountJournal = new openerp.web.Model('account.journal')
-
-            AccountJournal.call('get_journals', {
-                context: new openerp.web.CompoundContext()
-            }).then(response => {
-                commit('setJournals', response)
-
-                dispatch('autoSelectJournal', response)
-                dispatch('loaded', 'journals')
-
-                resolve()
-            }).fail(error => {
-                console.log(error)
-                reject(error)
-            })
-        })
-    },
-    autoSelectJournal({ dispatch }, payload) {
-        dispatch('selectJournal', payload.find(j => j.type === 'cash'))
-    },
-    selectJournal({ commit }, payload) {
-        commit('setSelectedJournal', payload)
-    },
-    fetchPaymentTerms({ commit, dispatch }) {
-        return new Promise((resolve, reject) => {
-            let AccountPaymentTerm = new openerp.web.Model('account.payment.term')
-
-            AccountPaymentTerm.call('get_account_payment_terms', {
-                context: new openerp.web.CompoundContext()
-            }).then(response => {
-                commit('setPaymentTerms', response)
-
-                dispatch('autoSelectPaymentTerm', response)
-                dispatch('loaded', 'paymentTerms')
-
-                resolve()
-            }).fail(error => {
-                console.log(error)
-                reject(error)
-            })
-        })
-    },
-    autoSelectPaymentTerm({ dispatch }, payload) {
-        dispatch('selectPaymentTerm', payload.find(t => t.lines.length === 1 && t.lines[0].days === 0) || payload.find(t => t.lines.length === 1 && t.lines[0].days >= 0))
-    },
-    selectPaymentTerm({ commit }, payload) {
-        commit('setSelectedPaymentTerm', payload)
-    },
-    togglePayment({ commit, dispatch, getters }, payload) {
-        dispatch('autoSelectPaymentTerm', payload === 'cash' ? getters.paymentTerms : getters.paymentTerms.filter(t => t.id !== getters.selectedPaymentTerm.id))
-        commit('togglePayment', payload)
-    },
-    setPaymentReference({ commit }, payload) {
-        commit('setPaymentReference', payload)
-    },
-    changeAmountPaid({ commit, getters, dispatch }, payload) {
-        if (getters.payment === 'cash') {
-            commit('setAmountPaid', payload)
-        } else {
-            if (payload <= (getters.total * 0.7)) {
-                commit('setAmountPaid', payload)
-            } else {
-                commit('setAmountPaid', getters.total * 0.7)
-                dispatch('notify', 'El monto entregado no puede sobrepasar el 70% del total')
-            }
-        }
-    }
-}
-
-export default {
-    state,
-    getters,
-    mutations,
-    actions
-}

+ 0 - 120
src/store/modules/cart.js

@@ -1,120 +0,0 @@
-const state = {
-    cart: [],
-    total: 0,
-    productToDiscount: null
-}
-
-const getters = {
-    cartItems(state) {
-        return state.cart
-    },
-    total(state) {
-        return state.total
-    },
-    cartIsEmpty(state) {
-        return state.cart.length !== 0
-    },
-    productToDiscount(state) {
-        return state.productToDiscount
-    }
-}
-
-const mutations = {
-    addToCart(state, payload) {
-        let finded = state.cart.find(item => item.id == payload.product.id)
-
-        if (finded) {
-            payload.product.qty = payload.product.qty + 1
-        } else {
-            state.cart = [payload.product, ...state.cart]
-        }
-    },
-    subtractFromCart(state, payload) {
-        let finded = state.cart.find(item => item.id == payload.product.id)
-        finded.qty = finded.qty - 1
-    },
-    discountFromCart(state, payload) {
-        state.productToDiscount = payload
-    },
-    applyDiscount(state, payload) {
-        let finded = state.cart.find(item => item.id == state.productToDiscount.id)
-        finded.price = state.productToDiscount.discount
-    },
-    removeFromCart(state, payload) {
-        let findedIndex = state.cart.findIndex(item => item.id == payload.product.id)
-        state.cart.splice(findedIndex, 1)
-    },
-    calculateTotal(state) {
-        let sum = 0
-
-        state.cart.forEach(item => {
-            sum = sum + ((item.price || item.list_price) * (item.qty || 1))
-        })
-
-        state.total = sum
-    }
-}
-
-const actions = {
-    addToCart({ commit, dispatch }, payload) {
-        commit('addToCart', {
-            product: payload
-        })
-        
-        commit('calculateTotal')
-
-        // dispatch('setSelectedProduct', {
-        //     product: null
-        // })
-    },
-    subtractFromCart({ commit, dispatch }, payload) {
-        if (payload.qty > 1) {
-            commit('subtractFromCart', {
-                product: payload
-            })
-        } else {
-            commit('removeFromCart', {
-                product: payload
-            })     
-        }
-        
-        commit('calculateTotal')
-    },
-    discountFromCart({ commit, dispatch }, payload) {
-        if (payload && !payload.minimum_price && !payload.maximum_price) {
-            dispatch('notify', 'No hay descuento para este producto')
-            return
-        }
-
-        commit('discountFromCart', payload)
-    },
-    applyDiscount({ getters, commit, dispatch  }, payload) {
-        if(payload.apply) {
-            let product = getters.productToDiscount
-
-            if (product.priceApply < product.minimum_price || product.priceApply > product.maximum_price) {
-                dispatch('notify', 'No se puede aplicar este descuento')
-                return
-            }
-
-            commit('applyDiscount', product.id)
-            commit('calculateTotal')
-        }
-
-        dispatch('discountFromCart', null)
-    },
-    removeFromCart({ commit, dispatch }, payload) {
-        commit('removeFromCart', {
-            product: payload
-        })
-        
-        commit('calculateTotal')
-    }
-}
-
-export default {
-    state,
-    getters,
-    mutations,
-    actions
-}

+ 0 - 51
src/store/modules/company.js

@@ -1,51 +0,0 @@
-const state = {
-    company: null
-}
-
-const getters = {
-    company(state) {
-        return state.company
-    },
-    currency(state) {
-        return state.company ? state.company.currency : ''
-    },
-    currencySymbol(state) {
-        return state.company ? state.company.currency.symbol : '$'
-    }
-}
-
-const mutations = {
-    setCompany(state, payload) {
-        state.company = payload.company
-    }
-}
-
-const actions = {
-    fetchCompany({ commit, dispatch }) {
-        return new Promise((resolve, reject) => {
-            let ResCompany = new openerp.web.Model('res.company')
-
-            ResCompany.call('get_company', {
-                context: new openerp.web.CompoundContext()
-            }).then(response => {
-                commit('setCompany', {
-                    company: response
-                })
-
-                dispatch('loaded', 'company')
-                
-                resolve()
-            }).fail(error => {
-                console.log(error)
-                reject(error)
-            })
-        })
-    }
-}
-
-export default {
-    state,
-    getters,
-    mutations,
-    actions
-}

+ 0 - 44
src/store/modules/currencies.js

@@ -1,44 +0,0 @@
-const state = {
-    currencies: []
-}
-
-const getters = {
-    currencies(state) {
-        return state.currencies
-    }
-}
-
-const mutations = {
-    pushCurrencies(state, payload) {
-        state.currencies = payload.currencies
-    }
-}
-
-const actions = {
-    fetchCurrencies({ commit, dispatch }) {
-        return new Promise((resolve, reject) => {
-            let ResCurrency = new openerp.web.Model('res.currency')
-
-            ResCurrency.call('get_currencies', {
-                context: new openerp.web.CompoundContext()
-            }).then(response => {
-                commit('pushCurrencies', {
-                    currencies: response
-                })
-                dispatch('loaded', 'currencies')
-
-                resolve()
-            }).fail(error => {
-                console.log(error);
-                reject(error)
-            })
-        })
-    }
-}
-
-export default {
-    state,
-    getters,
-    mutations,
-    actions
-}

+ 67 - 0
src/store/modules/currency.js

@@ -0,0 +1,67 @@
+import { SET_CURRENCIES, SET_LOADING_CURRENCIES } from '@/constants/mutationTypes'
+import { INIT_CURRENCIES } from '@/constants/actionTypes'
+
+const initialState = {
+    currencies: [],
+    loadingCurrencies: false
+}
+
+const state = {
+    currencies: initialState.currencies,
+    loadingCurrencies: !initialState.currencies    
+}
+
+const getters = {
+    /**
+     * 
+     * @param {*} state 
+     */
+    currencies(state) {
+        return state.currencies
+    },
+    /**
+     * 
+     * @param {*} state 
+     */
+    loadingCurrencies(state) {
+        return state.loadingCurrencies
+    }
+}
+
+const mutations = {
+    /**
+     * 
+     * @param {*} state 
+     * @param {*} payload 
+     */
+    [SET_CURRENCIES] (state, payload) {
+        state.currencies = payload
+    },
+    /**
+     * 
+     * @param {*} state 
+     * @param {*} payload 
+     */
+    [SET_LOADING_CURRENCIES] (state, payload) {
+        state.loadingCurrencies = !!payload
+    }
+}
+
+const actions = {
+    /**
+     * 
+     * @param {*} param0 
+     * @param {*} payload 
+     */
+    [INIT_CURRENCIES] ({ commit }, payload) {
+        commit(SET_CURRENCIES, payload)
+        commit(SET_LOADING_CURRENCIES)
+    }
+}
+
+export default {
+    state,
+    getters,
+    actions,
+    mutations
+}

+ 67 - 0
src/store/modules/customer.js

@@ -0,0 +1,67 @@
+import { SET_CUSTOMERS, SET_LOADING_CUSTOMERS } from  '../../constants/mutationTypes'
+import { INIT_CUSTOMERS } from  '../../constants/actionTypes'
+
+const initialState = {
+    customers: [],
+    loadingCustomers: false
+}
+
+const state = {
+    customers: initialState.customers,
+    loadingCustomers: !initialState.loadingCustomers
+}
+
+const getters = {
+    /**
+     * 
+     * @param {*} state 
+     */
+    customers(state) {
+        return state.customers
+    },
+    /**
+     * 
+     * @param {*} state 
+     */
+    loadingCustomers(state) {
+        return state.loadingCustomers
+    }
+}
+
+const mutations = {
+    /**
+     * 
+     * @param {*} state 
+     * @param {*} payload 
+     */
+    [SET_CUSTOMERS] (state, payload) {
+        state.customers = payload
+    },
+    /**
+     * 
+     * @param {*} state 
+     * @param {*} payload 
+     */
+    [SET_LOADING_CUSTOMERS] (state, payload) {
+        state.loadingCustomers = !!payload
+    }
+}
+
+const actions = {
+    /**
+     * 
+     * @param {*} param0 
+     * @param {*} payload 
+     */
+    [INIT_CUSTOMERS] ({ commit }, payload) {
+        commit(SET_CUSTOMERS, payload)
+        commit(SET_LOADING_CUSTOMERS)
+    }
+}
+
+export default {
+    state,
+    getters,
+    actions,
+    mutations
+}

+ 0 - 97
src/store/modules/customers.js

@@ -1,97 +0,0 @@
-const state = {
-    customers: [],
-    filtered: [],
-    selectedCustomer: null,
-    addCustomer: false
-}
-
-const getters = {
-    customers(state) {
-        return state.filtered.length === 0 ? state.customers : state.filtered
-    },
-    hasSelectedCustomer(state) {
-        return !!state.selectedCustomer
-    },
-    selectedCustomer(state) {
-        return state.selectedCustomer
-    },
-    addCustomer(state) {
-        return state.addCustomer
-    }
-}
-
-const mutations = {
-    pushCustomers(state, payload) {
-        state.customers = [...payload, ...state.customers]
-    },
-    selectCustomer(state, payload) {
-        state.selectedCustomer = payload.customer
-    },
-    applyCustomersFilter(state, payload) {
-        state.filtered = payload
-    },
-    addCustomer(state) {
-        state.addCustomer = !state.addCustomer
-    }
-}
-
-const actions = {
-    fetchCustomers({ commit, dispatch }) {
-        return new Promise((resolve, reject) => {
-            let ResPartner = new openerp.web.Model('res.partner')
-
-            ResPartner.call('get_customers', {
-                context: new openerp.web.CompoundContext()
-            }).then(response => {
-                commit('pushCustomers', response)
-
-                dispatch('loaded', 'customers')
-
-                resolve()
-            }).fail(error => {
-                console.log(error)
-                reject(error)
-            })
-        })
-    },
-    selectCustomer({ commit }, payload) {
-        commit('selectCustomer', {
-            customer: payload
-        })
-    },
-    filterCustomers({ commit }, payload) {
-        commit('applyCustomersFilter', payload)
-    },
-    addCustomer({ commit })  {
-        commit('addCustomer')
-    },
-    saveCustomer({ commit, dispatch }, payload) {
-        if (payload) {
-            if (!payload.name || !payload.ruc || !payload.phone) {
-                dispatch('notify', 'Complete los datos para guardar')
-                return
-            }
-            
-            let ResPartner = new openerp.web.Model('res.partner')
-            ResPartner.call('create_from_pos', [
-                payload
-            ], { 
-                context: new openerp.web.CompoundContext()
-            }).then(id => {
-                commit('pushCustomers', [{
-                    id,
-                    ...payload
-                }])
-            })
-        }
-
-        commit('addCustomer')
-    }
-}
-
-export default {
-    state,
-    getters,
-    mutations,
-    actions
-}

+ 67 - 0
src/store/modules/date.js

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

+ 67 - 0
src/store/modules/journal.js

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

+ 0 - 57
src/store/modules/loader.js

@@ -1,57 +0,0 @@
-const state = {
-    loaded: {
-        company: false,
-        currencies: false,
-        customers: false,
-        products: false,
-        journals: false,
-        paymentTerms: false
-    },
-    messages: [
-        'Cargando compañía',
-        'Cargando monedas',
-        'Cargando clientes',
-        'Cargando productos'
-    ]
-}
-
-const getters = {
-    messages(state) {
-        return state.messages
-    },
-    isLoaded(state) {
-        return state.loaded.company && 
-               state.loaded.currencies && 
-               state.loaded.customers && 
-               state.loaded.products &&
-               state.loaded.journals &&
-               state.loaded.paymentTerms
-    }
-}
-
-const mutations = {
-    setLoaded(state, payload) {
-        state.loaded[payload] = true
-    },
-    setUnloaded(state) {
-        for (let module in state.loaded) {
-            state.loaded[module] = false
-        }
-    }
-}
-
-const actions = {
-    loaded({ commit }, payload) {
-        commit('setLoaded', payload)
-    },
-    clear({ commit }) {
-        commit('setUnloaded')
-    }
-}
-
-export default {
-    state,
-    getters,
-    mutations,
-    actions
-}

+ 66 - 0
src/store/modules/payment.js

@@ -0,0 +1,66 @@
+import { SET_PAYMENT_TERMS, SET_LOADING_PAYMENT_TERMS } from '@/constants/mutationTypes'
+import { INIT_PAYMENT_TERMS } from '@/constants/actionTypes'
+
+const initialState = {
+    paymentTerms: [],
+    loadingPaymentTerms: false
+}
+
+const state = {
+    paymentTerms: initialState.paymentTerms,
+    loadingPaymentTerms: !initialState.loadingPaymentTerms
+}
+
+const getters = {
+    /**
+     * 
+     * @param {*} state 
+     */
+    paymentTerms(state) {
+        return state.paymentTerms
+    },
+    /**
+     * 
+     * @param {*} state 
+     */
+    loadingPaymentTerms(state) {
+        return state.loadingPaymentTerms
+    }
+}
+
+const mutations = {
+    /**
+     * 
+     * @param {*} state 
+     * @param {*} payload 
+     */
+    [SET_PAYMENT_TERMS] (state, payload) {
+        state.paymentTerms = payload
+    },
+    /**
+     * 
+     * @param {*} state 
+     */
+    [SET_LOADING_PAYMENT_TERMS] (state, payload) {
+        state.loadingPaymentTerms = !!payload
+    }
+}
+
+const actions = {
+    /**
+     * 
+     * @param {*} param0 
+     * @param {*} payload 
+     */
+    [INIT_PAYMENT_TERMS] ({ commit }, payload) {
+        commit(SET_PAYMENT_TERMS, payload)
+        commit(SET_LOADING_PAYMENT_TERMS)
+    }
+}
+
+export default {
+    state,
+    getters,
+    actions,
+    mutations
+}

+ 68 - 0
src/store/modules/product.js

@@ -0,0 +1,68 @@
+import { SET_PRODUCTS, SET_LOADING_PRODUCTS } from '@/constants/mutationTypes'
+import { INIT_PRODUCTS } from '@/constants/actionTypes'
+
+const initialState = {
+    products: [],
+    loadingProducts: false
+}
+
+const state = {
+    products: initialState.products,
+    loadingProducts: !initialState.loadingProducts
+}
+
+const getters = {
+    /**
+     * 
+     * @param {*} state 
+     */
+    users(state) {
+        return state.products
+    },
+    /**
+     * 
+     * @param {*} state 
+     */
+    loadingProducts(state) {
+        return state.loadingProducts
+    }
+}
+
+const mutations = {
+    /**
+     * 
+     * @param {*} state 
+     * @param {*} payload 
+     */
+    [SET_PRODUCTS] (state, payload) {
+        state.products = payload
+    },
+    /**
+     * 
+     * @param {*} state 
+     * @param {*} payload 
+     */
+    [SET_LOADING_PRODUCTS] (state, payload) {
+        state.loadingProducts = !!payload
+    }
+}
+
+const actions = {
+    /**
+     * 
+     * @param {*} param0 
+     * @param {*} payload 
+     */
+    [INIT_PRODUCTS] ({ commit }, payload) {
+        commit(SET_PRODUCTS, payload)
+        commit(SET_LOADING_PRODUCTS)
+    }
+}
+
+export default {
+    state,
+    getters,
+    actions,
+    mutations
+}
+

+ 0 - 93
src/store/modules/products.js

@@ -1,93 +0,0 @@
-const state = {
-    products: [],
-    filteredProducts: [],
-    filteredVariants: [],
-    selectedProduct: null
-}
-
-const getters = {
-    products(state) {
-        return state.filteredProducts.length === 0 ? state.products : state.filteredProducts
-    },
-    selectedProduct(state) {
-        return state.selectedProduct
-    },
-    variants(state) {
-        if (!!state.selectedProduct) {
-            return state.filteredVariants.length === 0 ? state.selectedProduct.variants : state.filteredVariants
-        }
-
-        return []
-    }
-}
-
-const mutations = {
-    pushProducts(state, payload) {
-        state.products = [...payload.products]
-    },
-    setSelectedProduct(state, payload) {
-        state.selectedProduct = payload.product
-    },
-    applyProductsFilter(state, payload) {
-        state.filteredProducts = payload
-    },
-    applyVariantsFilter(state, payload) {
-        state.filteredVariants = payload
-    }
-    
-}
-
-const actions = {
-    fetchProducts ({ commit, dispatch }) {
-        return new Promise((resolve, reject) => {
-            let ProductTemplate = new openerp.web.Model('product.template')
-
-            ProductTemplate.call('get_products', {
-                context: new openerp.web.CompoundContext()
-            }).then(response => {
-                commit('pushProducts', {
-                    products: response
-                })
-
-                dispatch('loaded', 'products')
-                
-                resolve()
-            }).fail(error => {
-                console.log(error)
-                reject(error)
-            })
-        })
-    },
-    selectProduct({ commit, dispatch }, payload) {
-        if (payload.variant_count > 1) {
-            commit('setSelectedProduct', {
-                product: payload
-            })
-        } else {
-            dispatch('addToCart', payload.variants[0])
-        }
-    },
-    selectVariant({ commit, dispatch }, payload) {
-        commit('setSelectedProduct', {
-            product: null
-        })
-
-        if (!payload)
-            return
-        
-        dispatch('addToCart', payload)
-    },
-    filterProducts({ commit }, payload) {
-        commit('applyProductsFilter', payload)
-    },
-    filterVariants({ commit }, payload) {
-        commit('applyVariantsFilter', payload)
-    }
-}
-
-export default {
-    state,
-    getters,
-    mutations,
-    actions
-}

+ 0 - 21
src/store/modules/sessions.js

@@ -1,21 +0,0 @@
-const state = {
-    sessions: {}
-}
-
-const getters = {
-    getSessions(state) {
-        return state.sessions
-    }
-}
-
-const mutations = {
-    pushSessions(state, payload) {
-
-    }
-}
-
-const actions = {
-    fetchSessions({ commit }, payload) {
-        
-    }
-}

+ 67 - 0
src/store/modules/user.js

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

+ 0 - 10
src/store/mutations.js

@@ -1,10 +0,0 @@
-const mutations = {
-    resetInitialStates(state) {
-
-    },
-    setPosInstance(state, payload) {
-        state.pos_instance = payload
-    }
-}
-
-export default mutations

+ 0 - 6
src/store/state.js

@@ -1,6 +0,0 @@
-const state = {
-    loading: false,
-    pos_instance: null
-}
-
-export default state

+ 19 - 0
yarn.lock

@@ -193,6 +193,13 @@ aws4@^1.2.1:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e"
 
+axios@^0.17.0:
+  version "0.17.0"
+  resolved "https://registry.yarnpkg.com/axios/-/axios-0.17.0.tgz#7da747916db803f761651d6091d708789b953c6a"
+  dependencies:
+    follow-redirects "^1.2.3"
+    is-buffer "^1.1.5"
+
 babel-code-frame@^6.11.0, babel-code-frame@^6.22.0:
   version "6.22.0"
   resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4"
@@ -1359,6 +1366,12 @@ debug@^2.1.1, debug@^2.2.0:
   dependencies:
     ms "2.0.0"
 
+debug@^2.6.9:
+  version "2.6.9"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+  dependencies:
+    ms "2.0.0"
+
 debug@~2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da"
@@ -1677,6 +1690,12 @@ flatten@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
 
+follow-redirects@^1.2.3:
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.2.5.tgz#ffd3e14cbdd5eaa72f61b6368c1f68516c2a26cc"
+  dependencies:
+    debug "^2.6.9"
+
 for-in@^0.1.3:
   version "0.1.8"
   resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1"