Sfoglia il codice sorgente

[FIX] invoice creation and product searcher

robert 6 anni fa
parent
commit
3a5ad68b06

+ 23 - 13
controllers/helpers/__init__.py

@@ -12,12 +12,13 @@ from purchase_order import (get_purchase_orders, get_purchase_order, create_purc
 from res_bank_cheque_type import get_cheque_types
 from res_bank_payments_type import get_bank_payment_types
 from res_bank import get_banks
-from res_currency import check_base_currency, get_currency_id
+from res_currency import check_base_currency, get_base_currency
 from res_partner import get_suppliers
 from res_users import get_current_user
 from server_date import get_date
 from stock_picking_type import get_picking_types
 from stock_picking import get_pickings
+from stock_warehouse import get_warehouses
 
 
 def get_data(mode=None):
@@ -25,34 +26,43 @@ def get_data(mode=None):
 
     check_base_currency()
 
+    warehouses = get_warehouses()
+    location_ids = map(lambda x: x.get('locationStock').get('id'), warehouses)
+    currencies = get_currencies_from_journal()
+
+    if len(currencies) == 0:
+        currencies = [get_base_currency()]
+
     if mode == 'purchase' or mode == 'expense':
         data = {
             'date': get_date(),
             'user': get_current_user(),
-            'currencies': get_currencies_from_journal(),
+            'currencies': currencies,
             'journals': get_journals(),
             'suppliers': get_suppliers(),
-            'products': get_products(mode),
+            'products': get_products(mode, location_ids),
             'pickingTypes': get_picking_types(),
             'paymentTerms': get_payment_terms(),
             'banks': get_banks(),
             'bankPaymentTypes': get_bank_payment_types(),
-            'chequeTypes': get_cheque_types()
+            'chequeTypes': get_cheque_types(),
+            'warehouses': warehouses
         }
 
     if mode == 'product_picking':
         data = {
             'date': get_date(),
             'user': get_current_user(),
-            'currencies': get_currencies_from_journal(),
+            'currencies': currencies,
             'suppliers': get_suppliers(),
-            'products': get_products()
+            'products': get_products('purchase', location_ids),
+            'warehouses': warehouses
         }
 
     if mode == 'payment':
         data = {
             'purchaseOrders': get_purchase_orders(),
-            'currencies': get_currencies_from_journal(),
+            'currencies': currencies,
             'suppliers': get_suppliers(),
             'journals': get_journals(),
             'paymentTerms': get_payment_terms(),
@@ -73,8 +83,7 @@ def process_data(data=None):
     if not data:
         return {}
 
-    def drafting_order(journal_id, supplier_id, cart_items, date_now, payment_term_id, warehouse_id):
-        currency_id = get_currency_id(journal_id)
+    def drafting_order(currency_id, supplier_id, cart_items, date_now, payment_term_id, warehouse_id):
         pricelist_id = get_pricelist_id(currency_id)
 
         order = create_purchase_order(supplier_id, cart_items, date_now, currency_id, pricelist_id, payment_term_id, warehouse_id)
@@ -133,27 +142,28 @@ def process_data(data=None):
 
     date_now = get_date()
     mode = data.get('mode')
+    currency_id = data.get('currencyId')
     journal_id = data.get('journalId')
     supplier_id = data.get('supplierId')
     supplier_invoice_number = data.get('supplierInvoiceNumber', None)
     cart_items = data.get('items')
     payment_term_id = data.get('paymentTermId')
     warehouse_id = data.get('warehouseId', None)
-    payment = float(data.get('payment'))
+    payment = float(data.get('payment', 0.0))
 
     if mode not in ('product_picking', 'payment', 'product_taking'):
         order_id = None
 
         if mode == 'purchase':
-            order_id, currency_id = drafting_order(journal_id, supplier_id, cart_items, date_now, payment_term_id)
+            order_id, currency_id = drafting_order(currency_id, supplier_id, cart_items, date_now, payment_term_id)
             making_order(order_id)
             complete_order(order_id)
         else:
             paying_order(order_id)
 
     if mode == 'product_picking':
-        order_id, currency_id = drafting_order(journal_id, supplier_id, cart_items, date_now, payment_term_id, warehouse_id)
-        making_order(order_id)
+        order_id, currency_id = drafting_order(currency_id, supplier_id, cart_items, date_now, payment_term_id, warehouse_id)
+        confirm_purchase_order(order_id)
 
     if mode == 'payment':
         order_id = data.get('orderId')

+ 0 - 1
controllers/helpers/account_bank_statement.py

@@ -47,4 +47,3 @@ def create_bank_statement(account_voucher_id, account_bank_statement_lines, date
         account_bank_statement.create(values)
 
     return account_bank_statement
-    

+ 6 - 2
controllers/helpers/account_move.py

@@ -1,5 +1,8 @@
 # -*- coding: utf-8 -*-
 from openerp.http import request as r
+from openerp.tools import float_round, DEFAULT_SERVER_DATE_FORMAT
+from dateutil.parser import parse
+from dateutil.relativedelta import relativedelta as rd
 
 
 def create_invoice_move_lines(invoice_ids, paid_amount, date_today):
@@ -23,13 +26,14 @@ def create_invoice_move_lines(invoice_ids, paid_amount, date_today):
         total = total * -1
         is_purchase = True
 
-    paid_percentage = abs(paid_amount / round(total, decimal_precision))
+    paid_amount = float_round(paid_amount, precision_digits=decimal_precision)
+    paid_percentage = abs(paid_amount / float_round(total, decimal_precision))
     distributed_percentage = -(paid_percentage / len(invoice.payment_term.line_ids))
 
     payment_lines = []
 
     for line in invoice.payment_term.line_ids:
-        date_due = (parse(date_today) + rd(days=line.days + line.days2)).strftime(DATE_FORMAT)
+        date_due = (parse(date_today) + rd(days=line.days + line.days2)).strftime(DEFAULT_SERVER_DATE_FORMAT)
 
         if paid_percentage and paid_percentage < 1.0:
             payment_lines.append([date_today, paid_percentage])

+ 3 - 0
controllers/helpers/product_pricelist.py

@@ -3,6 +3,9 @@ from openerp.http import request as r
 
 
 def get_pricelist_id(currency_id):
+    if not currency_id:
+        return None
+
     domain = [
         ('active', '=', True),
         ('type', '=', 'sale')

+ 32 - 6
controllers/helpers/product_template.py

@@ -2,18 +2,43 @@
 from openerp.http import request as r
 
 
-def get_products(type=None):
+def get_products(type=None, location_ids=[]):
     product_obj = r.env['product.template']
-    domain = [('standard_price', '>=', 0), ('active', '=', True)]
+    domain = [
+        ('standard_price', '>=', 0),
+        ('active', '=', True)
+    ]
 
     if type == 'purchase':
-        domain.append(('purchase_ok', '=', True))
+        domain += [('purchase_ok', '=', True)]
     else:
         if product_obj.fields_get('hr_expense_ok'):
-            domain.append(('hr_expense_ok', '=', True))
+            domain += [('hr_expense_ok', '=', True)]
         else:
             return []
 
+    in_locations = {}
+
+    if len(location_ids) > 0:
+        for q in r.env['stock.quant'].search([('location_id', 'in', location_ids)], order='product_id'):
+            entry = in_locations.get(q.product_id.id, None)
+
+            if entry:
+                if q.location_id.id not in map(lambda x: x.get('id'), entry):
+                    entry.append({
+                        'id': q.location_id.id,
+                        'name': q.location_id.display_name
+                    })
+
+                continue
+
+            in_locations[q.product_id.id] = [
+                {
+                    'id': q.location_id.id,
+                    'name': q.location_id.display_name
+                }
+            ]
+
     return [
         {
             'id': p.id,
@@ -37,13 +62,14 @@ def get_products(type=None):
                 'quantity': 1,
                 'price': v.standard_price,
                 'minimumPrice': p.minimum_price,
-                'maximumPrice': p.maximum_price
+                'maximumPrice': p.maximum_price,
+                'locations': in_locations.get(v.id, [])
             } for v in p.product_variant_ids if v.active]
         } for p in product_obj.search(domain)
     ]
 
 
-def create_product(self, values):
+def create_product(values):
     p = r.env['product.template'].create({
         'name': values.get('name'),
         'standard_price': float(values.get('price')),

+ 5 - 4
controllers/helpers/purchase_order.py

@@ -2,7 +2,7 @@
 from openerp.http import request as r
 from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
 from datetime import datetime
-from stock_location import get_stock_location_id
+from stock_warehouse import get_location_id
 
 
 def get_purchase_orders():
@@ -73,8 +73,9 @@ def create_purchase_order(supplier_id, cart_items, date_order, currency_id, pric
         'currency_id': currency_id,
         'pricelist_id': pricelist_id,
         'payment_term_id': payment_term_id,
-        'location_id': get_stock_location_id(),
-        'invoice_method': 'order'
+        'location_id': get_location_id(warehouse_id),
+        'invoice_method': 'order',
+        'from_pop': True
     }
 
     return r.env['purchase.order'].create(values)
@@ -95,7 +96,7 @@ def confirm_purchase_order(purchase_order_id):
     # po.write({
     #     'state': 'done'
     # })
-    
+
     return po.action_purchase_confirm()
 
 

+ 16 - 0
controllers/helpers/res_currency.py

@@ -14,3 +14,19 @@ def check_base_currency():
 def get_currency_id(journal_id):
     j = r.env['account.journal'].browse(journal_id)
     return j.default_debit_account_id.currency_id.id or j.default_debit_account_id.company_currency_id.id
+
+
+def get_base_currency():
+    currency = r.env.user.company_id.currency_id
+
+    return {
+        'id': currency.id,
+        'name': currency.display_name,
+        'base': currency.base,
+        'symbol': currency.symbol,
+        'position': currency.position,
+        'rateSilent': currency.rate_silent,
+        'decimalSeparator': currency.decimal_separator,
+        'decimalPlaces': currency.decimal_places,
+        'thousandsSeparator': currency.thousands_separator
+    }

+ 1 - 1
controllers/helpers/stock_location.py

@@ -4,5 +4,5 @@ from openerp.http import request as r
 
 def get_stock_location_id():
     stock_location = r.env['stock.location'].search([('usage', '=', 'internal')])
-    
+
     return stock_location.id

+ 22 - 0
controllers/helpers/stock_warehouse.py

@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+from openerp.http import request as r
+
+
+def get_warehouses():
+    return [
+        {
+            'id': w.id,
+            'name': w.display_name,
+            'locationStock': {
+                'id': w.lot_stock_id.id,
+                'name': w.lot_stock_id.display_name
+            }
+        } for w in r.env.user.store_id.warehouse_ids
+    ]
+
+
+def get_location_id(warehouse_id):
+    if not warehouse_id:
+        return None
+
+    return r.env.user.store_id.warehouse_ids.filtered(lambda x: x.id == warehouse_id).id

+ 8 - 2
models/purchase_order.py

@@ -1,13 +1,12 @@
 # -*- coding: utf-8 -*-
 from openerp import api, models, fields
 
+
 class PurchaseOrder(models.Model):
     _inherit = 'purchase.order'
 
     from_pop = fields.Boolean(string='Created from POP', default=False)
 
-    '''
-    '''
     def action_purchase_confirm(self, cr, uid, ids, context=None):
         if not context:
             context = {}
@@ -29,3 +28,10 @@ class PurchaseOrder(models.Model):
             for picking in purchase.picking_ids:
                 picking.force_assign()
                 picking.action_done()
+
+    @api.model
+    def action_invoice_create(self):
+        if self.from_pop:
+            return 0
+
+        return super(PurchaseOrder, self).action_invoice_create(self.ids)

+ 30 - 23
src/App.vue

@@ -1,16 +1,6 @@
 <template lang="pug">
     .purchases
-        form-wizard(
-            title=''
-            subtitle=''
-            finishButtonText='Finalizar'
-            :hideButtons='isProcessing'
-            color='#7c7bad'
-            nextButtonText='Continuar'
-            backButtonText='Volver'
-            @on-complete='createPurchase'
-            ref='wizard'
-        )
+        form-wizard(title='' subtitle='' color='#7c7bad' ref='wizard')
             template(v-if='isPurchase || isExpense')
                 tab-content(title='Cuál es su proveedor?' :beforeChange='checkSupplier')
                     supplier-step
@@ -33,28 +23,35 @@
                     delivery-step
                 tab-content(title='Confirmar entrega?')
                     delivery-confirm-step
+            template(slot='footer' slot-scope='props')
+                .wizard-footer-left
+                    wizard-button(v-if='props.activeTabIndex > 0' @click.native='goBack' :style='props.fillButtonStyle') Volver
+                .wizard-footer-right
+                    wizard-button(v-if='!props.isLastStep' class='wizard-footer-right' @click.native='goNext' :style='props.fillButtonStyle') Continuar
+                    wizard-button(v-else class='wizard-footer-right finish-button' @click.native='endProcess' :style='props.fillButtonStyle') {{ props.isLastStep ? 'Finalizar' : 'Continuar' }}
         loading-overlay(:show='isLoading')
 </template>
 
 <script>
     import { mapGetters, mapActions } from 'vuex'
 
-    import { FormWizard, TabContent } from 'vue-form-wizard'
+    import { FormWizard, TabContent, WizardButton } from 'vue-form-wizard'
     import 'vue-form-wizard/dist/vue-form-wizard.min.css'
 
-    import SupplierStep from '@/components/steps/SupplierStep'
-    import ProductStep from '@/components/steps/ProductStep'
-    import PaymentStep from '@/components/steps/PaymentStep'
-    import BudgetStep from '@/components/steps/BudgetStep'
+    import SupplierStep from './components/steps/SupplierStep'
+    import ProductStep from './components/steps/ProductStep'
+    import PaymentStep from './components/steps/PaymentStep'
+    import BudgetStep from './components/steps/BudgetStep'
     import DeliveryStep from './components/steps/DeliveryStep'
     import DeliveryConfirmStep from './components/steps/DeliveryConfirmStep'
 
-    import { LoadingOverlay } from '@/components/common'
+    import { LoadingOverlay } from './components/common'
 
     export default {
         components: {
             FormWizard,
             TabContent,
+            WizardButton,
             SupplierStep,
             ProductStep,
             PaymentStep,
@@ -76,13 +73,23 @@
             'mode'
         ]),
         methods: {
+            goNext() {
+                this.$refs.wizard.nextTab()
+
+                if (this.$refs.wizard.activeTabIndex >= 1) {
+                    // this.changeInitialPayment(this.initialPayment)
+                }
+            },
+            goBack() {
+                this.$refs.wizard.prevTab()
+            },
             ...mapActions([
-                'initPurchase',
-                'checkSupplier',
+                'initProcess',
                 'checkCart',
+                'checkSupplier',
                 'checkAmountReceived',
-                'createPurchase',
-                'resetPurchase'
+                'endProcess',
+                'resetProcess'
             ])
         },
         watch: {
@@ -92,11 +99,11 @@
                 }
 
                 this.$refs.wizard.changeTab(2, 0, false)
-                this.resetPurchase()
+                this.resetProcess()
             }
         },
         mounted() {
-            this.initPurchase(this.$root.mode)
+            this.initProcess(this.$root.mode)
         }
     }
 </script>

+ 2 - 1
src/components/steps/ProductStep.vue

@@ -1,7 +1,7 @@
 <template lang="pug">
     .purchase-step
         .products-selector
-            searcher(:items='products' :keys="['name', 'displayName', 'ean13']" @onSearch='filterProducts')
+            searcher(:items='variants' :keys="['name', 'displayName', 'ean13']" @onSearch='filterProducts')
             card-grid(:items='visibleProducts' :details="['price:c']" :options='selectedCurrency' @onSelect='selectProduct')
             variant-modal(:items='productWithVariant && productWithVariant.variants' :show='!!productWithVariant' @onSelect='selectProduct' @onClose='selectProduct')
             price-modal(:item='itemPriced' :options='selectedCurrency' :show='!!itemPriced' @onAccept='changePrice' @onCancel='changePrice')
@@ -25,6 +25,7 @@
         },
         computed: mapGetters([
             'products',
+            'variants',
             'visibleProducts',
             'loadingProducts',
             'cartItems',

+ 81 - 15
src/store/app.js

@@ -68,7 +68,7 @@ const mutations = {
 }
 
 const actions = {
-    initPurchase({ getters, commit, dispatch }, payload) {
+    initProcess({ getters, commit, dispatch }, payload) {
         commit('setMode', payload || getters.mode)
 
         commit('setLoading', true)
@@ -104,9 +104,6 @@ const actions = {
             console.log(response)
         }).catch(error => console.log(error))
     },
-    checkSupplier({ getters, dispatch}) {
-        return !!getters.selectedSupplier || dispatch('notify', 'Necesitas seleccionar un proveedor para continuar')
-    },
     checkCart({ getters, dispatch}) {
         for (let cartItem of getters.cartItems) {
             if (cartItem.price == 0) {
@@ -116,6 +113,15 @@ const actions = {
 
         return !!getters.cartItems.length || dispatch('notify', 'Necesitar agregar producto al carrito para continuar')
     },
+    checkSupplier({ getters, dispatch}) {
+        return !!getters.selectedSupplier || dispatch('notify', 'Necesitas seleccionar un proveedor para continuar')
+    },
+    checkPurchaseOrder({ getters, dispatch }) {
+        return !!getters.selectedPurchaseOrder || dispatch('notify', 'Necesitas seleccionar un presupuesto para continuar')
+    },
+    checkStockPicking({ getters, dispatch }) {
+        return !!getters.selectedStockPicking || dispatch('notify', 'Necesitas seleccionar una entrega para continuar')
+    },
     checkAmountReceived({ getters, dispatch }) {
         if (getters.paymentType == 'cash') {
             return getters.initialPayment >= getters.cartTotal || dispatch('notify', 'El monto recibido no puede ser menor al monto a pagar')
@@ -123,14 +129,26 @@ const actions = {
             return getters.initialPayment < getters.cartTotal || dispatch('notify', 'El monto recibido no puede ser igual o mayor al monto a pagar')
         }
     },
-    createPurchase({ getters, commit, dispatch }) {
+    endProcess({ getters, commit, dispatch }) {
+        const mode = getters.mode;
+
+        if (mode == 'purchase') {
+            if (getters.paymentType == 'cash' && getters.initialPayment < getters.amountToPay) {
+                return dispatch('notify', 'El monto recibido no puede ser menor al monto a pagar')
+            }
+
+            if (getters.paymentType != 'cash' && getters.initialPayment >= getters.amountToPay) {
+                return dispatch('notify', 'El monto recibido no puede ser igual o mayor al monto a pagar')
+            }
+        }
+
         commit('setLoading', true)
 
-        const data = {
-            jsonrpc: '2.0',
-            method: 'call',
-            params: {
-                mode: getters.mode,
+        let data = { mode }
+
+        if (['purchase', 'expense', 'product_picking'].includes(mode)) {
+            data = {
+                ...data,
                 items: getters.cartItems.map(item => {
                     return {
                         id: item.id,
@@ -140,23 +158,71 @@ const actions = {
                     }
                 }),
                 supplierId: getters.selectedSupplier.id,
+                currencyId: getters.selectedCurrency.id,
+                supplierInvoiceNumber: getters.supplierInvoiceNumber || null,
+                warehouseId: getters.selectedWarehouse.id
+            }
+        }
+        
+        if (['purchase', 'expense', 'payment'].includes(mode)) {
+            data = {
+                ...data,
+                total: getters.cartTotal,
+                purchaseOrderId: (getters.selectedPurchaseOrder && getters.selectedPurchaseOrder.id) || null,
                 paymentTermId: getters.selectedPaymentTerm.id,
                 journalId: getters.selectedJournal.id,
                 payment: getters.initialPayment > getters.cartTotal ? getters.cartTotal : getters.initialPayment,
-                supplierInvoiceNumber: getters.supplierInvoiceNumber || null
+                paymentMethod: getters.paymentMethod,
+                supplierId: getters.selectedSupplier.id,
+                currencyId: getters.selectedCurrency.id,
+                warehouseId: getters.selectedWarehouse.id                
+            }
+        }
+
+        if (mode == 'product_delivery') {
+            data = {
+                ...data,
+                stockPickingId: getters.selectedStockPicking.id
             }
         }
 
-        return axios.post(Urls.PROCESS_PURCHASE_URL, data).then(_ => {
+        // const data = {
+        //     jsonrpc: '2.0',
+        //     method: 'call',
+        //     params: {
+        //         mode: getters.mode,
+        //         items: getters.cartItems.map(item => {
+        //             return {
+        //                 id: item.id,
+        //                 name: item.name,
+        //                 quantity: item.quantity,
+        //                 price: item.price
+        //             }
+        //         }),
+        //         supplierId: getters.selectedSupplier.id,
+        //         paymentTermId: getters.selectedPaymentTerm.id,
+        //         journalId: getters.selectedJournal.id,
+        //         payment: getters.initialPayment > getters.cartTotal ? getters.cartTotal : getters.initialPayment,
+        //         supplierInvoiceNumber: getters.supplierInvoiceNumber || null
+        //     }
+        // }
+
+        return axios.post(Urls.PROCESS_PURCHASE_URL, {
+            jsonrpc: '2.0',
+            method: 'call',
+            params: {
+                ...data
+            }
+        }).then(_ => {
             dispatch('resetPurchase')
 
             commit('setLoading', false)
             commit('setCompleted', true)
         }).catch(error => {
-            console.log(error)
+            commit('setLoading', false)
         })
     },
-    resetPurchase({ rootState, dispatch }) {
+    resetProcess({ rootState, dispatch }) {
         for (let key in rootState) {
             if (!(rootState[key] instanceof Object)) {
                 continue
@@ -165,7 +231,7 @@ const actions = {
             dispatch(`reset${key[0].toUpperCase()}${key.slice(1)}`)
         }
 
-        dispatch('initPurchase')
+        dispatch('initProcess')
     },
     notify({ _ }, payload) {
         openerp.web.notification.do_warn('Atención', payload)

+ 7 - 1
src/store/index.js

@@ -15,6 +15,9 @@ import picking from '@/store/modules/picking'
 import product from '@/store/modules/product'
 import supplier from '@/store/modules/supplier'
 import user from '@/store/modules/user'
+import purchaseOrder from '@/store/modules/purchaseOrder'
+import stockPicking from '@/store/modules/stockPicking'
+import warehouse from '@/store/modules/warehouse'
 
 Vue.use(Vuex)
 
@@ -33,7 +36,10 @@ const store = new Vuex.Store({
         picking,
         product,
         supplier,
-        user
+        user,
+        purchaseOrder,
+        stockPicking,
+        warehouse
     }
 })
 

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

@@ -12,6 +12,9 @@ const getters = {
     visibleProducts(state) {
         return state.filteredProducts.length === 0 ? state.products : state.filteredProducts
     },
+    variants(_, getters) {
+        return getters.products.flatMap(p => p.variants)
+    },
     productWithVariant(state) {
         return state.productWithVariant
     },

+ 5 - 0
src/store/modules/purchaseOrder.js

@@ -45,6 +45,11 @@ const actions = {
     },
     selectPurchaseOrder({ commit, dispatch }, saleOrder) {
         commit('setSelectedPurchaseOrder', saleOrder)
+    },
+    resetPurchaseOrder({ commit }) {
+        commit('setLoadingPurchaseOrder', true)
+        commit('setPurchaseOrders', [])
+        commit('setSelectedPurchaseOrder', null)
     }
 }
 

+ 63 - 0
src/store/modules/stockPicking.js

@@ -0,0 +1,63 @@
+const state = {
+    stockPickings: [],
+    filteredStockPickings: [],
+    loadingStockPickings: false,
+    selectedStockPicking: null
+}
+
+const getters = {
+    loadingStockPickings(state) {
+        return state.loadingStockPickings
+    },
+    stockPickings(state) {
+        return state.stockPickings
+    },
+    visibleStockPickings(state)  {
+        return state.filteredStockPickings.length === 0 ? state.stockPickings : state.filteredStockPickings
+    },
+    selectedStockPickig(state) {
+        return state.selectedStockPicking
+    },
+    selectedMoveLines(state) {
+        return (state.selectedStockPicking && state.selectedStockPicking.moveLines) || []
+    }
+}
+
+const mutations = {
+    setLoadingStockPickings(state, loading) {
+        state.loadingStockPickings = !!loading
+    },
+    setStockPickings(state, stockPickings) {
+        state.stockPickings = stockPickings
+    },
+    setFilteredStockPickings(state, filtered) {
+        state.filteredStockPickings = filtered
+    },
+    setSelectedStockPicking(state, picking) {
+        state.selectedStockPicking = picking
+    }
+}
+
+const actions = {
+    initStockPickings({ commit }, stockPickings) {
+        commit('setStockPickings', stockPickings)
+        commit('setLoadingStockPickings')
+    },
+    filterStockPickings({ commit }, payload) {
+        commit('setFilteredStockPickings', payload)
+    },
+    selectStockPicking({ commit }, payload) {
+        commit('setSelectedStockPicking', payload)
+    },
+    resetStockPicking({ commit }) {
+        commit('setStockPickings', [])
+        commit('setSelectedStockPicking', null)
+    }
+}
+
+export default {
+    state,
+    getters,
+    actions,
+    mutations
+}

+ 71 - 0
src/store/modules/warehouse.js

@@ -0,0 +1,71 @@
+const state = {
+    loadingWarehouses: false,
+    warehouses: [],
+    selectedWarehouse: null
+}
+
+const getters = {
+    loadingWarehouses(state) {
+        return state.loadingWarehouses
+    },
+    warehouses(state) {
+        return state.warehouses
+    },
+    selectedWarehouse(state) {
+        return state.selectedWarehouse
+    },
+    showWarehouseSelector(state) {
+        return state.warehouses.length > 1
+    }
+}
+
+const mutations = {
+    setLoadingWarehouses(state, loading) {
+        state.loadingWarehouses = !!loading
+    },
+    setWarehouses(state, warehouses) {
+        state.warehouses = warehouses
+    },
+    setSelectedWarehouse(state, warehouseId) {
+        if (!warehouseId) {
+            state.selectedWarehouse = null
+            return
+        }
+
+        state.selectedWarehouse = state.warehouses.find(w => w.id == warehouseId)
+    },
+    setSelectedWarehouseAuto(state) {
+        state.selectedWarehouse = null
+
+        if (state.warehouses.length == 0) {
+            return
+        }
+
+        state.selectedWarehouse = state.warehouses[0]
+    }
+}
+
+const actions = {
+    initWarehouses({ commit }, warehouses) {
+        commit('setWarehouses', warehouses)
+        commit('setSelectedWarehouseAuto')
+        commit('setLoadingWarehouses')
+    },
+    selectWarehouse({ commit }, warehouseId) {
+        commit('setSelectedWarehouse', warehouseId)
+    },
+    autoSelectWarehouse({ commit }) {
+        commit('setSelectedWarehouseAuto')
+    },
+    resetWarehouse({ commit }) {
+        commit('setLoadingWarehouses')
+        commit('setWarehouses', [])
+    }
+}
+
+export default {
+    state,
+    getters,
+    actions,
+    mutations
+}