Luciano
10/7/2019 - 7:32 PM

VUE JS Child component for Invoices

<template>

    <div>

        <div v-if="warning_a" role="alert" aria-live="polite" aria-atomic="true" class="alert alert-danger mt-3"><i class="fas fa-exclamation-circle mr-2"></i> Algunos de los Comprobantes imputados no corresponden al Cliente seleccionado.</div>

        <div v-if="warning_b" role="alert" aria-live="polite" aria-atomic="true" class="alert alert-danger mt-3"><i class="fas fa-exclamation-circle mr-2"></i> Algunos de los Comprobantes imputados corresponden a Clientes del sistema que no coinciden con el seleccionado.</div>

        <!-- EDIT MODE -->
        <div v-if="actions" class="table-bordered table-hover bg-white">

            <table class="table mb-0">

                <thead>
                    <tr>
                        <th class="w-260">
                            <span class="d-inline-block float-left">Comprobantes <b-badge href="javascript:void(0)" pill variant="default"><small>{{ formalities.length }}</small></b-badge></span>
                            <b-btn class="btn btn-outline-primary btn-xs ml-4 d-inline-block float-left" @click="imputableModal = true"><i class="fas fa-plus mr-1"></i> Agregar</b-btn>
                        </th>
                        <th>Fecha</th>
                        <th>Cliente</th>
                        <th v-if="!hideColumns">Título</th>
                        <th v-if="!hideColumns" class="text-nowrap">Fecha de Entrega</th>
                        <th>Estado</th>
                        <th v-if="!hideColumns">Categoría</th>
                        <th>Total</th>
                        <th class="text-nowrap">Saldo Pendiente</th>
                        <th class="w-10">A pagar</th>
                    </tr>
                </thead>

                <tbody>

                    <tr v-for="(formality, index) in formalities">

                        <td class="pt-3">

                            <router-link
                                v-if="formality.group_id == env.mix_invoice_group_id"
                                tag="a"
                                target= "_blank"
                                :to="{name:'invoice-view', params: { invoice_id: formality.id } }">
                                {{ formality.number }}
                            </router-link>

                            <router-link
                                v-else-if="formality.group_id == env.mix_receipt_group_id"
                                tag="a"
                                target= "_blank"
                                :to="{name:'receipt-view', params: { receipt_id: formality.id } }">
                                {{ formality.number }}
                            </router-link>

                            <router-link
                                v-else-if="formality.group_id == env.mix_order_group_id"
                                tag="a"
                                target= "_blank"
                                :to="{name:'order-view', params: { order_id: formality.id } }">
                                {{ formality.number }}
                            </router-link>

                            <div v-else>{{ formality.number }}</div>
                        </td>

                        <td class="pt-3">{{ moment.utc(formality.date).format('DD/MM/YYYY') }}</td>

                        <td class="pt-3">
                            <router-link
                                v-if="formality.customer_id"
                                tag="a"
                                class="text-nowrap"
                                target="_blank"
                                :to="{name:'customer-view', params: { customer_id: formality.customer_id } }"
                                title="Ver">{{ formality.customer }}
                            </router-link>
                            <span v-else>{{ formality.customer }}</span>
                        </td>

                        <td class="pt-3" v-if="!hideColumns">{{ formality.title }}</td>
                        <td class="pt-3" v-if="!hideColumns">{{ moment.utc(formality.expiration_date).format('DD/MM/YYYY') }}</td>
                        <td class="pt-3">{{ formality.status }}</td>
                        <td class="pt-3" v-if="!hideColumns">{{ formality.category }}</td>
                        <td class="pt-3">
                            <span class="d-inline">{{ formatPriceWithSymbol(formality.total) }}</span>
                            <i v-if="formality.has_nulls" class="d-inline fas fa-exclamation-circle text-danger" title="Valorizado Parcial"></i>
                        </td>
                        <td class="pt-3">{{ formatPriceWithSymbol(formality.max_imputable - formality.amount) }}</td>

                        <td>
                            <b-input
                                type="text"
                                class="pl-1 w-75 float-left"
                                v-model="formality.amount"
                                :name="'formality_amount['+index+']'"
                                data-vv-as=" "
                                v-validate="{
                                    required:true,
                                    min_value:0,
                                    max_value: formality.max_imputable,
                                    notZero: true }"
                                :class="{ 'is-invalid': veeErrors.has('formality_amount['+index+']') }" />
                            <b-btn
                                title="Eliminar Material"
                                variant="btn btn-xs d-inline-block btn-outline-danger borderless icon-btn d-inline-block align-top ml-2 mt-2"
                                @click="confirmRemove(index)">×</b-btn>
                            <label class="small text-danger mb-0" v-if="veeErrors.first('formality_amount['+index+']')">{{ veeErrors.first('formality_amount['+index+']') }}</label>
                        </td>
                    </tr>

                    <tr v-if="!formalities.length" class="no-hover">
                        <td colspan="99" class="text-center">No hay Comprobantes imputados</td>
                    </tr>

                    <tr class="no-hover alert-primary">
                        <td class="text-right font-weight-semibold border-0" :colspan="hideColumns ? 6 : 9">Total Comprobantes:</td>
                        <td class="text-left font-weight-semibold border-0">{{ formatPriceWithSymbol(total_formalities) }}</td>
                    </tr>

                </tbody>

            </table>
        </div>

        <!-- VIEW MODE -->
        <div v-else class="table-bordered table-hover bg-white">
            <table class="table mb-0">
                <thead>
                    <tr>
                        <th>
                            <span class="d-inline-block float-left">Comprobantes <b-badge href="javascript:void(0)" pill variant="default"><small>{{ formalities.length }}</small></b-badge></span>
                        </th>
                        <th>Fecha</th>
                        <th>Cliente</th>
                        <th v-if="!hideColumns">Título</th>
                        <th v-if="!hideColumns">Fecha de Entrega</th>
                        <th>Estado</th>
                        <th v-if="!hideColumns">Categoría</th>
                        <th>Total</th>
                        <th class="w-10">A pagar</th>
                    </tr>
                </thead>

                <tbody>

                    <tr v-for="(formality, index) in formalities">

                        <td class="pt-3">
                            <router-link
                                v-if="formality.group_id == env.mix_invoice_group_id"
                                tag="a"
                                target= "_blank"
                                :to="{name:'invoice-view', params: { invoice_id: formality.id } }">
                                {{ formality.number }}
                            </router-link>

                            <router-link
                                v-else-if="formality.group_id == env.mix_receipt_group_id"
                                tag="a"
                                target= "_blank"
                                :to="{name:'receipt-view', params: { receipt_id: formality.id } }">
                                {{ formality.number }}
                            </router-link>

                            <router-link
                                v-else-if="formality.group_id == env.mix_order_group_id"
                                tag="a"
                                target= "_blank"
                                :to="{name:'order-view', params: { order_id: formality.id } }">
                                {{ formality.number }}
                            </router-link>

                            <div v-else>{{ formality.number }}</div>

                        </td>

                        <td class="pt-3">{{ moment.utc(formality.date).format('DD/MM/YYYY') }}</td>

                        <td class="pt-3">
                            <router-link
                                v-if="formality.customer_id"
                                tag="a"
                                class="text-nowrap"
                                target="_blank"
                                :to="{name:'customer-view', params: { customer_id: formality.customer_id } }"
                                title="Ver">{{ formality.customer }}
                            </router-link>
                            <span v-else>{{ formality.customer }}</span>
                        </td>

                        <td class="pt-3" v-if="!hideColumns">{{ formality.title }}</td>
                        <td class="pt-3" v-if="!hideColumns">{{ moment.utc(formality.expiration_date).format('DD/MM/YYYY') }}</td>
                        <td class="pt-3">{{ formality.status }}</td>
                        <td class="pt-3" v-if="!hideColumns">{{ formality.category }}</td>
                        <td class="pt-3">
                            <span class="d-inline">{{ formatPriceWithSymbol(formality.total) }}</span>
                            <i v-if="formality.has_nulls" class="d-inline fas fa-exclamation-circle text-danger" title="Valorizado Parcial"></i>
                        </td>
                        <td class="pt-3">{{ formatPriceWithSymbol(formality.amount) }}</td>
                    </tr>

                    <tr v-if="!formalities.length" class="no-hover">
                        <td colspan="99" class="text-center">No hay Comprobantes imputados</td>
                    </tr>

                    <tr class="no-hover alert-primary">
                        <td v-if="!hideColumns" class="text-right font-weight-semibold border-0" colspan="8">Total Comprobantes:</td>
                        <td v-else class="text-right font-weight-semibold border-0" colspan="5">Total Comprobantes:</td>
                        <td class="text-left font-weight-semibold border-0">{{ formatPriceWithSymbol(total_formalities) }}</td>
                    </tr>
                </tbody>
            </table>
        </div>

        <!-- IMPUTABLE FORMALITIES MODAL -->
        <b-modal
            v-if="actions"
            hide-footer
            v-model="imputableModal"
            size="xl"
            @show="updateChecked">

            <div slot="modal-title">Seleccionar <span class="font-weight-light">Comprobantes a imputar</span> - {{ customer && customer.id ? customer.full_name : 'Cliente eventual' }}</div>

            <!-- TABLE FILTER -->
            <div class="ui-bordered px-4 pt-4 mb-4">
                <b-form @submit.prevent="refreshTable">
                    <b-form-row>

                        <b-form-group class="col" label="Buscar">
                            <input
                                type="text"
                                class="form-control"
                                v-model="search.text"
                                :placeholder="'Ingrese términos a buscar...'">
                        </b-form-group>

                        <b-form-group class="input-group col" label="Fecha">
                            <div class="input-group">
                                <flat-pickr
                                    v-model="search.date"
                                    :config="dateRangeConfig"
                                    class="form-control"
                                    placeholder="Seleccione..."
                                    name="date">
                                </flat-pickr>
                                <div class="input-group-btn">
                                    <button class="btn btn-sm btn-danger md-btn-flat mt-2" type="button" title="Clear" data-clear>
                                        <i class="fa fa-times"><span aria-hidden="true" class="sr-only">Clear</span></i>
                                    </button>
                                </div>
                            </div>
                        </b-form-group>

                        <b-form-group class="input-group col" label="Fecha de Entrega">
                            <div class="input-group">
                                <flat-pickr
                                    v-model="search.expiration_date"
                                    :config="dateRangeConfig"
                                    class="form-control"
                                    placeholder="Seleccione..."
                                    name="date">
                                </flat-pickr>
                                <div class="input-group-btn">
                                    <button class="btn btn-sm btn-danger md-btn-flat mt-2" type="button" title="Clear" data-clear>
                                        <i class="fa fa-times"><span aria-hidden="true" class="sr-only">Clear</span></i>
                                    </button>
                                </div>
                            </div>
                        </b-form-group>

                        <b-form-group class="col" label="Estado">
                            <multiselect
                                :allow-empty="true"
                                :multiple="false"
                                :close-on-select="true"
                                :searchable="false"
                                :show-labels="false"
                                placeholder="Seleccione..."
                                name="status"
                                v-model="search.status"
                                label="full_name"
                                track-by="id"
                                :options="imputable_statuses">
                            </multiselect>
                        </b-form-group>

                        <b-form-group class="col" label="Talonario">
                            <multiselect
                                :allow-empty="true"
                                :multiple="false"
                                :close-on-select="true"
                                :searchable="false"
                                :show-labels="false"
                                placeholder="Seleccione..."
                                name="book"
                                v-model="search.book"
                                label="full_name"
                                track-by="id"
                                :options="imputable_books">
                            </multiselect>
                        </b-form-group>

                        <b-form-group class="col-md col-xl-2 mb-4">
                            <label class="form-label d-none d-md-block">&nbsp;</label>
                            <ladda-btn type="submit" :loading="loading" data-style="zoom-out" class="btn btn-secondary btn-block">Buscar</ladda-btn>
                        </b-form-group>

                    </b-form-row>
                </b-form>
            </div>

            <!-- FORMALITIES TABLE -->
            <v-server-table
                v-if="imputableModal"
                ref="table"
                :columns="columns"
                :options="options"
                @loading="loading"
                @loaded="loaded">

                <div slot="h__check">
                    <input
                        type="checkbox"
                        v-model='allMarked'
                        @click="toggleAll($event)">
                </div>

                <template slot="check" slot-scope="props">
                    <input type="checkbox"
                        class="custom-control"
                        :disabled="props.row.total == 0 ? true : false"
                        @click="unmarkAll($event)"
                        :value="props.row"
                        v-model="markedRows">
                </template>

                <template slot="id" slot-scope="props">

                    <router-link
                        v-if="props.row.group_id == env.mix_order_group_id"
                        tag="a"
                        :to="{name:'order-view', params: { order_id: props.row.id } }"
                        target= "_blank">{{ props.row.number }}
                    </router-link>

                    <router-link
                        v-else-if="props.row.group_id == env.mix_invoice_group_id"
                        tag="a"
                        target= "_blank"
                        :to="{name:'invoice-view', params: { invoice_id: props.row.id } }">
                        {{ props.row.number }}
                    </router-link>

                    <router-link
                        v-else-if="props.row.group_id == env.mix_receipt_group_id"
                        tag="a"
                        target= "_blank"
                        :to="{name:'receipt-view', params: { receipt_id: props.row.id } }">
                        {{ props.row.number }}
                    </router-link>

                    <div v-else>{{ props.row.number }}</div>

                </template>

                <template slot="date" slot-scope="props">
                    {{ moment.utc(props.row.date).format('DD/MM/YYYY') }}
                </template>

                <template slot="customer" slot-scope="props">
                    <router-link
                        v-if="props.row.customer_id"
                        tag="a"
                        :to="{name:'customer-view', params: { customer_id: props.row.customer_id } }"
                        title="Ver en nueva ventana"
                        target= "_blank">{{ props.row.customer }}
                    </router-link>
                    <span v-else>{{ props.row.customer }}</span>
                </template>

                <template slot="delivery_date" slot-scope="props">
                    {{ moment.utc(props.row.expiration_date).format('DD/MM/YYYY') }}
                </template>

                <template slot="total" slot-scope="props">
                    <span>{{ props.row.total }} <i v-if="props.row.has_nulls" class="d-inline fas fa-exclamation-circle text-danger" title="Valorizado Parcial"></i></span>
                </template>

                <template slot="status" slot-scope="props">

                    <span v-if="props.row.status_id == 1">
                        <span v-if="props.row.remaining_days == 1" class="badge badge-almostdanger">{{ props.row.status }}</span>
                        <span v-else-if="props.row.remaining_days < 1" class="badge badge-danger">{{ props.row.status }}</span>
                        <span v-else class="badge badge-warning">{{ props.row.status }}</span>
                    </span>

                    <span v-else-if="props.row.status_id == 3" class="badge badge-success">{{ props.row.status }}</span>
                    <span v-else class="badge badge-default">{{ props.row.status }}</span>

                </template>

            </v-server-table>

            <b-form-row>
                <b-form-group class="col-md-12 text-center mb-0">
                    <b-btn variant="secondary btn-sm mr-2" @click="imputableModal = false">Cancelar</b-btn>
                    <b-btn variant="primary btn-sm" @click="selectFormalities">Aceptar</b-btn>
                </b-form-group>
            </b-form-row>
        </b-modal>

        <!-- CONFIRM REMOVE MODAL -->
        <b-modal
            v-if="actions"
            v-model="confirmRemoveModal"
            size="md"
            ok-title="Eliminar"
            ok-variant="danger"
            cancel-title="Cancelar"
            @ok="remove()"
            @hide="removeIndex = null">

            <div slot="modal-title">Confirmar <span class="font-weight-light">Acción</span></div>
            Está seguro que desea eliminar el Comprobante de la lista?
        </b-modal>

    </div>

</template>

<script>

import NProgress from 'nprogress'
import flatPickr from 'vue-flatpickr-component'
import {Spanish} from 'flatpickr/dist/l10n/es.js'
import moment from 'moment'
import Multiselect from 'vue-multiselect'

export default {
    name: 'imputable-formalities-component',

    components: {
        NProgress,
        flatPickr,
        Multiselect
    },

    props: [
        'customer',
        'formalities',
        'actions',
        'imputable_books',
        'imputable_statuses',
        'hideColumns'
    ],

    data() {
        return {
            moment:moment,

            env: {
                mix_receipt_group_id: process.env.MIX_RECEIPT_GROUP_ID,
                mix_creditnote_group_id: process.env.MIX_CREDITNOTE_GROUP_ID,
                mix_debitnote_group_id: process.env.MIX_DEBITNOTE_GROUP_ID,
                mix_invoice_group_id: process.env.MIX_INVOICE_GROUP_ID,
                mix_order_group_id: process.env.MIX_ORDER_GROUP_ID
            },

            // Flatpicker
            dateRangeConfig: {
                altInput: true,
                altFormat: 'd/m/Y',
                allowInput: true,
                locale: Spanish,
                static: true,
                mode: 'range'
            },

            columns: [],
            options: {
                filterable: false,
                uniqueKey:'id',
                highlightMatches: true,
                perPage:10,
                sortable: [''],

                pagination: {
                    chunk: 5,
                    edge: true
                },

                sortIcon: {
                    is: 'fa-sort',
                    base: 'fas',
                    up: 'fa-sort-up',
                    down: 'fa-sort-down'
                },

                texts:{
                    count:"Mostrando {from} a {to} de {count} registros|{count} registros|Un registro",
                    first:'Primero',
                    last:'Último',
                    filter:"Buscar:",
                    filterPlaceholder:"Términos a buscar",
                    limit:"Registros:",
                    page:"Página:",
                    noResults:"No hay Comprobantes imputables",
                    filterBy:"Filtrar por {column}",
                    loading:'Cargando...',
                    defaultOption:'Seleccionar {column}',
                    columns:'Columnas'
                },

                headings: {
                    check: '',
                    id: 'Número',
                    date: 'Fecha',
                    delivery_date: 'Fecha de Entrega',
                    status: 'Estado',
                    title: 'Título',
                    category: 'Categoría',
                    customer: 'Cliente',
                    total: 'Total',
                    total_pending: 'Saldo Pendiente',
                },

                orderBy: {
                    ascending: false,
                    column:  'id'
                },

                requestKeys: {
                    query:'queryString'
                },

                columnsClasses: {
                    'total_pending':'w-10',
                    'id':'text-nowrap',
                    'customer':'text-nowrap'
                },

                requestFunction: function (data) {

                    data.date = this.search.date
                    data.expiration_date = this.search.expiration_date
                    data.text = this.search.text

                    if (this.date_until) data.date_until = this.date_until
                    if (this.status) data.status = this.status
                    if (this.customer && this.customer.id) data.customer = this.customer.id

                    data.status = this.search.status ? [this.search.status.id] : this.imputable_statuses.map(item => item.id)
                    data.book = this.search.book ? [this.search.book.id] : this.imputable_books.map(item => item.id)

                    return axios.get('/formalities/all-imputable', {
                        params: data
                    }).catch(function (e) {
                        this.dispatch('error', e);
                    }.bind(this));

                }.bind(this),

                responseAdapter: function(resp) {
                    var data = this.getResponseData(resp);
                    let array = Object.values(data.data);

                    return { data: array, count: data.count }
                },

                rowClassCallback: function(row){
                    return row;
                }
            },

            imputableModal: false,
            allMarked:false,
            markedRows:[],

            search: {},

            confirmRemoveModal: false,
            removeIndex: null, // for deletion purposes
        }
    },

    mounted(){
        if ( this.hideColumns ) {
            this.columns = [ 'check', 'id', 'date', 'customer', 'status', 'total', 'total_pending']
        } else {
            this.columns = [ 'check', 'id', 'date', 'customer', 'title', 'delivery_date', 'status','category', 'total', 'total_pending']
        }
    },

    computed: {

        warning_a: function(){

            let warn = false

            for (var k = 0; k <= this.formalities.length - 1; k++) {
                if (this.customer && this.customer.id != null && this.customer.id != this.formalities[k].customer_id) warn = true
            }

            return warn
        },

        warning_b: function(){

            let warn = false

            for (var k = 0; k <= this.formalities.length - 1; k++) {
                if (this.customer && this.customer.id == null && this.customer.id != this.formalities[k].customer_id) warn = true
            }

            return warn
        },

        total_formalities(){
            return _.sumBy(this.formalities, function(formality) { return parseFloat(formality.amount || 0); })
        }
    },

    methods: {

        loading(){
            NProgress.start()
        },

        loaded(){
            NProgress.done()
        },

        refreshTable(){
            this.$refs.table.getData()
        },

        unmarkAll(e) {
            e.stopPropagation()
            this.allMarked = false
        },

        toggleAll(e) {
            e.stopPropagation()
            this.markedRows = !this.allMarked ? this.$refs.table.data.map(row=>row) : []
        },


        // FORMALITIES --------------------------------------------------------------------
        updateChecked(){

            for (var i = 0; i <= this.markedRows.length - 1; i++) {

                if ( this.formalities.findIndex((item) => item.id === this.markedRows[i].id) == -1 ){
                    this.markedRows.splice(i,1)
                }
            }
        },

        selectFormalities(){

            // Add missing formalities
            for (var i = 0; i <= this.markedRows.length - 1; i++) {

                let selected_formality = {...this.markedRows[i]}

                if ( this.formalities.findIndex((item) => item.id === selected_formality.id) == -1 ){
                    selected_formality.amount = selected_formality.total_pending
                    this.formalities.push(selected_formality)
                }
            }

            this.imputableModal = false
        },

        confirmRemove(index){
            this.removeIndex = index
            this.confirmRemoveModal = true
        },

        remove(){
            this.formalities.splice(this.removeIndex,1)
        }
    }
}
</script>