Merge branch 'dev' into 8322-route
gitea/salix-front/pipeline/pr-dev This commit looks good Details

This commit is contained in:
PAU ROVIRA ROSALENY 2025-01-03 09:37:03 +00:00
commit e0a9a5ca72
49 changed files with 301 additions and 150 deletions

View File

@ -55,13 +55,6 @@ async function setCountry(countryFk, data) {
} }
// Province // Province
async function handleProvinces(data) {
provincesOptions.value = data;
if (postcodeFormData.countryFk) {
await fetchTowns();
}
}
async function setProvince(id, data) { async function setProvince(id, data) {
if (data.provinceFk === id) return; if (data.provinceFk === id) return;
const newProvince = provincesOptions.value.find((province) => province.id == id); const newProvince = provincesOptions.value.find((province) => province.id == id);

View File

@ -1,7 +1,7 @@
<script setup> <script setup>
import axios from 'axios'; import axios from 'axios';
import { onMounted, onUnmounted, computed, ref, watch, nextTick } from 'vue'; import { onMounted, onUnmounted, computed, ref, watch, nextTick } from 'vue';
import { onBeforeRouteLeave, useRouter } from 'vue-router'; import { onBeforeRouteLeave, useRouter, useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { useState } from 'src/composables/useState'; import { useState } from 'src/composables/useState';
@ -12,7 +12,6 @@ import SkeletonForm from 'components/ui/SkeletonForm.vue';
import VnConfirm from './ui/VnConfirm.vue'; import VnConfirm from './ui/VnConfirm.vue';
import { tMobile } from 'src/composables/tMobile'; import { tMobile } from 'src/composables/tMobile';
import { useArrayData } from 'src/composables/useArrayData'; import { useArrayData } from 'src/composables/useArrayData';
import { useRoute } from 'vue-router';
const { push } = useRouter(); const { push } = useRouter();
const quasar = useQuasar(); const quasar = useQuasar();

View File

@ -59,8 +59,8 @@ const $props = defineProps({
default: true, default: true,
}, },
bottom: { bottom: {
type: Object, type: Boolean,
default: null, default: false,
}, },
cardClass: { cardClass: {
type: String, type: String,
@ -478,29 +478,6 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
/> />
</QTd> </QTd>
</template> </template>
<template #bottom v-if="bottom">
<slot name="bottom-table">
<QBtn
@click="
() =>
createAsDialog
? (showForm = !showForm)
: handleOnDataSaved(create)
"
class="cursor-pointer fill-icon"
color="primary"
icon="add_circle"
size="md"
round
flat
shortcut="+"
:disabled="!disabledAttr"
/>
<QTooltip>
{{ createForm.title }}
</QTooltip>
</slot>
</template>
<template #item="{ row, colsMap }"> <template #item="{ row, colsMap }">
<component <component
:is="$props.redirect ? 'router-link' : 'span'" :is="$props.redirect ? 'router-link' : 'span'"
@ -625,6 +602,27 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
</QTr> </QTr>
</template> </template>
</QTable> </QTable>
<div class="full-width bottomButton" v-if="bottom">
<QBtn
@click="
() =>
createAsDialog
? (showForm = !showForm)
: handleOnDataSaved(create)
"
class="cursor-pointer fill-icon"
color="primary"
icon="add_circle"
size="md"
round
flat
shortcut="+"
:disabled="!disabledAttr"
/>
<QTooltip>
{{ createForm.title }}
</QTooltip>
</div>
</template> </template>
</CrudModel> </CrudModel>
<QPageSticky v-if="$props.create" :offset="[20, 20]" style="z-index: 2"> <QPageSticky v-if="$props.create" :offset="[20, 20]" style="z-index: 2">

View File

@ -0,0 +1,56 @@
import { createWrapper, axios } from 'app/test/vitest/helper';
import EditForm from 'components/EditTableCellValueForm.vue';
import { vi, afterEach, beforeAll, describe, expect, it } from 'vitest';
const fieldA = 'fieldA';
const fieldB = 'fieldB';
describe('EditForm', () => {
let vm;
const mockRows = [
{ id: 1, itemFk: 101 },
{ id: 2, itemFk: 102 },
];
const mockFieldsOptions = [
{ label: 'Field A', field: fieldA, component: 'input', attrs: {} },
{ label: 'Field B', field: fieldB, component: 'date', attrs: {} },
];
const editUrl = '/api/edit';
beforeAll(() => {
vi.spyOn(axios, 'post').mockResolvedValue({ status: 200 });
vm = createWrapper(EditForm, {
props: {
rows: mockRows,
fieldsOptions: mockFieldsOptions,
editUrl,
},
}).vm;
});
afterEach(() => {
vi.clearAllMocks();
});
describe('onSubmit()', () => {
it('should call axios.post with the correct parameters in the payload', async () => {
const selectedField = { field: fieldA, component: 'input', attrs: {} };
const newValue = 'Test Value';
vm.selectedField = selectedField;
vm.newValue = newValue;
await vm.onSubmit();
const payload = axios.post.mock.calls[0][1];
expect(axios.post).toHaveBeenCalledWith(editUrl, expect.any(Object));
expect(payload.field).toEqual(fieldA);
expect(payload.newValue).toEqual(newValue);
expect(payload.lines).toEqual(expect.arrayContaining(mockRows));
expect(vm.isLoading).toEqual(false);
});
});
});

View File

@ -11,9 +11,9 @@ function getBadgeAttrs(date) {
let timeDiff = today - timeTicket; let timeDiff = today - timeTicket;
if (timeDiff == 0) return { color: 'warning', 'text-color': 'black' }; if (timeDiff == 0) return { color: 'warning', class: 'black-text-color' };
if (timeDiff < 0) return { color: 'success', 'text-color': 'black' }; if (timeDiff < 0) return { color: 'success', class: 'black-text-color' };
return { color: 'transparent', 'text-color': 'white' }; return { color: 'transparent', class: 'normal-text-color' };
} }
function formatShippedDate(date) { function formatShippedDate(date) {
@ -29,3 +29,11 @@ function formatShippedDate(date) {
{{ formatShippedDate(date) }} {{ formatShippedDate(date) }}
</QBadge> </QBadge>
</template> </template>
<style lang="scss">
.black-text-color {
color: var(--vn-black-text-color);
}
.normal-text-color {
color: var(--vn-text-color);
}
</style>

View File

@ -166,7 +166,11 @@ onMounted(() => {
const arrayDataKey = const arrayDataKey =
$props.dataKey ?? ($props.url?.length > 0 ? $props.url : $attrs.name ?? $attrs.label); $props.dataKey ?? ($props.url?.length > 0 ? $props.url : $attrs.name ?? $attrs.label);
const arrayData = useArrayData(arrayDataKey, { url: $props.url, searchUrl: false }); const arrayData = useArrayData(arrayDataKey, {
url: $props.url,
searchUrl: false,
mapKey: $attrs['map-key'],
});
function findKeyInOptions() { function findKeyInOptions() {
if (!$props.options) return; if (!$props.options) return;

View File

@ -202,7 +202,11 @@ const getLocale = (label) => {
style="position: fixed; z-index: 1; right: 0; bottom: 0" style="position: fixed; z-index: 1; right: 0; bottom: 0"
icon="search" icon="search"
@click="search()" @click="search()"
></QBtn> >
<QTooltip bottom anchor="bottom right">
{{ t('globals.search') }}
</QTooltip>
</QBtn>
<QForm @submit="search" id="filterPanelForm" @keyup.enter="search()"> <QForm @submit="search" id="filterPanelForm" @keyup.enter="search()">
<QList dense> <QList dense>
<QItem class="q-mt-xs"> <QItem class="q-mt-xs">

View File

@ -54,6 +54,7 @@ function formatNumber(number) {
:offset="100" :offset="100"
:limit="5" :limit="5"
auto-load auto-load
map-key="smsFk"
> >
<template #body="{ rows }"> <template #body="{ rows }">
<QCard <QCard

View File

@ -3,20 +3,21 @@
@import '@quasar/quasar-ui-qcalendar/src/QCalendarMonth.sass'; @import '@quasar/quasar-ui-qcalendar/src/QCalendarMonth.sass';
body.body--light { body.body--light {
--font-color: black;
--vn-header-color: #cecece; --vn-header-color: #cecece;
--vn-page-color: #ffffff; --vn-page-color: #ffffff;
--vn-section-color: #e0e0e0; --vn-section-color: #e0e0e0;
--vn-section-hover-color: #b9b9b9; --vn-section-hover-color: #b9b9b9;
--vn-text-color: var(--font-color); --vn-text-color: black;
--vn-label-color: #5f5f5f; --vn-label-color: #5f5f5f;
--vn-accent-color: #e7e3e3; --vn-accent-color: #e7e3e3;
--vn-empty-tag: #acacac; --vn-empty-tag: #acacac;
--vn-black-text-color: black;
--vn-text-color-contrast: white;
background-color: var(--vn-page-color); background-color: var(--vn-page-color);
.q-header .q-toolbar { .q-header .q-toolbar {
color: var(--font-color); color: var(--vn-text-color);
} }
} }
body.body--dark { body.body--dark {
@ -28,6 +29,8 @@ body.body--dark {
--vn-label-color: #a8a8a8; --vn-label-color: #a8a8a8;
--vn-accent-color: #424242; --vn-accent-color: #424242;
--vn-empty-tag: #2d2d2d; --vn-empty-tag: #2d2d2d;
--vn-black-text-color: black;
--vn-text-color-contrast: black;
background-color: var(--vn-page-color); background-color: var(--vn-page-color);
} }
@ -86,6 +89,10 @@ select:-webkit-autofill {
background-color: var(--vn-section-hover-color); background-color: var(--vn-section-hover-color);
} }
.bg-vn-page {
background-color: var(--vn-page-color);
}
.color-vn-label { .color-vn-label {
color: var(--vn-label-color); color: var(--vn-label-color);
} }
@ -151,7 +158,8 @@ select:-webkit-autofill {
.q-card, .q-card,
.q-table, .q-table,
.q-table__bottom, .q-table__bottom,
.q-drawer { .q-drawer,
.bottomButton {
background-color: var(--vn-section-color); background-color: var(--vn-section-color);
} }
@ -188,7 +196,7 @@ select:-webkit-autofill {
.q-tooltip { .q-tooltip {
background-color: var(--vn-page-color); background-color: var(--vn-page-color);
color: var(--font-color); color: var(--vn-text-color);
font-size: medium; font-size: medium;
} }

View File

@ -611,7 +611,7 @@ worker:
fi: DNI/NIE/NIF fi: DNI/NIE/NIF
birth: Birth birth: Birth
isFreelance: Freelance isFreelance: Freelance
isSsDiscounted: Bonificación SS isSsDiscounted: SS Bonification
hasMachineryAuthorized: Machinery authorized hasMachineryAuthorized: Machinery authorized
isDisable: Disable isDisable: Disable
notificationsManager: notificationsManager:
@ -857,11 +857,11 @@ components:
value: Value value: Value
# ItemFixedPriceFilter # ItemFixedPriceFilter
buyerFk: Buyer buyerFk: Buyer
warehouseFk: Warehouse
started: From started: From
ended: To ended: To
mine: For me mine: For me
hasMinPrice: Minimum price hasMinPrice: Minimum price
warehouseFk: Warehouse
# LatestBuysFilter # LatestBuysFilter
salesPersonFk: Buyer salesPersonFk: Buyer
from: From from: From

View File

@ -608,6 +608,15 @@ worker:
role: Rol role: Rol
sipExtension: Extensión sipExtension: Extensión
locker: Taquilla locker: Taquilla
fiDueDate: F. caducidad DNI
sex: Sexo
seniority: Antigüedad
fi: DNI/NIE/NIF
birth: F. nacimiento
isFreelance: Autónomo
isSsDiscounted: Bonificación SS
hasMachineryAuthorized: Autorizado para maquinaria
isDisable: Deshabilitado
notificationsManager: notificationsManager:
activeNotifications: Notificaciones activas activeNotifications: Notificaciones activas
availableNotifications: Notificaciones disponibles availableNotifications: Notificaciones disponibles
@ -849,6 +858,7 @@ components:
value: Valor value: Valor
# ItemFixedPriceFilter # ItemFixedPriceFilter
buyerFk: Comprador buyerFk: Comprador
warehouseFk: Almacen
started: Desde started: Desde
ended: Hasta ended: Hasta
mine: Para mi mine: Para mi

View File

@ -50,7 +50,7 @@ const removeRole = async () => {
> >
<template #menu> <template #menu>
<QItem v-ripple clickable @click="removeRole()"> <QItem v-ripple clickable @click="removeRole()">
<QItemSection>{{ t('Delete') }}</QItemSection> <QItemSection>{{ t('globals.delete') }}</QItemSection>
</QItem> </QItem>
</template> </template>
<template #body="{ entity }"> <template #body="{ entity }">

View File

@ -84,6 +84,7 @@ const columns = computed(() => [
label: t('Creation date'), label: t('Creation date'),
format: ({ created }) => toDateHourMin(created), format: ({ created }) => toDateHourMin(created),
cardVisible: true, cardVisible: true,
style: 'color: var(--vn-label-color)',
}, },
{ {
align: 'left', align: 'left',

View File

@ -2,7 +2,7 @@
import { ref, computed, onBeforeMount } from 'vue'; import { ref, computed, onBeforeMount } from 'vue';
import axios from 'axios'; import axios from 'axios';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { toDate } from 'src/filters/index'; import { dateRange, toDate } from 'src/filters/index';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import VnTable from 'components/VnTable/VnTable.vue'; import VnTable from 'components/VnTable/VnTable.vue';
@ -104,18 +104,12 @@ function getParams() {
}; };
} }
const userParams = computed(() => { const userParams = computed(() => {
const minDate = Date.vnNew(); const campaign = campaignList.value[0]?.id;
minDate.setHours(0, 0, 0, 0); const userParams = {
minDate.setMonth(minDate.getMonth() - 2); campaign,
...updateDateParams(campaign, { from: Date.vnNew(), to: Date.vnNew() }),
const maxDate = Date.vnNew();
maxDate.setHours(23, 59, 59, 59);
return {
campaign: campaignList.value[0]?.id,
from: minDate,
to: maxDate,
}; };
return userParams;
}); });
const openReportPdf = () => { const openReportPdf = () => {
openReport(`Clients/${route.params.id}/campaign-metrics-pdf`, getParams()); openReport(`Clients/${route.params.id}/campaign-metrics-pdf`, getParams());
@ -134,6 +128,23 @@ const sendCampaignMetricsEmail = ({ address }) => {
...getParams(), ...getParams(),
}); });
}; };
const updateDateParams = (value, params) => {
if (!value) {
params.from = null;
params.to = null;
return;
}
const campaign = campaignList.value.find((c) => c.id === value);
if (!campaign) return;
const { dated, previousDays, scopeDays } = campaign;
const _date = new Date(dated);
const [from, to] = dateRange(_date);
params.from = new Date(from.setDate(from.getDate() - previousDays)).toISOString();
params.to = new Date(to.setDate(to.getDate() + scopeDays)).toISOString();
return params;
};
</script> </script>
<template> <template>
@ -144,7 +155,6 @@ const sendCampaignMetricsEmail = ({ address }) => {
:order="['itemTypeFk', 'itemName', 'itemSize', 'description']" :order="['itemTypeFk', 'itemName', 'itemSize', 'description']"
:columns="columns" :columns="columns"
search-url="consumption" search-url="consumption"
:filter="filter"
:user-params="userParams" :user-params="userParams"
:default-remove="false" :default-remove="false"
:default-reset="false" :default-reset="false"
@ -201,6 +211,7 @@ const sendCampaignMetricsEmail = ({ address }) => {
class="q-px-sm q-pt-none fit" class="q-px-sm q-pt-none fit"
dense dense
option-label="code" option-label="code"
@update:model-value="(data) => updateDateParams(data, params)"
> >
<template #option="scope"> <template #option="scope">
<QItem v-bind="scope.itemProps"> <QItem v-bind="scope.itemProps">

View File

@ -173,6 +173,8 @@ en:
phone: Phone phone: Phone
email: Email email: Email
zoneFk: Zone zoneFk: Zone
socialName : Social name
name: Name
postcode: Postcode postcode: Postcode
es: es:
params: params:
@ -184,6 +186,8 @@ es:
phone: Teléfono phone: Teléfono
email: Email email: Email
zoneFk: Zona zoneFk: Zona
socialName : Razón social
name: Nombre
postcode: CP postcode: CP
FI: NIF FI: NIF
Salesperson: Comercial Salesperson: Comercial

View File

@ -240,6 +240,7 @@ es:
defaulterSinced: Desde defaulterSinced: Desde
Client: Cliente Client: Cliente
Salesperson: Comercial Salesperson: Comercial
Departments: Departamentos
Country: País Country: País
P. Method: F. Pago P. Method: F. Pago
Balance D.: Saldo V. Balance D.: Saldo V.

View File

@ -74,8 +74,8 @@ const showEntryReport = () => {
</template> </template>
<template #body="{ entity }"> <template #body="{ entity }">
<VnLv :label="t('globals.agency')" :value="entity.travel?.agency?.name" /> <VnLv :label="t('globals.agency')" :value="entity.travel?.agency?.name" />
<VnLv :label="t('shipped')" :value="toDate(entity.travel?.shipped)" /> <VnLv :label="t('globals.shipped')" :value="toDate(entity.travel?.shipped)" />
<VnLv :label="t('landed')" :value="toDate(entity.travel?.landed)" /> <VnLv :label="t('globals.landed')" :value="toDate(entity.travel?.landed)" />
<VnLv <VnLv
:label="t('globals.warehouseOut')" :label="t('globals.warehouseOut')"
:value="entity.travel?.warehouseOut?.name" :value="entity.travel?.warehouseOut?.name"

View File

@ -212,7 +212,7 @@ const fetchEntryBuys = async () => {
:label="t('entry.summary.travelAgency')" :label="t('entry.summary.travelAgency')"
:value="entry.travel.agency?.name" :value="entry.travel.agency?.name"
/> />
<VnLv :label="t('shipped')" :value="toDate(entry.travel.shipped)" /> <VnLv :label="t('globals.shipped')" :value="toDate(entry.travel.shipped)" />
<VnLv <VnLv
:label="t('globals.warehouseOut')" :label="t('globals.warehouseOut')"
:value="entry.travel.warehouseOut?.name" :value="entry.travel.warehouseOut?.name"
@ -222,7 +222,7 @@ const fetchEntryBuys = async () => {
v-model="entry.travel.isDelivered" v-model="entry.travel.isDelivered"
:disable="true" :disable="true"
/> />
<VnLv :label="t('landed')" :value="toDate(entry.travel.landed)" /> <VnLv :label="t('globals.landed')" :value="toDate(entry.travel.landed)" />
<VnLv <VnLv
:label="t('globals.warehouseIn')" :label="t('globals.warehouseIn')"
:value="entry.travel.warehouseIn?.name" :value="entry.travel.warehouseIn?.name"

View File

@ -58,7 +58,7 @@ const tagValues = ref([]);
<QItem class="q-my-md"> <QItem class="q-my-md">
<QItemSection> <QItemSection>
<VnSelect <VnSelect
:label="t('components.itemsFilterPanel.supplierFk')" :label="t('globals.params.supplierFk')"
v-model="params.supplierFk" v-model="params.supplierFk"
:options="suppliersOptions" :options="suppliersOptions"
option-value="id" option-value="id"
@ -85,7 +85,7 @@ const tagValues = ref([]);
<QItem class="q-my-md"> <QItem class="q-my-md">
<QItemSection> <QItemSection>
<VnInputDate <VnInputDate
:label="t('components.itemsFilterPanel.from')" :label="t('components.itemsFilterPanel.started')"
v-model="params.from" v-model="params.from"
is-outlined is-outlined
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -95,7 +95,7 @@ const tagValues = ref([]);
<QItem class="q-my-md"> <QItem class="q-my-md">
<QItemSection> <QItemSection>
<VnInputDate <VnInputDate
:label="t('components.itemsFilterPanel.to')" :label="t('components.itemsFilterPanel.ended')"
v-model="params.to" v-model="params.to"
is-outlined is-outlined
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -113,7 +113,7 @@ const tagValues = ref([]);
</QItemSection> </QItemSection>
<QItemSection> <QItemSection>
<QCheckbox <QCheckbox
:label="t('components.itemsFilterPanel.visible')" :label="t('globals.visible')"
v-model="params.visible" v-model="params.visible"
toggle-indeterminate toggle-indeterminate
@update:model-value="searchFn()" @update:model-value="searchFn()"

View File

@ -65,6 +65,6 @@ onMounted(async () => {
es: es:
Date: Fecha Date: Fecha
params: params:
dated: Date dated: Fecha
workerFk: Trabajador workerFk: Trabajador
</i18n> </i18n>

View File

@ -161,7 +161,7 @@ const columns = computed(() => [
<QList> <QList>
<QItem> <QItem>
<VnSelect <VnSelect
:label="t('code')" :label="t('Code')"
class="full-width" class="full-width"
v-model="props.row['intrastatFk']" v-model="props.row['intrastatFk']"
:options="intrastats" :options="intrastats"

View File

@ -346,7 +346,7 @@ watchEffect(selectedRows);
<VnSelect <VnSelect
url="InvoiceOutSerials" url="InvoiceOutSerials"
v-model="data.serial" v-model="data.serial"
:label="t('invoiceIn.serial')" :label="t('InvoiceIn.serial')"
:options="invoiceOutSerialsOptions" :options="invoiceOutSerialsOptions"
option-label="description" option-label="description"
option-value="code" option-value="code"

View File

@ -203,6 +203,12 @@ const onIntrastatCreated = (response, formData) => {
v-model="data.hasKgPrice" v-model="data.hasKgPrice"
:label="t('item.basicData.hasKgPrice')" :label="t('item.basicData.hasKgPrice')"
/> />
<QCheckbox
v-model="data.isCustomInspectionRequired"
:label="t('item.basicData.isCustomInspectionRequired')"
/>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div> <div>
<QCheckbox <QCheckbox
v-model="data.isFragile" v-model="data.isFragile"

View File

@ -10,21 +10,12 @@ import { dashIfEmpty } from 'src/filters';
import { toCurrency } from 'filters/index'; import { toCurrency } from 'filters/index';
import { useArrayData } from 'composables/useArrayData'; import { useArrayData } from 'composables/useArrayData';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import axios from 'axios';
import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue'; import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue';
const { t } = useI18n(); const { t } = useI18n();
const route = useRoute(); const route = useRoute();
const from = ref(); const from = ref();
const to = ref(); const to = ref();
const hideInventory = ref(true);
const inventorySupplierFk = ref();
async function getInventorySupplier() {
inventorySupplierFk.value = (
await axios.get(`InventoryConfigs`)
)?.data[0]?.supplierFk;
}
const exprBuilder = (param, value) => { const exprBuilder = (param, value) => {
switch (param) { switch (param) {
@ -49,10 +40,6 @@ const where = {
itemFk: route.params.id, itemFk: route.params.id,
}; };
if (hideInventory.value) {
where.supplierFk = { neq: inventorySupplierFk };
}
const arrayData = useArrayData('ItemLastEntries', { const arrayData = useArrayData('ItemLastEntries', {
url: 'Items/lastEntriesFilter', url: 'Items/lastEntriesFilter',
order: ['landed DESC', 'buyFk DESC'], order: ['landed DESC', 'buyFk DESC'],
@ -110,7 +97,7 @@ const columns = computed(() => [
format: (val) => dashIfEmpty(val), format: (val) => dashIfEmpty(val),
}, },
{ {
label: t('shelvings.packing'), label: 'Packing',
name: 'packing', name: 'packing',
field: 'packing', field: 'packing',
align: 'center', align: 'center',
@ -182,15 +169,11 @@ const updateFilter = async () => {
const userFilter = arrayData.store.userFilter.where; const userFilter = arrayData.store.userFilter.where;
userFilter.landed = filter; userFilter.landed = filter;
if (hideInventory.value) userFilter.supplierFk = { neq: inventorySupplierFk };
else delete userFilter.supplierFk;
await fetchItemLastEntries(); await fetchItemLastEntries();
}; };
onMounted(async () => { onMounted(async () => {
await getInventorySupplier();
const _from = Date.vnNew(); const _from = Date.vnNew();
_from.setDate(_from.getDate() - 75); _from.setDate(_from.getDate() - 75);
from.value = getDate(_from, 'from'); from.value = getDate(_from, 'from');
@ -200,12 +183,16 @@ onMounted(async () => {
updateFilter(); updateFilter();
watch([from, to, hideInventory], ([nFrom, nTo], [oFrom, oTo]) => { watch([from, to], ([nFrom, nTo], [oFrom, oTo]) => {
if (nFrom && nFrom != oFrom) nFrom = getDate(new Date(nFrom), 'from'); if (nFrom && nFrom != oFrom) nFrom = getDate(new Date(nFrom), 'from');
if (nTo && nTo != oTo) nTo = getDate(new Date(nTo), 'to'); if (nTo && nTo != oTo) nTo = getDate(new Date(nTo), 'to');
updateFilter(); updateFilter();
}); });
}); });
function getBadgeClass(groupingMode, expectedGrouping) {
return groupingMode === expectedGrouping ? 'accent-badge' : 'simple-badge';
}
</script> </script>
<template> <template>
<VnSubToolbar> <VnSubToolbar>
@ -224,13 +211,6 @@ onMounted(async () => {
class="q-mr-lg" class="q-mr-lg"
data-cy="to" data-cy="to"
/> />
<QCheckbox
:label="t('Hide inventory supplier')"
v-model="hideInventory"
dense
class="q-mr-lg"
data-cy="hideInventory"
/>
</template> </template>
</VnSubToolbar> </VnSubToolbar>
<QPage class="column items-center q-pa-xd"> <QPage class="column items-center q-pa-xd">
@ -249,6 +229,11 @@ onMounted(async () => {
/> />
</QTd> </QTd>
</template> </template>
<template #body-cell-warehouse="{ row }">
<QTd>
<span>{{ row.warehouse }}</span>
</QTd>
</template>
<template #body-cell-date="{ row }"> <template #body-cell-date="{ row }">
<QTd class="text-center"> <QTd class="text-center">
<VnDateBadge :date="row.landed" /> <VnDateBadge :date="row.landed" />
@ -262,32 +247,37 @@ onMounted(async () => {
</div> </div>
</QTd> </QTd>
</template> </template>
<template #body-cell-pvp="{ value }">
<QTd @click.stop class="text-center">
<span> {{ value }}</span>
<QTooltip> {{ t('lastEntries.grouping') }}/Packing </QTooltip></QTd
>
</template>
<template #body-cell-printedStickers="{ row }">
<QTd @click.stop class="text-center">
<span style="color: var(--vn-label-color)">
{{ row.printedStickers }}</span
>
</QTd>
</template>
<template #body-cell-packing="{ row }"> <template #body-cell-packing="{ row }">
<QTd @click.stop> <QTd @click.stop>
<QBadge <QBadge
class="center-content" class="center-content"
:class="getBadgeClass(row.groupingMode, 'packing')"
rounded rounded
:color="row.groupingMode == 'packing' ? 'grey-13' : 'black'"
> >
{{ dashIfEmpty(row.packing) }} {{ dashIfEmpty(row.packing) }}
<QTooltip>{{ t('lastEntries.packing') }}</QTooltip> <QTooltip>Packing</QTooltip>
</QBadge> </QBadge>
</QTd> </QTd>
</template> </template>
<template #body-cell-pvp="{ value }">
<QTd @click.stop class="text-center">
<span> {{ value }}</span>
<QTooltip>
{{ t('lastEntries.grouping') }}/{{ t('lastEntries.packing') }}
</QTooltip></QTd
>
</template>
<template #body-cell-grouping="{ row }"> <template #body-cell-grouping="{ row }">
<QTd @click.stop> <QTd @click.stop>
<QBadge <QBadge
class="center-content" class="center-content"
:class="getBadgeClass(row.groupingMode, 'grouping')"
rounded rounded
:color="row.groupingMode == 'grouping' ? 'grey-13' : 'black'"
> >
{{ dashIfEmpty(row.grouping) }} {{ dashIfEmpty(row.grouping) }}
<QTooltip>{{ t('lastEntries.grouping') }}</QTooltip> <QTooltip>{{ t('lastEntries.grouping') }}</QTooltip>
@ -315,13 +305,16 @@ onMounted(async () => {
</template> </template>
<template #body-cell-supplier="{ row }"> <template #body-cell-supplier="{ row }">
<QTd @click.stop> <QTd @click.stop>
<div class="full-width flex justify-center"> <div class="full-width flex justify-left">
<SupplierDescriptorProxy <QBadge
:id="row.supplierFk" :class="
class="q-ma-none" row.isInventorySupplier ? 'bg-vn-page' : 'transparent'
"
dense dense
/> >
<SupplierDescriptorProxy :id="row.supplierFk" />
<span class="link">{{ row.supplier }}</span> <span class="link">{{ row.supplier }}</span>
</QBadge>
</div> </div>
</QTd> </QTd>
</template> </template>
@ -349,4 +342,13 @@ onMounted(async () => {
background-color: red; background-color: red;
} }
} }
.accent-badge {
background-color: var(--vn-label-color);
color: var(--vn-text-color-contrast);
}
.simple-badge {
background-color: transparent;
color: var(--vn-text-color);
font-size: 14px;
}
</style> </style>

View File

@ -128,7 +128,7 @@ const submitTags = async (data) => {
<VnSelect <VnSelect
v-if="row.tag?.isFree === false" v-if="row.tag?.isFree === false"
:key="row.tagFk" :key="row.tagFk"
:label="t('Value')" :label="t('itemTags.value')"
v-model="row.value" v-model="row.value"
:url="`Tags/${row.tagFk}/filterValue`" :url="`Tags/${row.tagFk}/filterValue`"
option-label="value" option-label="value"

View File

@ -376,6 +376,8 @@ const columns = computed(() => [
<i18n> <i18n>
es: es:
New item: Nuevo artículo New item: Nuevo artículo
Create Item: Crear artículo
You can search by id: Puedes buscar por id
Preview: Vista previa Preview: Vista previa
Regularize stock: Regularizar stock Regularize stock: Regularizar stock
</i18n> </i18n>

View File

@ -201,6 +201,7 @@ en:
to: To to: To
mine: For me mine: For me
state: State state: State
daysOnward: Days onward
myTeam: My team myTeam: My team
dateFiltersTooltip: Cannot choose a range of dates and days onward at the same time dateFiltersTooltip: Cannot choose a range of dates and days onward at the same time
denied: Denied denied: Denied
@ -218,6 +219,7 @@ es:
to: Hasta to: Hasta
mine: Para mi mine: Para mi
state: Estado state: Estado
daysOnward: Días en adelante
myTeam: Mi equipo myTeam: Mi equipo
dateFiltersTooltip: No se puede seleccionar un rango de fechas y días en adelante a la vez dateFiltersTooltip: No se puede seleccionar un rango de fechas y días en adelante a la vez
denied: Denegada denied: Denegada

View File

@ -158,6 +158,7 @@ item:
isFragileTooltip: Is shown at website, app that this item cannot travel (wreath, palms, ...) isFragileTooltip: Is shown at website, app that this item cannot travel (wreath, palms, ...)
isPhotoRequested: Do photo isPhotoRequested: Do photo
isPhotoRequestedTooltip: This item does need a photo isPhotoRequestedTooltip: This item does need a photo
isCustomInspectionRequired: Needs physical inspection (PIF)
description: Description description: Description
fixedPrice: fixedPrice:
itemFk: Item ID itemFk: Item ID

View File

@ -160,6 +160,7 @@ item:
isFragileTooltip: Se muestra en la web, app que este artículo no puede viajar (coronas, palmas, ...) isFragileTooltip: Se muestra en la web, app que este artículo no puede viajar (coronas, palmas, ...)
isPhotoRequested: Hacer foto isPhotoRequested: Hacer foto
isPhotoRequestedTooltip: Este artículo necesita una foto isPhotoRequestedTooltip: Este artículo necesita una foto
isCustomInspectionRequired: Necesita inspección física (PIF)
description: Descripción description: Descripción
fixedPrice: fixedPrice:
itemFk: ID Artículo itemFk: ID Artículo

View File

@ -1,6 +1,6 @@
<script setup> <script setup>
import toCurrency from 'src/filters/toCurrency'; import toCurrency from 'src/filters/toCurrency';
import { computed, inject, ref } from 'vue'; import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import axios from 'axios'; import axios from 'axios';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';

View File

@ -128,6 +128,7 @@ en:
from: From from: From
to: To to: To
orderFk: Order orderFk: Order
workerFk: Worker
sourceApp: Application sourceApp: Application
myTeam: My Team myTeam: My Team
isConfirmed: Is Confirmed isConfirmed: Is Confirmed
@ -151,6 +152,7 @@ es:
from: Desde from: Desde
to: Hasta to: Hasta
orderFk: Cesta orderFk: Cesta
workerFk: Trabajador
sourceApp: Aplicación sourceApp: Aplicación
myTeam: Mi Equipo myTeam: Mi Equipo
isConfirmed: Confirmado isConfirmed: Confirmado

View File

@ -225,8 +225,8 @@ es:
params: params:
agencyModeFk: Agencia ruta agencyModeFk: Agencia ruta
m3: m3:
from: Desde From: Desde
to: Hasta To: Hasta
date: Fecha date: Fecha
agencyFk: Agencia Acuerdo agencyFk: Agencia Acuerdo
packages: Bultos packages: Bultos

View File

@ -161,6 +161,7 @@ en:
warehouseFk: Warehouse warehouseFk: Warehouse
description: Description description: Description
m3: m3:
scopeDays: Days Onward
vehicleFk: Vehicle vehicleFk: Vehicle
agencyModeFk: Agency agencyModeFk: Agency
workerFk: Worker workerFk: Worker
@ -172,6 +173,7 @@ es:
warehouseFk: Almacén warehouseFk: Almacén
description: Descripción description: Descripción
m3: m3:
scopeDays: Días en adelante
vehicleFk: Vehículo vehicleFk: Vehículo
agencyModeFk: Agencia agencyModeFk: Agencia
workerFk: Trabajador workerFk: Trabajador

View File

@ -271,6 +271,8 @@ es:
Date: Fecha Date: Fecha
Agency route: Agencia Ruta Agency route: Agencia Ruta
Agency agreement: Agencia Acuerdo Agency agreement: Agencia Acuerdo
From: Desde
To: Hasta
Packages: Bultos Packages: Bultos
Price: Precio Price: Precio
Received: Recibida Received: Recibida

View File

@ -134,6 +134,7 @@ defineProps({
<i18n> <i18n>
en: en:
params: params:
supplierFk: Supplier
search: General search search: General search
itemId: Item id itemId: Item id
buyerId: Buyer buyerId: Buyer
@ -143,6 +144,7 @@ en:
to: To to: To
es: es:
params: params:
supplierFk: Proveedor
search: Búsqueda general search: Búsqueda general
itemId: Id Artículo itemId: Id Artículo
buyerId: Comprador buyerId: Comprador

View File

@ -149,7 +149,7 @@ const getUrl = (section) => `#/supplier/${entityId.value}/${section}`;
<VnLv :label="t('supplier.summary.taxNumber')" :value="supplier.nif" /> <VnLv :label="t('supplier.summary.taxNumber')" :value="supplier.nif" />
<VnLv :label="t('globals.street')" :value="supplier.street" /> <VnLv :label="t('globals.street')" :value="supplier.street" />
<VnLv :label="t('supplier.summary.city')" :value="supplier.city" /> <VnLv :label="t('supplier.summary.city')" :value="supplier.city" />
<VnLv :label="t('globals.postCode')" :value="supplier.postCode" /> <VnLv :label="t('globals.postcode')" :value="supplier.postCode" />
<VnLv <VnLv
:label="t('supplier.summary.province')" :label="t('supplier.summary.province')"
:value="supplier.province?.name" :value="supplier.province?.name"

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { ref, computed, onMounted, watch } from 'vue'; import { ref, computed, onMounted } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';

View File

@ -59,7 +59,7 @@ const columns = computed(() => [
}, },
{ {
label: t('basicData.item'), label: t('basicData.item'),
name: 'packagingItemFk', name: 'longName',
align: 'left', align: 'left',
cardVisible: true, cardVisible: true,
columnFilter: { columnFilter: {
@ -321,12 +321,18 @@ onMounted(async () => {
" "
order="created DESC" order="created DESC"
> >
<template #column-packagingItemFk="{ row }"> <template #column-freightItemName="{ row }">
<span class="link" @click.stop> <span class="link" @click.stop>
{{ row.packagingItemFk }} {{ row.freightItemName }}
<ItemDescriptorProxy :id="row.packagingItemFk" /> <ItemDescriptorProxy :id="row.packagingItemFk" />
</span> </span>
</template> </template>
<template #column-longName="{ row }">
<span class="link" @click.stop>
{{ row.longName }}
<ItemDescriptorProxy :id="row.itemFk" />
</span>
</template>
</VnTable> </VnTable>
<QDialog ref="newTicketDialogRef" transition-show="scale" transition-hide="scale"> <QDialog ref="newTicketDialogRef" transition-show="scale" transition-hide="scale">
<ExpeditionNewTicket <ExpeditionNewTicket

View File

@ -275,4 +275,5 @@ onMounted(() => (stateStore.rightDrawer = false));
New: Nueva New: Nueva
Denied: Denegada Denied: Denegada
Accepted: Aceptada Accepted: Aceptada
Create request: Crear petición de compra
</i18n> </i18n>

View File

@ -54,6 +54,7 @@ const transfer = ref({
}); });
const tableRef = ref([]); const tableRef = ref([]);
const canProceed = ref(); const canProceed = ref();
const isLoading = ref(false);
watch( watch(
() => route.params.id, () => route.params.id,
@ -213,6 +214,9 @@ const updateQuantity = async ({ quantity, id }) => {
}; };
const addSale = async (sale) => { const addSale = async (sale) => {
if (isLoading.value) return;
isLoading.value = true;
const params = { const params = {
barcode: sale.itemFk, barcode: sale.itemFk,
quantity: sale.quantity, quantity: sale.quantity,
@ -233,7 +237,7 @@ const addSale = async (sale) => {
sale.item = newSale.item; sale.item = newSale.item;
notify('globals.dataSaved', 'positive'); notify('globals.dataSaved', 'positive');
window.location.reload(); arrayData.fetch({});
}; };
const updateConcept = async (sale) => { const updateConcept = async (sale) => {
@ -464,6 +468,7 @@ const addRow = (original = null) => {
}; };
const endNewRow = (row) => { const endNewRow = (row) => {
if (!row) return;
if (row.itemFk && row.quantity) { if (row.itemFk && row.quantity) {
row.isNew = false; row.isNew = false;
} }

View File

@ -298,14 +298,19 @@ en:
to: To to: To
salesPersonFk: Salesperson salesPersonFk: Salesperson
stateFk: State stateFk: State
groupedStates: Grouped State
refFk: Invoice Ref. refFk: Invoice Ref.
scopeDays: Days onward
nickname: Nickname
myTeam: My team myTeam: My team
pending: Pending pending: Pending
hasInvoice: Invoiced hasInvoice: Invoiced
hasRoute: Routed hasRoute: Routed
problems: With problems
provinceFk: Province provinceFk: Province
agencyModeFk: Agency agencyModeFk: Agency
warehouseFk: Warehouse warehouseFk: Warehouse
collectionFk: Collection
FREE: Free FREE: Free
ON_PREPARATION: On preparation ON_PREPARATION: On preparation
PACKED: Packed PACKED: Packed
@ -320,11 +325,19 @@ es:
to: Hasta to: Hasta
salesPersonFk: Comercial salesPersonFk: Comercial
stateFk: Estado stateFk: Estado
groupedStates: Estado agrupado
refFk: Ref. Factura refFk: Ref. Factura
scopeDays: Días en adelante
nickname: Nombre mostrado
myTeam: Mi equipo myTeam: Mi equipo
pending: Pendiente pending: Pendiente
hasInvoice: Facturado hasInvoice: Facturado
hasRoute: Enrutado hasRoute: Enrutado
problems: Con problemas
provinceFk: Provincia
agencyModeFk: Agencia
warehouseFk: Almacén
collectionFk: Colección
Customer ID: ID Cliente Customer ID: ID Cliente
Order ID: ID Pedido Order ID: ID Pedido
From: Desde From: Desde

View File

@ -11,9 +11,8 @@ import VnInput from 'src/components/common/VnInput.vue';
import EntryDescriptorProxy from '../Entry/Card/EntryDescriptorProxy.vue'; import EntryDescriptorProxy from '../Entry/Card/EntryDescriptorProxy.vue';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import { toCurrency } from 'src/filters';
import { useArrayData } from 'composables/useArrayData'; import { useArrayData } from 'composables/useArrayData';
import { toDate } from 'src/filters'; import { toDate, toCurrency } from 'src/filters';
import { usePrintService } from 'composables/usePrintService'; import { usePrintService } from 'composables/usePrintService';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import axios from 'axios'; import axios from 'axios';
@ -128,6 +127,10 @@ const tableColumnComponents = {
component: 'span', component: 'span',
attrs: {}, attrs: {},
}, },
isCustomInspectionRequired: {
component: 'span',
attrs: {},
},
}; };
const columns = computed(() => [ const columns = computed(() => [
@ -589,7 +592,16 @@ const getColor = (percentage) => {
<QBtn flat class="link" dense>{{ entry.supplierName }}</QBtn> <QBtn flat class="link" dense>{{ entry.supplierName }}</QBtn>
<SupplierDescriptorProxy :id="entry.supplierFk" /> <SupplierDescriptorProxy :id="entry.supplierFk" />
</QTd> </QTd>
<QTd /> <QTd>
<QIcon
v-if="entry.isCustomInspectionRequired"
name="warning"
color="negative"
size="xs"
:title="t('requiresInspection')"
>
</QIcon>
</QTd>
<QTd> <QTd>
<span>{{ toCurrency(entry.invoiceAmount) }}</span> <span>{{ toCurrency(entry.invoiceAmount) }}</span>
</QTd> </QTd>
@ -704,12 +716,17 @@ en:
physicKg: Phy. KG physicKg: Phy. KG
shipped: W. shipped shipped: W. shipped
landed: W. landed landed: W. landed
requiresInspection: Requires inspection
BIP: Boder Inspection Point
notes: Notes
es: es:
searchExtraCommunity: Buscar por envío extra comunitario searchExtraCommunity: Buscar por envío extra comunitario
kg: KG Bloq. kg: KG Bloq.
physicKg: KG físico physicKg: KG físico
shipped: F. envío shipped: F. envío
landed: F. llegada landed: F. llegada
notes: Notas
Open as PDF: Abrir como PDF Open as PDF: Abrir como PDF
requiresInspection: Requiere inspección
BIP: Punto de Inspección Fronteriza
</i18n> </i18n>

View File

@ -286,6 +286,7 @@ es:
Search travel: Buscar envio Search travel: Buscar envio
Clone: Clonar Clone: Clonar
Add entry: Añadir Entrada Add entry: Añadir Entrada
Create Travels: Crear envíos
</i18n> </i18n>
<style lang="scss" scoped> <style lang="scss" scoped>
.is-active { .is-active {

View File

@ -168,3 +168,8 @@ async function remove(row) {
</VnTable> </VnTable>
</QPage> </QPage>
</template> </template>
<i18n>
es:
Create new wagon: Crear nuevo vagón
</i18n>

View File

@ -59,7 +59,7 @@ const arrayData = useArrayData('ZoneEvents');
const exclusionGeoCreate = async () => { const exclusionGeoCreate = async () => {
const params = { const params = {
zoneFk: parseInt(route.params.id), zoneFk: parseInt(route.params.id),
date: dated.value, date: dated,
geoIds: tickedNodes.value, geoIds: tickedNodes.value,
}; };
await axios.post('Zones/exclusionGeo', params); await axios.post('Zones/exclusionGeo', params);

View File

@ -89,7 +89,7 @@ watch(
v-model="formData.geoFk" v-model="formData.geoFk"
url="Postcodes/location" url="Postcodes/location"
:fields="['geoFk', 'code', 'townFk', 'countryFk']" :fields="['geoFk', 'code', 'townFk', 'countryFk']"
sort-by="code, townFk" :sort-by="['code ASC']"
option-value="geoFk" option-value="geoFk"
option-label="code" option-label="code"
:filter-options="['code']" :filter-options="['code']"
@ -97,6 +97,7 @@ watch(
dense dense
outlined outlined
rounded rounded
map-key="geoFk"
> >
<template #option="{ itemProps, opt }"> <template #option="{ itemProps, opt }">
<QItem v-bind="itemProps"> <QItem v-bind="itemProps">

View File

@ -41,6 +41,7 @@ summary:
basicData: Basic data basicData: Basic data
closeHour: Close hour closeHour: Close hour
filterPanel: filterPanel:
name: Name
agencyModeFk: Agency agencyModeFk: Agency
deliveryPanel: deliveryPanel:
pickup: Pick up pickup: Pick up

View File

@ -41,6 +41,7 @@ summary:
basicData: Datos básicos basicData: Datos básicos
closeHour: Hora de cierre closeHour: Hora de cierre
filterPanel: filterPanel:
name: Nombre
agencyModeFk: Agencia agencyModeFk: Agencia
deliveryPanel: deliveryPanel:
pickup: Recogida pickup: Recogida

View File

@ -1,20 +0,0 @@
describe('ItemLastEntries', () => {
beforeEach(() => {
cy.viewport(1280, 720);
cy.login('buyer');
cy.visit('/#/item/1/last-entries');
cy.intercept('GET', /.*lastEntriesFilter/).as('item');
cy.waitForElement('tbody');
});
it('should filter by agency', () => {
cy.get('tbody > tr')
.its('length')
.then((rowCount) => {
cy.get('[data-cy="hideInventory"]').click();
cy.wait('@item');
cy.waitForElement('tbody');
cy.get('tbody > tr').should('have.length.greaterThan', rowCount);
});
});
});