Merge branch 'dev' into 8945-migrateFixedAssetsSection
gitea/salix-front/pipeline/pr-dev This commit is unstable Details

This commit is contained in:
Jose Antonio Tubau 2025-05-08 07:06:16 +00:00
commit b25d421131
154 changed files with 5113 additions and 844 deletions

View File

@ -26,7 +26,7 @@ if (branchName) {
const splitedMsg = msg.split(':'); const splitedMsg = msg.split(':');
if (splitedMsg.length > 1) { if (splitedMsg.length > 1) {
const finalMsg = splitedMsg[0] + ': ' + referenceTag + splitedMsg.slice(1).join(':'); const finalMsg = `${splitedMsg[0]}: ${referenceTag}${splitedMsg.slice(1).join(':')}`;
writeFileSync(msgPath, finalMsg); writeFileSync(msgPath, finalMsg);
} }
} }

View File

@ -1,6 +1,9 @@
import { defineConfig } from 'cypress'; import { defineConfig } from 'cypress';
let urlHost, reporter, reporterOptions, timeouts; let urlHost;
let reporter;
let reporterOptions;
let timeouts;
if (process.env.CI) { if (process.env.CI) {
urlHost = 'front'; urlHost = 'front';

View File

@ -1,4 +1,4 @@
<!DOCTYPE html> <!doctype html>
<html> <html>
<head> <head>
<title><%= productName %></title> <title><%= productName %></title>
@ -12,7 +12,12 @@
content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width<% if (ctx.mode.cordova || ctx.mode.capacitor) { %>, viewport-fit=cover<% } %>" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width<% if (ctx.mode.cordova || ctx.mode.capacitor) { %>, viewport-fit=cover<% } %>"
/> />
<link rel="icon" type="image/png" sizes="128x128" href="icons/favicon-128x128.png" /> <link
rel="icon"
type="image/png"
sizes="128x128"
href="icons/favicon-128x128.png"
/>
<link rel="icon" type="image/png" sizes="96x96" href="icons/favicon-96x96.png" /> <link rel="icon" type="image/png" sizes="96x96" href="icons/favicon-96x96.png" />
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png" /> <link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png" /> <link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png" />

View File

@ -1,4 +1,3 @@
/* eslint-disable */
// https://github.com/michael-ciniawsky/postcss-load-config // https://github.com/michael-ciniawsky/postcss-load-config
import autoprefixer from 'autoprefixer'; import autoprefixer from 'autoprefixer';

View File

@ -13,7 +13,7 @@ import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite';
import path from 'path'; import path from 'path';
const target = `http://${process.env.CI ? 'back' : 'localhost'}:3000`; const target = `http://${process.env.CI ? 'back' : 'localhost'}:3000`;
export default configure(function (/* ctx */) { export default configure((/* ctx */) => {
return { return {
eslint: { eslint: {
// fix: true, // fix: true,

View File

@ -1,8 +1,6 @@
{ {
"@quasar/testing-unit-vitest": { "@quasar/testing-unit-vitest": {
"options": [ "options": ["scripts"]
"scripts"
]
}, },
"@quasar/qcalendar": {} "@quasar/qcalendar": {}
} }

View File

@ -11,8 +11,8 @@ export default function (component, key, value) {
}; };
break; break;
case 'undefined': case 'undefined':
throw new Error('unknown prop: ' + key); throw new Error(`unknown prop: ${key}`);
default: default:
throw new Error('unhandled type: ' + typeof prop); throw new Error(`unhandled type: ${typeof prop}`);
} }
} }

View File

@ -9,7 +9,7 @@ export default {
const keyBindingMap = routes const keyBindingMap = routes
.filter((route) => route.meta.keyBinding) .filter((route) => route.meta.keyBinding)
.reduce((map, route) => { .reduce((map, route) => {
map['Key' + route.meta.keyBinding.toUpperCase()] = route.path; map[`Key${route.meta.keyBinding.toUpperCase()}`] = route.path;
return map; return map;
}, {}); }, {});

View File

@ -1,4 +1,3 @@
/* eslint-disable eslint/export */
export * from './defaults/qTable'; export * from './defaults/qTable';
export * from './defaults/qInput'; export * from './defaults/qInput';
export * from './defaults/qSelect'; export * from './defaults/qSelect';

View File

@ -24,12 +24,9 @@ export default boot(({ app }) => {
switch (response?.status) { switch (response?.status) {
case 422: case 422:
if (error.name == 'ValidationError') if (error.name == 'ValidationError')
message += message += ` "${responseError.details.context}.${Object.keys(
' "' + responseError.details.codes,
responseError.details.context + ).join(',')}"`;
'.' +
Object.keys(responseError.details.codes).join(',') +
'"';
break; break;
case 500: case 500:
message = 'errors.statusInternalServerError'; message = 'errors.statusInternalServerError';

View File

@ -25,7 +25,7 @@ const autonomiesRef = ref([]);
const onDataSaved = (dataSaved, requestResponse) => { const onDataSaved = (dataSaved, requestResponse) => {
requestResponse.autonomy = autonomiesRef.value.opts.find( requestResponse.autonomy = autonomiesRef.value.opts.find(
(autonomy) => autonomy.id == requestResponse.autonomyFk (autonomy) => autonomy.id == requestResponse.autonomyFk,
); );
emit('onDataSaved', dataSaved, requestResponse); emit('onDataSaved', dataSaved, requestResponse);
}; };

View File

@ -347,8 +347,16 @@ watch(formUrl, async () => {
<QBtnDropdown <QBtnDropdown
v-if="$props.goTo && $props.defaultSave" v-if="$props.goTo && $props.defaultSave"
@click="onSubmitAndGo" @click="onSubmitAndGo"
:label="tMobile('globals.saveAndContinue')" :label="
:title="t('globals.saveAndContinue')" tMobile('globals.saveAndContinue') +
' ' +
t('globals.' + $props.goTo.split('/').pop())
"
:title="
t('globals.saveAndContinue') +
' ' +
t('globals.' + $props.goTo.split('/').pop())
"
:disable="!hasChanges" :disable="!hasChanges"
color="primary" color="primary"
icon="save" icon="save"

View File

@ -8,10 +8,8 @@ import '@quasar/quasar-ui-qcalendar/src/QCalendarVariables.scss';
import { useWeekdayStore } from 'src/stores/useWeekdayStore'; import { useWeekdayStore } from 'src/stores/useWeekdayStore';
import useWeekdaysOrder from 'src/composables/getWeekdays'; import useWeekdaysOrder from 'src/composables/getWeekdays';
const formatDate = (dateToFormat, format = 'YYYY-MM-DD') => ( const formatDate = (dateToFormat, format = 'YYYY-MM-DD') =>
date.formatDate(dateToFormat, format) date.formatDate(dateToFormat, format);
);
const props = defineProps({ const props = defineProps({
year: { year: {
@ -67,7 +65,7 @@ const handleDateClick = (timestamp) => {
emit('onDateSelected', { emit('onDateSelected', {
date, date,
isNewMode: !event, isNewMode: !event,
event: event?.[0] || null event: event?.[0] || null,
}); });
}; };
@ -107,7 +105,11 @@ defineExpose({ getEventByTimestamp, handleDateClick });
mini-mode mini-mode
> >
<template #day="{ scope: { timestamp } }"> <template #day="{ scope: { timestamp } }">
<slot name="day" :timestamp="timestamp" :getEventAttrs="getEventAttrs"> <slot
name="day"
:timestamp="timestamp"
:getEventAttrs="getEventAttrs"
>
<QBtn <QBtn
v-if="getEventByTimestamp(timestamp)" v-if="getEventByTimestamp(timestamp)"
v-bind="{ ...getEventAttrs(timestamp) }" v-bind="{ ...getEventAttrs(timestamp) }"

View File

@ -16,7 +16,7 @@ const props = defineProps({
additionalProps: { additionalProps: {
type: Object, type: Object,
default: () => ({}), default: () => ({}),
} },
}); });
const stateStore = useStateStore(); const stateStore = useStateStore();
@ -74,7 +74,7 @@ const date = computed({
const headerTitle = computed(() => { const headerTitle = computed(() => {
if (!months.value?.length) return ''; if (!months.value?.length) return '';
const getMonthName = date => const getMonthName = (date) =>
`${weekdayStore.getLocaleMonths[date.getMonth()].locale} ${date.getFullYear()}`; `${weekdayStore.getLocaleMonths[date.getMonth()].locale} ${date.getFullYear()}`;
return `${getMonthName(months.value[0])} - ${getMonthName(months.value[months.value.length - 1])}`; return `${getMonthName(months.value[0])} - ${getMonthName(months.value[months.value.length - 1])}`;
}); });
@ -87,7 +87,7 @@ const step = (direction) => {
defineExpose({ defineExpose({
firstDay, firstDay,
lastDay lastDay,
}); });
</script> </script>
@ -119,7 +119,7 @@ defineExpose({
:year="month.getFullYear()" :year="month.getFullYear()"
:month-date="month" :month-date="month"
v-bind="additionalProps" v-bind="additionalProps"
@on-date-selected="data => emit('onDateSelected', data)" @on-date-selected="(data) => emit('onDateSelected', data)"
/> />
</div> </div>
</QCard> </QCard>

View File

@ -380,8 +380,16 @@ defineExpose({
data-cy="saveAndContinueDefaultBtn" data-cy="saveAndContinueDefaultBtn"
v-if="$props.goTo" v-if="$props.goTo"
@click="saveAndGo" @click="saveAndGo"
:label="tMobile('globals.saveAndContinue')" :label="
:title="t('globals.saveAndContinue')" tMobile('globals.saveAndContinue') +
' ' +
t('globals.' + $props.goTo.split('/').pop())
"
:title="
t('globals.saveAndContinue') +
' ' +
t('globals.' + $props.goTo.split('/').pop())
"
:disable="!hasChanges" :disable="!hasChanges"
color="primary" color="primary"
icon="save" icon="save"

View File

@ -26,7 +26,7 @@ async function redirect() {
if (route?.params?.id) if (route?.params?.id)
return (window.location.href = await getUrl( return (window.location.href = await getUrl(
`${section}/${route.params.id}/summary` `${section}/${route.params.id}/summary`,
)); ));
return (window.location.href = await getUrl(section + '/index')); return (window.location.href = await getUrl(section + '/index'));
} }

View File

@ -55,7 +55,7 @@ const refund = async () => {
(data) => ( (data) => (
(rectificativeTypeOptions = data), (rectificativeTypeOptions = data),
(invoiceParams.cplusRectificationTypeFk = data.filter( (invoiceParams.cplusRectificationTypeFk = data.filter(
(type) => type.description == 'I Por diferencias' (type) => type.description == 'I Por diferencias',
)[0].id) )[0].id)
) )
" "
@ -68,7 +68,7 @@ const refund = async () => {
(data) => ( (data) => (
(siiTypeInvoiceOutsOptions = data), (siiTypeInvoiceOutsOptions = data),
(invoiceParams.siiTypeInvoiceOutFk = data.filter( (invoiceParams.siiTypeInvoiceOutFk = data.filter(
(type) => type.code == 'R4' (type) => type.code == 'R4',
)[0].id) )[0].id)
) )
" "

View File

@ -52,7 +52,7 @@ watch(
} else filter.value.where = {}; } else filter.value.where = {};
await provincesFetchDataRef.value.fetch({}); await provincesFetchDataRef.value.fetch({});
emit('onProvinceFetched', provincesOptions.value); emit('onProvinceFetched', provincesOptions.value);
} },
); );
</script> </script>

View File

@ -6,7 +6,7 @@ export default function (initialFooter, data) {
}); });
return acc; return acc;
}, },
{ ...initialFooter } { ...initialFooter },
); );
return footer; return footer;
} }

View File

@ -241,7 +241,7 @@ describe('CrudModel', () => {
await vm.saveChanges(data); await vm.saveChanges(data);
expect(postMock).toHaveBeenCalledWith(vm.url + '/crud', data); expect(postMock).toHaveBeenCalledWith(`${vm.url}/crud`, data);
expect(vm.isLoading).toBe(false); expect(vm.isLoading).toBe(false);
expect(vm.hasChanges).toBe(false); expect(vm.hasChanges).toBe(false);
expect(vm.originalData).toEqual(JSON.parse(JSON.stringify(vm.formData))); expect(vm.originalData).toEqual(JSON.parse(JSON.stringify(vm.formData)));

View File

@ -142,14 +142,14 @@ describe('getRoutes', () => {
const fn = (props) => getRoutes(props, getMethodA, getMethodB); const fn = (props) => getRoutes(props, getMethodA, getMethodB);
it('should call getMethodB when source is card', () => { it('should call getMethodB when source is card', () => {
let props = { source: 'methodB' }; const props = { source: 'methodB' };
fn(props); fn(props);
expect(getMethodB).toHaveBeenCalled(); expect(getMethodB).toHaveBeenCalled();
expect(getMethodA).not.toHaveBeenCalled(); expect(getMethodA).not.toHaveBeenCalled();
}); });
it('should call getMethodA when source is main', () => { it('should call getMethodA when source is main', () => {
let props = { source: 'methodA' }; const props = { source: 'methodA' };
fn(props); fn(props);
expect(getMethodA).toHaveBeenCalled(); expect(getMethodA).toHaveBeenCalled();
@ -157,7 +157,7 @@ describe('getRoutes', () => {
}); });
it('should call getMethodA when source is not exists or undefined', () => { it('should call getMethodA when source is not exists or undefined', () => {
let props = { source: 'methodC' }; const props = { source: 'methodC' };
expect(() => fn(props)).toThrowError('Method not defined'); expect(() => fn(props)).toThrowError('Method not defined');
expect(getMethodA).not.toHaveBeenCalled(); expect(getMethodA).not.toHaveBeenCalled();

View File

@ -15,7 +15,7 @@ let root = ref(null);
watchEffect(() => { watchEffect(() => {
matched.value = currentRoute.value.matched.filter( matched.value = currentRoute.value.matched.filter(
(matched) => !!matched?.meta?.title || !!matched?.meta?.icon (matched) => !!matched?.meta?.title || !!matched?.meta?.icon,
); );
breadcrumbs.value.length = 0; breadcrumbs.value.length = 0;
if (!matched.value[0]) return; if (!matched.value[0]) return;

View File

@ -21,7 +21,7 @@ watch(
(newValue) => { (newValue) => {
if (!modelValue.value) return; if (!modelValue.value) return;
modelValue.value = formatLocation(newValue) ?? null; modelValue.value = formatLocation(newValue) ?? null;
} },
); );
const mixinRules = [requiredFieldRule]; const mixinRules = [requiredFieldRule];
@ -45,7 +45,7 @@ const formatLocation = (obj, properties = locationProperties) => {
}); });
const filteredParts = parts.filter( const filteredParts = parts.filter(
(part) => part !== null && part !== undefined && part !== '' (part) => part !== null && part !== undefined && part !== '',
); );
return filteredParts.join(', '); return filteredParts.join(', ');

View File

@ -2,7 +2,7 @@
import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue'; import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue';
const props = defineProps({ const props = defineProps({
scrollTarget: { type: [String, Object], default: 'window' } scrollTarget: { type: [String, Object], default: 'window' },
}); });
const scrollPosition = ref(0); const scrollPosition = ref(0);
@ -39,7 +39,7 @@ const updateScrollContainer = (container) => {
}; };
defineExpose({ defineExpose({
updateScrollContainer updateScrollContainer,
}); });
const initScrollContainer = async () => { const initScrollContainer = async () => {
@ -51,11 +51,10 @@ const initScrollContainer = async () => {
scrollContainer = window; scrollContainer = window;
} }
if (!scrollContainer) return if (!scrollContainer) return;
scrollContainer.addEventListener('scroll', onScroll); scrollContainer.addEventListener('scroll', onScroll);
}; };
onMounted(() => { onMounted(() => {
initScrollContainer(); initScrollContainer();
}); });
@ -97,4 +96,3 @@ onUnmounted(() => {
filter: brightness(0.8); filter: brightness(0.8);
} }
</style> </style>

View File

@ -35,7 +35,7 @@ describe('VnSmsDialog', () => {
expect.objectContaining({ expect.objectContaining({
message: 'You must enter a new password', message: 'You must enter a new password',
type: 'negative', type: 'negative',
}) }),
); );
}); });
@ -47,7 +47,7 @@ describe('VnSmsDialog', () => {
expect.objectContaining({ expect.objectContaining({
message: `Passwords don't match`, message: `Passwords don't match`,
type: 'negative', type: 'negative',
}) }),
); );
}); });

View File

@ -25,6 +25,9 @@ describe('VnDmsList', () => {
deleteModel: 'WorkerDms', deleteModel: 'WorkerDms',
downloadModel: 'WorkerDms', downloadModel: 'WorkerDms',
}, },
global: {
stubs: ['VnUserLink'],
},
}).vm; }).vm;
}); });

View File

@ -6,8 +6,8 @@ function buildComponent(data) {
return createWrapper(VnLocation, { return createWrapper(VnLocation, {
global: { global: {
props: { props: {
location: data location: data,
} },
}, },
}).vm; }).vm;
} }
@ -24,7 +24,7 @@ describe('formatLocation', () => {
postcode: '46680', postcode: '46680',
city: 'Algemesi', city: 'Algemesi',
province: { name: 'Valencia' }, province: { name: 'Valencia' },
country: { name: 'Spain' } country: { name: 'Spain' },
}; };
}); });
@ -47,7 +47,12 @@ describe('formatLocation', () => {
}); });
it('should return the country', () => { it('should return the country', () => {
const location = { ...locationBase, postcode: undefined, city: undefined, province: undefined }; const location = {
...locationBase,
postcode: undefined,
city: undefined,
province: undefined,
};
const vm = buildComponent(location); const vm = buildComponent(location);
expect(vm.formatLocation(location)).toEqual('Spain'); expect(vm.formatLocation(location)).toEqual('Spain');
}); });
@ -61,7 +66,7 @@ describe('showLabel', () => {
code: '46680', code: '46680',
town: 'Algemesi', town: 'Algemesi',
province: 'Valencia', province: 'Valencia',
country: 'Spain' country: 'Spain',
}; };
}); });
@ -84,7 +89,12 @@ describe('showLabel', () => {
}); });
it('should show the label with country', () => { it('should show the label with country', () => {
const location = { ...locationBase, code: undefined, town: undefined, province: undefined }; const location = {
...locationBase,
code: undefined,
town: undefined,
province: undefined,
};
const vm = buildComponent(location); const vm = buildComponent(location);
expect(vm.showLabel(location)).toEqual('Spain'); expect(vm.showLabel(location)).toEqual('Spain');
}); });

View File

@ -90,7 +90,7 @@ describe('VnLog', () => {
vm = createWrapper(VnLog, { vm = createWrapper(VnLog, {
global: { global: {
stubs: ['FetchData', 'vue-i18n'], stubs: ['FetchData', 'vue-i18n', 'VnUserLink'],
mocks: { mocks: {
fetch: vi.fn(), fetch: vi.fn(),
}, },

View File

@ -2,13 +2,14 @@ import { createWrapper } from 'app/test/vitest/helper';
import VnSmsDialog from 'components/common/VnSmsDialog.vue'; import VnSmsDialog from 'components/common/VnSmsDialog.vue';
import { vi, afterEach, beforeAll, describe, expect, it } from 'vitest'; import { vi, afterEach, beforeAll, describe, expect, it } from 'vitest';
describe('VnSmsDialog', () => { describe('VnSmsDialog', () => {
let vm; let vm;
const orderId = 1; const orderId = 1;
const shipped = new Date(); const shipped = new Date();
const phone = '012345678'; const phone = '012345678';
const promise = (response) => {return response;}; const promise = (response) => {
return response;
};
const template = 'minAmount'; const template = 'minAmount';
const locale = 'en'; const locale = 'en';
@ -17,13 +18,13 @@ describe('VnSmsDialog', () => {
propsData: { propsData: {
data: { data: {
orderId, orderId,
shipped shipped,
}, },
template, template,
locale, locale,
phone, phone,
promise promise,
} },
}).vm; }).vm;
}); });
@ -35,7 +36,9 @@ describe('VnSmsDialog', () => {
it('should update the message value with the correct template and parameters', () => { it('should update the message value with the correct template and parameters', () => {
vm.updateMessage(); vm.updateMessage();
expect(vm.message).toEqual(`A minimum amount of 50€ (VAT excluded) is required for your order ${orderId} of ${shipped} to receive it without additional shipping costs.`); expect(vm.message).toEqual(
`A minimum amount of 50€ (VAT excluded) is required for your order ${orderId} of ${shipped} to receive it without additional shipping costs.`,
);
}); });
}); });
@ -47,7 +50,7 @@ describe('VnSmsDialog', () => {
orderId, orderId,
shipped, shipped,
destination: phone, destination: phone,
message: vm.message message: vm.message,
}; };
await vm.send(); await vm.send();

View File

@ -17,7 +17,7 @@ const token = getTokenMultimedia();
const { t } = useI18n(); const { t } = useI18n();
const src = computed( const src = computed(
() => `/api/Images/user/160x160/${$props.workerId}/download?access_token=${token}` () => `/api/Images/user/160x160/${$props.workerId}/download?access_token=${token}`,
); );
const title = computed(() => $props.title?.toUpperCase() || t('globals.system')); const title = computed(() => $props.title?.toUpperCase() || t('globals.system'));
const showLetter = ref(false); const showLetter = ref(false);

View File

@ -13,7 +13,7 @@ const src = computed({
get() { get() {
return new URL( return new URL(
`../../assets/${$props.logo}${Dark.isActive ? '_dark' : ''}.svg`, `../../assets/${$props.logo}${Dark.isActive ? '_dark' : ''}.svg`,
import.meta.url import.meta.url,
).href; ).href;
}, },
}); });

View File

@ -1,10 +1,11 @@
<script setup> <script setup>
import axios from 'axios'; import axios from 'axios';
import { ref, reactive, useAttrs, computed } from 'vue'; import { ref, reactive, useAttrs, computed, onMounted, nextTick } from 'vue';
import { onBeforeRouteLeave } 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 { useStateStore } from 'stores/useStateStore';
import { tMobile } from 'src/composables/tMobile';
import { toDateHourMin } from 'src/filters'; import { toDateHourMin } from 'src/filters';
import VnPaginate from 'components/ui/VnPaginate.vue'; import VnPaginate from 'components/ui/VnPaginate.vue';
@ -33,18 +34,24 @@ const $props = defineProps({
addNote: { type: Boolean, default: false }, addNote: { type: Boolean, default: false },
selectType: { type: Boolean, default: false }, selectType: { type: Boolean, default: false },
justInput: { type: Boolean, default: false }, justInput: { type: Boolean, default: false },
goTo: { type: String, default: '' },
}); });
const { t } = useI18n(); const { t } = useI18n();
const quasar = useQuasar(); const quasar = useQuasar();
const stateStore = useStateStore();
const router = useRouter();
const route = useRoute();
const componentIsRendered = ref(false);
const newNote = reactive({ text: null, observationTypeFk: null }); const newNote = reactive({ text: null, observationTypeFk: null });
const observationTypes = ref([]); const observationTypes = ref([]);
const vnPaginateRef = ref(); const vnPaginateRef = ref();
const defaultObservationType = computed(() => const defaultObservationType = computed(
observationTypes.value.find(ot => ot.code === 'salesPerson')?.id () => observationTypes.value.find((ot) => ot.code === 'salesPerson')?.id,
); );
let savedNote = false;
let originalText; let originalText;
function handleClick(e) { function handleClick(e) {
@ -68,6 +75,7 @@ async function insert() {
}; };
await axios.post($props.url, newBody); await axios.post($props.url, newBody);
await vnPaginateRef.value.fetch(); await vnPaginateRef.value.fetch();
savedNote = true;
} }
function confirmAndUpdate() { function confirmAndUpdate() {
@ -129,8 +137,44 @@ const handleObservationTypes = (data) => {
} }
}; };
onMounted(() => {
nextTick(() => (componentIsRendered.value = true));
});
async function saveAndGo() {
savedNote = false;
await insert();
await savedNote;
router.push({ path: $props.goTo });
}
</script> </script>
<template> <template>
<Teleport
to="#st-actions"
v-if="
stateStore?.isSubToolbarShown() &&
componentIsRendered &&
$props.goTo &&
!route.path.includes('summary')
"
>
<QBtn
:label="
tMobile('globals.saveAndContinue') +
' ' +
t('globals.' + $props.goTo.split('/').pop())
"
:title="
t('globals.saveAndContinue') +
' ' +
t('globals.' + $props.goTo.split('/').pop())
"
color="primary"
icon="save"
@click="saveAndGo"
data-cy="saveContinueNoteButton"
/>
</Teleport>
<FetchData <FetchData
v-if="selectType" v-if="selectType"
url="ObservationTypes" url="ObservationTypes"
@ -176,7 +220,7 @@ const handleObservationTypes = (data) => {
:required="'required' in originalAttrs" :required="'required' in originalAttrs"
clearable clearable
> >
<template #append> <template #append v-if="!$props.goTo">
<QBtn <QBtn
:title="t('Save (Enter)')" :title="t('Save (Enter)')"
icon="save" icon="save"
@ -205,9 +249,7 @@ const handleObservationTypes = (data) => {
class="show" class="show"
v-bind="$attrs" v-bind="$attrs"
:search-url="false" :search-url="false"
@on-fetch=" @on-fetch="newNote.text = ''"
newNote.text = '';
"
> >
<template #body="{ rows }"> <template #body="{ rows }">
<TransitionGroup name="list" tag="div" class="column items-center full-width"> <TransitionGroup name="list" tag="div" class="column items-center full-width">

View File

@ -87,7 +87,7 @@ function formatNumber(number) {
<QItemLabel caption>{{ <QItemLabel caption>{{
date.formatDate( date.formatDate(
row.sms.created, row.sms.created,
'YYYY-MM-DD HH:mm:ss' 'YYYY-MM-DD HH:mm:ss',
) )
}}</QItemLabel> }}</QItemLabel>
<QItemLabel class="row center"> <QItemLabel class="row center">

View File

@ -37,8 +37,7 @@ onBeforeUnmount(() => stateStore.toggleSubToolbar() && hasSubToolbar);
class="justify-end sticky" class="justify-end sticky"
> >
<slot name="st-data"> <slot name="st-data">
<div id="st-data" :class="{ 'full-width': !actionsChildCount() }"> <div id="st-data" :class="{ 'full-width': !actionsChildCount() }"></div>
</div>
</slot> </slot>
<QSpace /> <QSpace />
<slot name="st-actions"> <slot name="st-actions">

View File

@ -1,18 +1,39 @@
<script setup> <script setup>
import AccountDescriptorProxy from 'src/pages/Account/Card/AccountDescriptorProxy.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import { ref, onMounted } from 'vue';
defineProps({ import axios from 'axios';
const $props = defineProps({
name: { type: String, default: null }, name: { type: String, default: null },
tag: { type: String, default: null }, tag: { type: String, default: null },
workerId: { type: Number, default: null }, workerId: { type: Number, default: null },
defaultName: { type: Boolean, default: false }, defaultName: { type: Boolean, default: false },
}); });
const isWorker = ref(false);
onMounted(async () => {
if (!$props.workerId) return;
try {
const {
data: { exists },
} = await axios(`/Workers/${$props.workerId}/exists`);
isWorker.value = exists;
} catch (error) {
if (error.status === 403) return;
throw error;
}
});
</script> </script>
<template> <template>
<slot name="link"> <slot name="link">
<span :class="{ link: workerId }"> <span :class="{ link: workerId }">
{{ defaultName ? name ?? $t('globals.system') : name }} {{ defaultName ? (name ?? $t('globals.system')) : name }}
</span> </span>
</slot> </slot>
<WorkerDescriptorProxy v-if="workerId" :id="workerId" /> <WorkerDescriptorProxy
v-if="isWorker"
:id="workerId"
@on-fetch="(data) => (isWorker = data?.workerId !== undefined)"
/>
<AccountDescriptorProxy v-else :id="workerId" />
</template> </template>

View File

@ -12,12 +12,12 @@ function generateWrapper(storage = 'images') {
id: 123, id: 123,
zoomResolution: '400x400', zoomResolution: '400x400',
storage, storage,
} },
}); });
wrapper = wrapper.wrapper; wrapper = wrapper.wrapper;
vm = wrapper.vm; vm = wrapper.vm;
vm.timeStamp = 'timestamp'; vm.timeStamp = 'timestamp';
}; }
vi.mock('src/composables/useSession', () => ({ vi.mock('src/composables/useSession', () => ({
useSession: () => ({ useSession: () => ({
@ -31,7 +31,6 @@ vi.mock('src/composables/useRole', () => ({
}), }),
})); }));
describe('VnImg', () => { describe('VnImg', () => {
beforeEach(() => { beforeEach(() => {
isEmployeeMock.mockReset(); isEmployeeMock.mockReset();
@ -63,7 +62,9 @@ describe('VnImg', () => {
generateWrapper(); generateWrapper();
await vm.$nextTick(); await vm.$nextTick();
const url = vm.getUrl(); const url = vm.getUrl();
expect(url).toBe('/api/images/catalog/200x200/123/download?access_token=token&timestamp'); expect(url).toBe(
'/api/images/catalog/200x200/123/download?access_token=token&timestamp',
);
}); });
it('should return /api/{storage}/{collection}/{curResolution}/{id}/download?access_token={token}&{timeStamp} when zoom is true and role is employee and storage is not dms', async () => { it('should return /api/{storage}/{collection}/{curResolution}/{id}/download?access_token={token}&{timeStamp} when zoom is true and role is employee and storage is not dms', async () => {
@ -71,7 +72,9 @@ describe('VnImg', () => {
generateWrapper(); generateWrapper();
await vm.$nextTick(); await vm.$nextTick();
const url = vm.getUrl(true); const url = vm.getUrl(true);
expect(url).toBe('/api/images/catalog/400x400/123/download?access_token=token&timestamp'); expect(url).toBe(
'/api/images/catalog/400x400/123/download?access_token=token&timestamp',
);
}); });
}); });

View File

@ -53,26 +53,26 @@ describe('useAcl', () => {
expect( expect(
acl.hasAny([ acl.hasAny([
{ model: 'Worker', props: 'updateAttributes', accessType: 'WRITE' }, { model: 'Worker', props: 'updateAttributes', accessType: 'WRITE' },
]) ]),
).toBeFalsy(); ).toBeFalsy();
}); });
it('should return false if no roles matched', async () => { it('should return false if no roles matched', async () => {
expect( expect(
acl.hasAny([{ model: 'Worker', props: 'holidays', accessType: 'READ' }]) acl.hasAny([{ model: 'Worker', props: 'holidays', accessType: 'READ' }]),
).toBeTruthy(); ).toBeTruthy();
}); });
describe('*', () => { describe('*', () => {
it('should return true if an acl matched', async () => { it('should return true if an acl matched', async () => {
expect( expect(
acl.hasAny([{ model: 'Address', props: '*', accessType: 'WRITE' }]) acl.hasAny([{ model: 'Address', props: '*', accessType: 'WRITE' }]),
).toBeTruthy(); ).toBeTruthy();
}); });
it('should return false if no acls matched', async () => { it('should return false if no acls matched', async () => {
expect( expect(
acl.hasAny([{ model: 'Worker', props: '*', accessType: 'READ' }]) acl.hasAny([{ model: 'Worker', props: '*', accessType: 'READ' }]),
).toBeFalsy(); ).toBeFalsy();
}); });
}); });
@ -80,13 +80,15 @@ describe('useAcl', () => {
describe('$authenticated', () => { describe('$authenticated', () => {
it('should return false if no acls matched', async () => { it('should return false if no acls matched', async () => {
expect( expect(
acl.hasAny([{ model: 'Url', props: 'getByUser', accessType: '*' }]) acl.hasAny([{ model: 'Url', props: 'getByUser', accessType: '*' }]),
).toBeFalsy(); ).toBeFalsy();
}); });
it('should return true if an acl matched', async () => { it('should return true if an acl matched', async () => {
expect( expect(
acl.hasAny([{ model: 'Url', props: 'getByUser', accessType: 'READ' }]) acl.hasAny([
{ model: 'Url', props: 'getByUser', accessType: 'READ' },
]),
).toBeTruthy(); ).toBeTruthy();
}); });
}); });
@ -96,7 +98,7 @@ describe('useAcl', () => {
expect( expect(
acl.hasAny([ acl.hasAny([
{ model: 'TpvTransaction', props: 'start', accessType: 'READ' }, { model: 'TpvTransaction', props: 'start', accessType: 'READ' },
]) ]),
).toBeFalsy(); ).toBeFalsy();
}); });
@ -104,7 +106,7 @@ describe('useAcl', () => {
expect( expect(
acl.hasAny([ acl.hasAny([
{ model: 'TpvTransaction', props: 'start', accessType: 'WRITE' }, { model: 'TpvTransaction', props: 'start', accessType: 'WRITE' },
]) ]),
).toBeTruthy(); ).toBeTruthy();
}); });
}); });

View File

@ -18,7 +18,7 @@ export async function downloadFile(id, model = 'dms', urlPath = '/downloadFile',
export async function downloadDocuware(url, params) { export async function downloadDocuware(url, params) {
const appUrl = await getAppUrl(); const appUrl = await getAppUrl();
const response = await axios.get(`${appUrl}/api/` + url, { const response = await axios.get(`${appUrl}/api/${url}`, {
responseType: 'blob', responseType: 'blob',
params, params,
}); });

View File

@ -20,5 +20,5 @@ export function getColAlign(col) {
if (/^is[A-Z]/.test(col.name) || /^has[A-Z]/.test(col.name)) align = 'center'; if (/^is[A-Z]/.test(col.name) || /^has[A-Z]/.test(col.name)) align = 'center';
return 'text-' + (align ?? 'center'); return `text-${align ?? 'center'}`;
} }

View File

@ -1,10 +1,10 @@
export function getDateQBadgeColor(date) { export function getDateQBadgeColor(date) {
let today = Date.vnNew(); const today = Date.vnNew();
today.setHours(0, 0, 0, 0); today.setHours(0, 0, 0, 0);
let timeTicket = new Date(date); const timeTicket = new Date(date);
timeTicket.setHours(0, 0, 0, 0); timeTicket.setHours(0, 0, 0, 0);
let comparation = today - timeTicket; const comparation = today - timeTicket;
if (comparation == 0) return 'warning'; if (comparation == 0) return 'warning';
if (comparation < 0) return 'success'; if (comparation < 0) return 'success';

View File

@ -2,9 +2,8 @@ import { ref } from 'vue';
import moment from 'moment'; import moment from 'moment';
export default function useWeekdaysOrder() { export default function useWeekdaysOrder() {
const firstDay = moment().weekday(1).day(); const firstDay = moment().weekday(1).day();
const weekdays = [...Array(7).keys()].map(i => (i + firstDay) % 7); const weekdays = [...Array(7).keys()].map((i) => (i + firstDay) % 7);
return ref(weekdays); return ref(weekdays);
} }

View File

@ -7,7 +7,7 @@ export async function beforeSave(data, getChanges, modelOrigin) {
const patchPromises = []; const patchPromises = [];
for (const change of changes) { for (const change of changes) {
let patchData = {}; const patchData = {};
if ('hasMinPrice' in change.data) { if ('hasMinPrice' in change.data) {
patchData.hasMinPrice = change.data?.hasMinPrice; patchData.hasMinPrice = change.data?.hasMinPrice;

View File

@ -3,5 +3,4 @@ import { useQuasar } from 'quasar';
export default function () { export default function () {
const quasar = useQuasar(); const quasar = useQuasar();
return quasar.screen.gt.xs ? 'q-pa-md' : 'q-pa-xs'; return quasar.screen.gt.xs ? 'q-pa-md' : 'q-pa-xs';
} }

View File

@ -6,7 +6,7 @@ export function djb2a(string) {
} }
export function useColor(value) { export function useColor(value) {
return '#' + colors[djb2a(value || '') % colors.length]; return `#${colors[djb2a(value || '') % colors.length]}`;
} }
const colors = [ const colors = [

View File

@ -15,18 +15,16 @@ export function usePrintService() {
message: t('globals.notificationSent'), message: t('globals.notificationSent'),
type: 'positive', type: 'positive',
icon: 'check', icon: 'check',
}) }),
); );
} }
function openReport(path, params, isNewTab = '_self') { function openReport(path, params, isNewTab = '_self') {
if (typeof params === 'string') params = JSON.parse(params); if (typeof params === 'string') params = JSON.parse(params);
params = Object.assign( params = {
{
access_token: getTokenMultimedia(), access_token: getTokenMultimedia(),
}, ...params,
params };
);
const query = new URLSearchParams(params).toString(); const query = new URLSearchParams(params).toString();
window.open(`api/${path}?${query}`, isNewTab); window.open(`api/${path}?${query}`, isNewTab);

View File

@ -17,7 +17,7 @@ export function useSession() {
let intervalId = null; let intervalId = null;
function setSession(data) { function setSession(data) {
let keepLogin = data.keepLogin; const keepLogin = data.keepLogin;
const storage = keepLogin ? localStorage : sessionStorage; const storage = keepLogin ? localStorage : sessionStorage;
storage.setItem(TOKEN, data.token); storage.setItem(TOKEN, data.token);
storage.setItem(TOKEN_MULTIMEDIA, data.tokenMultimedia); storage.setItem(TOKEN_MULTIMEDIA, data.tokenMultimedia);

View File

@ -8,7 +8,7 @@ export function useVnConfirm() {
message, message,
promise, promise,
successFn, successFn,
customHTML = {} customHTML = {},
) => { ) => {
const { component, props } = customHTML; const { component, props } = customHTML;
Dialog.create({ Dialog.create({
@ -19,7 +19,7 @@ export function useVnConfirm() {
message: message, message: message,
promise: promise, promise: promise,
}, },
{ customHTML: () => h(component, props) } { customHTML: () => h(component, props) },
), ),
}).onOk(async () => { }).onOk(async () => {
if (successFn) successFn(); if (successFn) successFn();

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,8 @@
@font-face { @font-face {
font-family: 'icon'; font-family: 'icon';
src: url('fonts/icon.eot?uocffs'); src: url('fonts/icon.eot?uocffs');
src: url('fonts/icon.eot?uocffs#iefix') format('embedded-opentype'), src:
url('fonts/icon.eot?uocffs#iefix') format('embedded-opentype'),
url('fonts/icon.ttf?uocffs') format('truetype'), url('fonts/icon.ttf?uocffs') format('truetype'),
url('fonts/icon.woff?uocffs') format('woff'), url('fonts/icon.woff?uocffs') format('woff'),
url('fonts/icon.svg?uocffs#icon') format('svg'); url('fonts/icon.svg?uocffs#icon') format('svg');
@ -10,7 +11,8 @@
font-display: block; font-display: block;
} }
[class^="icon-"], [class*=" icon-"] { [class^='icon-'],
[class*=' icon-'] {
/* use !important to prevent issues with browser extensions that change fonts */ /* use !important to prevent issues with browser extensions that change fonts */
font-family: 'icon' !important; font-family: 'icon' !important;
speak: never; speak: never;
@ -26,428 +28,428 @@
} }
.icon-inactive-car:before { .icon-inactive-car:before {
content: "\e978"; content: '\e978';
} }
.icon-hasItemLost:before { .icon-hasItemLost:before {
content: "\e957"; content: '\e957';
} }
.icon-hasItemDelay:before { .icon-hasItemDelay:before {
content: "\e96d"; content: '\e96d';
} }
.icon-add_entries:before { .icon-add_entries:before {
content: "\e953"; content: '\e953';
} }
.icon-100:before { .icon-100:before {
content: "\e901"; content: '\e901';
} }
.icon-Client_unpaid:before { .icon-Client_unpaid:before {
content: "\e98c"; content: '\e98c';
} }
.icon-History:before { .icon-History:before {
content: "\e902"; content: '\e902';
} }
.icon-Person:before { .icon-Person:before {
content: "\e903"; content: '\e903';
} }
.icon-accessory:before { .icon-accessory:before {
content: "\e904"; content: '\e904';
} }
.icon-account:before { .icon-account:before {
content: "\e905"; content: '\e905';
} }
.icon-actions:before { .icon-actions:before {
content: "\e907"; content: '\e907';
} }
.icon-addperson:before { .icon-addperson:before {
content: "\e908"; content: '\e908';
} }
.icon-agencia_tributaria:before { .icon-agencia_tributaria:before {
content: "\e948"; content: '\e948';
} }
.icon-agency:before { .icon-agency:before {
content: "\e92a"; content: '\e92a';
} }
.icon-agency-term:before { .icon-agency-term:before {
content: "\e909"; content: '\e909';
} }
.icon-albaran:before { .icon-albaran:before {
content: "\e92c"; content: '\e92c';
} }
.icon-anonymous:before { .icon-anonymous:before {
content: "\e90b"; content: '\e90b';
} }
.icon-apps:before { .icon-apps:before {
content: "\e90c"; content: '\e90c';
} }
.icon-artificial:before { .icon-artificial:before {
content: "\e90d"; content: '\e90d';
} }
.icon-attach:before { .icon-attach:before {
content: "\e90e"; content: '\e90e';
} }
.icon-barcode:before { .icon-barcode:before {
content: "\e90f"; content: '\e90f';
} }
.icon-basket:before { .icon-basket:before {
content: "\e910"; content: '\e910';
} }
.icon-basketadd:before { .icon-basketadd:before {
content: "\e911"; content: '\e911';
} }
.icon-bin:before { .icon-bin:before {
content: "\e913"; content: '\e913';
} }
.icon-botanical:before { .icon-botanical:before {
content: "\e914"; content: '\e914';
} }
.icon-bucket:before { .icon-bucket:before {
content: "\e915"; content: '\e915';
} }
.icon-buscaman:before { .icon-buscaman:before {
content: "\e916"; content: '\e916';
} }
.icon-buyrequest:before { .icon-buyrequest:before {
content: "\e917"; content: '\e917';
} }
.icon-calc_volum .path1:before { .icon-calc_volum .path1:before {
content: "\e918"; content: '\e918';
color: rgb(0, 0, 0); color: rgb(0, 0, 0);
} }
.icon-calc_volum .path2:before { .icon-calc_volum .path2:before {
content: "\e919"; content: '\e919';
margin-left: -1em; margin-left: -1em;
color: rgb(0, 0, 0); color: rgb(0, 0, 0);
} }
.icon-calc_volum .path3:before { .icon-calc_volum .path3:before {
content: "\e91c"; content: '\e91c';
margin-left: -1em; margin-left: -1em;
color: rgb(0, 0, 0); color: rgb(0, 0, 0);
} }
.icon-calc_volum .path4:before { .icon-calc_volum .path4:before {
content: "\e91d"; content: '\e91d';
margin-left: -1em; margin-left: -1em;
color: rgb(0, 0, 0); color: rgb(0, 0, 0);
} }
.icon-calc_volum .path5:before { .icon-calc_volum .path5:before {
content: "\e91e"; content: '\e91e';
margin-left: -1em; margin-left: -1em;
color: rgb(0, 0, 0); color: rgb(0, 0, 0);
} }
.icon-calc_volum .path6:before { .icon-calc_volum .path6:before {
content: "\e91f"; content: '\e91f';
margin-left: -1em; margin-left: -1em;
color: rgb(255, 255, 255); color: rgb(255, 255, 255);
} }
.icon-calendar:before { .icon-calendar:before {
content: "\e920"; content: '\e920';
} }
.icon-catalog:before { .icon-catalog:before {
content: "\e921"; content: '\e921';
} }
.icon-claims:before { .icon-claims:before {
content: "\e922"; content: '\e922';
} }
.icon-client:before { .icon-client:before {
content: "\e923"; content: '\e923';
} }
.icon-clone:before { .icon-clone:before {
content: "\e924"; content: '\e924';
} }
.icon-columnadd:before { .icon-columnadd:before {
content: "\e925"; content: '\e925';
} }
.icon-columndelete:before { .icon-columndelete:before {
content: "\e926"; content: '\e926';
} }
.icon-components:before { .icon-components:before {
content: "\e927"; content: '\e927';
} }
.icon-consignatarios:before { .icon-consignatarios:before {
content: "\e928"; content: '\e928';
} }
.icon-control:before { .icon-control:before {
content: "\e929"; content: '\e929';
} }
.icon-credit:before { .icon-credit:before {
content: "\e92b"; content: '\e92b';
} }
.icon-defaulter:before { .icon-defaulter:before {
content: "\e92d"; content: '\e92d';
} }
.icon-deletedTicket:before { .icon-deletedTicket:before {
content: "\e92e"; content: '\e92e';
} }
.icon-deleteline:before { .icon-deleteline:before {
content: "\e92f"; content: '\e92f';
} }
.icon-delivery:before { .icon-delivery:before {
content: "\e930"; content: '\e930';
} }
.icon-deliveryprices:before { .icon-deliveryprices:before {
content: "\e932"; content: '\e932';
} }
.icon-details:before { .icon-details:before {
content: "\e933"; content: '\e933';
} }
.icon-dfiscales:before { .icon-dfiscales:before {
content: "\e934"; content: '\e934';
} }
.icon-disabled:before { .icon-disabled:before {
content: "\e935"; content: '\e935';
} }
.icon-doc:before { .icon-doc:before {
content: "\e936"; content: '\e936';
} }
.icon-entry:before { .icon-entry:before {
content: "\e937"; content: '\e937';
} }
.icon-entry_lastbuys:before { .icon-entry_lastbuys:before {
content: "\e91a"; content: '\e91a';
} }
.icon-exit:before { .icon-exit:before {
content: "\e938"; content: '\e938';
} }
.icon-eye:before { .icon-eye:before {
content: "\e939"; content: '\e939';
} }
.icon-fixedPrice:before { .icon-fixedPrice:before {
content: "\e93a"; content: '\e93a';
} }
.icon-flower:before { .icon-flower:before {
content: "\e93b"; content: '\e93b';
} }
.icon-frozen:before { .icon-frozen:before {
content: "\e93c"; content: '\e93c';
} }
.icon-fruit:before { .icon-fruit:before {
content: "\e93d"; content: '\e93d';
} }
.icon-funeral:before { .icon-funeral:before {
content: "\e93e"; content: '\e93e';
} }
.icon-grafana:before { .icon-grafana:before {
content: "\e906"; content: '\e906';
} }
.icon-greenery:before { .icon-greenery:before {
content: "\e93f"; content: '\e93f';
} }
.icon-greuge:before { .icon-greuge:before {
content: "\e940"; content: '\e940';
} }
.icon-grid:before { .icon-grid:before {
content: "\e941"; content: '\e941';
} }
.icon-handmade:before { .icon-handmade:before {
content: "\e942"; content: '\e942';
} }
.icon-handmadeArtificial:before { .icon-handmadeArtificial:before {
content: "\e943"; content: '\e943';
} }
.icon-headercol:before { .icon-headercol:before {
content: "\e945"; content: '\e945';
} }
.icon-info:before { .icon-info:before {
content: "\e946"; content: '\e946';
} }
.icon-inventory:before { .icon-inventory:before {
content: "\e947"; content: '\e947';
} }
.icon-invoice:before { .icon-invoice:before {
content: "\e968"; content: '\e968';
color: #5f5f5f; color: #5f5f5f;
} }
.icon-invoice-in:before { .icon-invoice-in:before {
content: "\e949"; content: '\e949';
} }
.icon-invoice-in-create:before { .icon-invoice-in-create:before {
content: "\e94a"; content: '\e94a';
} }
.icon-invoice-out:before { .icon-invoice-out:before {
content: "\e94b"; content: '\e94b';
} }
.icon-isTooLittle:before { .icon-isTooLittle:before {
content: "\e94c"; content: '\e94c';
} }
.icon-item:before { .icon-item:before {
content: "\e94d"; content: '\e94d';
} }
.icon-languaje:before { .icon-languaje:before {
content: "\e970"; content: '\e970';
} }
.icon-lines:before { .icon-lines:before {
content: "\e94e"; content: '\e94e';
} }
.icon-linesprepaired:before { .icon-linesprepaired:before {
content: "\e94f"; content: '\e94f';
} }
.icon-link-to-corrected:before { .icon-link-to-corrected:before {
content: "\e931"; content: '\e931';
} }
.icon-link-to-correcting:before { .icon-link-to-correcting:before {
content: "\e944"; content: '\e944';
} }
.icon-logout:before { .icon-logout:before {
content: "\e973"; content: '\e973';
} }
.icon-mana:before { .icon-mana:before {
content: "\e950"; content: '\e950';
} }
.icon-mandatory:before { .icon-mandatory:before {
content: "\e951"; content: '\e951';
} }
.icon-net:before { .icon-net:before {
content: "\e952"; content: '\e952';
} }
.icon-newalbaran:before { .icon-newalbaran:before {
content: "\e954"; content: '\e954';
} }
.icon-niche:before { .icon-niche:before {
content: "\e955"; content: '\e955';
} }
.icon-no036:before { .icon-no036:before {
content: "\e956"; content: '\e956';
} }
.icon-noPayMethod:before { .icon-noPayMethod:before {
content: "\e958"; content: '\e958';
} }
.icon-notes:before { .icon-notes:before {
content: "\e959"; content: '\e959';
} }
.icon-noweb:before { .icon-noweb:before {
content: "\e95a"; content: '\e95a';
} }
.icon-onlinepayment:before { .icon-onlinepayment:before {
content: "\e95b"; content: '\e95b';
} }
.icon-package:before { .icon-package:before {
content: "\e95c"; content: '\e95c';
} }
.icon-payment:before { .icon-payment:before {
content: "\e95d"; content: '\e95d';
} }
.icon-pbx:before { .icon-pbx:before {
content: "\e95e"; content: '\e95e';
} }
.icon-pets:before { .icon-pets:before {
content: "\e95f"; content: '\e95f';
} }
.icon-photo:before { .icon-photo:before {
content: "\e960"; content: '\e960';
} }
.icon-plant:before { .icon-plant:before {
content: "\e961"; content: '\e961';
} }
.icon-polizon:before { .icon-polizon:before {
content: "\e962"; content: '\e962';
} }
.icon-preserved:before { .icon-preserved:before {
content: "\e963"; content: '\e963';
} }
.icon-recovery:before { .icon-recovery:before {
content: "\e964"; content: '\e964';
} }
.icon-regentry:before { .icon-regentry:before {
content: "\e965"; content: '\e965';
} }
.icon-reserva:before { .icon-reserva:before {
content: "\e966"; content: '\e966';
} }
.icon-revision:before { .icon-revision:before {
content: "\e967"; content: '\e967';
} }
.icon-risk:before { .icon-risk:before {
content: "\e969"; content: '\e969';
} }
.icon-saysimple:before { .icon-saysimple:before {
content: "\e912"; content: '\e912';
} }
.icon-services:before { .icon-services:before {
content: "\e96a"; content: '\e96a';
} }
.icon-settings:before { .icon-settings:before {
content: "\e96b"; content: '\e96b';
} }
.icon-shipment:before { .icon-shipment:before {
content: "\e96c"; content: '\e96c';
} }
.icon-sign:before { .icon-sign:before {
content: "\e90a"; content: '\e90a';
} }
.icon-sms:before { .icon-sms:before {
content: "\e96e"; content: '\e96e';
} }
.icon-solclaim:before { .icon-solclaim:before {
content: "\e96f"; content: '\e96f';
} }
.icon-solunion:before { .icon-solunion:before {
content: "\e971"; content: '\e971';
} }
.icon-splitline:before { .icon-splitline:before {
content: "\e972"; content: '\e972';
} }
.icon-splur:before { .icon-splur:before {
content: "\e974"; content: '\e974';
} }
.icon-stowaway:before { .icon-stowaway:before {
content: "\e975"; content: '\e975';
} }
.icon-supplier:before { .icon-supplier:before {
content: "\e976"; content: '\e976';
} }
.icon-supplierfalse:before { .icon-supplierfalse:before {
content: "\e977"; content: '\e977';
} }
.icon-tags:before { .icon-tags:before {
content: "\e979"; content: '\e979';
} }
.icon-tax:before { .icon-tax:before {
content: "\e97a"; content: '\e97a';
} }
.icon-thermometer:before { .icon-thermometer:before {
content: "\e97b"; content: '\e97b';
} }
.icon-ticket:before { .icon-ticket:before {
content: "\e97c"; content: '\e97c';
} }
.icon-ticketAdd:before { .icon-ticketAdd:before {
content: "\e97e"; content: '\e97e';
} }
.icon-traceability:before { .icon-traceability:before {
content: "\e97f"; content: '\e97f';
} }
.icon-transaction:before { .icon-transaction:before {
content: "\e91b"; content: '\e91b';
} }
.icon-treatments:before { .icon-treatments:before {
content: "\e980"; content: '\e980';
} }
.icon-trolley:before { .icon-trolley:before {
content: "\e900"; content: '\e900';
} }
.icon-troncales:before { .icon-troncales:before {
content: "\e982"; content: '\e982';
} }
.icon-unavailable:before { .icon-unavailable:before {
content: "\e983"; content: '\e983';
} }
.icon-visible_columns:before { .icon-visible_columns:before {
content: "\e984"; content: '\e984';
} }
.icon-volume:before { .icon-volume:before {
content: "\e985"; content: '\e985';
} }
.icon-wand:before { .icon-wand:before {
content: "\e986"; content: '\e986';
} }
.icon-web:before { .icon-web:before {
content: "\e987"; content: '\e987';
} }
.icon-wiki:before { .icon-wiki:before {
content: "\e989"; content: '\e989';
} }
.icon-worker:before { .icon-worker:before {
content: "\e98a"; content: '\e98a';
} }
.icon-zone:before { .icon-zone:before {
content: "\e98b"; content: '\e98b';
} }

View File

@ -30,10 +30,12 @@ export function isValidDate(date) {
export function toDateFormat(date, locale = 'es-ES', opts = {}) { export function toDateFormat(date, locale = 'es-ES', opts = {}) {
if (!isValidDate(date)) return ''; if (!isValidDate(date)) return '';
const format = Object.assign( const format = {
{ year: 'numeric', month: '2-digit', day: '2-digit' }, year: 'numeric',
opts month: '2-digit',
); day: '2-digit',
...opts,
};
return new Date(date).toLocaleDateString(locale, format); return new Date(date).toLocaleDateString(locale, format);
} }
@ -104,17 +106,17 @@ export function secondsToHoursMinutes(seconds, includeHSuffix = true) {
const hours = Math.floor(seconds / 3600); const hours = Math.floor(seconds / 3600);
const remainingMinutes = seconds % 3600; const remainingMinutes = seconds % 3600;
const minutes = Math.floor(remainingMinutes / 60); const minutes = Math.floor(remainingMinutes / 60);
const formattedHours = hours < 10 ? '0' + hours : hours; const formattedHours = hours < 10 ? `0${hours}` : hours;
const formattedMinutes = minutes < 10 ? '0' + minutes : minutes; const formattedMinutes = minutes < 10 ? `0${minutes}` : minutes;
// Append "h." if includeHSuffix is true // Append "h." if includeHSuffix is true
const suffix = includeHSuffix ? ' h.' : ''; const suffix = includeHSuffix ? ' h.' : '';
// Return formatted string // Return formatted string
return formattedHours + ':' + formattedMinutes + suffix; return `${formattedHours}:${formattedMinutes}${suffix}`;
} }
export function getTimeDifferenceWithToday(date) { export function getTimeDifferenceWithToday(date) {
let today = Date.vnNew(); const today = Date.vnNew();
today.setHours(0, 0, 0, 0); today.setHours(0, 0, 0, 0);
date = new Date(date); date = new Date(date);

View File

@ -5,12 +5,12 @@
* @return {Object} The fields as object * @return {Object} The fields as object
*/ */
function fieldsToObject(fields) { function fieldsToObject(fields) {
let fieldsObj = {}; const fieldsObj = {};
if (Array.isArray(fields)) { if (Array.isArray(fields)) {
for (let field of fields) fieldsObj[field] = true; for (const field of fields) fieldsObj[field] = true;
} else if (typeof fields == 'object') { } else if (typeof fields == 'object') {
for (let field in fields) { for (const field in fields) {
if (fields[field]) fieldsObj[field] = true; if (fields[field]) fieldsObj[field] = true;
} }
} }
@ -26,7 +26,7 @@ function fieldsToObject(fields) {
* @return {Array} The merged fields as an array * @return {Array} The merged fields as an array
*/ */
function mergeFields(src, dst) { function mergeFields(src, dst) {
let fields = {}; const fields = {};
Object.assign(fields, fieldsToObject(src), fieldsToObject(dst)); Object.assign(fields, fieldsToObject(src), fieldsToObject(dst));
return Object.keys(fields); return Object.keys(fields);
} }
@ -39,7 +39,7 @@ function mergeFields(src, dst) {
* @return {Array} The merged wheres * @return {Array} The merged wheres
*/ */
function mergeWhere(src, dst) { function mergeWhere(src, dst) {
let and = []; const and = [];
if (src) and.push(src); if (src) and.push(src);
if (dst) and.push(dst); if (dst) and.push(dst);
return simplifyOperation(and, 'and'); return simplifyOperation(and, 'and');
@ -53,7 +53,7 @@ function mergeWhere(src, dst) {
* @return {Object} The result filter * @return {Object} The result filter
*/ */
function mergeFilters(src, dst) { function mergeFilters(src, dst) {
let res = Object.assign({}, dst); const res = { ...dst };
if (!src) return res; if (!src) return res;
@ -80,12 +80,12 @@ function simplifyOperation(operation, operator) {
} }
function buildFilter(params, builderFunc) { function buildFilter(params, builderFunc) {
let and = []; const and = [];
for (let param in params) { for (const param in params) {
let value = params[param]; const value = params[param];
if (value == null) continue; if (value == null) continue;
let expr = builderFunc(param, value); const expr = builderFunc(param, value);
if (expr) and.push(expr); if (expr) and.push(expr);
} }
return simplifyOperation(and, 'and'); return simplifyOperation(and, 'and');

View File

@ -1,14 +1,14 @@
export default function getDifferences(obj1, obj2) { export default function getDifferences(obj1, obj2) {
let diff = {}; const diff = {};
delete obj1.$index; delete obj1.$index;
delete obj2.$index; delete obj2.$index;
for (let key in obj1) { for (const key in obj1) {
if (obj2[key] && JSON.stringify(obj1[key]) !== JSON.stringify(obj2[key])) { if (obj2[key] && JSON.stringify(obj1[key]) !== JSON.stringify(obj2[key])) {
diff[key] = obj2[key]; diff[key] = obj2[key];
} }
} }
for (let key in obj2) { for (const key in obj2) {
if ( if (
obj1[key] === undefined || obj1[key] === undefined ||
JSON.stringify(obj1[key]) !== JSON.stringify(obj2[key]) JSON.stringify(obj1[key]) !== JSON.stringify(obj2[key])

View File

@ -1,10 +1,10 @@
export default function toDateString(date) { export default function toDateString(date) {
let day = date.getDate(); let day = date.getDate();
let month = date.getMonth() + 1; let month = date.getMonth() + 1;
let year = date.getFullYear(); const year = date.getFullYear();
if (day < 10) day = `0${day}`; if (day < 10) day = `0${day}`;
if (month < 10) month = `0${month}`; if (month < 10) month = `0${month}`;
return `${year}-${month}-${day}` return `${year}-${month}-${day}`;
} }

View File

@ -1,5 +1,5 @@
export default function toLowerCamel(value) { export default function toLowerCamel(value) {
if (!value) return; if (!value) return;
if (typeof (value) !== 'string') return value; if (typeof value !== 'string') return value;
return value.charAt(0).toLowerCase() + value.slice(1); return value.charAt(0).toLowerCase() + value.slice(1);
} }

View File

@ -9,7 +9,7 @@ export default function formatDate(dateVal) {
const dateZeroTime = new Date(dateVal); const dateZeroTime = new Date(dateVal);
dateZeroTime.setHours(0, 0, 0, 0); dateZeroTime.setHours(0, 0, 0, 0);
const diff = Math.trunc( const diff = Math.trunc(
(today.getTime() - dateZeroTime.getTime()) / (1000 * 3600 * 24) (today.getTime() - dateZeroTime.getTime()) / (1000 * 3600 * 24),
); );
let format; let format;
if (diff === 0) format = t('globals.today'); if (diff === 0) format = t('globals.today');

View File

@ -11,12 +11,12 @@ for (const file in files) {
translations[lang] = g.default; translations[lang] = g.default;
}) })
.finally(() => { .finally(() => {
const actualLang = lang + '.yml'; const actualLang = `${lang}.yml`;
for (const module in modules) { for (const module in modules) {
if (!module.endsWith(actualLang)) continue; if (!module.endsWith(actualLang)) continue;
modules[module]().then((t) => { modules[module]().then((t) => {
Object.assign(translations[lang], t.default); Object.assign(translations[lang], t.default);
}) });
} }
}); });
} }

View File

@ -24,13 +24,14 @@ globals:
dataDeleted: Data deleted dataDeleted: Data deleted
delete: Delete delete: Delete
search: Search search: Search
lines: Lines
changes: Changes changes: Changes
dataCreated: Data created dataCreated: Data created
add: Add add: Add
create: Create create: Create
edit: Edit edit: Edit
save: Save save: Save
saveAndContinue: Save and continue saveAndContinue: Save and go to
remove: Remove remove: Remove
reset: Reset reset: Reset
close: Close close: Close
@ -107,6 +108,8 @@ globals:
from: From from: From
to: To to: To
notes: Notes notes: Notes
photos: Photos
due-day: Due day
refresh: Refresh refresh: Refresh
item: Item item: Item
ticket: Ticket ticket: Ticket
@ -400,6 +403,7 @@ errors:
updateUserConfig: Error updating user config updateUserConfig: Error updating user config
tokenConfig: Error fetching token config tokenConfig: Error fetching token config
writeRequest: The requested operation could not be completed writeRequest: The requested operation could not be completed
claimBeginningQuantity: Cannot import a line with a claimed quantity of 0
login: login:
title: Login title: Login
username: Username username: Username

View File

@ -25,12 +25,13 @@ globals:
openDetail: Ver detalle openDetail: Ver detalle
delete: Eliminar delete: Eliminar
search: Buscar search: Buscar
lines: Lineas
changes: Cambios changes: Cambios
add: Añadir add: Añadir
create: Crear create: Crear
edit: Modificar edit: Modificar
save: Guardar save: Guardar
saveAndContinue: Guardar y continuar saveAndContinue: Guardar e ir a
remove: Eliminar remove: Eliminar
reset: Restaurar reset: Restaurar
close: Cerrar close: Cerrar
@ -111,6 +112,8 @@ globals:
from: Desde from: Desde
to: Hasta to: Hasta
notes: Notas notes: Notas
photos: Fotos
due-day: Vencimiento
refresh: Actualizar refresh: Actualizar
item: Artículo item: Artículo
ticket: Ticket ticket: Ticket
@ -396,6 +399,7 @@ errors:
updateUserConfig: Error al actualizar la configuración de usuario updateUserConfig: Error al actualizar la configuración de usuario
tokenConfig: Error al obtener configuración de token tokenConfig: Error al obtener configuración de token
writeRequest: No se pudo completar la operación solicitada writeRequest: No se pudo completar la operación solicitada
claimBeginningQuantity: No se puede importar una linea sin una cantidad reclamada
login: login:
title: Inicio de sesión title: Inicio de sesión
username: Nombre de usuario username: Nombre de usuario

View File

@ -80,7 +80,7 @@ const killSession = async ({ userId, created }) => {
openConfirmationModal( openConfirmationModal(
t('Session will be killed'), t('Session will be killed'),
t('Are you sure you want to continue?'), t('Are you sure you want to continue?'),
() => killSession(row) () => killSession(row),
) )
" "
outline outline

View File

@ -57,7 +57,7 @@ watch(
store.url = urlPath.value; store.url = urlPath.value;
store.filter = filter; store.filter = filter;
fetchAliases(); fetchAliases();
} },
); );
const fetchAliases = () => paginateRef.value.fetch(); const fetchAliases = () => paginateRef.value.fetch();
@ -91,7 +91,7 @@ const fetchAliases = () => paginateRef.value.fetch();
openConfirmationModal( openConfirmationModal(
t('User will be removed from alias'), t('User will be removed from alias'),
t('Are you sure you want to continue?'), t('Are you sure you want to continue?'),
() => deleteAlias(row) () => deleteAlias(row),
) )
" "
> >

View File

@ -28,7 +28,7 @@ const loading = ref(false);
const hasDataChanged = computed( const hasDataChanged = computed(
() => () =>
formData.value.forwardTo !== initialData.value.forwardTo || formData.value.forwardTo !== initialData.value.forwardTo ||
initialData.value.hasData !== hasData.value initialData.value.hasData !== hasData.value,
); );
const fetchMailForwards = async () => { const fetchMailForwards = async () => {
@ -77,7 +77,7 @@ const setInitialData = async () => {
watch( watch(
() => route.params.id, () => route.params.id,
() => setInitialData() () => setInitialData(),
); );
onMounted(async () => await setInitialData()); onMounted(async () => await setInitialData());

View File

@ -14,7 +14,7 @@ const rolesOptions = ref([]);
const formModelRef = ref(); const formModelRef = ref();
watch( watch(
() => route.params.id, () => route.params.id,
() => formModelRef.value.reset() () => formModelRef.value.reset(),
); );
</script> </script>
<template> <template>

View File

@ -44,7 +44,7 @@ watch(
store.filter = filter.value; store.filter = filter.value;
store.limit = 0; store.limit = 0;
fetchSubRoles(); fetchSubRoles();
} },
); );
const fetchSubRoles = () => paginateRef.value.fetch(); const fetchSubRoles = () => paginateRef.value.fetch();

View File

@ -43,7 +43,7 @@ watch(
store.url = urlPath.value; store.url = urlPath.value;
store.filter = filter.value; store.filter = filter.value;
fetchSubRoles(); fetchSubRoles();
} },
); );
const fetchSubRoles = () => paginateRef.value.fetch(); const fetchSubRoles = () => paginateRef.value.fetch();

View File

@ -13,8 +13,10 @@ import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue'; import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
import { useArrayData } from 'composables/useArrayData'; import { useArrayData } from 'composables/useArrayData';
import RightMenu from 'src/components/common/RightMenu.vue'; import RightMenu from 'src/components/common/RightMenu.vue';
import useNotify from 'src/composables/useNotify.js';
const { t } = useI18n(); const { t } = useI18n();
const { notify } = useNotify();
const quasar = useQuasar(); const quasar = useQuasar();
const route = useRoute(); const route = useRoute();
const claim = ref(null); const claim = ref(null);
@ -176,12 +178,17 @@ async function save(data) {
} }
async function importToNewRefundTicket() { async function importToNewRefundTicket() {
try {
await post(`ClaimBeginnings/${claimId}/importToNewRefundTicket`); await post(`ClaimBeginnings/${claimId}/importToNewRefundTicket`);
await claimActionsForm.value.reload(); await claimActionsForm.value.reload();
quasar.notify({ quasar.notify({
message: t('globals.dataSaved'), message: t('globals.dataSaved'),
type: 'positive', type: 'positive',
}); });
} catch (error) {
const errorMessage = error.response?.data?.error?.message;
notify(t(errorMessage), 'negative');
}
} }
async function post(query, params) { async function post(query, params) {

View File

@ -33,6 +33,7 @@ function onBeforeSave(formData, originalData) {
<FetchData url="ClaimStates" @on-fetch="setClaimStates" auto-load /> <FetchData url="ClaimStates" @on-fetch="setClaimStates" auto-load />
<FormModel <FormModel
model="Claim" model="Claim"
:go-to="`/claim/${route.params.id}/notes`"
:url-update="`Claims/updateClaim/${route.params.id}`" :url-update="`Claims/updateClaim/${route.params.id}`"
:mapper="onBeforeSave" :mapper="onBeforeSave"
auto-load auto-load

View File

@ -238,7 +238,9 @@ const columns = computed(() => [
<style lang="scss" scoped> <style lang="scss" scoped>
.grid-style-transition { .grid-style-transition {
transition: transform 0.28s, background-color 0.28s; transition:
transform 0.28s,
background-color 0.28s;
} }
</style> </style>

View File

@ -78,6 +78,8 @@ const columns = computed(() => [
label: t('Quantity'), label: t('Quantity'),
field: ({ sale }) => sale.quantity, field: ({ sale }) => sale.quantity,
sortable: true, sortable: true,
style: 'padding-right: 2%;',
headerStyle: 'padding-right: 2%;',
}, },
{ {
name: 'claimed', name: 'claimed',
@ -110,6 +112,8 @@ const columns = computed(() => [
field: ({ sale }) => totalRow(sale), field: ({ sale }) => totalRow(sale),
format: (value) => toCurrency(value), format: (value) => toCurrency(value),
sortable: true, sortable: true,
style: 'padding-right: 2%;',
headerStyle: 'padding-right: 2%;',
}, },
]); ]);
@ -152,10 +156,29 @@ function showImportDialog() {
.onOk(() => claimLinesForm.value.reload()); .onOk(() => claimLinesForm.value.reload());
} }
async function saveWhenHasChanges() { function fillClaimedQuantities() {
if (claimLinesForm.value.getChanges().updates) { const formData = claimLinesForm.value.formData;
await claimLinesForm.value.onSubmit(); let hasChanges = false;
onFetch(claimLinesForm.value.formData);
const selectedRows = formData.filter((row) => selected.value.includes(row));
for (const row of selectedRows) {
if (row.quantity === 0 || row.quantity === null) {
row.quantity = row.sale.quantity;
hasChanges = true;
}
}
if (hasChanges) {
quasar.notify({
message: t('Quantities filled automatically'),
type: 'positive',
});
} else {
quasar.notify({
message: t('No quantities to fill'),
type: 'info',
});
} }
} }
</script> </script>
@ -186,14 +209,14 @@ async function saveWhenHasChanges() {
/> />
<div class="q-pa-md"> <div class="q-pa-md">
<CrudModel <CrudModel
data-key="ClaimLines" data-key="claimLines"
ref="claimLinesForm" ref="claimLinesForm"
:go-to="`photos`"
:url="`Claims/${route.params.id}/lines`" :url="`Claims/${route.params.id}/lines`"
save-url="ClaimBeginnings/crud" save-url="ClaimBeginnings/crud"
:user-filter="linesFilter" :user-filter="linesFilter"
@on-fetch="onFetch" @on-fetch="onFetch"
v-model:selected="selected" v-model:selected="selected"
:default-save="false"
:default-reset="false" :default-reset="false"
auto-load auto-load
:limit="0" :limit="0"
@ -210,13 +233,7 @@ async function saveWhenHasChanges() {
> >
<template #body-cell-claimed="{ row }"> <template #body-cell-claimed="{ row }">
<QTd auto-width align="right" class="text-primary shrink"> <QTd auto-width align="right" class="text-primary shrink">
<QInput <QInput v-model.number="row.quantity" type="number" dense />
v-model.number="row.quantity"
type="number"
dense
@keyup.enter="saveWhenHasChanges()"
@blur="saveWhenHasChanges()"
/>
</QTd> </QTd>
</template> </template>
<template #body-cell-description="{ row, value }"> <template #body-cell-description="{ row, value }">
@ -272,10 +289,6 @@ async function saveWhenHasChanges() {
type="number" type="number"
dense dense
autofocus autofocus
@keyup.enter="
saveWhenHasChanges()
"
@blur="saveWhenHasChanges()"
/> />
</QItemLabel> </QItemLabel>
</template> </template>
@ -313,6 +326,18 @@ async function saveWhenHasChanges() {
</template> </template>
</QTable> </QTable>
</template> </template>
<template #moreBeforeActions>
<QBtn
color="primary"
text-color="white"
:unelevated="true"
:label="t('Rellenar cantidades')"
:title="t('Rellenar cantidades')"
icon="auto_fix_high"
:disabled="!selected.length"
@click="fillClaimedQuantities"
/>
</template>
</CrudModel> </CrudModel>
</div> </div>
@ -358,6 +383,8 @@ es:
Delete claimed sales: Eliminar ventas reclamadas Delete claimed sales: Eliminar ventas reclamadas
Discount updated: Descuento actualizado Discount updated: Descuento actualizado
Claimed quantity: Cantidad reclamada Claimed quantity: Cantidad reclamada
Quantities filled automatically: Cantidades rellenadas automáticamente
No quantities to fill: No hay cantidades para rellenar
You are about to remove {count} rows: ' You are about to remove {count} rows: '
Vas a eliminar <strong>{count}</strong> línea | Vas a eliminar <strong>{count}</strong> línea |
Vas a eliminar <strong>{count}</strong> líneas' Vas a eliminar <strong>{count}</strong> líneas'

View File

@ -35,9 +35,11 @@ const body = {
workerFk: user.value.id, workerFk: user.value.id,
}; };
</script> </script>
<template> <template>
<VnNotes <VnNotes
url="claimObservations" url="claimObservations"
:go-to="`/claim/${route.params.id}/lines`"
:add-note="$props.addNote" :add-note="$props.addNote"
:user-filter="claimFilter" :user-filter="claimFilter"
:filter="{ where: { claimFk: claimId } }" :filter="{ where: { claimFk: claimId } }"

View File

@ -27,7 +27,7 @@ describe('ClaimDescriptorMenu', () => {
await vm.remove(); await vm.remove();
expect(vm.quasar.notify).toHaveBeenCalledWith( expect(vm.quasar.notify).toHaveBeenCalledWith(
expect.objectContaining({ type: 'positive' }) expect.objectContaining({ type: 'positive' }),
); );
}); });
}); });

View File

@ -17,6 +17,7 @@ import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.v
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue'; import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
import VnSelect from 'components/common/VnSelect.vue'; import VnSelect from 'components/common/VnSelect.vue';
import VnInputDate from 'components/common/VnInputDate.vue'; import VnInputDate from 'components/common/VnInputDate.vue';
import VnCheckbox from 'src/components/common/VnCheckbox.vue';
const arrayData = useArrayData('Customer'); const arrayData = useArrayData('Customer');
const { t } = useI18n(); const { t } = useI18n();
@ -50,7 +51,7 @@ const columns = computed(() => [
label: t('globals.ticket'), label: t('globals.ticket'),
cardVisible: true, cardVisible: true,
columnFilter: { columnFilter: {
inWhere: true, name: 'ticketId',
}, },
}, },
{ {
@ -84,7 +85,8 @@ const columns = computed(() => [
label: t('globals.description'), label: t('globals.description'),
columnClass: 'expand', columnClass: 'expand',
columnFilter: { columnFilter: {
inWhere: true, name: 'description',
}, },
}, },
{ {
@ -92,17 +94,10 @@ const columns = computed(() => [
label: t('globals.quantity'), label: t('globals.quantity'),
cardVisible: true, cardVisible: true,
visible: true, visible: true,
columnFilter: { columnFilter: false
inWhere: true,
},
},
{
name: 'grouped',
label: t('Group by items'),
component: 'checkbox',
visible: false,
orderBy: false,
}, },
]); ]);
onBeforeMount(async () => { onBeforeMount(async () => {
@ -170,7 +165,6 @@ const updateDateParams = (value, params) => {
v-if="campaignList" v-if="campaignList"
data-key="CustomerConsumption" data-key="CustomerConsumption"
url="Clients/consumption" url="Clients/consumption"
:order="['itemTypeFk', 'itemName', 'itemSize', 'description']"
:filter="{ where: { clientFk: route.params.id } }" :filter="{ where: { clientFk: route.params.id } }"
:columns="columns" :columns="columns"
search-url="consumption" search-url="consumption"
@ -218,9 +212,9 @@ const updateDateParams = (value, params) => {
<div v-if="row.subName" class="subName"> <div v-if="row.subName" class="subName">
{{ row.subName }} {{ row.subName }}
</div> </div>
<FetchedTags :item="row" /> <FetchedTags :item="row" :columns="6"/>
</template> </template>
<template #moreFilterPanel="{ params }"> <template #moreFilterPanel="{ params, searchFn}">
<div class="column no-wrap flex-center q-gutter-y-md q-mt-xs q-pr-xl"> <div class="column no-wrap flex-center q-gutter-y-md q-mt-xs q-pr-xl">
<VnSelect <VnSelect
:filled="true" :filled="true"
@ -290,6 +284,13 @@ const updateDateParams = (value, params) => {
class="q-px-xs q-pt-none fit" class="q-px-xs q-pt-none fit"
dense dense
/> />
<VnCheckbox
v-model="params.grouped"
:label="t('Group by items')"
class="q-px-xs q-pt-none fit"
dense
@update:modelValue="() => searchFn()"
/>
</div> </div>
</template> </template>
</VnTable> </VnTable>

View File

@ -99,7 +99,13 @@ async function acceptPropagate({ isEqualizated }) {
</VnRow> </VnRow>
<VnRow> <VnRow>
<VnInput :label="t('Street')" clearable v-model="data.street" required /> <VnInput
:label="t('Street')"
clearable
v-model="data.street"
:uppercase="true"
required
/>
</VnRow> </VnRow>
<VnRow> <VnRow>
@ -111,8 +117,6 @@ async function acceptPropagate({ isEqualizated }) {
option-value="id" option-value="id"
v-model="data.sageTaxTypeFk" v-model="data.sageTaxTypeFk"
data-cy="sageTaxTypeFk" data-cy="sageTaxTypeFk"
:required="data.isTaxDataChecked"
:rules="[(val) => validations.required(data.isTaxDataChecked, val)]"
/> />
<VnSelect <VnSelect
:label="t('Sage transaction type')" :label="t('Sage transaction type')"
@ -122,10 +126,6 @@ async function acceptPropagate({ isEqualizated }) {
option-value="id" option-value="id"
data-cy="sageTransactionTypeFk" data-cy="sageTransactionTypeFk"
v-model="data.sageTransactionTypeFk" v-model="data.sageTransactionTypeFk"
:required="data.isTaxDataChecked"
:rules="[
(val) => validations.required(data.sageTransactionTypeFk, val),
]"
> >
<template #option="scope"> <template #option="scope">
<QItem v-bind="scope.itemProps"> <QItem v-bind="scope.itemProps">

View File

@ -50,8 +50,11 @@ const filterClientFindOne = {
> >
<template #form="{ data }"> <template #form="{ data }">
<VnRow> <VnRow>
<QCheckbox :label="t('Unpaid client')" v-model="data.unpaid" <QCheckbox
data-cy="UnpaidCheckBox" /> :label="t('Unpaid client')"
v-model="data.unpaid"
data-cy="UnpaidCheckBox"
/>
</VnRow> </VnRow>
<VnRow class="row q-gutter-md q-mb-md" v-show="data.unpaid"> <VnRow class="row q-gutter-md q-mb-md" v-show="data.unpaid">

View File

@ -224,7 +224,7 @@ const toCustomerFileManagement = () => {
<QTooltip max-width="30rem"> <QTooltip max-width="30rem">
{{ {{
`${t( `${t(
'Allowed content types' 'Allowed content types',
)}: ${allowedContentTypes.join(', ')}` )}: ${allowedContentTypes.join(', ')}`
}} }}
</QTooltip> </QTooltip>

View File

@ -200,7 +200,7 @@ const toCustomerFileManagement = () => {
<QTooltip max-width="30rem"> <QTooltip max-width="30rem">
{{ {{
`${t( `${t(
'Allowed content types' 'Allowed content types',
)}: ${allowedContentTypes.join(', ')}` )}: ${allowedContentTypes.join(', ')}`
}} }}
</QTooltip> </QTooltip>

View File

@ -130,20 +130,22 @@ async function onDataSaved(formData, { id }) {
} }
} }
async function getSupplierClientReferences(value) { async function getSupplierClientReferences(data) {
if (!value) return (initialData.description = ''); if (!data) return (initialData.description = '');
const params = { bankAccount: value }; const params = { bankAccount: data.compensationAccount };
const { data } = await axios(`Clients/getClientOrSupplierReference`, { params }); const { data: reference } = await axios(`Clients/getClientOrSupplierReference`, {
if (!data.clientId) { params,
initialData.description = t('Supplier Compensation Reference', { });
supplierId: data.supplierId, if (reference.supplierId) {
supplierName: data.supplierName, data.description = t('Supplier Compensation Reference', {
supplierId: reference.supplierId,
supplierName: reference.supplierName,
}); });
return; return;
} }
initialData.description = t('Client Compensation Reference', { data.description = t('Client Compensation Reference', {
clientId: data.clientId, clientId: reference.clientId,
clientName: data.clientName, clientName: reference.clientName,
}); });
} }
@ -222,6 +224,7 @@ async function getAmountPaid() {
clearable clearable
v-model.number="data.amountPaid" v-model.number="data.amountPaid"
data-cy="paymentAmount" data-cy="paymentAmount"
:positive="false"
/> />
</VnRow> </VnRow>
<VnRow> <VnRow>
@ -251,7 +254,7 @@ async function getAmountPaid() {
:label="t('Compensation account')" :label="t('Compensation account')"
clearable clearable
v-model="data.compensationAccount" v-model="data.compensationAccount"
@blur="getSupplierClientReferences(data.compensationAccount)" @blur="getSupplierClientReferences(data)"
/> />
</VnRow> </VnRow>
</div> </div>
@ -287,6 +290,9 @@ async function getAmountPaid() {
</template> </template>
<i18n> <i18n>
en:
Supplier Compensation Reference: ({supplierId}) Ntro Proveedor {supplierName}
Client Compensation Reference: ({clientId}) Ntro Cliente {clientName}
es: es:
New payment: Añadir pago New payment: Añadir pago
Date: Fecha Date: Fecha

View File

@ -13,4 +13,4 @@ export async function getClient(clientId, _filter = {}) {
}; };
const params = { filter: JSON.stringify(filter) }; const params = { filter: JSON.stringify(filter) };
return await axios.get('Clients', { params }); return await axios.get('Clients', { params });
}; }

View File

@ -123,3 +123,4 @@ customer:
ticketFk: Ticket Id ticketFk: Ticket Id
description: Description description: Description
quantity: Quantity quantity: Quantity
ticketId: Ticket

View File

@ -123,3 +123,4 @@ customer:
ticketFk: Id Ticket ticketFk: Id Ticket
description: Descripción description: Descripción
quantity: Cantidad quantity: Cantidad
ticketId: Ticket

View File

@ -126,7 +126,7 @@ const fetchBuys = async (buys) => {
const params = { buys }; const params = { buys };
const { data } = await axios.post( const { data } = await axios.post(
`Entries/${route.params.id}/importBuysPreview`, `Entries/${route.params.id}/importBuysPreview`,
params params,
); );
importData.value.buys = data; importData.value.buys = data;
}; };

View File

@ -3,7 +3,7 @@ import axios from 'axios';
export async function setRectificative(route) { export async function setRectificative(route) {
const card = route.matched.find((route) => route.name === 'InvoiceInCard'); const card = route.matched.find((route) => route.name === 'InvoiceInCard');
const corrective = card.children.find( const corrective = card.children.find(
(route) => route.name === 'InvoiceInCorrective' (route) => route.name === 'InvoiceInCorrective',
); );
corrective.meta.hidden = !( corrective.meta.hidden = !(

View File

@ -37,7 +37,7 @@ async function onSubmit() {
await axios.post( await axios.post(
'VnUsers/reset-password', 'VnUsers/reset-password',
{ newPassword: newPassword.value }, { newPassword: newPassword.value },
{ headers } { headers },
); );
router.push('Login'); router.push('Login');
quasar.notify({ quasar.notify({

View File

@ -71,9 +71,7 @@ onMounted(async () => (stateStore.rightDrawer = false));
auto-load auto-load
/> />
<QCard v-if="volumeSummary" class="order-volume-summary q-pa-lg"> <QCard v-if="volumeSummary" class="order-volume-summary q-pa-lg">
<VnLv <VnLv :label="`${t('total')}: `" :value="`${volumeSummary?.totalVolume} m³`" />
:label="`${t('total')}: `"
:value="`${volumeSummary?.totalVolume} m³`" />
<VnLv <VnLv
:label="`${t('boxes')}: `" :label="`${t('boxes')}: `"
:value="`${dashIfEmpty(volumeSummary?.totalBoxes)} U`" :value="`${dashIfEmpty(volumeSummary?.totalBoxes)} U`"

View File

@ -3,5 +3,10 @@ import AgencyDescriptor from 'pages/Route/Agency/Card/AgencyDescriptor.vue';
import VnCard from 'src/components/common/VnCard.vue'; import VnCard from 'src/components/common/VnCard.vue';
</script> </script>
<template> <template>
<VnCard data-key="Agency" url="Agencies" :descriptor="AgencyDescriptor" :filter="{ where: { id: $route.params.id } }" /> <VnCard
data-key="Agency"
url="Agencies"
:descriptor="AgencyDescriptor"
:filter="{ where: { id: $route.params.id } }"
/>
</template> </template>

View File

@ -9,7 +9,7 @@ export async function getAgencies(formData, client, _filter = {}) {
}; };
let agency = null; let agency = null;
let params = { const params = {
filter: JSON.stringify(filter), filter: JSON.stringify(filter),
warehouseFk: formData.warehouseId, warehouseFk: formData.warehouseId,
addressFk: formData.addressId, addressFk: formData.addressId,

View File

@ -23,7 +23,7 @@ async function openRouteReport() {
const token = getTokenMultimedia(); const token = getTokenMultimedia();
window.open( window.open(
`${url}/api/Routes/${routeId}/driver-route-pdf?access_token=${token}`, `${url}/api/Routes/${routeId}/driver-route-pdf?access_token=${token}`,
'_blank' '_blank',
); );
} }

View File

@ -106,8 +106,8 @@ const setTicketsRoute = async () => {
(selectedRows.value || []) (selectedRows.value || [])
.filter((ticket) => ticket?.id) .filter((ticket) => ticket?.id)
.map((ticket) => .map((ticket) =>
axios.patch(`Routes/${$props.id}/insertTicket`, { ticketId: ticket.id }) axios.patch(`Routes/${$props.id}/insertTicket`, { ticketId: ticket.id }),
) ),
); );
await axios.post(`Routes/${$props.id}/updateVolume`); await axios.post(`Routes/${$props.id}/updateVolume`);
emit('ok'); emit('ok');

View File

@ -72,13 +72,9 @@ const createVehicleEvent = async () => {
if (isNew.value) { if (isNew.value) {
await axios.post(`VehicleEvents`, vehicleFormData.value); await axios.post(`VehicleEvents`, vehicleFormData.value);
} else { } else {
await axios.patch( await axios.patch(`VehicleEvents/${props.event?.id}`, vehicleFormData.value);
`VehicleEvents/${props.event?.id}`,
vehicleFormData.value,
);
} }
await refetchEvents(); await refetchEvents();
}; };
const deleteVehicleEvent = async () => { const deleteVehicleEvent = async () => {
@ -98,26 +94,24 @@ const refetchEvents = async () => {
{ {
or: [ or: [
{ started: { lte: props.lastDay?.toISOString() } }, { started: { lte: props.lastDay?.toISOString() } },
{ started: null } { started: null },
] ],
}, },
{ {
or: [ or: [
{ finished: { gte: props.firstDay?.toISOString() } }, { finished: { gte: props.firstDay?.toISOString() } },
{ finished: null } { finished: null },
] ],
} },
] ],
} },
} },
} },
}); });
emit('refresh-events'); emit('refresh-events');
notify(t('globals.dataSaved'), 'positive'); notify(t('globals.dataSaved'), 'positive');
emit('closeForm'); emit('closeForm');
}; };
</script> </script>
<template> <template>

View File

@ -38,7 +38,6 @@ const onVehicleEventFormClose = () => {
showVehicleEventForm.value = false; showVehicleEventForm.value = false;
vehicleEventsFormProps.value = {}; vehicleEventsFormProps.value = {};
}; };
</script> </script>
<template> <template>

View File

@ -63,18 +63,22 @@ const fetchData = async () => {
where: { where: {
vehicleFk: route.params.id, vehicleFk: route.params.id,
and: [ and: [
{ or: [ {
or: [
{ started: { lte: props.lastDay } }, { started: { lte: props.lastDay } },
{ started: null } { started: null },
]}, ],
{ or: [ },
{
or: [
{ finished: { gte: props.firstDay } }, { finished: { gte: props.firstDay } },
{ finished: null } { finished: null },
]} ],
] },
} ],
} },
} },
},
}); });
emit('update:events', arrayData.store.data || []); emit('update:events', arrayData.store.data || []);
} catch (error) { } catch (error) {
@ -95,7 +99,7 @@ watch(
(newEvents) => { (newEvents) => {
emit('update:events', newEvents); emit('update:events', newEvents);
}, },
{ deep: true } { deep: true },
); );
const deleteEvent = async (id) => { const deleteEvent = async (id) => {
@ -119,9 +123,8 @@ onMounted(async () => {
}); });
defineExpose({ defineExpose({
fetchData fetchData,
}); });
</script> </script>
<template> <template>

View File

@ -2,12 +2,8 @@
import EntityCalendar from 'src/components/EntityCalendar.vue'; import EntityCalendar from 'src/components/EntityCalendar.vue';
const emit = defineEmits(['onDateSelected']); const emit = defineEmits(['onDateSelected']);
</script> </script>
<template> <template>
<EntityCalendar <EntityCalendar v-bind="$props" @onDateSelected="(e) => emit('onDateSelected', e)" />
v-bind="$props"
@onDateSelected="(e) => emit('onDateSelected', e)"
/>
</template> </template>

View File

@ -36,8 +36,8 @@ const refreshEvents = () => {
const eventStart = event.started ? new Date(event.started).getTime() : null; const eventStart = event.started ? new Date(event.started).getTime() : null;
const eventEnd = event.finished ? new Date(event.finished).getTime() : null; const eventEnd = event.finished ? new Date(event.finished).getTime() : null;
let match = (!eventStart || stamp >= eventStart) && let match =
(!eventEnd || stamp <= eventEnd); (!eventStart || stamp >= eventStart) && (!eventEnd || stamp <= eventEnd);
if (match) { if (match) {
dayEvents.push(event); dayEvents.push(event);
@ -75,14 +75,19 @@ watch(
{ immediate: true }, { immediate: true },
); );
watch(() => entityCalendarRef.value?.firstDay, (newVal) => { watch(
() => entityCalendarRef.value?.firstDay,
(newVal) => {
if (newVal) firstDay.value = new Date(newVal); if (newVal) firstDay.value = new Date(newVal);
}); },
);
watch(() => entityCalendarRef.value?.lastDay, (newVal) => { watch(
() => entityCalendarRef.value?.lastDay,
(newVal) => {
if (newVal) lastDay.value = new Date(newVal); if (newVal) lastDay.value = new Date(newVal);
}); },
);
</script> </script>
<template> <template>

View File

@ -76,7 +76,7 @@ const getUrl = (section) => `#/supplier/${entityId.value}/${section}`;
{{ {{
dashIfEmpty( dashIfEmpty(
supplier.companySize && supplier.companySize &&
t('globals.' + supplier.companySize) t('globals.' + supplier.companySize),
) )
}} }}
</span> </span>

View File

@ -38,7 +38,7 @@ watch(
if (ticketData.value?.zone && ticketData.value?.zone?.isVolumetric) if (ticketData.value?.zone && ticketData.value?.zone?.isVolumetric)
getTicketVolume(); getTicketVolume();
}, },
{ immediate: true } { immediate: true },
); );
const salesFilter = computed(() => ({ const salesFilter = computed(() => ({

View File

@ -46,7 +46,9 @@ async function getClaims() {
originalTicket.value = data[0]?.originalTicketFk; originalTicket.value = data[0]?.originalTicketFk;
} }
async function getProblems() { async function getProblems() {
const { data } = await axios.get(`Tickets/getTicketProblems`, {params: { ids: [entityId.value] }}); const { data } = await axios.get(`Tickets/getTicketProblems`, {
params: { ids: [entityId.value] },
});
if (!data) return; if (!data) return;
problems.value = data[0]; problems.value = data[0];
} }

View File

@ -89,7 +89,7 @@ defineExpose({ save });
</div> </div>
<div v-if="newPrice" class="column items-center q-mt-lg"> <div v-if="newPrice" class="column items-center q-mt-lg">
<span class="text-primary">{{ t('New price') }}</span> <span class="text-primary">{{ t('basicData.newPrice') }}</span>
<span class="text-subtitle1">{{ toCurrency(newPrice) }}</span> <span class="text-subtitle1">{{ toCurrency(newPrice) }}</span>
</div> </div>
</div> </div>
@ -135,3 +135,4 @@ defineExpose({ save });
min-width: 230px; min-width: 230px;
} }
</style> </style>
<

View File

@ -30,7 +30,7 @@ watch(
async (val) => { async (val) => {
crudModelFilter.where.ticketFk = val; crudModelFilter.where.ticketFk = val;
tableRef.value.reload(); tableRef.value.reload();
} },
); );
const crudModelFilter = reactive({ const crudModelFilter = reactive({
@ -149,9 +149,9 @@ const columns = computed(() => [
openConfirmationModal( openConfirmationModal(
t('You are going to delete this ticket purchase request'), t('You are going to delete this ticket purchase request'),
t( t(
'This ticket will be removed from ticket purchase requests! Continue anyway?' 'This ticket will be removed from ticket purchase requests! Continue anyway?',
), ),
() => removeLine(row.id) () => removeLine(row.id),
), ),
}, },
], ],

View File

@ -314,7 +314,7 @@ const changePrice = async (sale) => {
const updatePrice = async (sale, newPrice) => { const updatePrice = async (sale, newPrice) => {
try { try {
await axios.post(`Sales/${sale.id}/updatePrice`, { await axios.post(`Sales/${sale.id}/updatePrice`, {
newPrice: newPrice, newPrice,
componentId: componentId.value, componentId: componentId.value,
}); });
notify('globals.dataSaved', 'positive'); notify('globals.dataSaved', 'positive');

View File

@ -41,7 +41,7 @@ const transferSales = async (ticketId) => {
const { data } = await axios.post( const { data } = await axios.post(
`tickets/${$props.ticket.id}/transferSales`, `tickets/${$props.ticket.id}/transferSales`,
params params,
); );
if (data && data.id === $props.ticket.id) emit('refreshData'); if (data && data.id === $props.ticket.id) emit('refreshData');

View File

@ -50,7 +50,7 @@ describe('TicketBoxing', () => {
await vm.getVideoList(expeditionId, timed); await vm.getVideoList(expeditionId, timed);
expect(vm.quasar.notify).toHaveBeenCalledWith( expect(vm.quasar.notify).toHaveBeenCalledWith(
expect.objectContaining({ type: 'negative' }) expect.objectContaining({ type: 'negative' }),
); );
}); });
}); });

Some files were not shown because too many files have changed in this diff Show More