#7936 improve InvoiceIn #1004

Merged
jorgep merged 55 commits from 7936-improveInvoiceIn into dev 2024-12-18 09:22:09 +00:00
36 changed files with 561 additions and 360 deletions

View File

@ -1,13 +1,28 @@
<script setup> <script setup>
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import { ref } from 'vue';
import { useAttrs } from 'vue'; defineProps({
step: { type: Number, default: 0.01 },
decimalPlaces: { type: Number, default: 2 },
positive: { type: Boolean, default: true },
});
const model = defineModel({ type: [Number, String] }); const model = defineModel({ type: [Number, String] });
const $attrs = useAttrs();
const step = ref($attrs.step || 0.01);
</script> </script>
<template> <template>
<VnInput v-bind="$attrs" v-model.number="model" type="number" :step="step" /> <VnInput
v-bind="$attrs"
v-model.number="model"
type="number"
:step="step"
@input="
(evt) => {
const val = evt.target.value;
if (positive && val < 0) return (model = 0);
Review

Me he dado cuenta de que se podían poner números negativos, esto en la mayoría de casos no se quiere.

Me he dado cuenta de que se podían poner números negativos, esto en la mayoría de casos no se quiere.
Review

Tanto codigo lo sacaria a una funcion en js

Tanto codigo lo sacaria a una funcion en js
const [, decimal] = val.split('.');
if (val && decimal?.length > decimalPlaces)
model = parseFloat(val).toFixed(decimalPlaces);
}
"
/>
</template> </template>

View File

@ -4,6 +4,7 @@ import { useI18n } from 'vue-i18n';
import { useArrayData } from 'src/composables/useArrayData'; import { useArrayData } from 'src/composables/useArrayData';
import { useRequired } from 'src/composables/useRequired'; import { useRequired } from 'src/composables/useRequired';
import dataByOrder from 'src/utils/dataByOrder'; import dataByOrder from 'src/utils/dataByOrder';
import { QItemLabel } from 'quasar';
const emit = defineEmits(['update:modelValue', 'update:options', 'remove']); const emit = defineEmits(['update:modelValue', 'update:options', 'remove']);
const $attrs = useAttrs(); const $attrs = useAttrs();
@ -33,6 +34,10 @@ const $props = defineProps({
type: String, type: String,
default: 'id', default: 'id',
}, },
optionCaption: {
type: String,
default: null,
},
optionFilter: { optionFilter: {
type: String, type: String,
default: null, default: null,
@ -101,6 +106,10 @@ const $props = defineProps({
type: String, type: String,
default: null, default: null,
}, },
isOutlined: {
type: Boolean,
default: false,
},
}); });
const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])]; const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])];
@ -115,6 +124,15 @@ const noOneOpt = ref({
[optionValue.value]: false, [optionValue.value]: false,
[optionLabel.value]: noOneText, [optionLabel.value]: noOneText,
}); });
const styleAttrs = computed(() => {
return $props.isOutlined
? {
dense: true,
outlined: true,
rounded: true,
}
: {};
});
const isLoading = ref(false); const isLoading = ref(false);
const useURL = computed(() => $props.url); const useURL = computed(() => $props.url);
const value = computed({ const value = computed({
@ -288,7 +306,7 @@ function handleKeyDown(event) {
} }
const focusableElements = document.querySelectorAll( const focusableElements = document.querySelectorAll(
'a, button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])' 'a:not([disabled]), button:not([disabled]), input:not([disabled]), textarea:not([disabled]), select:not([disabled]), details:not([disabled]), [tabindex]:not([tabindex="-1"]):not([disabled])'
); );
const currentIndex = Array.prototype.indexOf.call( const currentIndex = Array.prototype.indexOf.call(
focusableElements, focusableElements,
@ -307,9 +325,8 @@ function handleKeyDown(event) {
:options="myOptions" :options="myOptions"
:option-label="optionLabel" :option-label="optionLabel"
:option-value="optionValue" :option-value="optionValue"
v-bind="$attrs" v-bind="{ ...$attrs, ...styleAttrs }"
@filter="filterHandler" @filter="filterHandler"
@keydown="handleKeyDown"
:emit-value="nullishToTrue($attrs['emit-value'])" :emit-value="nullishToTrue($attrs['emit-value'])"
:map-options="nullishToTrue($attrs['map-options'])" :map-options="nullishToTrue($attrs['map-options'])"
:use-input="nullishToTrue($attrs['use-input'])" :use-input="nullishToTrue($attrs['use-input'])"
@ -324,13 +341,14 @@ function handleKeyDown(event) {
:input-debounce="useURL ? '300' : '0'" :input-debounce="useURL ? '300' : '0'"
:loading="isLoading" :loading="isLoading"
@virtual-scroll="onScroll" @virtual-scroll="onScroll"
@keydown="handleKeyDown"
:data-cy="$attrs.dataCy ?? $attrs.label + '_select'" :data-cy="$attrs.dataCy ?? $attrs.label + '_select'"
> >
<template #append> <template #append>
<QIcon <QIcon
v-show="isClearable && value" v-show="isClearable && value"
name="close" name="close"
@click.stop=" @click="
() => { () => {
value = null; value = null;
emit('remove'); emit('remove');
@ -358,6 +376,21 @@ function handleKeyDown(event) {
</div> </div>
<slot v-else :name="slotName" v-bind="slotData ?? {}" :key="slotName" /> <slot v-else :name="slotName" v-bind="slotData ?? {}" :key="slotName" />
</template> </template>
<template #option="{ opt, itemProps }">
<QItem v-bind="itemProps">
<QItemSection v-if="opt[optionValue] == opt[optionLabel]">
<QItemLabel>{{ opt[optionLabel] }}</QItemLabel>
</QItemSection>
<QItemSection v-else>
<QItemLabel>
{{ opt[optionLabel] }}
</QItemLabel>
<QItemLabel caption v-if="optionCaption !== false">
Review

Sacado de esta tarea https://redmine.verdnatura.es/issues/8117 . Lo he añadido en esta porque me lo estaban pidiendo ya ... En la tarea que se revise en cada sitio que campos mostrar.

Sacado de esta tarea https://redmine.verdnatura.es/issues/8117 . Lo he añadido en esta porque me lo estaban pidiendo ya ... En la tarea que se revise en cada sitio que campos mostrar.
{{ `#${opt[optionCaption] || opt[optionValue]}` }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</QSelect> </QSelect>
</template> </template>

View File

@ -6,7 +6,7 @@ import { useRoute } from 'vue-router';
import toDate from 'filters/toDate'; import toDate from 'filters/toDate';
import VnFilterPanelChip from 'components/ui/VnFilterPanelChip.vue'; import VnFilterPanelChip from 'components/ui/VnFilterPanelChip.vue';
const { t } = useI18n(); const { t, te } = useI18n();
const $props = defineProps({ const $props = defineProps({
modelValue: { modelValue: {
type: Object, type: Object,
@ -228,6 +228,14 @@ function sanitizer(params) {
} }
return params; return params;
} }
Review

Se usa en varios sitios

Se usa en varios sitios
const getLocale = (label) => {
const param = label.split('.').at(-1);
const globalLocale = `globals.params.${param}`;
if (te(globalLocale)) return t(globalLocale);
else if (te(t(`params.${param}`)));
else return t(`${route.meta.moduleName}.params.${param}`);
};
</script> </script>
<template> <template>
@ -277,7 +285,12 @@ function sanitizer(params) {
@remove="remove(chip.label)" @remove="remove(chip.label)"
data-cy="vnFilterPanelChip" data-cy="vnFilterPanelChip"
> >
<slot name="tags" :tag="chip" :format-fn="formatValue"> <slot
name="tags"
:tag="chip"
:format-fn="formatValue"
:get-locale="getLocale"
>
<div class="q-gutter-x-xs"> <div class="q-gutter-x-xs">
<strong>{{ chip.label }}:</strong> <strong>{{ chip.label }}:</strong>
<span>"{{ formatValue(chip.value) }}"</span> <span>"{{ formatValue(chip.value) }}"</span>
@ -290,6 +303,7 @@ function sanitizer(params) {
:params="userParams" :params="userParams"
:tags="customTags" :tags="customTags"
:format-fn="formatValue" :format-fn="formatValue"
:get-locale="getLocale"
:search-fn="search" :search-fn="search"
/> />
</div> </div>
@ -297,7 +311,12 @@ function sanitizer(params) {
<QSeparator /> <QSeparator />
</QList> </QList>
<QList dense class="list q-gutter-y-sm q-mt-sm"> <QList dense class="list q-gutter-y-sm q-mt-sm">
<slot name="body" :params="sanitizer(userParams)" :search-fn="search"></slot> <slot
name="body"
:params="sanitizer(userParams)"
:get-locale="getLocale"
:search-fn="search"
></slot>
</QList> </QList>
</QForm> </QForm>
<QInnerLoading <QInnerLoading

View File

@ -0,0 +1,16 @@
import axios from 'axios';
export async function getExchange(amount, currencyFk, dated, decimalPlaces = 2) {
try {
const { data } = await axios.get('ReferenceRates/findOne', {
params: {
filter: {
fields: ['value'],
where: { currencyFk, dated },
},
},
});
return (amount / data.value).toFixed(decimalPlaces);
} catch (e) {
return null;
}
}

View File

@ -1,10 +1,10 @@
import { toCurrency } from 'src/filters'; import { toCurrency } from 'src/filters';
export function getTotal(rows, key, opts = {}) { export function getTotal(rows, key, opts = {}) {
const { currency, cb } = opts; const { currency, cb, decimalPlaces } = opts;
const total = rows.reduce((acc, row) => acc + +(cb ? cb(row) : row[key] || 0), 0); const total = rows.reduce((acc, row) => acc + +(cb ? cb(row) : row[key] || 0), 0);
return currency return currency
? toCurrency(total, currency == 'default' ? undefined : currency) ? toCurrency(total, currency == 'default' ? undefined : currency)
: total; : parseFloat(total).toFixed(decimalPlaces ?? 2);
} }

View File

@ -0,0 +1,4 @@
export function useAccountShortToStandard(val) {
Review

Sacado de Salix

Sacado de Salix
if (!val || !/^\d+(\.\d*)$/.test(val)) return;
return val?.replace('.', '0'.repeat(11 - val.length));
}

View File

@ -333,11 +333,23 @@ globals:
packing: ITP packing: ITP
myTeam: My team myTeam: My team
departmentFk: Department departmentFk: Department
from: From
to: To
supplierFk: Supplier
supplierRef: Supplier ref
serial: Serial
amount: Importe
awbCode: AWB
correctedFk: Rectified
correctingFk: Rectificative
daysOnward: Days onward
countryFk: Country countryFk: Country
companyFk: Company
changePass: Change password changePass: Change password
deleteConfirmTitle: Delete selected elements deleteConfirmTitle: Delete selected elements
changeState: Change state changeState: Change state
raid: 'Raid {daysInForward} days' raid: 'Raid {daysInForward} days'
isVies: Vies
errors: errors:
statusUnauthorized: Access denied statusUnauthorized: Access denied
statusInternalServerError: An internal server error has ocurred statusInternalServerError: An internal server error has ocurred
@ -731,7 +743,6 @@ supplier:
sageTransactionTypeFk: Sage transaction type sageTransactionTypeFk: Sage transaction type
supplierActivityFk: Supplier activity supplierActivityFk: Supplier activity
isTrucker: Trucker isTrucker: Trucker
isVies: Vies
billingData: billingData:
payMethodFk: Billing data payMethodFk: Billing data
payDemFk: Payment deadline payDemFk: Payment deadline

View File

@ -336,12 +336,22 @@ globals:
SSN: NSS SSN: NSS
fi: NIF fi: NIF
myTeam: Mi equipo myTeam: Mi equipo
from: Desde
to: Hasta
supplierFk: Proveedor
supplierRef: Ref. proveedor
serial: Serie
amount: Importe
awbCode: AWB
daysOnward: Días adelante
packing: ITP packing: ITP
countryFk: País countryFk: País
companyFk: Empresa
changePass: Cambiar contraseña changePass: Cambiar contraseña
deleteConfirmTitle: Eliminar los elementos seleccionados deleteConfirmTitle: Eliminar los elementos seleccionados
changeState: Cambiar estado changeState: Cambiar estado
raid: 'Redada {daysInForward} días' raid: 'Redada {daysInForward} días'
isVies: Vies
errors: errors:
statusUnauthorized: Acceso denegado statusUnauthorized: Acceso denegado
statusInternalServerError: Ha ocurrido un error interno del servidor statusInternalServerError: Ha ocurrido un error interno del servidor
@ -726,7 +736,6 @@ supplier:
sageTransactionTypeFk: Tipo de transacción sage sageTransactionTypeFk: Tipo de transacción sage
supplierActivityFk: Actividad proveedor supplierActivityFk: Actividad proveedor
isTrucker: Transportista isTrucker: Transportista
isVies: Vies
billingData: billingData:
payMethodFk: Forma de pago payMethodFk: Forma de pago
payDemFk: Plazo de pago payDemFk: Plazo de pago

View File

@ -110,7 +110,7 @@ function handleLocation(data, location) {
<VnRow> <VnRow>
<QCheckbox :label="t('Has to invoice')" v-model="data.hasToInvoice" /> <QCheckbox :label="t('Has to invoice')" v-model="data.hasToInvoice" />
<div> <div>
<QCheckbox :label="t('Vies')" v-model="data.isVies" /> <QCheckbox :label="t('globals.isVies')" v-model="data.isVies" />
<QIcon name="info" class="cursor-info q-ml-sm" size="sm"> <QIcon name="info" class="cursor-info q-ml-sm" size="sm">
<QTooltip> <QTooltip>
{{ t('whenActivatingIt') }} {{ t('whenActivatingIt') }}
@ -169,7 +169,6 @@ es:
Active: Activo Active: Activo
Frozen: Congelado Frozen: Congelado
Has to invoice: Factura Has to invoice: Factura
Vies: Vies
Notify by email: Notificar vía e-mail Notify by email: Notificar vía e-mail
Invoice by address: Facturar por consignatario Invoice by address: Facturar por consignatario
Is equalizated: Recargo de equivalencia Is equalizated: Recargo de equivalencia

View File

@ -173,7 +173,7 @@ const sumRisk = ({ clientRisks }) => {
:label="t('customer.summary.notifyByEmail')" :label="t('customer.summary.notifyByEmail')"
:value="entity.isToBeMailed" :value="entity.isToBeMailed"
/> />
<VnLv :label="t('customer.summary.vies')" :value="entity.isVies" /> <VnLv :label="t('globals.isVies')" :value="entity.isVies" />
</VnRow> </VnRow>
</QCard> </QCard>
<QCard class="vn-one"> <QCard class="vn-one">

View File

@ -263,7 +263,7 @@ const columns = computed(() => [
}, },
{ {
align: 'left', align: 'left',
label: t('customer.extendedList.tableVisibleColumns.isVies'), label: t('globals.isVies'),
name: 'isVies', name: 'isVies',
columnFilter: { columnFilter: {
inWhere: true, inWhere: true,

View File

@ -88,7 +88,6 @@ customer:
businessTypeFk: Business type businessTypeFk: Business type
sageTaxTypeFk: Sage tax type sageTaxTypeFk: Sage tax type
sageTransactionTypeFk: Sage tr. type sageTransactionTypeFk: Sage tr. type
isVies: Vies
isTaxDataChecked: Verified data isTaxDataChecked: Verified data
isFreezed: Freezed isFreezed: Freezed
hasToInvoice: Invoice hasToInvoice: Invoice

View File

@ -90,7 +90,6 @@ customer:
businessTypeFk: Tipo de negocio businessTypeFk: Tipo de negocio
sageTaxTypeFk: Tipo de impuesto Sage sageTaxTypeFk: Tipo de impuesto Sage
sageTransactionTypeFk: Tipo tr. sage sageTransactionTypeFk: Tipo tr. sage
isVies: Vies
isTaxDataChecked: Datos comprobados isTaxDataChecked: Datos comprobados
isFreezed: Congelado isFreezed: Congelado
hasToInvoice: Factura hasToInvoice: Factura

View File

@ -249,6 +249,7 @@ function deleteFile(dmsFk) {
:options="currencies" :options="currencies"
option-value="id" option-value="id"
option-label="code" option-label="code"
sort-by="id"
/> />
<VnSelect <VnSelect
@ -262,7 +263,7 @@ function deleteFile(dmsFk) {
</VnRow> </VnRow>
<VnRow> <VnRow>
<VnSelect <VnSelect
:label="t('invoiceIn.summary.sage')" :label="t('InvoiceIn.summary.sage')"
v-model="data.withholdingSageFk" v-model="data.withholdingSageFk"
:options="sageWithholdings" :options="sageWithholdings"
option-value="id" option-value="id"

View File

@ -1,23 +1,21 @@
<script setup> <script setup>
import { ref, computed } from 'vue'; import { ref, computed, capitalize } from 'vue';
import { useRouter } from 'vue-router'; import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useArrayData } from 'src/composables/useArrayData'; import { useArrayData } from 'src/composables/useArrayData';
import { useCapitalize } from 'src/composables/useCapitalize';
import CrudModel from 'src/components/CrudModel.vue'; import CrudModel from 'src/components/CrudModel.vue';
import FetchData from 'src/components/FetchData.vue'; import FetchData from 'src/components/FetchData.vue';
import VnSelect from 'src/components/common/VnSelect.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
const { push, currentRoute } = useRouter(); const route = useRoute();
const { t } = useI18n(); const { t } = useI18n();
const invoiceId = +currentRoute.value.params.id;
const arrayData = useArrayData(); const arrayData = useArrayData();
const invoiceIn = computed(() => arrayData.store.data); const invoiceIn = computed(() => arrayData.store.data);
const invoiceInCorrectionRef = ref(); const invoiceInCorrectionRef = ref();
const filter = { const filter = {
include: { relation: 'invoiceIn' }, include: { relation: 'invoiceIn' },
where: { correctingFk: invoiceId }, where: { correctingFk: route.params.id },
}; };
const columns = computed(() => [ const columns = computed(() => [
{ {
@ -31,7 +29,7 @@ const columns = computed(() => [
}, },
{ {
name: 'type', name: 'type',
label: useCapitalize(t('globals.type')), label: capitalize(t('globals.type')),
field: (row) => row.cplusRectificationTypeFk, field: (row) => row.cplusRectificationTypeFk,
options: cplusRectificationTypes.value, options: cplusRectificationTypes.value,
model: 'cplusRectificationTypeFk', model: 'cplusRectificationTypeFk',
@ -43,10 +41,10 @@ const columns = computed(() => [
}, },
{ {
name: 'class', name: 'class',
label: useCapitalize(t('globals.class')), label: capitalize(t('globals.class')),
field: (row) => row.siiTypeInvoiceOutFk, field: (row) => row.siiTypeInvoiceInFk,
options: siiTypeInvoiceOuts.value, options: siiTypeInvoiceIns.value,
model: 'siiTypeInvoiceOutFk', model: 'siiTypeInvoiceInFk',
optionValue: 'id', optionValue: 'id',
optionLabel: 'code', optionLabel: 'code',
sortable: true, sortable: true,
@ -55,7 +53,7 @@ const columns = computed(() => [
}, },
{ {
name: 'reason', name: 'reason',
label: useCapitalize(t('globals.reason')), label: capitalize(t('globals.reason')),
field: (row) => row.invoiceCorrectionTypeFk, field: (row) => row.invoiceCorrectionTypeFk,
options: invoiceCorrectionTypes.value, options: invoiceCorrectionTypes.value,
model: 'invoiceCorrectionTypeFk', model: 'invoiceCorrectionTypeFk',
@ -67,13 +65,10 @@ const columns = computed(() => [
}, },
]); ]);
const cplusRectificationTypes = ref([]); const cplusRectificationTypes = ref([]);
const siiTypeInvoiceOuts = ref([]); const siiTypeInvoiceIns = ref([]);
const invoiceCorrectionTypes = ref([]); const invoiceCorrectionTypes = ref([]);
const rowsSelected = ref([]);
const requiredFieldRule = (val) => val || t('globals.requiredField'); const requiredFieldRule = (val) => val || t('globals.requiredField');
const onSave = (data) => data.deletes && push(`/invoice-in/${invoiceId}/summary`);
</script> </script>
<template> <template>
<FetchData <FetchData
@ -82,9 +77,9 @@ const onSave = (data) => data.deletes && push(`/invoice-in/${invoiceId}/summary`
auto-load auto-load
/> />
<FetchData <FetchData
url="SiiTypeInvoiceOuts" url="SiiTypeInvoiceIns"
:where="{ code: { like: 'R%' } }" :where="{ code: { like: 'R%' } }"
@on-fetch="(data) => (siiTypeInvoiceOuts = data)" @on-fetch="(data) => (siiTypeInvoiceIns = data)"
auto-load auto-load
/> />
<FetchData <FetchData
@ -99,17 +94,14 @@ const onSave = (data) => data.deletes && push(`/invoice-in/${invoiceId}/summary`
url="InvoiceInCorrections" url="InvoiceInCorrections"
:filter="filter" :filter="filter"
auto-load auto-load
v-model:selected="rowsSelected"
primary-key="correctingFk" primary-key="correctingFk"
@save-changes="onSave" :default-remove="false"
> >
<template #body="{ rows }"> <template #body="{ rows }">
<QTable <QTable
v-model:selected="rowsSelected"
:columns="columns" :columns="columns"
:rows="rows" :rows="rows"
row-key="$index" row-key="$index"
selection="single"
:grid="$q.screen.lt.sm" :grid="$q.screen.lt.sm"
:pagination="{ rowsPerPage: 0 }" :pagination="{ rowsPerPage: 0 }"
> >
@ -121,8 +113,17 @@ const onSave = (data) => data.deletes && push(`/invoice-in/${invoiceId}/summary`
:options="col.options" :options="col.options"
:option-value="col.optionValue" :option-value="col.optionValue"
:option-label="col.optionLabel" :option-label="col.optionLabel"
:readonly="row.invoiceIn.isBooked" :disable="row.invoiceIn.isBooked"
/> :filter-options="['description']"
>
<template #option="{ opt, itemProps }">
<QItem v-bind="itemProps">
<QItemSection>
<QItemLabel>{{ opt.description }}</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
</QTd> </QTd>
</template> </template>
<template #body-cell-class="{ row, col }"> <template #body-cell-class="{ row, col }">
@ -134,8 +135,20 @@ const onSave = (data) => data.deletes && push(`/invoice-in/${invoiceId}/summary`
:option-value="col.optionValue" :option-value="col.optionValue"
:option-label="col.optionLabel" :option-label="col.optionLabel"
:rules="[requiredFieldRule]" :rules="[requiredFieldRule]"
:readonly="row.invoiceIn.isBooked" :filter-options="['code', 'description']"
/> :disable="row.invoiceIn.isBooked"
>
<template #option="{ opt, itemProps }">
<QItem v-bind="itemProps">
<QItemSection>
<QItemLabel
>{{ opt.code }} -
{{ opt.description }}</QItemLabel
>
</QItemSection>
</QItem>
</template>
</VnSelect>
</QTd> </QTd>
</template> </template>
<template #body-cell-reason="{ row, col }"> <template #body-cell-reason="{ row, col }">
@ -147,7 +160,7 @@ const onSave = (data) => data.deletes && push(`/invoice-in/${invoiceId}/summary`
:option-value="col.optionValue" :option-value="col.optionValue"
:option-label="col.optionLabel" :option-label="col.optionLabel"
:rules="[requiredFieldRule]" :rules="[requiredFieldRule]"
:readonly="row.invoiceIn.isBooked" :disable="row.invoiceIn.isBooked"
/> />
</QTd> </QTd>
</template> </template>
@ -155,7 +168,6 @@ const onSave = (data) => data.deletes && push(`/invoice-in/${invoiceId}/summary`
</template> </template>
</CrudModel> </CrudModel>
</template> </template>
<style lang="scss" scoped></style>
<i18n> <i18n>
es: es:
Original invoice: Factura origen Original invoice: Factura origen

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { ref, reactive, computed, onBeforeMount } from 'vue'; import { ref, reactive, computed, onBeforeMount, capitalize } from 'vue';
import { useRouter, onBeforeRouteUpdate } from 'vue-router'; import { useRouter, onBeforeRouteUpdate } from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
@ -15,7 +15,6 @@ import FetchData from 'src/components/FetchData.vue';
import SendEmailDialog from 'components/common/SendEmailDialog.vue'; import SendEmailDialog from 'components/common/SendEmailDialog.vue';
import VnConfirm from 'src/components/ui/VnConfirm.vue'; import VnConfirm from 'src/components/ui/VnConfirm.vue';
import VnSelect from 'src/components/common/VnSelect.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import { useCapitalize } from 'src/composables/useCapitalize';
import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue'; import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue';
import InvoiceInToBook from '../InvoiceInToBook.vue'; import InvoiceInToBook from '../InvoiceInToBook.vue';
@ -37,7 +36,7 @@ const totalAmount = ref();
const currentAction = ref(); const currentAction = ref();
const config = ref(); const config = ref();
const cplusRectificationTypes = ref([]); const cplusRectificationTypes = ref([]);
const siiTypeInvoiceOuts = ref([]); const siiTypeInvoiceIns = ref([]);
const invoiceCorrectionTypes = ref([]); const invoiceCorrectionTypes = ref([]);
const actions = { const actions = {
unbook: { unbook: {
@ -91,7 +90,7 @@ const routes = reactive({
return { return {
name: 'InvoiceInList', name: 'InvoiceInList',
query: { query: {
params: JSON.stringify({ supplierFk: id }), table: JSON.stringify({ supplierFk: id }),
}, },
}; };
}, },
@ -100,7 +99,7 @@ const routes = reactive({
return { return {
name: 'InvoiceInList', name: 'InvoiceInList',
query: { query: {
params: JSON.stringify({ correctedFk: entityId.value }), table: JSON.stringify({ correctedFk: entityId.value }),
}, },
}; };
} }
@ -119,21 +118,21 @@ const routes = reactive({
const correctionFormData = reactive({ const correctionFormData = reactive({
invoiceReason: 2, invoiceReason: 2,
invoiceType: 2, invoiceType: 2,
invoiceClass: 6, invoiceClass: 8,
}); });
const isNotFilled = computed(() => Object.values(correctionFormData).includes(null)); const isNotFilled = computed(() => Object.values(correctionFormData).includes(null));
onBeforeMount(async () => { onBeforeMount(async () => {
await setInvoiceCorrection(entityId.value); await setInvoiceCorrection(entityId.value);
const { data } = await axios.get(`InvoiceIns/${entityId.value}/getTotals`); const { data } = await axios.get(`InvoiceIns/${entityId.value}/getTotals`);
totalAmount.value = data.totalDueDay; totalAmount.value = data.totalTaxableBase;
}); });
onBeforeRouteUpdate(async (to, from) => { onBeforeRouteUpdate(async (to, from) => {
if (to.params.id !== from.params.id) { if (to.params.id !== from.params.id) {
await setInvoiceCorrection(to.params.id); await setInvoiceCorrection(to.params.id);
const { data } = await axios.get(`InvoiceIns/${to.params.id}/getTotals`); const { data } = await axios.get(`InvoiceIns/${to.params.id}/getTotals`);
totalAmount.value = data.totalDueDay; totalAmount.value = data.totalTaxableBase;
} }
}); });
@ -207,7 +206,8 @@ const isAgricultural = () => {
}; };
function showPdfInvoice() { function showPdfInvoice() {
if (isAgricultural()) openReport(`InvoiceIns/${entityId.value}/invoice-in-pdf`); if (isAgricultural())
openReport(`InvoiceIns/${entityId.value}/invoice-in-pdf`, null, '_blank');
Review

Han pedido que se abra en una nueva ventana.

Han pedido que se abra en una nueva ventana.
} }
function sendPdfInvoiceConfirmation() { function sendPdfInvoiceConfirmation() {
@ -262,9 +262,9 @@ const createInvoiceInCorrection = async () => {
auto-load auto-load
/> />
<FetchData <FetchData
url="SiiTypeInvoiceOuts" url="siiTypeInvoiceIns"
:where="{ code: { like: 'R%' } }" :where="{ code: { like: 'R%' } }"
@on-fetch="(data) => (siiTypeInvoiceOuts = data)" @on-fetch="(data) => (siiTypeInvoiceIns = data)"
auto-load auto-load
/> />
<FetchData <FetchData
@ -355,10 +355,13 @@ const createInvoiceInCorrection = async () => {
</QItem> </QItem>
</template> </template>
<template #body="{ entity }"> <template #body="{ entity }">
<VnLv :label="t('invoiceIn.list.issued')" :value="toDate(entity.issued)" /> <VnLv :label="t('InvoiceIn.list.issued')" :value="toDate(entity.issued)" />
<VnLv :label="t('invoiceIn.summary.booked')" :value="toDate(entity.booked)" /> <VnLv
<VnLv :label="t('invoiceIn.list.amount')" :value="toCurrency(totalAmount)" /> :label="t('InvoiceIn.summary.bookedDate')"
<VnLv :label="t('invoiceIn.list.supplier')"> :value="toDate(entity.booked)"
/>
<VnLv :label="t('InvoiceIn.list.amount')" :value="toCurrency(totalAmount)" />
<VnLv :label="t('InvoiceIn.list.supplier')">
<template #value> <template #value>
<span class="link"> <span class="link">
{{ entity?.supplier?.nickname }} {{ entity?.supplier?.nickname }}
@ -375,7 +378,7 @@ const createInvoiceInCorrection = async () => {
color="primary" color="primary"
:to="routes.getSupplier(entity.supplierFk)" :to="routes.getSupplier(entity.supplierFk)"
> >
<QTooltip>{{ t('invoiceIn.list.supplier') }}</QTooltip> <QTooltip>{{ t('InvoiceIn.list.supplier') }}</QTooltip>
</QBtn> </QBtn>
<QBtn <QBtn
size="md" size="md"
@ -391,7 +394,7 @@ const createInvoiceInCorrection = async () => {
color="primary" color="primary"
:to="routes.getTickets(entity.supplierFk)" :to="routes.getTickets(entity.supplierFk)"
> >
<QTooltip>{{ t('invoiceOut.card.ticketList') }}</QTooltip> <QTooltip>{{ t('InvoiceIn.descriptor.ticketList') }}</QTooltip>
</QBtn> </QBtn>
<QBtn <QBtn
v-if=" v-if="
@ -435,9 +438,9 @@ const createInvoiceInCorrection = async () => {
readonly readonly
/> />
<VnSelect <VnSelect
:label="`${useCapitalize(t('globals.class'))}`" :label="`${capitalize(t('globals.class'))}`"
v-model="correctionFormData.invoiceClass" v-model="correctionFormData.invoiceClass"
:options="siiTypeInvoiceOuts" :options="siiTypeInvoiceIns"
option-value="id" option-value="id"
option-label="code" option-label="code"
:required="true" :required="true"
@ -445,15 +448,27 @@ const createInvoiceInCorrection = async () => {
</QItemSection> </QItemSection>
<QItemSection> <QItemSection>
<VnSelect <VnSelect
:label="`${useCapitalize(t('globals.type'))}`" :label="`${capitalize(t('globals.type'))}`"
v-model="correctionFormData.invoiceType" v-model="correctionFormData.invoiceType"
:options="cplusRectificationTypes" :options="cplusRectificationTypes"
option-value="id" option-value="id"
option-label="description" option-label="description"
:required="true" :required="true"
/> >
<template #option="{ opt }">
<QItem>
<QItemSection>
<QItemLabel
>{{ opt.code }} -
{{ opt.description }}</QItemLabel
>
</QItemSection>
</QItem>
<div></div>
</template>
</VnSelect>
<VnSelect <VnSelect
:label="`${useCapitalize(t('globals.reason'))}`" :label="`${capitalize(t('globals.reason'))}`"
v-model="correctionFormData.invoiceReason" v-model="correctionFormData.invoiceReason"
:options="invoiceCorrectionTypes" :options="invoiceCorrectionTypes"
option-value="id" option-value="id"

View File

@ -25,6 +25,7 @@ const banks = ref([]);
const invoiceInFormRef = ref(); const invoiceInFormRef = ref();
const invoiceId = +route.params.id; const invoiceId = +route.params.id;
const filter = { where: { invoiceInFk: invoiceId } }; const filter = { where: { invoiceInFk: invoiceId } };
const areRows = ref(false);
const columns = computed(() => [ const columns = computed(() => [
{ {
@ -143,8 +144,6 @@ async function insert() {
}" }"
:disable="!isNotEuro(currency)" :disable="!isNotEuro(currency)"
v-model="row.foreignValue" v-model="row.foreignValue"
clearable
clear-icon="close"
/> />
</QTd> </QTd>
</template> </template>
@ -230,7 +229,14 @@ async function insert() {
</template> </template>
</CrudModel> </CrudModel>
<QPageSticky position="bottom-right" :offset="[25, 25]"> <QPageSticky position="bottom-right" :offset="[25, 25]">
<QBtn color="primary" icon="add" shortcut="+" size="lg" round @click="insert" /> <QBtn
color="primary"
icon="add"
shortcut="+"
size="lg"
round
@click="!areRows ? insert() : invoiceInFormRef.insert()"
/>
</QPageSticky> </QPageSticky>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -26,7 +26,7 @@ const columns = computed(() => [
options: intrastats.value, options: intrastats.value,
model: 'intrastatFk', model: 'intrastatFk',
optionValue: 'id', optionValue: 'id',
optionLabel: 'description', optionLabel: (row) => `${row.id}: ${row.description}`,
sortable: true, sortable: true,
tabIndex: 1, tabIndex: 1,
align: 'left', align: 'left',
@ -68,12 +68,6 @@ const columns = computed(() => [
align: 'left', align: 'left',
}, },
]); ]);
const formatOpt = (row, { model, options }, prop) => {
const obj = row[model];
const option = options.find(({ id }) => id == obj);
return option ? `${obj}:${option[prop]}` : '';
};
</script> </script>
<template> <template>
<FetchData <FetchData
@ -118,12 +112,10 @@ const formatOpt = (row, { model, options }, prop) => {
<VnSelect <VnSelect
v-model="row[col.model]" v-model="row[col.model]"
:options="col.options" :options="col.options"
option-value="id" :option-value="col.optionValue"
option-label="description" :option-label="col.optionLabel"
:filter-options="['id', 'description']" :filter-options="['id', 'description']"
:hide-selected="false" data-cy="intrastat-code"
:fill-input="false"
:display-value="formatOpt(row, col, 'description')"
> >
<template #option="scope"> <template #option="scope">
<QItem v-bind="scope.itemProps"> <QItem v-bind="scope.itemProps">
@ -138,8 +130,8 @@ const formatOpt = (row, { model, options }, prop) => {
<VnSelect <VnSelect
v-model="row[col.model]" v-model="row[col.model]"
:options="col.options" :options="col.options"
option-value="id" :option-value="col.optionValue"
option-label="code" :option-label="col.optionLabel"
/> />
</QTd> </QTd>
</template> </template>
@ -154,7 +146,7 @@ const formatOpt = (row, { model, options }, prop) => {
{{ getTotal(rows, 'net') }} {{ getTotal(rows, 'net') }}
</QTd> </QTd>
<QTd> <QTd>
{{ getTotal(rows, 'stems') }} {{ getTotal(rows, 'stems', { decimalPlaces: 0 }) }}
</QTd> </QTd>
<QTd /> <QTd />
</QTr> </QTr>
@ -174,7 +166,9 @@ const formatOpt = (row, { model, options }, prop) => {
v-model="props.row['intrastatFk']" v-model="props.row['intrastatFk']"
:options="intrastats" :options="intrastats"
option-value="id" option-value="id"
option-label="description" :option-label="
(row) => `${row.id}:${row.description}`
"
:filter-options="['id', 'description']" :filter-options="['id', 'description']"
> >
<template #option="scope"> <template #option="scope">
@ -248,11 +242,6 @@ const formatOpt = (row, { model, options }, prop) => {
} }
} }
</style> </style>
<style lang="scss" scoped>
:deep(.q-table tr .q-td:nth-child(2) input) {
display: none;
}
</style>
<i18n> <i18n>
en: en:
amount: Amount amount: Amount
@ -261,7 +250,7 @@ const formatOpt = (row, { model, options }, prop) => {
country: Country country: Country
es: es:
Code: Código Code: Código
amount: Cantidad amount: Valor mercancía
net: Neto net: Neto
stems: Tallos stems: Tallos
country: País country: País

View File

@ -26,14 +26,14 @@ const intrastatTotals = ref({ amount: 0, net: 0, stems: 0 });
const vatColumns = ref([ const vatColumns = ref([
{ {
name: 'expense', name: 'expense',
label: 'invoiceIn.summary.expense', label: 'InvoiceIn.summary.expense',
field: (row) => row.expenseFk, field: (row) => row.expenseFk,
sortable: true, sortable: true,
align: 'left', align: 'left',
}, },
{ {
name: 'landed', name: 'landed',
label: 'invoiceIn.summary.taxableBase', label: 'InvoiceIn.summary.taxableBase',
field: (row) => row.taxableBase, field: (row) => row.taxableBase,
format: (value) => toCurrency(value), format: (value) => toCurrency(value),
sortable: true, sortable: true,
@ -41,7 +41,7 @@ const vatColumns = ref([
}, },
{ {
name: 'vat', name: 'vat',
label: 'invoiceIn.summary.sageVat', label: 'InvoiceIn.summary.sageVat',
field: (row) => { field: (row) => {
if (row.taxTypeSage) return `#${row.taxTypeSage.id} : ${row.taxTypeSage.vat}`; if (row.taxTypeSage) return `#${row.taxTypeSage.id} : ${row.taxTypeSage.vat}`;
}, },
@ -51,7 +51,7 @@ const vatColumns = ref([
}, },
{ {
name: 'transaction', name: 'transaction',
label: 'invoiceIn.summary.sageTransaction', label: 'InvoiceIn.summary.sageTransaction',
field: (row) => { field: (row) => {
if (row.transactionTypeSage) if (row.transactionTypeSage)
return `#${row.transactionTypeSage.id} : ${row.transactionTypeSage?.transaction}`; return `#${row.transactionTypeSage.id} : ${row.transactionTypeSage?.transaction}`;
@ -62,7 +62,7 @@ const vatColumns = ref([
}, },
{ {
name: 'rate', name: 'rate',
label: 'invoiceIn.summary.rate', label: 'InvoiceIn.summary.rate',
field: (row) => taxRate(row.taxableBase, row.taxTypeSage?.rate), field: (row) => taxRate(row.taxableBase, row.taxTypeSage?.rate),
format: (value) => toCurrency(value), format: (value) => toCurrency(value),
sortable: true, sortable: true,
@ -70,7 +70,7 @@ const vatColumns = ref([
}, },
{ {
name: 'currency', name: 'currency',
label: 'invoiceIn.summary.currency', label: 'InvoiceIn.summary.currency',
field: (row) => row.foreignValue, field: (row) => row.foreignValue,
format: (val) => val && toCurrency(val, currency.value), format: (val) => val && toCurrency(val, currency.value),
sortable: true, sortable: true,
@ -81,21 +81,21 @@ const vatColumns = ref([
const dueDayColumns = ref([ const dueDayColumns = ref([
{ {
name: 'date', name: 'date',
label: 'invoiceIn.summary.dueDay', label: 'InvoiceIn.summary.dueDay',
field: (row) => toDate(row.dueDated), field: (row) => toDate(row.dueDated),
sortable: true, sortable: true,
align: 'left', align: 'left',
}, },
{ {
name: 'bank', name: 'bank',
label: 'invoiceIn.summary.bank', label: 'InvoiceIn.summary.bank',
field: (row) => row.bank.bank, field: (row) => row.bank.bank,
sortable: true, sortable: true,
align: 'left', align: 'left',
}, },
{ {
name: 'amount', name: 'amount',
label: 'invoiceIn.list.amount', label: 'InvoiceIn.list.amount',
field: (row) => row.amount, field: (row) => row.amount,
format: (value) => toCurrency(value), format: (value) => toCurrency(value),
sortable: true, sortable: true,
@ -103,7 +103,7 @@ const dueDayColumns = ref([
}, },
{ {
name: 'landed', name: 'landed',
label: 'invoiceIn.summary.foreignValue', label: 'InvoiceIn.summary.foreignValue',
field: (row) => row.foreignValue, field: (row) => row.foreignValue,
format: (val) => val && toCurrency(val, currency.value), format: (val) => val && toCurrency(val, currency.value),
sortable: true, sortable: true,
@ -114,7 +114,7 @@ const dueDayColumns = ref([
const intrastatColumns = ref([ const intrastatColumns = ref([
{ {
name: 'code', name: 'code',
label: 'invoiceIn.summary.code', label: 'InvoiceIn.summary.code',
field: (row) => { field: (row) => {
return `${row.intrastat.id}: ${row.intrastat?.description}`; return `${row.intrastat.id}: ${row.intrastat?.description}`;
}, },
@ -123,21 +123,21 @@ const intrastatColumns = ref([
}, },
{ {
name: 'amount', name: 'amount',
label: 'invoiceIn.list.amount', label: 'InvoiceIn.list.amount',
field: (row) => toCurrency(row.amount), field: (row) => toCurrency(row.amount),
sortable: true, sortable: true,
align: 'left', align: 'left',
}, },
{ {
name: 'net', name: 'net',
label: 'invoiceIn.summary.net', label: 'InvoiceIn.summary.net',
field: (row) => row.net, field: (row) => row.net,
sortable: true, sortable: true,
align: 'left', align: 'left',
}, },
{ {
name: 'stems', name: 'stems',
label: 'invoiceIn.summary.stems', label: 'InvoiceIn.summary.stems',
field: (row) => row.stems, field: (row) => row.stems,
format: (value) => value, format: (value) => value,
sortable: true, sortable: true,
@ -145,7 +145,7 @@ const intrastatColumns = ref([
}, },
{ {
name: 'landed', name: 'landed',
label: 'invoiceIn.summary.country', label: 'InvoiceIn.summary.country',
field: (row) => row.country?.code, field: (row) => row.country?.code,
format: (value) => value, format: (value) => value,
sortable: true, sortable: true,
@ -210,7 +210,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
/> />
</QCardSection> </QCardSection>
<VnLv <VnLv
:label="t('invoiceIn.list.supplier')" :label="t('InvoiceIn.list.supplier')"
:value="entity.supplier?.name" :value="entity.supplier?.name"
> >
<template #value> <template #value>
@ -221,14 +221,18 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
</template> </template>
</VnLv> </VnLv>
<VnLv <VnLv
:label="t('invoiceIn.list.supplierRef')" :label="t('InvoiceIn.list.supplierRef')"
:value="entity.supplierRef" :value="entity.supplierRef"
/> />
<VnLv <VnLv
:label="t('invoiceIn.summary.currency')" :label="t('InvoiceIn.summary.currency')"
:value="entity.currency?.code" :value="entity.currency?.code"
/> />
<VnLv :label="t('invoiceIn.serial')" :value="`${entity.serial}`" /> <VnLv :label="t('InvoiceIn.serial')" :value="`${entity.serial}`" />
<VnLv
:label="t('globals.country')"
:value="entity.supplier?.country?.code"
/>
</QCard> </QCard>
<QCard class="vn-one"> <QCard class="vn-one">
<QCardSection class="q-pa-none"> <QCardSection class="q-pa-none">
@ -239,21 +243,22 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
</QCardSection> </QCardSection>
<VnLv <VnLv
:ellipsis-value="false" :ellipsis-value="false"
:label="t('invoiceIn.summary.issued')" :label="t('InvoiceIn.summary.issued')"
:value="toDate(entity.issued)" :value="toDate(entity.issued)"
/> />
<VnLv <VnLv
:label="t('invoiceIn.summary.operated')" :label="t('InvoiceIn.summary.operated')"
:value="toDate(entity.operated)" :value="toDate(entity.operated)"
/> />
<VnLv <VnLv
:label="t('invoiceIn.summary.bookEntried')" :label="t('InvoiceIn.summary.bookEntried')"
:value="toDate(entity.bookEntried)" :value="toDate(entity.bookEntried)"
/> />
<VnLv <VnLv
:label="t('invoiceIn.summary.bookedDate')" :label="t('InvoiceIn.summary.bookedDate')"
:value="toDate(entity.booked)" :value="toDate(entity.booked)"
/> />
<VnLv :label="t('globals.isVies')" :value="entity.supplier?.isVies" />
</QCard> </QCard>
<QCard class="vn-one"> <QCard class="vn-one">
<QCardSection class="q-pa-none"> <QCardSection class="q-pa-none">
@ -263,18 +268,18 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
/> />
</QCardSection> </QCardSection>
<VnLv <VnLv
:label="t('invoiceIn.summary.sage')" :label="t('InvoiceIn.summary.sage')"
:value="entity.sageWithholding?.withholding" :value="entity.sageWithholding?.withholding"
/> />
<VnLv <VnLv
:label="t('invoiceIn.summary.vat')" :label="t('InvoiceIn.summary.vat')"
:value="entity.expenseDeductible?.name" :value="entity.expenseDeductible?.name"
/> />
<VnLv <VnLv
:label="t('invoiceIn.card.company')" :label="t('InvoiceIn.card.company')"
:value="entity.company?.code" :value="entity.company?.code"
/> />
<VnLv :label="t('invoiceIn.isBooked')" :value="invoiceIn?.isBooked" /> <VnLv :label="t('InvoiceIn.isBooked')" :value="invoiceIn?.isBooked" />
</QCard> </QCard>
<QCard class="vn-one"> <QCard class="vn-one">
<QCardSection class="q-pa-none"> <QCardSection class="q-pa-none">
@ -285,11 +290,11 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
</QCardSection> </QCardSection>
<QCardSection class="q-pa-none"> <QCardSection class="q-pa-none">
<VnLv <VnLv
:label="t('invoiceIn.summary.taxableBase')" :label="t('InvoiceIn.summary.taxableBase')"
:value="toCurrency(entity.totals.totalTaxableBase)" :value="toCurrency(entity.totals.totalTaxableBase)"
/> />
<VnLv label="Total" :value="toCurrency(entity.totals.totalVat)" /> <VnLv label="Total" :value="toCurrency(entity.totals.totalVat)" />
<VnLv :label="t('invoiceIn.summary.dueTotal')"> <VnLv :label="t('InvoiceIn.summary.dueTotal')">
<template #value> <template #value>
<QChip <QChip
dense dense
@ -297,8 +302,8 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
:color="amountsNotMatch ? 'negative' : 'transparent'" :color="amountsNotMatch ? 'negative' : 'transparent'"
:title=" :title="
amountsNotMatch amountsNotMatch
? t('invoiceIn.summary.noMatch') ? t('InvoiceIn.summary.noMatch')
: t('invoiceIn.summary.dueTotal') : t('InvoiceIn.summary.dueTotal')
" "
> >
{{ toCurrency(entity.totals.totalDueDay) }} {{ toCurrency(entity.totals.totalDueDay) }}
@ -309,7 +314,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
</QCard> </QCard>
<!--Vat--> <!--Vat-->
<QCard v-if="entity.invoiceInTax.length" class="vat"> <QCard v-if="entity.invoiceInTax.length" class="vat">
<VnTitle :url="getLink('vat')" :text="t('invoiceIn.card.vat')" /> <VnTitle :url="getLink('vat')" :text="t('InvoiceIn.card.vat')" />
<QTable <QTable
:columns="vatColumns" :columns="vatColumns"
:rows="entity.invoiceInTax" :rows="entity.invoiceInTax"
@ -357,7 +362,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
</QCard> </QCard>
<!--Due Day--> <!--Due Day-->
<QCard v-if="entity.invoiceInDueDay.length" class="due-day"> <QCard v-if="entity.invoiceInDueDay.length" class="due-day">
<VnTitle :url="getLink('due-day')" :text="t('invoiceIn.card.dueDay')" /> <VnTitle :url="getLink('due-day')" :text="t('InvoiceIn.card.dueDay')" />
<QTable :columns="dueDayColumns" :rows="entity.invoiceInDueDay" flat> <QTable :columns="dueDayColumns" :rows="entity.invoiceInDueDay" flat>
<template #header="dueDayProps"> <template #header="dueDayProps">
<QTr :props="dueDayProps" class="bg"> <QTr :props="dueDayProps" class="bg">
@ -395,7 +400,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
<QCard v-if="entity.invoiceInIntrastat.length"> <QCard v-if="entity.invoiceInIntrastat.length">
<VnTitle <VnTitle
:url="getLink('intrastat')" :url="getLink('intrastat')"
:text="t('invoiceIn.card.intrastat')" :text="t('InvoiceIn.card.intrastat')"
/> />
<QTable <QTable
:columns="intrastatColumns" :columns="intrastatColumns"

View File

@ -11,12 +11,14 @@ import CrudModel from 'src/components/CrudModel.vue';
import VnInputNumber from 'src/components/common/VnInputNumber.vue'; import VnInputNumber from 'src/components/common/VnInputNumber.vue';
import VnSelectDialog from 'src/components/common/VnSelectDialog.vue'; import VnSelectDialog from 'src/components/common/VnSelectDialog.vue';
import CreateNewExpenseForm from 'src/components/CreateNewExpenseForm.vue'; import CreateNewExpenseForm from 'src/components/CreateNewExpenseForm.vue';
import { getExchange } from 'src/composables/getExchange';
import { useAccountShortToStandard } from 'src/composables/useAccountShortToStandard';
const { t } = useI18n(); const { t } = useI18n();
const arrayData = useArrayData(); const arrayData = useArrayData();
const route = useRoute();
const invoiceIn = computed(() => arrayData.store.data); const invoiceIn = computed(() => arrayData.store.data);
const invoiceId = +useRoute().params.id;
const currency = computed(() => invoiceIn.value?.currency?.code); const currency = computed(() => invoiceIn.value?.currency?.code);
const expenses = ref([]); const expenses = ref([]);
const sageTaxTypes = ref([]); const sageTaxTypes = ref([]);
@ -39,9 +41,8 @@ const columns = computed(() => [
options: expenses.value, options: expenses.value,
model: 'expenseFk', model: 'expenseFk',
optionValue: 'id', optionValue: 'id',
optionLabel: 'id', optionLabel: (row) => `${row.id}: ${row.name}`,
Review

Este no funciona al cargar, pone undefined @alexm no consigo encontrar el porque, necesitaría que lo miremos.

Este no funciona al cargar, pone **undefined** @alexm no consigo encontrar el porque, necesitaría que lo miremos.
Review

Vale, pido los cambios hasta este archivo

Vale, pido los cambios hasta este archivo
sortable: true, sortable: true,
tabIndex: 1,
align: 'left', align: 'left',
}, },
{ {
@ -50,7 +51,6 @@ const columns = computed(() => [
field: (row) => row.taxableBase, field: (row) => row.taxableBase,
model: 'taxableBase', model: 'taxableBase',
sortable: true, sortable: true,
tabIndex: 2,
align: 'left', align: 'left',
}, },
{ {
@ -60,9 +60,8 @@ const columns = computed(() => [
options: sageTaxTypes.value, options: sageTaxTypes.value,
model: 'taxTypeSageFk', model: 'taxTypeSageFk',
optionValue: 'id', optionValue: 'id',
optionLabel: 'id', optionLabel: (row) => `${row.id}: ${row.vat}`,
sortable: true, sortable: true,
tabindex: 3,
align: 'left', align: 'left',
}, },
{ {
@ -72,16 +71,14 @@ const columns = computed(() => [
options: sageTransactionTypes.value, options: sageTransactionTypes.value,
model: 'transactionTypeSageFk', model: 'transactionTypeSageFk',
optionValue: 'id', optionValue: 'id',
optionLabel: 'id', optionLabel: (row) => `${row.id}: ${row.transaction}`,
sortable: true, sortable: true,
tabIndex: 4,
align: 'left', align: 'left',
}, },
{ {
name: 'rate', name: 'rate',
label: t('Rate'), label: t('Rate'),
sortable: true, sortable: true,
tabIndex: 5,
field: (row) => taxRate(row, row.taxTypeSageFk), field: (row) => taxRate(row, row.taxTypeSageFk),
align: 'left', align: 'left',
}, },
@ -89,7 +86,6 @@ const columns = computed(() => [
name: 'foreignvalue', name: 'foreignvalue',
label: t('Foreign value'), label: t('Foreign value'),
sortable: true, sortable: true,
tabIndex: 6,
field: (row) => row.foreignValue, field: (row) => row.foreignValue,
align: 'left', align: 'left',
}, },
@ -106,7 +102,7 @@ const filter = {
'transactionTypeSageFk', 'transactionTypeSageFk',
], ],
where: { where: {
invoiceInFk: invoiceId, invoiceInFk: route.params.id,
}, },
}; };
@ -120,14 +116,20 @@ function taxRate(invoiceInTax) {
const taxTypeSage = taxRateSelection?.rate ?? 0; const taxTypeSage = taxRateSelection?.rate ?? 0;
const taxableBase = invoiceInTax?.taxableBase ?? 0; const taxableBase = invoiceInTax?.taxableBase ?? 0;
return (taxTypeSage / 100) * taxableBase; return ((taxTypeSage / 100) * taxableBase).toFixed(2);
} }
const formatOpt = (row, { model, options }, prop) => { function autocompleteExpense(evt, row, col) {
const obj = row[model]; const val = evt.target.value;
const option = options.find(({ id }) => id == obj); if (!val) return;
return option ? `${obj}:${option[prop]}` : '';
}; const param = isNaN(val) ? row[col.model] : val;
const lookup = expenses.value.find(
({ id }) => id == useAccountShortToStandard(param)
);
if (lookup) row[col.model] = lookup;
}
</script> </script>
<template> <template>
<FetchData <FetchData
@ -148,10 +150,10 @@ const formatOpt = (row, { model, options }, prop) => {
data-key="InvoiceInTaxes" data-key="InvoiceInTaxes"
url="InvoiceInTaxes" url="InvoiceInTaxes"
:filter="filter" :filter="filter"
:data-required="{ invoiceInFk: invoiceId }" :data-required="{ invoiceInFk: $route.params.id }"
auto-load auto-load
v-model:selected="rowsSelected" v-model:selected="rowsSelected"
:go-to="`/invoice-in/${invoiceId}/due-day`" :go-to="`/invoice-in/${$route.params.id}/due-day`"
> >
<template #body="{ rows }"> <template #body="{ rows }">
<QTable <QTable
@ -171,6 +173,7 @@ const formatOpt = (row, { model, options }, prop) => {
:option-label="col.optionLabel" :option-label="col.optionLabel"
:filter-options="['id', 'name']" :filter-options="['id', 'name']"
:tooltip="t('Create a new expense')" :tooltip="t('Create a new expense')"
@keydown.tab="autocompleteExpense($event, row, col)"
> >
<template #option="scope"> <template #option="scope">
<QItem v-bind="scope.itemProps"> <QItem v-bind="scope.itemProps">
@ -187,13 +190,7 @@ const formatOpt = (row, { model, options }, prop) => {
</template> </template>
<template #body-cell-taxablebase="{ row }"> <template #body-cell-taxablebase="{ row }">
<QTd> <QTd>
{{ currency }}
<VnInputNumber <VnInputNumber
:class="{
'no-pointer-events': isNotEuro(currency),
}"
:disable="isNotEuro(currency)"
label=""
clear-icon="close" clear-icon="close"
v-model="row.taxableBase" v-model="row.taxableBase"
clearable clearable
@ -208,9 +205,7 @@ const formatOpt = (row, { model, options }, prop) => {
:option-value="col.optionValue" :option-value="col.optionValue"
:option-label="col.optionLabel" :option-label="col.optionLabel"
:filter-options="['id', 'vat']" :filter-options="['id', 'vat']"
:hide-selected="false" data-cy="vat-sageiva"
:fill-input="false"
:display-value="formatOpt(row, col, 'vat')"
> >
<template #option="scope"> <template #option="scope">
<QItem v-bind="scope.itemProps"> <QItem v-bind="scope.itemProps">
@ -233,11 +228,6 @@ const formatOpt = (row, { model, options }, prop) => {
:option-value="col.optionValue" :option-value="col.optionValue"
:option-label="col.optionLabel" :option-label="col.optionLabel"
:filter-options="['id', 'transaction']" :filter-options="['id', 'transaction']"
:autofocus="col.tabIndex == 1"
input-debounce="0"
:hide-selected="false"
:fill-input="false"
:display-value="formatOpt(row, col, 'transaction')"
> >
<template #option="scope"> <template #option="scope">
<QItem v-bind="scope.itemProps"> <QItem v-bind="scope.itemProps">
@ -262,6 +252,16 @@ const formatOpt = (row, { model, options }, prop) => {
}" }"
:disable="!isNotEuro(currency)" :disable="!isNotEuro(currency)"
v-model="row.foreignValue" v-model="row.foreignValue"
@update:model-value="
async (val) => {
if (!isNotEuro(currency)) return;
row.taxableBase = await getExchange(
val,
row.currencyFk,
invoiceIn.issued
);
}
"
/> />
</QTd> </QTd>
</template> </template>
@ -305,7 +305,7 @@ const formatOpt = (row, { model, options }, prop) => {
v-model="props.row['expenseFk']" v-model="props.row['expenseFk']"
:options="expenses" :options="expenses"
option-value="id" option-value="id"
option-label="name" :option-label="(row) => `${row.id}:${row.name}`"
:filter-options="['id', 'name']" :filter-options="['id', 'name']"
:tooltip="t('Create a new expense')" :tooltip="t('Create a new expense')"
> >
@ -339,7 +339,7 @@ const formatOpt = (row, { model, options }, prop) => {
v-model="props.row['taxTypeSageFk']" v-model="props.row['taxTypeSageFk']"
:options="sageTaxTypes" :options="sageTaxTypes"
option-value="id" option-value="id"
option-label="vat" :option-label="(row) => `${row.id}:${row.vat}`"
:filter-options="['id', 'vat']" :filter-options="['id', 'vat']"
> >
<template #option="scope"> <template #option="scope">
@ -362,7 +362,9 @@ const formatOpt = (row, { model, options }, prop) => {
v-model="props.row['transactionTypeSageFk']" v-model="props.row['transactionTypeSageFk']"
:options="sageTransactionTypes" :options="sageTransactionTypes"
option-value="id" option-value="id"
option-label="transaction" :option-label="
(row) => `${row.id}:${row.transaction}`
"
:filter-options="['id', 'transaction']" :filter-options="['id', 'transaction']"
> >
<template #option="scope"> <template #option="scope">
@ -418,11 +420,6 @@ const formatOpt = (row, { model, options }, prop) => {
.bg { .bg {
background-color: var(--vn-light-gray); background-color: var(--vn-light-gray);
} }
:deep(.q-table tr td:nth-child(n + 4):nth-child(-n + 5) input) {
display: none;
}
@media (max-width: $breakpoint-xs) { @media (max-width: $breakpoint-xs) {
.q-dialog { .q-dialog {
.q-card { .q-card {

View File

@ -83,7 +83,7 @@ const redirectToInvoiceInBasicData = (__, { id }) => {
</template> </template>
</VnSelect> </VnSelect>
<VnInput <VnInput
:label="t('invoiceIn.list.supplierRef')" :label="t('InvoiceIn.list.supplierRef')"
v-model="data.supplierRef" v-model="data.supplierRef"
/> />
</VnRow> </VnRow>
@ -97,10 +97,10 @@ const redirectToInvoiceInBasicData = (__, { id }) => {
map-options map-options
hide-selected hide-selected
:required="true" :required="true"
:rules="validate('invoiceIn.companyFk')" :rules="validate('InvoiceIn.companyFk')"
/> />
<VnInputDate <VnInputDate
:label="t('invoiceIn.summary.issued')" :label="t('InvoiceIn.summary.issued')"
v-model="data.issued" v-model="data.issued"
/> />
</VnRow> </VnRow>

View File

@ -1,41 +1,66 @@
<script setup> <script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import VnSelect from 'components/common/VnSelect.vue'; import VnSelect from 'components/common/VnSelect.vue';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnInputDate from 'components/common/VnInputDate.vue'; import VnInputDate from 'components/common/VnInputDate.vue';
import FetchData from 'components/FetchData.vue';
import VnInputNumber from 'src/components/common/VnInputNumber.vue'; import VnInputNumber from 'src/components/common/VnInputNumber.vue';
import { dateRange } from 'src/filters';
import { date } from 'quasar';
const { t } = useI18n();
defineProps({ dataKey: { type: String, required: true } }); defineProps({ dataKey: { type: String, required: true } });
const activities = ref([]); const dateFormat = 'YYYY-MM-DDTHH:mm:ss.SSSZ';
function handleDaysAgo(params, daysAgo) {
const [from, to] = dateRange(Date.vnNew());
if (!daysAgo && daysAgo !== 0) {
Object.assign(params, { from: undefined, to: undefined });
} else {
from.setDate(from.getDate() - daysAgo);
Object.assign(params, {
from: date.formatDate(from, dateFormat),
to: date.formatDate(to, dateFormat),
});
}
}
</script> </script>
<template> <template>
<FetchData <VnFilterPanel :data-key="dataKey" :search-button="true" :hidden-tags="['daysAgo']">
url="SupplierActivities" <template #tags="{ tag, formatFn, getLocale }">
auto-load
@on-fetch="(data) => (activities = data)"
/>
<VnFilterPanel :data-key="dataKey" :search-button="true">
<template #tags="{ tag, formatFn }">
<div class="q-gutter-x-xs"> <div class="q-gutter-x-xs">
<strong>{{ t(`params.${tag.label}`) }}: </strong> <strong>{{ getLocale(tag.label) }}: </strong>
<span>{{ formatFn(tag.value) }}</span> <span>{{ formatFn(tag.value) }}</span>
</div> </div>
</template> </template>
<template #body="{ params, searchFn }"> <template #body="{ params, searchFn, getLocale }">
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnInputDate :label="t('From')" v-model="params.from" is-outlined /> <VnInputDate
:label="$t('globals.from')"
v-model="params.from"
is-outlined
/>
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnInputDate :label="t('To')" v-model="params.to" is-outlined /> <VnInputDate
:label="$t('globals.to')"
v-model="params.to"
is-outlined
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInputNumber
:label="$t('globals.daysAgo')"
v-model="params.daysAgo"
is-outlined
:step="0"
@update:model-value="(val) => handleDaysAgo(params, val)"
@remove="(val) => handleDaysAgo(params, val)"
/>
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem> <QItem>
@ -44,20 +69,18 @@ const activities = ref([]);
v-model="params.supplierFk" v-model="params.supplierFk"
url="Suppliers" url="Suppliers"
:fields="['id', 'nickname']" :fields="['id', 'nickname']"
:label="t('params.supplierFk')" :label="getLocale('supplierFk')"
option-value="id"
option-label="nickname" option-label="nickname"
dense dense
outlined outlined
rounded rounded
:filter-options="['id', 'name']"
/> />
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnInput <VnInput
:label="t('params.supplierRef')" :label="getLocale('supplierRef')"
v-model="params.supplierRef" v-model="params.supplierRef"
is-outlined is-outlined
lazy-rules lazy-rules
@ -67,7 +90,7 @@ const activities = ref([]);
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnInput <VnInput
:label="t('params.fi')" :label="getLocale('fi')"
v-model="params.fi" v-model="params.fi"
is-outlined is-outlined
lazy-rules lazy-rules
@ -77,7 +100,7 @@ const activities = ref([]);
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnInput <VnInput
:label="t('params.serial')" :label="getLocale('serial')"
v-model="params.serial" v-model="params.serial"
is-outlined is-outlined
lazy-rules lazy-rules
@ -87,7 +110,7 @@ const activities = ref([]);
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnInput <VnInput
:label="t('params.account')" :label="getLocale('account')"
v-model="params.account" v-model="params.account"
is-outlined is-outlined
lazy-rules lazy-rules
@ -97,7 +120,7 @@ const activities = ref([]);
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnInput <VnInput
:label="t('params.awb')" :label="getLocale('globals.params.awbCode')"
v-model="params.awbCode" v-model="params.awbCode"
is-outlined is-outlined
lazy-rules lazy-rules
@ -107,24 +130,34 @@ const activities = ref([]);
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnInputNumber <VnInputNumber
:label="t('Amount')" :label="$t('globals.amount')"
v-model="params.amount" v-model="params.amount"
is-outlined is-outlined
/> />
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem>
<QItemSection>
<VnSelect
v-model="params.companyFk"
:label="$t('globals.company')"
url="Companies"
option-label="code"
:fields="['id', 'code']"
is-outlined
/>
</QItemSection>
</QItem>
<QItem> <QItem>
<QItemSection> <QItemSection>
<QCheckbox <QCheckbox
:label="t('invoiceIn.isBooked')" :label="$t('InvoiceIn.isBooked')"
v-model="params.isBooked" v-model="params.isBooked"
@update:model-value="searchFn()" @update:model-value="searchFn()"
toggle-indeterminate toggle-indeterminate
/> />
</QItemSection>
<QItemSection>
<QCheckbox <QCheckbox
:label="t('params.correctingFk')" :label="getLocale('params.correctingFk')"
v-model="params.correctingFk" v-model="params.correctingFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
toggle-indeterminate toggle-indeterminate
@ -134,54 +167,3 @@ const activities = ref([]);
</template> </template>
</VnFilterPanel> </VnFilterPanel>
</template> </template>
<i18n>
en:
params:
search: Id or supplier name
supplierRef: Supplier ref.
supplierFk: Supplier
fi: Supplier fiscal id
clientFk: Customer
amount: Amount
created: Created
awb: AWB
dued: Dued
serialNumber: Serial Number
serial: Serial
account: Ledger account
isBooked: is booked
correctedFk: Rectified
issued: Issued
to: To
from: From
awbCode: AWB
correctingFk: Rectificative
supplierActivityFk: Supplier activity
es:
params:
search: Id o nombre proveedor
supplierRef: Ref. proveedor
supplierFk: Proveedor
clientFk: Cliente
fi: CIF proveedor
serialNumber: Num. serie
serial: Serie
awb: AWB
amount: Importe
issued: Emitida
isBooked: Contabilizada
account: Cuenta contable
created: Creada
dued: Vencida
correctedFk: Rectificada
correctingFk: Rectificativa
supplierActivityFk: Actividad proveedor
from: Desde
to: Hasta
From: Desde
To: Hasta
Amount: Importe
Issued: Fecha factura
Id or supplier: Id o proveedor
</i18n>

View File

@ -2,6 +2,7 @@
import { ref, computed, onMounted, onUnmounted } from 'vue'; import { ref, computed, onMounted, onUnmounted } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import { useState } from 'src/composables/useState';
import { downloadFile } from 'src/composables/downloadFile'; import { downloadFile } from 'src/composables/downloadFile';
import { toDate, toCurrency } from 'src/filters/index'; import { toDate, toCurrency } from 'src/filters/index';
import InvoiceInFilter from './InvoiceInFilter.vue'; import InvoiceInFilter from './InvoiceInFilter.vue';
@ -14,8 +15,10 @@ import VnTable from 'src/components/VnTable/VnTable.vue';
import VnSelect from 'src/components/common/VnSelect.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue';
import FetchData from 'src/components/FetchData.vue';
const stateStore = useStateStore(); const stateStore = useStateStore();
const user = useState().getUser();
const { viewSummary } = useSummaryDialog(); const { viewSummary } = useSummaryDialog();
const { t } = useI18n(); const { t } = useI18n();
@ -23,7 +26,14 @@ onMounted(async () => (stateStore.rightDrawer = true));
onUnmounted(() => (stateStore.rightDrawer = false)); onUnmounted(() => (stateStore.rightDrawer = false));
const tableRef = ref(); const tableRef = ref();
const companies = ref([]);
const cols = computed(() => [ const cols = computed(() => [
{
align: 'left',
name: 'isBooked',
label: t('InvoiceIn.isBooked'),
columnFilter: false,
},
{ {
align: 'left', align: 'left',
name: 'id', name: 'id',
@ -32,7 +42,7 @@ const cols = computed(() => [
{ {
align: 'left', align: 'left',
name: 'supplierFk', name: 'supplierFk',
label: t('invoiceIn.list.supplier'), label: t('InvoiceIn.list.supplier'),
columnFilter: { columnFilter: {
component: 'select', component: 'select',
attrs: { attrs: {
@ -45,16 +55,16 @@ const cols = computed(() => [
{ {
align: 'left', align: 'left',
name: 'supplierRef', name: 'supplierRef',
label: t('invoiceIn.list.supplierRef'), label: t('InvoiceIn.list.supplierRef'),
}, },
{ {
align: 'left', align: 'left',
name: 'serial', name: 'serial',
label: t('invoiceIn.serial'), label: t('InvoiceIn.serial'),
}, },
{ {
align: 'left', align: 'left',
label: t('invoiceIn.list.issued'), label: t('InvoiceIn.list.issued'),
name: 'issued', name: 'issued',
component: null, component: null,
columnFilter: { columnFilter: {
@ -64,21 +74,38 @@ const cols = computed(() => [
}, },
{ {
align: 'left', align: 'left',
name: 'isBooked', label: t('InvoiceIn.list.dueDated'),
label: t('invoiceIn.isBooked'), name: 'dueDated',
columnFilter: false, component: null,
columnFilter: {
component: 'date',
},
format: (row, dashIfEmpty) => dashIfEmpty(toDate(row.dueDated)),
}, },
{ {
align: 'left', align: 'left',
name: 'awbCode', name: 'awbCode',
label: t('invoiceIn.list.awb'), label: t('InvoiceIn.list.awb'),
}, },
{ {
align: 'left', align: 'left',
name: 'amount', name: 'amount',
label: t('invoiceIn.list.amount'), label: t('InvoiceIn.list.amount'),
format: ({ amount }) => toCurrency(amount), format: ({ amount }) => toCurrency(amount),
}, },
{
name: 'companyFk',
label: t('globals.company'),
columnFilter: {
component: 'select',
attrs: {
url: 'Companies',
fields: ['id', 'code'],
optionLabel: 'code',
},
},
format: (row) => row.code,
},
{ {
align: 'right', align: 'right',
name: 'tableActions', name: 'tableActions',
@ -101,6 +128,7 @@ const cols = computed(() => [
]); ]);
</script> </script>
<template> <template>
<FetchData url="Companies" @on-fetch="(data) => (companies = data)" auto-load />
<InvoiceInSearchbar /> <InvoiceInSearchbar />
<RightMenu> <RightMenu>
<template #right-panel> <template #right-panel>
@ -116,7 +144,7 @@ const cols = computed(() => [
urlCreate: 'InvoiceIns', urlCreate: 'InvoiceIns',
title: t('globals.createInvoiceIn'), title: t('globals.createInvoiceIn'),
onDataSaved: ({ id }) => tableRef.redirect(id), onDataSaved: ({ id }) => tableRef.redirect(id),
formInitialData: {}, formInitialData: { companyFk: user.companyFk, issued: Date.vnNew() },
}" }"
redirect="invoice-in" redirect="invoice-in"
:columns="cols" :columns="cols"
@ -151,7 +179,7 @@ const cols = computed(() => [
</template> </template>
</VnSelect> </VnSelect>
<VnInput <VnInput
:label="t('invoiceIn.list.supplierRef')" :label="t('InvoiceIn.list.supplierRef')"
v-model="data.supplierRef" v-model="data.supplierRef"
/> />
<VnSelect <VnSelect
@ -163,7 +191,7 @@ const cols = computed(() => [
option-label="code" option-label="code"
:required="true" :required="true"
/> />
<VnInputDate :label="t('invoiceIn.summary.issued')" v-model="data.issued" /> <VnInputDate :label="t('InvoiceIn.summary.issued')" v-model="data.issued" />
</template> </template>
</VnTable> </VnTable>
</template> </template>

View File

@ -58,6 +58,14 @@ onBeforeMount(async () => {
:right-search="false" :right-search="false"
:user-params="{ daysAgo }" :user-params="{ daysAgo }"
:disable-option="{ card: true }" :disable-option="{ card: true }"
:row-click="
(row) => {
$router.push({
name: 'InvoiceInList',
query: { table: JSON.stringify({ serial: row.serial }) },
});
}
"
auto-load auto-load
/> />
</template> </template>

View File

@ -8,7 +8,11 @@ defineProps({ dataKey: { type: String, required: true } });
const { t } = useI18n(); const { t } = useI18n();
</script> </script>
<template> <template>
<VnFilterPanel :data-key="dataKey" :search-button="true"> <VnFilterPanel
:data-key="dataKey"
:search-button="true"
:unremovable-params="['daysAgo']"
>
<template #tags="{ tag, formatFn }"> <template #tags="{ tag, formatFn }">
<div class="q-gutter-x-xs"> <div class="q-gutter-x-xs">
<strong>{{ t(`params.${tag.label}`) }}: </strong> <strong>{{ t(`params.${tag.label}`) }}: </strong>

View File

@ -1,4 +1,4 @@
invoiceIn: InvoiceIn:
serial: Serial serial: Serial
isBooked: Is booked isBooked: Is booked
list: list:
@ -7,8 +7,11 @@ invoiceIn:
supplierRef: Supplier ref. supplierRef: Supplier ref.
file: File file: File
issued: Issued issued: Issued
dueDated: Due dated
awb: AWB awb: AWB
amount: Amount amount: Amount
descriptor:
ticketList: Ticket list
card: card:
client: Client client: Client
company: Company company: Company
@ -39,3 +42,9 @@ invoiceIn:
net: Net net: Net
stems: Stems stems: Stems
country: Country country: Country
params:
search: Id or supplier name
account: Ledger account
correctingFk: Rectificative
correctedFk: Corrected
isBooked: Is booked

View File

@ -1,15 +1,17 @@
invoiceIn: InvoiceIn:
serial: Serie serial: Serie
isBooked: Contabilizada isBooked: Contabilizada
list: list:
ref: Referencia ref: Referencia
supplier: Proveedor supplier: Proveedor
supplierRef: Ref. proveedor supplierRef: Ref. proveedor
shortIssued: F. emisión issued: F. emisión
dueDated: F. vencimiento
file: Fichero file: Fichero
issued: Fecha emisión
awb: AWB awb: AWB
amount: Importe amount: Importe
descriptor:
ticketList: Listado de tickets
card: card:
client: Cliente client: Cliente
company: Empresa company: Empresa
@ -38,3 +40,8 @@ invoiceIn:
net: Neto net: Neto
stems: Tallos stems: Tallos
country: País country: País
params:
search: Id o nombre proveedor
account: Cuenta contable
correctingFk: Rectificativa
correctedFk: Rectificada

View File

@ -180,10 +180,7 @@ function handleLocation(data, location) {
:label="t('supplier.fiscalData.isTrucker')" :label="t('supplier.fiscalData.isTrucker')"
/> />
<div class="row items-center"> <div class="row items-center">
<QCheckbox <QCheckbox v-model="data.isVies" :label="t('globals.isVies')" />
v-model="data.isVies"
:label="t('supplier.fiscalData.isVies')"
/>
<QIcon name="info" size="xs" class="cursor-pointer q-ml-sm"> <QIcon name="info" size="xs" class="cursor-pointer q-ml-sm">
<QTooltip> <QTooltip>
{{ {{

View File

@ -2,7 +2,7 @@
describe('InvoiceInIntrastat', () => { describe('InvoiceInIntrastat', () => {
const firstRow = 'tbody > :nth-child(1)'; const firstRow = 'tbody > :nth-child(1)';
const thirdRow = 'tbody > :nth-child(3)'; const thirdRow = 'tbody > :nth-child(3)';
const firstRowCode = `${firstRow} > :nth-child(2)`; const codes = `[data-cy="intrastat-code"]`;
const firstRowAmount = `${firstRow} > :nth-child(3)`; const firstRowAmount = `${firstRow} > :nth-child(3)`;
beforeEach(() => { beforeEach(() => {
@ -11,13 +11,12 @@ describe('InvoiceInIntrastat', () => {
}); });
it('should edit the first line', () => { it('should edit the first line', () => {
cy.selectOption(firstRowCode, 'Plantas vivas: Esqueje/injerto, Vid'); cy.selectOption(`${firstRow} ${codes}`, 'Plantas vivas: Esqueje/injerto, Vid');
cy.get(firstRowAmount).clear(); cy.get(firstRowAmount).clear();
cy.saveCard(); cy.saveCard();
cy.get(`${firstRowCode} span`).should( cy.get(codes)
'have.text', .eq(0)
'6021010:Plantas vivas: Esqueje/injerto, Vid' .should('have.value', '6021010: Plantas vivas: Esqueje/injerto, Vid');
);
}); });
it('should add a new row', () => { it('should add a new row', () => {

View File

@ -1,7 +1,7 @@
/// <reference types="cypress" /> /// <reference types="cypress" />
describe('InvoiceInList', () => { describe('InvoiceInList', () => {
const firstRow = 'tbody.q-virtual-scroll__content tr:nth-child(1)'; const firstRow = 'tbody.q-virtual-scroll__content tr:nth-child(1)';
const firstId = `${firstRow} > td:nth-child(1) span`; const firstId = `${firstRow} > td:nth-child(2) span`;
const firstDetailBtn = `${firstRow} .q-btn:nth-child(1)`; const firstDetailBtn = `${firstRow} .q-btn:nth-child(1)`;
const summaryHeaders = '.summaryBody .header-link'; const summaryHeaders = '.summaryBody .header-link';

View File

@ -2,6 +2,7 @@
describe('InvoiceInVat', () => { describe('InvoiceInVat', () => {
const thirdRow = 'tbody > :nth-child(3)'; const thirdRow = 'tbody > :nth-child(3)';
const firstLineVat = 'tbody > :nth-child(1) > :nth-child(4)'; const firstLineVat = 'tbody > :nth-child(1) > :nth-child(4)';
const vats = '[data-cy="vat-sageiva"]';
const dialogInputs = '.q-dialog label input'; const dialogInputs = '.q-dialog label input';
const addBtn = 'tbody tr:nth-child(1) td:nth-child(2) .--add-icon'; const addBtn = 'tbody tr:nth-child(1) td:nth-child(2) .--add-icon';
const randomInt = Math.floor(Math.random() * 100); const randomInt = Math.floor(Math.random() * 100);
@ -14,9 +15,9 @@ describe('InvoiceInVat', () => {
}); });
it('should edit the sage iva', () => { it('should edit the sage iva', () => {
cy.selectOption(firstLineVat, 'H.P. IVA 21% CEE'); cy.selectOption(`${firstLineVat} ${vats}`, 'H.P. IVA 21% CEE');
cy.saveCard(); cy.saveCard();
cy.get(`${firstLineVat} span`).should('have.text', '8:H.P. IVA 21% CEE'); cy.get(vats).eq(0).should('have.value', '8: H.P. IVA 21% CEE');
}); });
it('should add a new row', () => { it('should add a new row', () => {

View File

@ -0,0 +1,45 @@
import { describe, expect, it, vi } from 'vitest';
import axios from 'axios';
import { getExchange } from 'src/composables/getExchange';
vi.mock('axios');
describe('getExchange()', () => {
it('should return the correct exchange rate', async () => {
axios.get.mockResolvedValue({
data: { value: 1.2 },
});
const amount = 100;
const currencyFk = 1;
const dated = '2023-01-01';
const result = await getExchange(amount, currencyFk, dated);
expect(result).toBe('83.33');
});
it('should return the correct exchange rate with custom decimal places', async () => {
axios.get.mockResolvedValue({
data: { value: 1.2 },
});
const amount = 100;
const currencyFk = 1;
const dated = '2023-01-01';
const decimalPlaces = 3;
const result = await getExchange(amount, currencyFk, dated, decimalPlaces);
expect(result).toBe('83.333');
});
it('should return null if the API call fails', async () => {
axios.get.mockRejectedValue(new Error('Network error'));
const amount = 100;
const currencyFk = 1;
const dated = '2023-01-01';
const result = await getExchange(amount, currencyFk, dated);
expect(result).toBeNull();
});
});

View File

@ -0,0 +1,55 @@
import { vi, describe, expect, it } from 'vitest';
import { getTotal } from 'src/composables/getTotal';
vi.mock('src/filters', () => ({
toCurrency: vi.fn((value, currency) => `${currency} ${value.toFixed(2)}`),
}));
describe('getTotal()', () => {
const rows = [
{ amount: 10.5, tax: 2.1 },
{ amount: 20.75, tax: 3.25 },
{ amount: 30.25, tax: 4.75 },
];
it('should calculate the total for a given key', () => {
const total = getTotal(rows, 'amount');
expect(total).toBe('61.50');
});
it('should calculate the total with a callback function', () => {
const total = getTotal(rows, null, { cb: (row) => row.amount + row.tax });
expect(total).toBe('71.60');
});
it('should format the total as currency', () => {
const total = getTotal(rows, 'amount', { currency: 'USD' });
expect(total).toBe('USD 61.50');
});
it('should format the total as currency with default currency', () => {
const total = getTotal(rows, 'amount', { currency: 'default' });
expect(total).toBe('undefined 61.50');
});
it('should calculate the total with integer formatting', () => {
const total = getTotal(rows, 'amount', { decimalPlaces: 0 });
expect(total).toBe('62');
});
it('should calculate the total with custom decimal places', () => {
const total = getTotal(rows, 'amount', { decimalPlaces: 1 });
expect(total).toBe('61.5');
});
it('should handle rows with missing keys', () => {
const rowsWithMissingKeys = [{ amount: 10.5 }, { amount: 20.75 }, {}];
const total = getTotal(rowsWithMissingKeys, 'amount');
expect(total).toBe('31.25');
});
it('should handle empty rows', () => {
const total = getTotal([], 'amount');
expect(total).toBe('0.00');
});
});

View File

@ -0,0 +1,9 @@
import { describe, expect, it } from 'vitest';
import { useAccountShortToStandard } from 'src/composables/useAccountShortToStandard';
describe('useAccountShortToStandard()', () => {
it('should pad the decimal part with zeros for short numbers', () => {
expect(useAccountShortToStandard('123.45')).toBe('1230000045');
expect(useAccountShortToStandard('123.')).toBe('1230000000');
});
});

View File

@ -1,34 +0,0 @@
import { vi, describe, expect, it, beforeAll } from 'vitest';
import { createWrapper, axios } from 'app/test/vitest/helper';
import InvoiceInIntrastat from 'src/pages/InvoiceIn/Card/InvoiceInIntrastat.vue';
describe('InvoiceInIntrastat', () => {
let vm;
beforeAll(() => {
vm = createWrapper(InvoiceInIntrastat, {
global: {
stubs: ['vnPaginate'],
mocks: {
fetch: vi.fn(),
},
},
}).vm;
vi.spyOn(axios, 'get').mockResolvedValue({ data: [{}] });
});
describe('getTotal()', () => {
it('should correctly handle the sum', () => {
const invoceInIntrastat = [
{ amount: 10, stems: 162 },
{ amount: 20, stems: 21 },
];
const totalAmount = vm.getTotal(invoceInIntrastat, 'amount');
const totalStems = vm.getTotal(invoceInIntrastat, 'stems');
expect(totalAmount).toBe(10 + 20);
expect(totalStems).toBe(162 + 21);
});
});
});

View File

@ -1,38 +0,0 @@
import { vi, describe, expect, it, beforeAll } from 'vitest';
import { createWrapper } from 'app/test/vitest/helper';
import InvoiceInVat from 'src/pages/InvoiceIn/Card/InvoiceInVat.vue';
describe('InvoiceInVat', () => {
let vm;
beforeAll(() => {
vm = createWrapper(InvoiceInVat, {
global: {
stubs: [],
mocks: {
fetch: vi.fn(),
},
},
}).vm;
});
describe('taxRate()', () => {
it('should correctly compute the tax rate', () => {
const invoiceInTax = { taxableBase: 100, taxTypeSageFk: 1 };
vm.sageTaxTypes = [
{ id: 1, rate: 10 },
{ id: 2, rate: 20 },
];
const result = vm.taxRate(invoiceInTax);
expect(result).toBe((10 / 100) * 100);
});
it('should return 0 if there is not tax rate', () => {
const invoiceInTax = { taxableBase: 100, taxTypeSageFk: 1 };
vm.sageTaxTypes = [];
const result = vm.taxRate(invoiceInTax);
expect(result).toBe(0);
});
});
});