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(':');
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);
}
}

View File

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

View File

@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html>
<head>
<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<% } %>"
/>
<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="32x32" href="icons/favicon-32x32.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
import autoprefixer from 'autoprefixer';

View File

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

View File

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

View File

@ -11,8 +11,8 @@ export default function (component, key, value) {
};
break;
case 'undefined':
throw new Error('unknown prop: ' + key);
throw new Error(`unknown prop: ${key}`);
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
.filter((route) => route.meta.keyBinding)
.reduce((map, route) => {
map['Key' + route.meta.keyBinding.toUpperCase()] = route.path;
map[`Key${route.meta.keyBinding.toUpperCase()}`] = route.path;
return map;
}, {});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -241,7 +241,7 @@ describe('CrudModel', () => {
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.hasChanges).toBe(false);
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);
it('should call getMethodB when source is card', () => {
let props = { source: 'methodB' };
const props = { source: 'methodB' };
fn(props);
expect(getMethodB).toHaveBeenCalled();
expect(getMethodA).not.toHaveBeenCalled();
});
it('should call getMethodA when source is main', () => {
let props = { source: 'methodA' };
const props = { source: 'methodA' };
fn(props);
expect(getMethodA).toHaveBeenCalled();
@ -157,7 +157,7 @@ describe('getRoutes', () => {
});
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(getMethodA).not.toHaveBeenCalled();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,8 +6,8 @@ function buildComponent(data) {
return createWrapper(VnLocation, {
global: {
props: {
location: data
}
location: data,
},
},
}).vm;
}
@ -24,7 +24,7 @@ describe('formatLocation', () => {
postcode: '46680',
city: 'Algemesi',
province: { name: 'Valencia' },
country: { name: 'Spain' }
country: { name: 'Spain' },
};
});
@ -47,7 +47,12 @@ describe('formatLocation', () => {
});
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);
expect(vm.formatLocation(location)).toEqual('Spain');
});
@ -61,7 +66,7 @@ describe('showLabel', () => {
code: '46680',
town: 'Algemesi',
province: 'Valencia',
country: 'Spain'
country: 'Spain',
};
});
@ -84,7 +89,12 @@ describe('showLabel', () => {
});
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);
expect(vm.showLabel(location)).toEqual('Spain');
});

View File

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

View File

@ -2,13 +2,14 @@ import { createWrapper } from 'app/test/vitest/helper';
import VnSmsDialog from 'components/common/VnSmsDialog.vue';
import { vi, afterEach, beforeAll, describe, expect, it } from 'vitest';
describe('VnSmsDialog', () => {
let vm;
const orderId = 1;
const shipped = new Date();
const phone = '012345678';
const promise = (response) => {return response;};
const promise = (response) => {
return response;
};
const template = 'minAmount';
const locale = 'en';
@ -17,13 +18,13 @@ describe('VnSmsDialog', () => {
propsData: {
data: {
orderId,
shipped
shipped,
},
template,
locale,
phone,
promise
}
promise,
},
}).vm;
});
@ -35,7 +36,9 @@ describe('VnSmsDialog', () => {
it('should update the message value with the correct template and parameters', () => {
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,
shipped,
destination: phone,
message: vm.message
message: vm.message,
};
await vm.send();

View File

@ -17,7 +17,7 @@ const token = getTokenMultimedia();
const { t } = useI18n();
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 showLetter = ref(false);

View File

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

View File

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

View File

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

View File

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

View File

@ -1,18 +1,39 @@
<script setup>
import AccountDescriptorProxy from 'src/pages/Account/Card/AccountDescriptorProxy.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
defineProps({
import { ref, onMounted } from 'vue';
import axios from 'axios';
const $props = defineProps({
name: { type: String, default: null },
tag: { type: String, default: null },
workerId: { type: Number, default: null },
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>
<template>
<slot name="link">
<span :class="{ link: workerId }">
{{ defaultName ? name ?? $t('globals.system') : name }}
{{ defaultName ? (name ?? $t('globals.system')) : name }}
</span>
</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>

View File

@ -12,12 +12,12 @@ function generateWrapper(storage = 'images') {
id: 123,
zoomResolution: '400x400',
storage,
}
},
});
wrapper = wrapper.wrapper;
vm = wrapper.vm;
vm.timeStamp = 'timestamp';
};
}
vi.mock('src/composables/useSession', () => ({
useSession: () => ({
@ -31,7 +31,6 @@ vi.mock('src/composables/useRole', () => ({
}),
}));
describe('VnImg', () => {
beforeEach(() => {
isEmployeeMock.mockReset();
@ -63,7 +62,9 @@ describe('VnImg', () => {
generateWrapper();
await vm.$nextTick();
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 () => {
@ -71,7 +72,9 @@ describe('VnImg', () => {
generateWrapper();
await vm.$nextTick();
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(
acl.hasAny([
{ model: 'Worker', props: 'updateAttributes', accessType: 'WRITE' },
])
]),
).toBeFalsy();
});
it('should return false if no roles matched', async () => {
expect(
acl.hasAny([{ model: 'Worker', props: 'holidays', accessType: 'READ' }])
acl.hasAny([{ model: 'Worker', props: 'holidays', accessType: 'READ' }]),
).toBeTruthy();
});
describe('*', () => {
it('should return true if an acl matched', async () => {
expect(
acl.hasAny([{ model: 'Address', props: '*', accessType: 'WRITE' }])
acl.hasAny([{ model: 'Address', props: '*', accessType: 'WRITE' }]),
).toBeTruthy();
});
it('should return false if no acls matched', async () => {
expect(
acl.hasAny([{ model: 'Worker', props: '*', accessType: 'READ' }])
acl.hasAny([{ model: 'Worker', props: '*', accessType: 'READ' }]),
).toBeFalsy();
});
});
@ -80,13 +80,15 @@ describe('useAcl', () => {
describe('$authenticated', () => {
it('should return false if no acls matched', async () => {
expect(
acl.hasAny([{ model: 'Url', props: 'getByUser', accessType: '*' }])
acl.hasAny([{ model: 'Url', props: 'getByUser', accessType: '*' }]),
).toBeFalsy();
});
it('should return true if an acl matched', async () => {
expect(
acl.hasAny([{ model: 'Url', props: 'getByUser', accessType: 'READ' }])
acl.hasAny([
{ model: 'Url', props: 'getByUser', accessType: 'READ' },
]),
).toBeTruthy();
});
});
@ -96,7 +98,7 @@ describe('useAcl', () => {
expect(
acl.hasAny([
{ model: 'TpvTransaction', props: 'start', accessType: 'READ' },
])
]),
).toBeFalsy();
});
@ -104,7 +106,7 @@ describe('useAcl', () => {
expect(
acl.hasAny([
{ model: 'TpvTransaction', props: 'start', accessType: 'WRITE' },
])
]),
).toBeTruthy();
});
});

View File

@ -18,7 +18,7 @@ export async function downloadFile(id, model = 'dms', urlPath = '/downloadFile',
export async function downloadDocuware(url, params) {
const appUrl = await getAppUrl();
const response = await axios.get(`${appUrl}/api/` + url, {
const response = await axios.get(`${appUrl}/api/${url}`, {
responseType: 'blob',
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';
return 'text-' + (align ?? 'center');
return `text-${align ?? 'center'}`;
}

View File

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

View File

@ -2,9 +2,8 @@ import { ref } from 'vue';
import moment from 'moment';
export default function useWeekdaysOrder() {
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);
}

View File

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

View File

@ -3,5 +3,4 @@ import { useQuasar } from 'quasar';
export default function () {
const quasar = useQuasar();
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) {
return '#' + colors[djb2a(value || '') % colors.length];
return `#${colors[djb2a(value || '') % colors.length]}`;
}
const colors = [

View File

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

View File

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

View File

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

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

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

View File

@ -1,10 +1,10 @@
export default function toDateString(date) {
let day = date.getDate();
let month = date.getMonth() + 1;
let year = date.getFullYear();
const year = date.getFullYear();
if (day < 10) day = `0${day}`;
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) {
if (!value) return;
if (typeof (value) !== 'string') return value;
if (typeof value !== 'string') return value;
return value.charAt(0).toLowerCase() + value.slice(1);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -57,7 +57,7 @@ watch(
store.url = urlPath.value;
store.filter = filter;
fetchAliases();
}
},
);
const fetchAliases = () => paginateRef.value.fetch();
@ -91,7 +91,7 @@ const fetchAliases = () => paginateRef.value.fetch();
openConfirmationModal(
t('User will be removed from alias'),
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(
() =>
formData.value.forwardTo !== initialData.value.forwardTo ||
initialData.value.hasData !== hasData.value
initialData.value.hasData !== hasData.value,
);
const fetchMailForwards = async () => {
@ -77,7 +77,7 @@ const setInitialData = async () => {
watch(
() => route.params.id,
() => setInitialData()
() => setInitialData(),
);
onMounted(async () => await setInitialData());

View File

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

View File

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

View File

@ -43,7 +43,7 @@ watch(
store.url = urlPath.value;
store.filter = filter.value;
fetchSubRoles();
}
},
);
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 { useArrayData } from 'composables/useArrayData';
import RightMenu from 'src/components/common/RightMenu.vue';
import useNotify from 'src/composables/useNotify.js';
const { t } = useI18n();
const { notify } = useNotify();
const quasar = useQuasar();
const route = useRoute();
const claim = ref(null);
@ -176,12 +178,17 @@ async function save(data) {
}
async function importToNewRefundTicket() {
try {
await post(`ClaimBeginnings/${claimId}/importToNewRefundTicket`);
await claimActionsForm.value.reload();
quasar.notify({
message: t('globals.dataSaved'),
type: 'positive',
});
} catch (error) {
const errorMessage = error.response?.data?.error?.message;
notify(t(errorMessage), 'negative');
}
}
async function post(query, params) {

View File

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

View File

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

View File

@ -78,6 +78,8 @@ const columns = computed(() => [
label: t('Quantity'),
field: ({ sale }) => sale.quantity,
sortable: true,
style: 'padding-right: 2%;',
headerStyle: 'padding-right: 2%;',
},
{
name: 'claimed',
@ -110,6 +112,8 @@ const columns = computed(() => [
field: ({ sale }) => totalRow(sale),
format: (value) => toCurrency(value),
sortable: true,
style: 'padding-right: 2%;',
headerStyle: 'padding-right: 2%;',
},
]);
@ -152,10 +156,29 @@ function showImportDialog() {
.onOk(() => claimLinesForm.value.reload());
}
async function saveWhenHasChanges() {
if (claimLinesForm.value.getChanges().updates) {
await claimLinesForm.value.onSubmit();
onFetch(claimLinesForm.value.formData);
function fillClaimedQuantities() {
const formData = claimLinesForm.value.formData;
let hasChanges = false;
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>
@ -186,14 +209,14 @@ async function saveWhenHasChanges() {
/>
<div class="q-pa-md">
<CrudModel
data-key="ClaimLines"
data-key="claimLines"
ref="claimLinesForm"
:go-to="`photos`"
:url="`Claims/${route.params.id}/lines`"
save-url="ClaimBeginnings/crud"
:user-filter="linesFilter"
@on-fetch="onFetch"
v-model:selected="selected"
:default-save="false"
:default-reset="false"
auto-load
:limit="0"
@ -210,13 +233,7 @@ async function saveWhenHasChanges() {
>
<template #body-cell-claimed="{ row }">
<QTd auto-width align="right" class="text-primary shrink">
<QInput
v-model.number="row.quantity"
type="number"
dense
@keyup.enter="saveWhenHasChanges()"
@blur="saveWhenHasChanges()"
/>
<QInput v-model.number="row.quantity" type="number" dense />
</QTd>
</template>
<template #body-cell-description="{ row, value }">
@ -272,10 +289,6 @@ async function saveWhenHasChanges() {
type="number"
dense
autofocus
@keyup.enter="
saveWhenHasChanges()
"
@blur="saveWhenHasChanges()"
/>
</QItemLabel>
</template>
@ -313,6 +326,18 @@ async function saveWhenHasChanges() {
</template>
</QTable>
</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>
</div>
@ -358,6 +383,8 @@ es:
Delete claimed sales: Eliminar ventas reclamadas
Discount updated: Descuento actualizado
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: '
Vas a eliminar <strong>{count}</strong> línea |
Vas a eliminar <strong>{count}</strong> líneas'

View File

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

View File

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

View File

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

View File

@ -50,8 +50,11 @@ const filterClientFindOne = {
>
<template #form="{ data }">
<VnRow>
<QCheckbox :label="t('Unpaid client')" v-model="data.unpaid"
data-cy="UnpaidCheckBox" />
<QCheckbox
:label="t('Unpaid client')"
v-model="data.unpaid"
data-cy="UnpaidCheckBox"
/>
</VnRow>
<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">
{{
`${t(
'Allowed content types'
'Allowed content types',
)}: ${allowedContentTypes.join(', ')}`
}}
</QTooltip>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -106,8 +106,8 @@ const setTicketsRoute = async () => {
(selectedRows.value || [])
.filter((ticket) => ticket?.id)
.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`);
emit('ok');

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -46,7 +46,9 @@ async function getClaims() {
originalTicket.value = data[0]?.originalTicketFk;
}
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;
problems.value = data[0];
}

View File

@ -89,7 +89,7 @@ defineExpose({ save });
</div>
<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>
</div>
</div>
@ -135,3 +135,4 @@ defineExpose({ save });
min-width: 230px;
}
</style>
<

View File

@ -30,7 +30,7 @@ watch(
async (val) => {
crudModelFilter.where.ticketFk = val;
tableRef.value.reload();
}
},
);
const crudModelFilter = reactive({
@ -149,9 +149,9 @@ const columns = computed(() => [
openConfirmationModal(
t('You are going to delete this ticket purchase request'),
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) => {
try {
await axios.post(`Sales/${sale.id}/updatePrice`, {
newPrice: newPrice,
newPrice,
componentId: componentId.value,
});
notify('globals.dataSaved', 'positive');

View File

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

View File

@ -50,7 +50,7 @@ describe('TicketBoxing', () => {
await vm.getVideoList(expeditionId, timed);
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