forked from verdnatura/salix-front
Merge branch 'salix-dev' into feature/worker-log
This commit is contained in:
commit
f6e3b3607a
|
@ -0,0 +1,174 @@
|
||||||
|
<script setup>
|
||||||
|
import { reactive, ref, computed } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import FormModelPopup from './FormModelPopup.vue';
|
||||||
|
import VnInputDate from './common/VnInputDate.vue';
|
||||||
|
|
||||||
|
const emit = defineEmits(['onDataSaved']);
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const manualInvoiceFormData = reactive({
|
||||||
|
maxShipped: Date.vnNew(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const formModelPopupRef = ref();
|
||||||
|
const invoiceOutSerialsOptions = ref([]);
|
||||||
|
const taxAreasOptions = ref([]);
|
||||||
|
const ticketsOptions = ref([]);
|
||||||
|
const clientsOptions = ref([]);
|
||||||
|
const isLoading = computed(() => formModelPopupRef.value?.isLoading);
|
||||||
|
|
||||||
|
const onDataSaved = async (formData, requestResponse) => {
|
||||||
|
emit('onDataSaved', formData, requestResponse);
|
||||||
|
if (requestResponse && requestResponse.id)
|
||||||
|
router.push({ name: 'InvoiceOutSummary', params: { id: requestResponse.id } });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="InvoiceOutSerials"
|
||||||
|
:filter="{ where: { code: { neq: 'R' } }, order: ['code'] }"
|
||||||
|
@on-fetch="(data) => (invoiceOutSerialsOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="TaxAreas"
|
||||||
|
:filter="{ order: ['code'] }"
|
||||||
|
@on-fetch="(data) => (taxAreasOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="Tickets"
|
||||||
|
:filter="{ fields: ['id', 'nickname'], order: 'shipped DESC', limit: 30 }"
|
||||||
|
@on-fetch="(data) => (ticketsOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="Clients"
|
||||||
|
:filter="{ fields: ['id', 'name'], order: 'name ASC', limit: 30 }"
|
||||||
|
@on-fetch="(data) => (clientsOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FormModelPopup
|
||||||
|
ref="formModelPopupRef"
|
||||||
|
:title="t('Create manual invoice')"
|
||||||
|
url-create="InvoiceOuts/createManualInvoice"
|
||||||
|
model="invoiceOut"
|
||||||
|
:form-initial-data="manualInvoiceFormData"
|
||||||
|
@on-data-saved="onDataSaved"
|
||||||
|
>
|
||||||
|
<template #form-inputs="{ data }">
|
||||||
|
<span v-if="isLoading" class="text-primary invoicing-text">
|
||||||
|
<QIcon name="warning" class="fill-icon q-mr-sm" size="md" />
|
||||||
|
{{ t('Invoicing in progress...') }}
|
||||||
|
</span>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Ticket')"
|
||||||
|
:options="ticketsOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="id"
|
||||||
|
option-value="id"
|
||||||
|
v-model="data.ticketFk"
|
||||||
|
@update:model-value="data.clientFk = null"
|
||||||
|
>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel> #{{ scope.opt?.id }} </QItemLabel>
|
||||||
|
<QItemLabel caption>{{
|
||||||
|
scope.opt?.nickname
|
||||||
|
}}</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</div>
|
||||||
|
<span class="row items-center" style="max-width: max-content">{{
|
||||||
|
t('Or')
|
||||||
|
}}</span>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Client')"
|
||||||
|
:options="clientsOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="name"
|
||||||
|
option-value="id"
|
||||||
|
v-model="data.clientFk"
|
||||||
|
@update:model-value="data.ticketFk = null"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInputDate :label="t('Max date')" v-model="data.maxShipped" />
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Serial')"
|
||||||
|
:options="invoiceOutSerialsOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="description"
|
||||||
|
option-value="code"
|
||||||
|
v-model="data.serial"
|
||||||
|
:required="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Area')"
|
||||||
|
:options="taxAreasOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="code"
|
||||||
|
option-value="code"
|
||||||
|
v-model="data.taxArea"
|
||||||
|
:required="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<VnInput
|
||||||
|
:label="t('Reference')"
|
||||||
|
type="textarea"
|
||||||
|
v-model="data.reference"
|
||||||
|
fill-input
|
||||||
|
autogrow
|
||||||
|
/>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormModelPopup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.invoicing-text {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: $primary;
|
||||||
|
font-size: 24px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Create manual invoice: Crear factura manual
|
||||||
|
Ticket: Ticket
|
||||||
|
Client: Cliente
|
||||||
|
Max date: Fecha límite
|
||||||
|
Serial: Serie
|
||||||
|
Area: Area
|
||||||
|
Reference: Referencia
|
||||||
|
Or: O
|
||||||
|
Invoicing in progress...: Facturación en progreso...
|
||||||
|
</i18n>
|
|
@ -78,10 +78,6 @@ const $props = defineProps({
|
||||||
|
|
||||||
const emit = defineEmits(['onFetch', 'onDataSaved']);
|
const emit = defineEmits(['onFetch', 'onDataSaved']);
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
save,
|
|
||||||
});
|
|
||||||
|
|
||||||
const componentIsRendered = ref(false);
|
const componentIsRendered = ref(false);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
@ -227,6 +223,11 @@ watch(formUrl, async () => {
|
||||||
reset();
|
reset();
|
||||||
fetch();
|
fetch();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
save,
|
||||||
|
isLoading,
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="column items-center full-width">
|
<div class="column items-center full-width">
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import FormModel from 'components/FormModel.vue';
|
import FormModel from 'components/FormModel.vue';
|
||||||
|
@ -39,21 +39,28 @@ const $props = defineProps({
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const formModelRef = ref(null);
|
||||||
const closeButton = ref(null);
|
const closeButton = ref(null);
|
||||||
const isLoading = ref(false);
|
|
||||||
|
|
||||||
const onDataSaved = (formData, requestResponse) => {
|
const onDataSaved = (formData, requestResponse) => {
|
||||||
emit('onDataSaved', formData, requestResponse);
|
emit('onDataSaved', formData, requestResponse);
|
||||||
closeForm();
|
closeForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeForm = () => {
|
const isLoading = computed(() => formModelRef.value?.isLoading);
|
||||||
|
|
||||||
|
const closeForm = async () => {
|
||||||
if (closeButton.value) closeButton.value.click();
|
if (closeButton.value) closeButton.value.click();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
isLoading,
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<FormModel
|
<FormModel
|
||||||
|
ref="formModelRef"
|
||||||
:form-initial-data="formInitialData"
|
:form-initial-data="formInitialData"
|
||||||
:observe-form-changes="false"
|
:observe-form-changes="false"
|
||||||
:default-actions="false"
|
:default-actions="false"
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const emit = defineEmits(['onSubmit']);
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
subtitle: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
defaultSubmitButton: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
defaultCancelButton: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
customSubmitButtonLabel: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const closeButton = ref(null);
|
||||||
|
const isLoading = ref(false);
|
||||||
|
|
||||||
|
const onSubmit = () => {
|
||||||
|
emit('onSubmit');
|
||||||
|
closeForm();
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeForm = () => {
|
||||||
|
if (closeButton.value) closeButton.value.click();
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<QForm
|
||||||
|
@submit="onSubmit($event)"
|
||||||
|
class="all-pointer-events full-width"
|
||||||
|
style="max-width: 800px"
|
||||||
|
>
|
||||||
|
<QCard class="q-pa-lg">
|
||||||
|
<span ref="closeButton" class="close-icon" v-close-popup>
|
||||||
|
<QIcon name="close" size="sm" />
|
||||||
|
</span>
|
||||||
|
<h1 class="title">{{ title }}</h1>
|
||||||
|
<p>{{ subtitle }}</p>
|
||||||
|
<slot name="form-inputs" />
|
||||||
|
<div class="q-mt-lg row justify-end">
|
||||||
|
<QBtn
|
||||||
|
v-if="defaultSubmitButton"
|
||||||
|
:label="customSubmitButtonLabel || t('globals.save')"
|
||||||
|
type="submit"
|
||||||
|
color="primary"
|
||||||
|
:disabled="isLoading"
|
||||||
|
:loading="isLoading"
|
||||||
|
/>
|
||||||
|
<QBtn
|
||||||
|
v-if="defaultCancelButton"
|
||||||
|
:label="t('globals.cancel')"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
class="q-ml-sm"
|
||||||
|
:disabled="isLoading"
|
||||||
|
:loading="isLoading"
|
||||||
|
v-close-popup
|
||||||
|
/>
|
||||||
|
<slot name="customButtons" />
|
||||||
|
</div>
|
||||||
|
</QCard>
|
||||||
|
</QForm>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.title {
|
||||||
|
font-size: 17px;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,168 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||||
|
import FormPopup from './FormPopup.vue';
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
import useNotify from 'src/composables/useNotify.js';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
invoiceOutData: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const router = useRouter();
|
||||||
|
const { notify } = useNotify();
|
||||||
|
|
||||||
|
const transferInvoiceParams = reactive({
|
||||||
|
id: $props.invoiceOutData?.id,
|
||||||
|
refFk: $props.invoiceOutData?.ref,
|
||||||
|
});
|
||||||
|
const closeButton = ref(null);
|
||||||
|
const clientsOptions = ref([]);
|
||||||
|
const rectificativeTypeOptions = ref([]);
|
||||||
|
const siiTypeInvoiceOutsOptions = ref([]);
|
||||||
|
const invoiceCorrectionTypesOptions = ref([]);
|
||||||
|
|
||||||
|
const closeForm = () => {
|
||||||
|
if (closeButton.value) closeButton.value.click();
|
||||||
|
};
|
||||||
|
|
||||||
|
const transferInvoice = async () => {
|
||||||
|
try {
|
||||||
|
const { data } = await axios.post(
|
||||||
|
'InvoiceOuts/transferInvoice',
|
||||||
|
transferInvoiceParams
|
||||||
|
);
|
||||||
|
notify(t('Transferred invoice'), 'positive');
|
||||||
|
closeForm();
|
||||||
|
router.push('InvoiceOutSummary', { id: data.id });
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error transfering invoice', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="Clients"
|
||||||
|
@on-fetch="(data) => (clientsOptions = data)"
|
||||||
|
:filter="{ fields: ['id', 'name'], order: 'id', limit: 30 }"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="CplusRectificationTypes"
|
||||||
|
:filter="{ order: 'description' }"
|
||||||
|
@on-fetch="(data) => (rectificativeTypeOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="SiiTypeInvoiceOuts"
|
||||||
|
:filter="{ where: { code: { like: 'R%' } } }"
|
||||||
|
@on-fetch="(data) => (siiTypeInvoiceOutsOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="InvoiceCorrectionTypes"
|
||||||
|
@on-fetch="(data) => (invoiceCorrectionTypesOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FormPopup
|
||||||
|
@on-submit="transferInvoice()"
|
||||||
|
:title="t('Transfer invoice')"
|
||||||
|
:custom-submit-button-label="t('Transfer client')"
|
||||||
|
:default-cancel-button="false"
|
||||||
|
>
|
||||||
|
<template #form-inputs>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Client')"
|
||||||
|
:options="clientsOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="name"
|
||||||
|
option-value="id"
|
||||||
|
v-model="transferInvoiceParams.newClientFk"
|
||||||
|
:required="true"
|
||||||
|
>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>
|
||||||
|
#{{ scope.opt?.id }} -
|
||||||
|
{{ scope.opt?.name }}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Rectificative type')"
|
||||||
|
:options="rectificativeTypeOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="description"
|
||||||
|
option-value="id"
|
||||||
|
v-model="transferInvoiceParams.cplusRectificationTypeFk"
|
||||||
|
:required="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Class')"
|
||||||
|
:options="siiTypeInvoiceOutsOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="description"
|
||||||
|
option-value="id"
|
||||||
|
v-model="transferInvoiceParams.siiTypeInvoiceOutFk"
|
||||||
|
:required="true"
|
||||||
|
>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>
|
||||||
|
{{ scope.opt?.code }} -
|
||||||
|
{{ scope.opt?.description }}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Type')"
|
||||||
|
:options="invoiceCorrectionTypesOptions"
|
||||||
|
hide-selected
|
||||||
|
option-label="description"
|
||||||
|
option-value="id"
|
||||||
|
v-model="transferInvoiceParams.invoiceCorrectionTypeFk"
|
||||||
|
:required="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormPopup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Transfer invoice: Transferir factura
|
||||||
|
Transfer client: Transferir cliente
|
||||||
|
Client: Cliente
|
||||||
|
Rectificative type: Tipo rectificativa
|
||||||
|
Class: Clase
|
||||||
|
Type: Tipo
|
||||||
|
Transferred invoice: Factura transferida
|
||||||
|
</i18n>
|
|
@ -0,0 +1,23 @@
|
||||||
|
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
|
||||||
|
export function useVnConfirm() {
|
||||||
|
const quasar = useQuasar();
|
||||||
|
|
||||||
|
const openConfirmationModal = (title, message, promise, successFn) => {
|
||||||
|
quasar
|
||||||
|
.dialog({
|
||||||
|
component: VnConfirm,
|
||||||
|
componentProps: {
|
||||||
|
title: title,
|
||||||
|
message: message,
|
||||||
|
promise: promise,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.onOk(async () => {
|
||||||
|
if (successFn) successFn();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return { openConfirmationModal };
|
||||||
|
}
|
|
@ -97,6 +97,10 @@ select:-webkit-autofill {
|
||||||
background-color: var(--vn-light-gray);
|
background-color: var(--vn-light-gray);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fill-icon {
|
||||||
|
font-variation-settings: 'FILL' 1;
|
||||||
|
}
|
||||||
|
|
||||||
.vn-table-separation-row {
|
.vn-table-separation-row {
|
||||||
height: 16px !important;
|
height: 16px !important;
|
||||||
background-color: var(--vn-gray) !important;
|
background-color: var(--vn-gray) !important;
|
||||||
|
|
|
@ -592,6 +592,7 @@ export default {
|
||||||
company: 'Company',
|
company: 'Company',
|
||||||
dued: 'Due date',
|
dued: 'Due date',
|
||||||
shortDued: 'Due date',
|
shortDued: 'Due date',
|
||||||
|
amount: 'Amount',
|
||||||
},
|
},
|
||||||
card: {
|
card: {
|
||||||
issued: 'Issued',
|
issued: 'Issued',
|
||||||
|
@ -625,7 +626,7 @@ export default {
|
||||||
fillDates: 'Invoice date and the max date should be filled',
|
fillDates: 'Invoice date and the max date should be filled',
|
||||||
invoiceDateLessThanMaxDate: 'Invoice date can not be less than max date',
|
invoiceDateLessThanMaxDate: 'Invoice date can not be less than max date',
|
||||||
invoiceWithFutureDate: 'Exists an invoice with a future date',
|
invoiceWithFutureDate: 'Exists an invoice with a future date',
|
||||||
noTicketsToInvoice: 'There are not clients to invoice',
|
noTicketsToInvoice: 'There are not tickets to invoice',
|
||||||
criticalInvoiceError: 'Critical invoicing error, process stopped',
|
criticalInvoiceError: 'Critical invoicing error, process stopped',
|
||||||
},
|
},
|
||||||
table: {
|
table: {
|
||||||
|
|
|
@ -592,6 +592,7 @@ export default {
|
||||||
company: 'Empresa',
|
company: 'Empresa',
|
||||||
dued: 'Fecha vencimineto',
|
dued: 'Fecha vencimineto',
|
||||||
shortDued: 'F. vencimiento',
|
shortDued: 'F. vencimiento',
|
||||||
|
amount: 'Importe',
|
||||||
},
|
},
|
||||||
card: {
|
card: {
|
||||||
issued: 'Fecha emisión',
|
issued: 'Fecha emisión',
|
||||||
|
@ -627,7 +628,7 @@ export default {
|
||||||
invoiceDateLessThanMaxDate:
|
invoiceDateLessThanMaxDate:
|
||||||
'La fecha de la factura no puede ser menor que la fecha máxima',
|
'La fecha de la factura no puede ser menor que la fecha máxima',
|
||||||
invoiceWithFutureDate: 'Existe una factura con una fecha futura',
|
invoiceWithFutureDate: 'Existe una factura con una fecha futura',
|
||||||
noTicketsToInvoice: 'No hay clientes para facturar',
|
noTicketsToInvoice: 'No existen tickets para facturar',
|
||||||
criticalInvoiceError: 'Error crítico en la facturación, proceso detenido',
|
criticalInvoiceError: 'Error crítico en la facturación, proceso detenido',
|
||||||
},
|
},
|
||||||
table: {
|
table: {
|
||||||
|
|
|
@ -2,13 +2,15 @@
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { toCurrency, toDate } from 'src/filters';
|
|
||||||
import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
||||||
import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
|
import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
|
||||||
import VnLv from 'src/components/ui/VnLv.vue';
|
import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
import useCardDescription from 'src/composables/useCardDescription';
|
|
||||||
import InvoiceOutDescriptorMenu from './InvoiceOutDescriptorMenu.vue';
|
import InvoiceOutDescriptorMenu from './InvoiceOutDescriptorMenu.vue';
|
||||||
|
|
||||||
|
import useCardDescription from 'src/composables/useCardDescription';
|
||||||
|
import { toCurrency, toDate } from 'src/filters';
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
id: {
|
id: {
|
||||||
type: Number,
|
type: Number,
|
||||||
|
@ -23,7 +25,6 @@ const { t } = useI18n();
|
||||||
const entityId = computed(() => {
|
const entityId = computed(() => {
|
||||||
return $props.id || route.params.id;
|
return $props.id || route.params.id;
|
||||||
});
|
});
|
||||||
const descriptor = ref();
|
|
||||||
|
|
||||||
const filter = {
|
const filter = {
|
||||||
include: [
|
include: [
|
||||||
|
@ -42,6 +43,8 @@ const filter = {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const descriptor = ref();
|
||||||
|
|
||||||
function ticketFilter(invoice) {
|
function ticketFilter(invoice) {
|
||||||
return JSON.stringify({ refFk: invoice.ref });
|
return JSON.stringify({ refFk: invoice.ref });
|
||||||
}
|
}
|
||||||
|
@ -61,7 +64,7 @@ const setData = (entity) => (data.value = useCardDescription(entity.ref, entity.
|
||||||
data-key="invoiceOutData"
|
data-key="invoiceOutData"
|
||||||
>
|
>
|
||||||
<template #menu="{ entity }">
|
<template #menu="{ entity }">
|
||||||
<InvoiceOutDescriptorMenu :invoice-out="entity" />
|
<InvoiceOutDescriptorMenu :invoice-out-data="entity" />
|
||||||
</template>
|
</template>
|
||||||
<template #body="{ entity }">
|
<template #body="{ entity }">
|
||||||
<VnLv :label="t('invoiceOut.card.issued')" :value="toDate(entity.issued)" />
|
<VnLv :label="t('invoiceOut.card.issued')" :value="toDate(entity.issued)" />
|
||||||
|
|
|
@ -1,40 +1,260 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
|
||||||
|
import TransferInvoiceForm from 'src/components/TransferInvoiceForm.vue';
|
||||||
|
import SendEmailDialog from 'components/common/SendEmailDialog.vue';
|
||||||
|
|
||||||
|
import useNotify from 'src/composables/useNotify';
|
||||||
|
import { useSession } from 'src/composables/useSession';
|
||||||
|
import { usePrintService } from 'composables/usePrintService';
|
||||||
|
import { useVnConfirm } from 'composables/useVnConfirm';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
invoiceOutData: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { notify } = useNotify();
|
||||||
|
const router = useRouter();
|
||||||
|
const session = useSession();
|
||||||
|
const token = session.getToken();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
const { openReport, sendEmail } = usePrintService();
|
||||||
|
const { openConfirmationModal } = useVnConfirm();
|
||||||
|
const quasar = useQuasar();
|
||||||
|
|
||||||
|
const transferInvoiceDialogRef = ref();
|
||||||
|
const invoiceFormType = ref('pdf');
|
||||||
|
const defaultEmailAddress = ref($props.invoiceOutData.client?.email);
|
||||||
|
|
||||||
|
const showInvoicePdf = () => {
|
||||||
|
const url = `api/InvoiceOuts/${$props.invoiceOutData.id}/download?access_token=${token}`;
|
||||||
|
window.open(url, '_blank');
|
||||||
|
};
|
||||||
|
|
||||||
|
const showInvoiceCsv = () => {
|
||||||
|
openReport(`InvoiceOuts/${$props.invoiceOutData.ref}/invoice-csv`, {
|
||||||
|
recipientId: $props.invoiceOutData.client.id,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const showSendInvoiceDialog = (type) => {
|
||||||
|
invoiceFormType.value = type;
|
||||||
|
quasar.dialog({
|
||||||
|
component: SendEmailDialog,
|
||||||
|
componentProps: {
|
||||||
|
data: {
|
||||||
|
address: defaultEmailAddress.value,
|
||||||
|
},
|
||||||
|
promise: sendEmailInvoice,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const sendEmailInvoice = async ({ address }) => {
|
||||||
|
try {
|
||||||
|
if (!address) notify(`The email can't be empty`, 'negative');
|
||||||
|
|
||||||
|
if (invoiceFormType.value === 'pdf') {
|
||||||
|
return sendEmail(`InvoiceOuts/${$props.invoiceOutData.ref}/invoice-email`, {
|
||||||
|
recipientId: $props.invoiceOutData.client.id,
|
||||||
|
recipient: address,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return sendEmail(
|
||||||
|
`InvoiceOuts/${$props.invoiceOutData.ref}/invoice-csv-email`,
|
||||||
|
{
|
||||||
|
recipientId: $props.invoiceOutData.client.id,
|
||||||
|
recipient: address,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error sending email', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const redirectToInvoiceOutList = () => {
|
||||||
|
router.push({ name: 'InvoiceOutList' });
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteInvoice = async () => {
|
||||||
|
try {
|
||||||
|
await axios.post(`InvoiceOuts/${$props.invoiceOutData.id}/delete`);
|
||||||
|
notify(t('InvoiceOut deleted'), 'positive');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error deleting invoice out', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const bookInvoice = async () => {
|
||||||
|
try {
|
||||||
|
await axios.post(`InvoiceOuts/${$props.invoiceOutData.ref}/book`);
|
||||||
|
notify(t('InvoiceOut booked'), 'positive');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error booking invoice out', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateInvoicePdf = async () => {
|
||||||
|
try {
|
||||||
|
await axios.post(`InvoiceOuts/${$props.invoiceOutData.id}/createPdf`);
|
||||||
|
notify(t('The invoice PDF document has been regenerated'), 'positive');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error generating invoice out pdf', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const refundInvoice = async (withWarehouse) => {
|
||||||
|
try {
|
||||||
|
const params = { ref: $props.invoiceOutData.ref, withWarehouse: withWarehouse };
|
||||||
|
const { data } = await axios.post('InvoiceOuts/refund', params);
|
||||||
|
notify(
|
||||||
|
t('refundInvoiceSuccessMessage', {
|
||||||
|
refundTicket: data[0].id,
|
||||||
|
}),
|
||||||
|
'positive'
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error generating invoice out pdf', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<QItem v-ripple clickable>
|
<QItem v-ripple clickable @click="transferInvoiceDialogRef.show()">
|
||||||
<QItemSection>{{ t('Transfer invoice to') }}</QItemSection>
|
<QItemSection>{{ t('Transfer invoice to...') }}</QItemSection>
|
||||||
</QItem>
|
</QItem>
|
||||||
<QItem v-ripple clickable>
|
<QItem v-ripple clickable>
|
||||||
<QItemSection>{{ t('See invoice') }}</QItemSection>
|
<QItemSection>{{ t('Show invoice...') }}</QItemSection>
|
||||||
|
<QItemSection side>
|
||||||
|
<QIcon name="keyboard_arrow_right" />
|
||||||
|
</QItemSection>
|
||||||
|
<QMenu anchor="top end" self="top start">
|
||||||
|
<QList>
|
||||||
|
<QItem v-ripple clickable @click="showInvoicePdf()">
|
||||||
|
<QItemSection>{{ t('As PDF') }}</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem v-ripple clickable @click="showInvoiceCsv()">
|
||||||
|
<QItemSection>{{ t('As CSV') }}</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</QList>
|
||||||
|
</QMenu>
|
||||||
</QItem>
|
</QItem>
|
||||||
<QItem v-ripple clickable>
|
<QItem v-ripple clickable>
|
||||||
<QItemSection>{{ t('Send invoice') }}</QItemSection>
|
<QItemSection>{{ t('Send invoice...') }}</QItemSection>
|
||||||
|
<QItemSection side>
|
||||||
|
<QIcon name="keyboard_arrow_right" />
|
||||||
|
</QItemSection>
|
||||||
|
<QMenu anchor="top end" self="top start">
|
||||||
|
<QList>
|
||||||
|
<QItem v-ripple clickable @click="showSendInvoiceDialog('pdf')">
|
||||||
|
<QItemSection>{{ t('Send PDF') }}</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem v-ripple clickable @click="showSendInvoiceDialog('csv')">
|
||||||
|
<QItemSection>{{ t('Send CSV') }}</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</QList>
|
||||||
|
</QMenu>
|
||||||
</QItem>
|
</QItem>
|
||||||
<QItem v-ripple clickable>
|
<QItem
|
||||||
|
v-ripple
|
||||||
|
clickable
|
||||||
|
@click="
|
||||||
|
openConfirmationModal(
|
||||||
|
t('Confirm deletion'),
|
||||||
|
t('Are you sure you want to delete this invoice?'),
|
||||||
|
deleteInvoice,
|
||||||
|
redirectToInvoiceOutList
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
<QItemSection>{{ t('Delete invoice') }}</QItemSection>
|
<QItemSection>{{ t('Delete invoice') }}</QItemSection>
|
||||||
</QItem>
|
</QItem>
|
||||||
<QItem v-ripple clickable>
|
<QItem
|
||||||
<QItemSection>{{ t('Post invoice') }}</QItemSection>
|
v-ripple
|
||||||
|
clickable
|
||||||
|
@click="
|
||||||
|
openConfirmationModal(
|
||||||
|
'',
|
||||||
|
t('Are you sure you want to book this invoice?'),
|
||||||
|
bookInvoice
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<QItemSection>{{ t('Book invoice') }}</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem
|
||||||
|
v-ripple
|
||||||
|
clickable
|
||||||
|
@click="
|
||||||
|
openConfirmationModal(
|
||||||
|
t('Generate PDF invoice document'),
|
||||||
|
t('Are you sure you want to generate/regenerate the PDF invoice?'),
|
||||||
|
generateInvoicePdf
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<QItemSection>{{ t('Generate PDF invoice') }}</QItemSection>
|
||||||
</QItem>
|
</QItem>
|
||||||
<QItem v-ripple clickable>
|
<QItem v-ripple clickable>
|
||||||
<QItemSection>{{ t('Regenerate invoice PDF') }}</QItemSection>
|
<QItemSection>{{ t('Refund...') }}</QItemSection>
|
||||||
</QItem>
|
<QItemSection side>
|
||||||
<QItem v-ripple clickable>
|
<QIcon name="keyboard_arrow_right" />
|
||||||
<QItemSection>{{ t('Pass') }}</QItemSection>
|
</QItemSection>
|
||||||
|
<QMenu anchor="top end" self="top start">
|
||||||
|
<QList>
|
||||||
|
<QItem v-ripple clickable @click="refundInvoice(true)">
|
||||||
|
<QItemSection>{{ t('With warehouse') }}</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem v-ripple clickable @click="refundInvoice(false)">
|
||||||
|
<QItemSection>{{ t('Without warehouse') }}</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</QList>
|
||||||
|
</QMenu>
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('Create a single ticket with all the content of the current invoice') }}
|
||||||
|
</QTooltip>
|
||||||
</QItem>
|
</QItem>
|
||||||
|
|
||||||
|
<QDialog ref="transferInvoiceDialogRef">
|
||||||
|
<TransferInvoiceForm :invoice-out-data="invoiceOutData" />
|
||||||
|
</QDialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<i18n>
|
<i18n>
|
||||||
es:
|
es:
|
||||||
Transfer invoice to: Transferir factura a
|
Transfer invoice to...: Transferir factura a...
|
||||||
See invoice: Ver factura
|
Show invoice...: Ver factura...
|
||||||
Send invoice: Enviar factura
|
Send invoice...: Enviar factura...
|
||||||
Delete invoice: Eliminar factura
|
Delete invoice: Eliminar factura
|
||||||
Post invoice: Asentar factura
|
Book invoice: Asentar factura
|
||||||
Regenerate invoice PDF: Regenerar PDF factura
|
Generate PDF invoice: Generar PDF factura
|
||||||
Pass: Abono
|
Refund...: Abono
|
||||||
|
As PDF: como PDF
|
||||||
|
As CSV: como CSV
|
||||||
|
Send PDF: Enviar PDF
|
||||||
|
Send CSV: Enviar CSV
|
||||||
|
With warehouse: Con almacén
|
||||||
|
Without warehouse: Sin almacén
|
||||||
|
InvoiceOut deleted: Factura eliminada
|
||||||
|
Confirm deletion: Confirmar eliminación
|
||||||
|
Are you sure you want to delete this invoice?: Estas seguro de eliminar esta factura?
|
||||||
|
Are you sure you want to book this invoice?: Estas seguro de querer asentar esta factura?
|
||||||
|
InvoiceOut booked: Factura asentada
|
||||||
|
Generate PDF invoice document: Generar PDF de la factura
|
||||||
|
Are you sure you want to generate/regenerate the PDF invoice?: ¿Seguro que quieres generar/regenerar el PDF de la factura?
|
||||||
|
The invoice PDF document has been regenerated: El documento PDF de la factura ha sido regenerado
|
||||||
|
Create a single ticket with all the content of the current invoice: Crear un ticket único con todo el contenido de la factura actual
|
||||||
|
refundInvoiceSuccessMessage: Se ha creado el siguiente ticket de abono {refundTicket}
|
||||||
|
The email can't be empty: El email no puede estar vacío
|
||||||
|
|
||||||
|
en:
|
||||||
|
refundInvoiceSuccessMessage: The following refund ticket have been created {refundTicket}
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -7,6 +7,8 @@ import { toCurrency, toDate } from 'src/filters';
|
||||||
import CardSummary from 'components/ui/CardSummary.vue';
|
import CardSummary from 'components/ui/CardSummary.vue';
|
||||||
import VnLv from 'src/components/ui/VnLv.vue';
|
import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
import { getUrl } from 'src/composables/getUrl';
|
import { getUrl } from 'src/composables/getUrl';
|
||||||
|
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
|
||||||
|
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
fetch();
|
fetch();
|
||||||
|
@ -67,29 +69,33 @@ const taxColumns = ref([
|
||||||
const ticketsColumns = ref([
|
const ticketsColumns = ref([
|
||||||
{
|
{
|
||||||
name: 'item',
|
name: 'item',
|
||||||
label: 'invoiceOut.summary.ticketId',
|
label: t('invoiceOut.summary.ticketId'),
|
||||||
field: (row) => row.id,
|
field: (row) => row.id,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'quantity',
|
name: 'quantity',
|
||||||
label: 'invoiceOut.summary.nickname',
|
label: t('invoiceOut.summary.nickname'),
|
||||||
field: (row) => row.nickname,
|
field: (row) => row.nickname,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'landed',
|
name: 'landed',
|
||||||
label: 'invoiceOut.summary.shipped',
|
label: t('invoiceOut.summary.shipped'),
|
||||||
field: (row) => row.shipped,
|
field: (row) => row.shipped,
|
||||||
format: (value) => toDate(value),
|
format: (value) => toDate(value),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'landed',
|
name: 'landed',
|
||||||
label: 'invoiceOut.summary.totalWithVat',
|
label: t('invoiceOut.summary.totalWithVat'),
|
||||||
field: (row) => row.totalWithVat,
|
field: (row) => row.totalWithVat,
|
||||||
format: (value) => toCurrency(value),
|
format: (value) => toCurrency(value),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
</script>
|
</script>
|
||||||
|
@ -151,12 +157,21 @@ const ticketsColumns = ref([
|
||||||
<QIcon name="open_in_new" />
|
<QIcon name="open_in_new" />
|
||||||
</a>
|
</a>
|
||||||
<QTable v-if="tickets" :columns="ticketsColumns" :rows="tickets" flat>
|
<QTable v-if="tickets" :columns="ticketsColumns" :rows="tickets" flat>
|
||||||
<template #header="props">
|
<template #body-cell-item="{ value }">
|
||||||
<QTr :props="props">
|
<QTd>
|
||||||
<QTh v-for="col in props.cols" :key="col.name" :props="props">
|
<QBtn flat color="primary">
|
||||||
{{ t(col.label) }}
|
{{ value }}
|
||||||
</QTh>
|
<TicketDescriptorProxy :id="value" />
|
||||||
</QTr>
|
</QBtn>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-quantity="{ value, row }">
|
||||||
|
<QTd>
|
||||||
|
<QBtn flat color="primary" dense>
|
||||||
|
{{ value }}
|
||||||
|
<CustomerDescriptorProxy :id="row.id" />
|
||||||
|
</QBtn>
|
||||||
|
</QTd>
|
||||||
</template>
|
</template>
|
||||||
</QTable>
|
</QTable>
|
||||||
</QCard>
|
</QCard>
|
||||||
|
|
|
@ -21,6 +21,7 @@ const {
|
||||||
nPdfs,
|
nPdfs,
|
||||||
totalPdfs,
|
totalPdfs,
|
||||||
errors,
|
errors,
|
||||||
|
addresses,
|
||||||
} = storeToRefs(invoiceOutGlobalStore);
|
} = storeToRefs(invoiceOutGlobalStore);
|
||||||
|
|
||||||
const selectedCustomerId = ref(null);
|
const selectedCustomerId = ref(null);
|
||||||
|
@ -86,6 +87,14 @@ const selectCustomerId = (id) => {
|
||||||
selectedCustomerId.value = id;
|
selectedCustomerId.value = id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const statusText = computed(() => {
|
||||||
|
return status.value === 'invoicing'
|
||||||
|
? `${t(`status.${status.value}`)} ${
|
||||||
|
addresses.value[getAddressNumber.value]?.clientId
|
||||||
|
}`
|
||||||
|
: t(`status.${status.value}`);
|
||||||
|
});
|
||||||
|
|
||||||
onMounted(() => (stateStore.rightDrawer = true));
|
onMounted(() => (stateStore.rightDrawer = true));
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
stateStore.rightDrawer = false;
|
stateStore.rightDrawer = false;
|
||||||
|
@ -103,7 +112,7 @@ onUnmounted(() => {
|
||||||
<QPage class="column items-center q-pa-md">
|
<QPage class="column items-center q-pa-md">
|
||||||
<QCard v-if="status" class="card">
|
<QCard v-if="status" class="card">
|
||||||
<QCardSection class="card-section">
|
<QCardSection class="card-section">
|
||||||
<span class="text">{{ t(`status.${status}`) }}</span>
|
<span class="text">{{ statusText }}</span>
|
||||||
<span class="text">{{
|
<span class="text">{{
|
||||||
t('invoiceOut.globalInvoices.statusCard.percentageText', {
|
t('invoiceOut.globalInvoices.statusCard.percentageText', {
|
||||||
getPercentage: getPercentage,
|
getPercentage: getPercentage,
|
||||||
|
|
|
@ -13,13 +13,8 @@ const { t } = useI18n();
|
||||||
const invoiceOutGlobalStore = useInvoiceOutGlobalStore();
|
const invoiceOutGlobalStore = useInvoiceOutGlobalStore();
|
||||||
|
|
||||||
// invoiceOutGlobalStore state and getters
|
// invoiceOutGlobalStore state and getters
|
||||||
const {
|
const { initialDataLoading, formInitialData, invoicing, status } =
|
||||||
initialDataLoading,
|
storeToRefs(invoiceOutGlobalStore);
|
||||||
formInitialData,
|
|
||||||
|
|
||||||
invoicing,
|
|
||||||
status,
|
|
||||||
} = storeToRefs(invoiceOutGlobalStore);
|
|
||||||
|
|
||||||
// invoiceOutGlobalStore actions
|
// invoiceOutGlobalStore actions
|
||||||
const { makeInvoice, setStatusValue } = invoiceOutGlobalStore;
|
const { makeInvoice, setStatusValue } = invoiceOutGlobalStore;
|
||||||
|
@ -32,13 +27,7 @@ const printersOptions = ref([]);
|
||||||
|
|
||||||
const clientsOptions = ref([]);
|
const clientsOptions = ref([]);
|
||||||
|
|
||||||
const formData = ref({
|
const formData = ref({});
|
||||||
companyFk: null,
|
|
||||||
invoiceDate: null,
|
|
||||||
maxShipped: null,
|
|
||||||
clientId: null,
|
|
||||||
printer: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
const optionsInitialData = computed(() => {
|
const optionsInitialData = computed(() => {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -2,25 +2,32 @@
|
||||||
import { onMounted, onUnmounted, ref } from 'vue';
|
import { onMounted, onUnmounted, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { exportFile, useQuasar } from 'quasar';
|
|
||||||
import { useStateStore } from 'stores/useStateStore';
|
|
||||||
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
||||||
import InvoiceOutSummary from './Card/InvoiceOutSummary.vue';
|
import InvoiceOutSummary from './Card/InvoiceOutSummary.vue';
|
||||||
import { toDate, toCurrency } from 'src/filters/index';
|
|
||||||
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||||
import InvoiceOutFilter from './InvoiceOutFilter.vue';
|
|
||||||
import VnLv from 'src/components/ui/VnLv.vue';
|
import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
import CardList from 'src/components/ui/CardList.vue';
|
import CardList from 'src/components/ui/CardList.vue';
|
||||||
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
||||||
|
import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vue';
|
||||||
|
import CreateManualInvoiceForm from 'src/components/CreateManualInvoiceForm.vue';
|
||||||
|
|
||||||
|
import InvoiceOutFilter from './InvoiceOutFilter.vue';
|
||||||
|
import { toDate, toCurrency } from 'src/filters/index';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
||||||
|
import { useSession } from 'src/composables/useSession';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const selectedCards = ref(new Map());
|
|
||||||
const quasar = useQuasar();
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const stateStore = useStateStore();
|
const stateStore = useStateStore();
|
||||||
|
const session = useSession();
|
||||||
|
const token = session.getToken();
|
||||||
const { viewSummary } = useSummaryDialog();
|
const { viewSummary } = useSummaryDialog();
|
||||||
|
|
||||||
|
const manualInvoiceDialogRef = ref(null);
|
||||||
|
const selectedCards = ref(new Map());
|
||||||
|
|
||||||
onMounted(() => (stateStore.rightDrawer = true));
|
onMounted(() => (stateStore.rightDrawer = true));
|
||||||
onUnmounted(() => (stateStore.rightDrawer = false));
|
onUnmounted(() => (stateStore.rightDrawer = false));
|
||||||
|
|
||||||
|
@ -52,37 +59,37 @@ const toggleAllCards = (cardsData) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const downloadCsv = () => {
|
const openPdf = () => {
|
||||||
if (selectedCards.value.size === 0) return;
|
try {
|
||||||
const selectedCardsArray = Array.from(selectedCards.value.values());
|
if (selectedCards.value.size === 0) return;
|
||||||
let file;
|
const selectedCardsArray = Array.from(selectedCards.value.values());
|
||||||
for (var i = 0; i < selectedCardsArray.length; i++) {
|
|
||||||
if (i == 0) file += Object.keys(selectedCardsArray[i]).join(';') + '\n';
|
if (selectedCards.value.size === 1) {
|
||||||
file +=
|
const [invoiceOut] = selectedCardsArray;
|
||||||
Object.keys(selectedCardsArray[i])
|
const url = `api/InvoiceOuts/${invoiceOut.id}/download?access_token=${token}`;
|
||||||
.map(function (key) {
|
window.open(url, '_blank');
|
||||||
return selectedCardsArray[i][key];
|
} else {
|
||||||
})
|
const invoiceOutIdsArray = selectedCardsArray.map(
|
||||||
.join(';') + '\n';
|
(invoiceOut) => invoiceOut.id
|
||||||
}
|
);
|
||||||
const status = exportFile('file.csv', file, {
|
const invoiceOutIds = invoiceOutIdsArray.join(',');
|
||||||
encoding: 'windows-1252',
|
|
||||||
mimeType: 'text/csv;charset=windows-1252;',
|
const params = new URLSearchParams({
|
||||||
});
|
access_token: token,
|
||||||
if (status === true) {
|
ids: invoiceOutIds,
|
||||||
quasar.notify({
|
});
|
||||||
message: t('fileAllowed'),
|
|
||||||
color: 'positive',
|
const url = `api/InvoiceOuts/downloadZip?${params}`;
|
||||||
icon: 'check',
|
window.open(url, '_blank');
|
||||||
});
|
}
|
||||||
} else {
|
} catch (err) {
|
||||||
quasar.notify({
|
console.error('Error opening PDF');
|
||||||
message: t('fileDenied'),
|
|
||||||
color: 'negative',
|
|
||||||
icon: 'warning',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openCreateInvoiceModal = () => {
|
||||||
|
manualInvoiceDialogRef.value.show();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -119,52 +126,21 @@ const downloadCsv = () => {
|
||||||
<VnPaginate
|
<VnPaginate
|
||||||
auto-load
|
auto-load
|
||||||
data-key="InvoiceOutList"
|
data-key="InvoiceOutList"
|
||||||
order="issued DESC, id DESC"
|
:order="['issued DESC', 'id DESC']"
|
||||||
url="InvoiceOuts/filter"
|
url="InvoiceOuts/filter"
|
||||||
>
|
>
|
||||||
<template #body="{ rows }">
|
<template #body="{ rows }">
|
||||||
<VnSubToolbar class="bg-vn-dark justify-end">
|
<VnSubToolbar class="bg-vn-dark justify-end">
|
||||||
<template #st-actions>
|
<template #st-actions>
|
||||||
<QBtn
|
<QBtn
|
||||||
@click="downloadCsv()"
|
@click="openPdf()"
|
||||||
class="q-mr-xl"
|
class="q-mr-md"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
icon="cloud_download"
|
||||||
:disable="selectedCards.size === 0"
|
:disable="selectedCards.size === 0"
|
||||||
:label="t('globals.download')"
|
|
||||||
/>
|
|
||||||
<!-- <QBtnDropdown
|
|
||||||
class="q-mr-xl"
|
|
||||||
color="primary"
|
|
||||||
:disable="!manageCheckboxes && arrayElements.length < 1"
|
|
||||||
:label="t('globals.download')"
|
|
||||||
v-else
|
|
||||||
>
|
>
|
||||||
<QList>
|
<QTooltip>{{ t('downloadPdf') }}</QTooltip>
|
||||||
<QItem clickable v-close-popup @click="downloadCsv(rows)">
|
</QBtn>
|
||||||
<QItemSection>
|
|
||||||
<QItemLabel>
|
|
||||||
{{
|
|
||||||
t('globals.allRows', {
|
|
||||||
numberRows: rows.length,
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
</QItemLabel>
|
|
||||||
</QItemSection>
|
|
||||||
</QItem>
|
|
||||||
|
|
||||||
<QItem clickable v-close-popup @click="downloadCsv(rows)">
|
|
||||||
<QItemSection>
|
|
||||||
<QItemLabel>
|
|
||||||
{{
|
|
||||||
t('globals.selectRows', {
|
|
||||||
numberRows: rows.length,
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
</QItemLabel>
|
|
||||||
</QItemSection>
|
|
||||||
</QItem>
|
|
||||||
</QList>
|
|
||||||
</QBtnDropdown> -->
|
|
||||||
<QCheckbox
|
<QCheckbox
|
||||||
left-label
|
left-label
|
||||||
:label="t('globals.markAll')"
|
:label="t('globals.markAll')"
|
||||||
|
@ -189,18 +165,23 @@ const downloadCsv = () => {
|
||||||
>
|
>
|
||||||
<template #list-items>
|
<template #list-items>
|
||||||
<VnLv
|
<VnLv
|
||||||
:label="t('invoiceOut.list.shortIssued')"
|
:label="t('invoiceOut.list.issued')"
|
||||||
:title-label="t('invoiceOut.list.issued')"
|
|
||||||
:value="toDate(row.issued)"
|
:value="toDate(row.issued)"
|
||||||
/>
|
/>
|
||||||
<VnLv
|
<VnLv
|
||||||
:label="t('invoiceOut.list.amount')"
|
:label="t('invoiceOut.list.amount')"
|
||||||
:value="toCurrency(row.amount)"
|
:value="toCurrency(row.amount)"
|
||||||
/>
|
/>
|
||||||
<VnLv
|
<VnLv :label="t('invoiceOut.list.client')">
|
||||||
:label="t('invoiceOut.list.client')"
|
<template #value>
|
||||||
:value="row.clientSocialName"
|
<span class="link" @click.stop>
|
||||||
/>
|
{{ row?.clientSocialName }}
|
||||||
|
<CustomerDescriptorProxy
|
||||||
|
:id="row?.clientFk"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</VnLv>
|
||||||
<VnLv
|
<VnLv
|
||||||
:label="t('invoiceOut.list.shortCreated')"
|
:label="t('invoiceOut.list.shortCreated')"
|
||||||
:title-label="t('invoiceOut.list.created')"
|
:title-label="t('invoiceOut.list.created')"
|
||||||
|
@ -217,13 +198,6 @@ const downloadCsv = () => {
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<QBtn
|
|
||||||
:label="t('components.smartCard.openCard')"
|
|
||||||
@click.stop="navigate(row.id)"
|
|
||||||
class="bg-vn-dark"
|
|
||||||
outline
|
|
||||||
type="reset"
|
|
||||||
/>
|
|
||||||
<QBtn
|
<QBtn
|
||||||
:label="t('components.smartCard.openSummary')"
|
:label="t('components.smartCard.openSummary')"
|
||||||
@click.stop="viewSummary(row.id, InvoiceOutSummary)"
|
@click.stop="viewSummary(row.id, InvoiceOutSummary)"
|
||||||
|
@ -237,6 +211,20 @@ const downloadCsv = () => {
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</VnPaginate>
|
</VnPaginate>
|
||||||
|
<QPageSticky :offset="[20, 20]">
|
||||||
|
<QBtn fab icon="add" color="primary" @click="openCreateInvoiceModal()" />
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('createInvoice') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QPageSticky>
|
||||||
|
|
||||||
|
<QDialog
|
||||||
|
ref="manualInvoiceDialogRef"
|
||||||
|
transition-show="scale"
|
||||||
|
transition-hide="scale"
|
||||||
|
>
|
||||||
|
<CreateManualInvoiceForm />
|
||||||
|
</QDialog>
|
||||||
</QPage>
|
</QPage>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -246,9 +234,13 @@ en:
|
||||||
fileDenied: Browser denied file download...
|
fileDenied: Browser denied file download...
|
||||||
fileAllowed: Successful download of CSV file
|
fileAllowed: Successful download of CSV file
|
||||||
youCanSearchByInvoiceReference: You can search by invoice reference
|
youCanSearchByInvoiceReference: You can search by invoice reference
|
||||||
|
downloadPdf: Download PDF
|
||||||
|
createInvoice: Make invoice
|
||||||
es:
|
es:
|
||||||
searchInvoice: Buscar factura emitida
|
searchInvoice: Buscar factura emitida
|
||||||
fileDenied: El navegador denegó la descarga de archivos...
|
fileDenied: El navegador denegó la descarga de archivos...
|
||||||
fileAllowed: Descarga exitosa de archivo CSV
|
fileAllowed: Descarga exitosa de archivo CSV
|
||||||
youCanSearchByInvoiceReference: Puedes buscar por referencia de la factura
|
youCanSearchByInvoiceReference: Puedes buscar por referencia de la factura
|
||||||
|
downloadPdf: Descargar PDF
|
||||||
|
createInvoice: Crear factura
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onBeforeMount } from 'vue';
|
import { ref, computed, onBeforeMount, onMounted, nextTick } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { QCheckbox, QBtn } from 'quasar';
|
|
||||||
|
|
||||||
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
|
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
|
||||||
import InvoiceOutNegativeFilter from './InvoiceOutNegativeBasesFilter.vue';
|
import InvoiceOutNegativeFilter from './InvoiceOutNegativeBasesFilter.vue';
|
||||||
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
||||||
|
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
|
||||||
|
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
|
||||||
|
|
||||||
import { toCurrency } from 'src/filters';
|
import { toCurrency } from 'src/filters';
|
||||||
import { useInvoiceOutGlobalStore } from 'src/stores/invoiceOutGlobal.js';
|
import { useInvoiceOutGlobalStore } from 'src/stores/invoiceOutGlobal.js';
|
||||||
import { useStateStore } from 'stores/useStateStore';
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import { useArrayData } from 'composables/useArrayData';
|
import { useArrayData } from 'composables/useArrayData';
|
||||||
import VnUserLink from 'src/components/ui/VnUserLink.vue';
|
|
||||||
|
|
||||||
const invoiceOutGlobalStore = useInvoiceOutGlobalStore();
|
const invoiceOutGlobalStore = useInvoiceOutGlobalStore();
|
||||||
const stateStore = useStateStore();
|
const stateStore = useStateStore();
|
||||||
|
@ -45,78 +45,16 @@ onBeforeMount(async () => {
|
||||||
stateStore.rightDrawer = true;
|
stateStore.rightDrawer = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const componentIsRendered = ref(false);
|
||||||
|
|
||||||
|
onMounted(() =>
|
||||||
|
nextTick(() => {
|
||||||
|
componentIsRendered.value = true;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const rows = computed(() => arrayData.value.store.data);
|
const rows = computed(() => arrayData.value.store.data);
|
||||||
|
|
||||||
const selectedCustomerId = ref(0);
|
|
||||||
const selectedWorkerId = ref(0);
|
|
||||||
|
|
||||||
const tableColumnComponents = {
|
|
||||||
company: {
|
|
||||||
component: 'span',
|
|
||||||
props: () => {},
|
|
||||||
event: () => {},
|
|
||||||
},
|
|
||||||
country: {
|
|
||||||
component: 'span',
|
|
||||||
props: () => {},
|
|
||||||
event: () => {},
|
|
||||||
},
|
|
||||||
clientId: {
|
|
||||||
component: QBtn,
|
|
||||||
props: () => ({ flat: true, color: 'blue' }),
|
|
||||||
event: (prop) => selectCustomerId(prop.value),
|
|
||||||
},
|
|
||||||
client: {
|
|
||||||
component: 'span',
|
|
||||||
props: () => {},
|
|
||||||
event: () => {},
|
|
||||||
},
|
|
||||||
amount: {
|
|
||||||
component: 'span',
|
|
||||||
props: () => {},
|
|
||||||
event: () => {},
|
|
||||||
},
|
|
||||||
base: {
|
|
||||||
component: 'span',
|
|
||||||
props: () => {},
|
|
||||||
event: () => {},
|
|
||||||
},
|
|
||||||
ticketId: {
|
|
||||||
component: 'span',
|
|
||||||
props: () => {},
|
|
||||||
event: () => {},
|
|
||||||
},
|
|
||||||
active: {
|
|
||||||
component: QCheckbox,
|
|
||||||
props: (prop) => ({
|
|
||||||
disable: true,
|
|
||||||
'model-value': Boolean(prop.value),
|
|
||||||
}),
|
|
||||||
event: () => {},
|
|
||||||
},
|
|
||||||
hasToInvoice: {
|
|
||||||
component: QCheckbox,
|
|
||||||
props: (prop) => ({
|
|
||||||
disable: true,
|
|
||||||
'model-value': Boolean(prop.value),
|
|
||||||
}),
|
|
||||||
event: () => {},
|
|
||||||
},
|
|
||||||
verifiedData: {
|
|
||||||
component: QCheckbox,
|
|
||||||
props: (prop) => ({
|
|
||||||
disable: true,
|
|
||||||
'model-value': Boolean(prop.value),
|
|
||||||
}),
|
|
||||||
event: () => {},
|
|
||||||
},
|
|
||||||
comercial: {
|
|
||||||
component: QBtn,
|
|
||||||
props: () => ({ flat: true, color: 'blue' }),
|
|
||||||
event: (prop) => selectWorkerId(prop.row.comercialId),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const columns = computed(() => [
|
const columns = computed(() => [
|
||||||
{
|
{
|
||||||
label: t('invoiceOut.negativeBases.company'),
|
label: t('invoiceOut.negativeBases.company'),
|
||||||
|
@ -205,20 +143,17 @@ const downloadCSV = async () => {
|
||||||
params
|
params
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectCustomerId = (id) => {
|
|
||||||
selectedCustomerId.value = id;
|
|
||||||
};
|
|
||||||
|
|
||||||
const selectWorkerId = (id) => {
|
|
||||||
selectedWorkerId.value = id;
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<template v-if="stateStore.isHeaderMounted()">
|
<template v-if="stateStore.isHeaderMounted()">
|
||||||
<Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
|
<Teleport
|
||||||
<QBtn color="primary" icon-right="archive" no-caps @click="downloadCSV()" />
|
to="#st-actions"
|
||||||
|
v-if="stateStore?.isSubToolbarShown() && componentIsRendered"
|
||||||
|
>
|
||||||
|
<QBtn color="primary" icon-right="download" no-caps @click="downloadCSV()">
|
||||||
|
<QTooltip>{{ t('Download as CSV') }}</QTooltip>
|
||||||
|
</QBtn>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
</template>
|
</template>
|
||||||
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||||
|
@ -236,31 +171,37 @@ const selectWorkerId = (id) => {
|
||||||
:pagination="{ rowsPerPage: 0 }"
|
:pagination="{ rowsPerPage: 0 }"
|
||||||
class="full-width q-mt-md"
|
class="full-width q-mt-md"
|
||||||
>
|
>
|
||||||
<template #body-cell="props">
|
<template #body-cell-clientId="{ row }">
|
||||||
<QTd :props="props">
|
<QTd>
|
||||||
<component
|
<QBtn flat dense color="blue"> {{ row.clientId }}</QBtn>
|
||||||
:is="tableColumnComponents[props.col.name].component"
|
<CustomerDescriptorProxy :id="row.clientId" />
|
||||||
class="col-content"
|
</QTd>
|
||||||
v-bind="tableColumnComponents[props.col.name].props(props)"
|
</template>
|
||||||
@click="tableColumnComponents[props.col.name].event(props)"
|
<template #body-cell-ticketId="{ row }">
|
||||||
>
|
<QTd>
|
||||||
<template
|
<QBtn flat dense color="blue"> {{ row.ticketFk }}</QBtn>
|
||||||
v-if="
|
<TicketDescriptorProxy :id="row.ticketFk" />
|
||||||
props.col.name !== 'active' &&
|
</QTd>
|
||||||
props.col.name !== 'hasToInvoice' &&
|
</template>
|
||||||
props.col.name !== 'verifiedData'
|
<template #body-cell-comercial="{ row }">
|
||||||
"
|
<QTd>
|
||||||
>{{ props.value }}
|
<QBtn flat dense color="blue">{{ row.comercialName }}</QBtn>
|
||||||
</template>
|
<WorkerDescriptorProxy :id="row.comercialId" />
|
||||||
<CustomerDescriptorProxy
|
</QTd>
|
||||||
v-if="props.col.name === 'clientId'"
|
</template>
|
||||||
:id="selectedCustomerId"
|
<template #body-cell-active="{ row }">
|
||||||
/>
|
<QTd>
|
||||||
<VnUserLink
|
<QCheckbox :model-value="!!row.isActive" disable />
|
||||||
v-if="props.col.name === 'comercial'"
|
</QTd>
|
||||||
:worker-id="selectedWorkerId"
|
</template>
|
||||||
/>
|
<template #body-cell-hasToInvoice="{ row }">
|
||||||
</component>
|
<QTd>
|
||||||
|
<QCheckbox :model-value="!!row.hasToInvoice" disable />
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-verifiedData="{ row }">
|
||||||
|
<QTd>
|
||||||
|
<QCheckbox :model-value="!!row.isTaxDataChecked" disable />
|
||||||
</QTd>
|
</QTd>
|
||||||
</template>
|
</template>
|
||||||
</QTable>
|
</QTable>
|
||||||
|
@ -274,4 +215,7 @@ const selectWorkerId = (id) => {
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<i18n></i18n>
|
<i18n>
|
||||||
|
es:
|
||||||
|
Download as CSV: Descargar como CSV
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -44,7 +44,7 @@ export default {
|
||||||
name: 'InvoiceOutNegativeBases',
|
name: 'InvoiceOutNegativeBases',
|
||||||
meta: {
|
meta: {
|
||||||
title: 'negativeBases',
|
title: 'negativeBases',
|
||||||
icon: 'view_list',
|
icon: 'vn:ticket',
|
||||||
},
|
},
|
||||||
component: () =>
|
component: () =>
|
||||||
import('src/pages/InvoiceOut/InvoiceOutNegativeBases.vue'),
|
import('src/pages/InvoiceOut/InvoiceOutNegativeBases.vue'),
|
||||||
|
|
|
@ -73,6 +73,9 @@ export const useInvoiceOutGlobalStore = defineStore({
|
||||||
const stringDate = data.issued.substring(0, 10);
|
const stringDate = data.issued.substring(0, 10);
|
||||||
this.minInvoicingDate = stringDate;
|
this.minInvoicingDate = stringDate;
|
||||||
this.formInitialData.invoiceDate = stringDate;
|
this.formInitialData.invoiceDate = stringDate;
|
||||||
|
|
||||||
|
this.minInvoicingDate = new Date(data.issued);
|
||||||
|
this.formInitialData.invoiceDate = this.minInvoicingDate;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error fetching invoice out global initial data');
|
console.error('Error fetching invoice out global initial data');
|
||||||
throw new Error();
|
throw new Error();
|
||||||
|
@ -103,12 +106,8 @@ export const useInvoiceOutGlobalStore = defineStore({
|
||||||
|
|
||||||
if (clientsToInvoice == 'all') params.clientId = undefined;
|
if (clientsToInvoice == 'all') params.clientId = undefined;
|
||||||
|
|
||||||
const addressesResponse = await await axios.post(
|
const { data } = await axios.post('InvoiceOuts/clientsToInvoice', params);
|
||||||
'InvoiceOuts/clientsToInvoice',
|
this.addresses = data;
|
||||||
params
|
|
||||||
);
|
|
||||||
|
|
||||||
this.addresses = addressesResponse.data;
|
|
||||||
|
|
||||||
if (!this.addresses || !this.addresses.length > 0) {
|
if (!this.addresses || !this.addresses.length > 0) {
|
||||||
notify(
|
notify(
|
||||||
|
@ -118,9 +117,9 @@ export const useInvoiceOutGlobalStore = defineStore({
|
||||||
throw new Error("There aren't addresses to invoice");
|
throw new Error("There aren't addresses to invoice");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.addresses.forEach(async (address) => {
|
for (const address of this.addresses) {
|
||||||
await this.invoiceClient(address, formData);
|
await this.invoiceClient(address, formData);
|
||||||
});
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.handleError(err);
|
this.handleError(err);
|
||||||
}
|
}
|
||||||
|
@ -186,15 +185,10 @@ export const useInvoiceOutGlobalStore = defineStore({
|
||||||
this.status = 'invoicing';
|
this.status = 'invoicing';
|
||||||
this.invoicing = true;
|
this.invoicing = true;
|
||||||
|
|
||||||
const invoiceResponse = await axios.post(
|
const { data } = await axios.post('InvoiceOuts/invoiceClient', params);
|
||||||
'InvoiceOuts/invoiceClient',
|
|
||||||
params
|
|
||||||
);
|
|
||||||
|
|
||||||
if (invoiceResponse.data) {
|
|
||||||
this.makePdfAndNotify(invoiceResponse.data, address);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (data) await this.makePdfAndNotify(data, address);
|
||||||
|
this.addressIndex++;
|
||||||
this.isInvoicing = false;
|
this.isInvoicing = false;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (
|
if (
|
||||||
|
@ -203,8 +197,7 @@ export const useInvoiceOutGlobalStore = defineStore({
|
||||||
err.response.status >= 400 &&
|
err.response.status >= 400 &&
|
||||||
err.response.status < 500
|
err.response.status < 500
|
||||||
) {
|
) {
|
||||||
this.invoiceClientError(address, err.response);
|
this.invoiceClientError(address, err.response?.data?.error?.message);
|
||||||
this.addressIndex++;
|
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
this.invoicing = false;
|
this.invoicing = false;
|
||||||
|
@ -227,12 +220,11 @@ export const useInvoiceOutGlobalStore = defineStore({
|
||||||
this.nPdfs++;
|
this.nPdfs++;
|
||||||
this.nRequests--;
|
this.nRequests--;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.invoiceClientError(client, err, true);
|
this.invoiceClientError(client, err.response?.data?.error?.message, true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
invoiceClientError(client, response, isWarning) {
|
invoiceClientError(client, message, isWarning) {
|
||||||
const message = response.data?.error?.message || response.message;
|
|
||||||
this.errors.unshift({ client, message, isWarning });
|
this.errors.unshift({ client, message, isWarning });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { vi, describe, expect, it, beforeAll, afterEach } from 'vitest';
|
||||||
import { createWrapper, axios } from 'app/test/vitest/helper';
|
import { createWrapper, axios } from 'app/test/vitest/helper';
|
||||||
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
||||||
|
|
||||||
describe.skip('VnPaginate', () => {
|
describe('VnPaginate', () => {
|
||||||
const expectedUrl = '/api/customers';
|
const expectedUrl = '/api/customers';
|
||||||
|
|
||||||
let vm;
|
let vm;
|
||||||
|
@ -49,6 +49,8 @@ describe.skip('VnPaginate', () => {
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
vm.arrayData.hasMoreData.value = true;
|
vm.arrayData.hasMoreData.value = true;
|
||||||
|
await vm.$nextTick();
|
||||||
|
|
||||||
vm.store.data = [
|
vm.store.data = [
|
||||||
{ id: 1, name: 'Tony Stark' },
|
{ id: 1, name: 'Tony Stark' },
|
||||||
{ id: 2, name: 'Jessica Jones' },
|
{ id: 2, name: 'Jessica Jones' },
|
||||||
|
@ -57,13 +59,13 @@ describe.skip('VnPaginate', () => {
|
||||||
|
|
||||||
await vm.paginate();
|
await vm.paginate();
|
||||||
|
|
||||||
expect(vm.store.skip).toEqual(6);
|
expect(vm.store.skip).toEqual(3);
|
||||||
expect(vm.store.data.length).toEqual(9);
|
expect(vm.store.data.length).toEqual(6);
|
||||||
|
|
||||||
await vm.paginate();
|
await vm.paginate();
|
||||||
|
|
||||||
expect(vm.store.skip).toEqual(9);
|
expect(vm.store.skip).toEqual(6);
|
||||||
expect(vm.store.data.length).toEqual(12);
|
expect(vm.store.data.length).toEqual(9);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue