Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix-front into 6426-refactorConstants

This commit is contained in:
Carlos Satorres 2025-02-06 09:53:09 +01:00
commit ded1dae481
127 changed files with 1359 additions and 985 deletions

View File

@ -1,74 +1,74 @@
{ {
"name": "salix-front", "name": "salix-front",
"version": "25.06.0", "version": "25.08.0",
"description": "Salix frontend", "description": "Salix frontend",
"productName": "Salix", "productName": "Salix",
"author": "Verdnatura", "author": "Verdnatura",
"private": true, "private": true,
"packageManager": "pnpm@8.15.1", "packageManager": "pnpm@8.15.1",
"type": "module", "type": "module",
"scripts": { "scripts": {
"resetDatabase": "cd ../salix && gulp docker", "resetDatabase": "cd ../salix && gulp docker",
"lint": "eslint --ext .js,.vue ./", "lint": "eslint --ext .js,.vue ./",
"format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore", "format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore",
"test:e2e": "cypress open", "test:e2e": "cypress open",
"test:e2e:ci": "npm run resetDatabase && cd ../salix-front && cypress run", "test:e2e:ci": "npm run resetDatabase && cd ../salix-front && cypress run",
"test": "echo \"See package.json => scripts for available tests.\" && exit 0", "test": "echo \"See package.json => scripts for available tests.\" && exit 0",
"test:unit": "vitest", "test:unit": "vitest",
"test:unit:ci": "vitest run", "test:unit:ci": "vitest run",
"commitlint": "commitlint --edit", "commitlint": "commitlint --edit",
"prepare": "npx husky install", "prepare": "npx husky install",
"addReferenceTag": "node .husky/addReferenceTag.js", "addReferenceTag": "node .husky/addReferenceTag.js",
"docs:dev": "vitepress dev docs", "docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs", "docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs" "docs:preview": "vitepress preview docs"
}, },
"dependencies": { "dependencies": {
"@quasar/cli": "^2.4.1", "@quasar/cli": "^2.4.1",
"@quasar/extras": "^1.16.16", "@quasar/extras": "^1.16.16",
"axios": "^1.4.0", "axios": "^1.4.0",
"chromium": "^3.0.3", "chromium": "^3.0.3",
"croppie": "^2.6.5", "croppie": "^2.6.5",
"moment": "^2.30.1", "moment": "^2.30.1",
"pinia": "^2.1.3", "pinia": "^2.1.3",
"quasar": "^2.17.7", "quasar": "^2.17.7",
"validator": "^13.9.0", "validator": "^13.9.0",
"vue": "^3.5.13", "vue": "^3.5.13",
"vue-i18n": "^9.3.0", "vue-i18n": "^9.3.0",
"vue-router": "^4.2.5" "vue-router": "^4.2.5"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^19.2.1", "@commitlint/cli": "^19.2.1",
"@commitlint/config-conventional": "^19.1.0", "@commitlint/config-conventional": "^19.1.0",
"@intlify/unplugin-vue-i18n": "^0.8.2", "@intlify/unplugin-vue-i18n": "^0.8.2",
"@pinia/testing": "^0.1.2", "@pinia/testing": "^0.1.2",
"@quasar/app-vite": "^2.0.8", "@quasar/app-vite": "^2.0.8",
"@quasar/quasar-app-extension-qcalendar": "^4.0.2", "@quasar/quasar-app-extension-qcalendar": "^4.0.2",
"@quasar/quasar-app-extension-testing-unit-vitest": "^0.4.0", "@quasar/quasar-app-extension-testing-unit-vitest": "^0.4.0",
"@vue/test-utils": "^2.4.4", "@vue/test-utils": "^2.4.4",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",
"cypress": "^13.6.6", "cypress": "^13.6.6",
"cypress-mochawesome-reporter": "^3.8.2", "cypress-mochawesome-reporter": "^3.8.2",
"eslint": "^9.18.0", "eslint": "^9.18.0",
"eslint-config-prettier": "^10.0.1", "eslint-config-prettier": "^10.0.1",
"eslint-plugin-cypress": "^4.1.0", "eslint-plugin-cypress": "^4.1.0",
"eslint-plugin-vue": "^9.32.0", "eslint-plugin-vue": "^9.32.0",
"husky": "^8.0.0", "husky": "^8.0.0",
"postcss": "^8.4.23", "postcss": "^8.4.23",
"prettier": "^3.4.2", "prettier": "^3.4.2",
"sass": "^1.83.4", "sass": "^1.83.4",
"vitepress": "^1.6.3", "vitepress": "^1.6.3",
"vitest": "^0.34.0" "vitest": "^0.34.0"
}, },
"engines": { "engines": {
"node": "^20 || ^18 || ^16", "node": "^20 || ^18 || ^16",
"npm": ">= 8.1.2", "npm": ">= 8.1.2",
"yarn": ">= 1.21.1", "yarn": ">= 1.21.1",
"bun": ">= 1.0.25" "bun": ">= 1.0.25"
}, },
"overrides": { "overrides": {
"@vitejs/plugin-vue": "^5.2.1", "@vitejs/plugin-vue": "^5.2.1",
"vite": "^6.0.11", "vite": "^6.0.11",
"vitest": "^0.31.1" "vitest": "^0.31.1"
} }
} }

View File

@ -1,6 +1,6 @@
export default { export default {
mounted: function (el, binding) { mounted(el, binding) {
const shortcut = binding.value ?? '+'; const shortcut = binding.value || '+';
const { key, ctrl, alt, callback } = const { key, ctrl, alt, callback } =
typeof shortcut === 'string' typeof shortcut === 'string'
@ -8,25 +8,24 @@ export default {
key: shortcut, key: shortcut,
ctrl: true, ctrl: true,
alt: true, alt: true,
callback: () => callback: () => el?.click(),
document
.querySelector(`button[shortcut="${shortcut}"]`)
?.click(),
} }
: binding.value; : binding.value;
if (!el.hasAttribute('shortcut')) {
el.setAttribute('shortcut', key);
}
const handleKeydown = (event) => { const handleKeydown = (event) => {
if (event.key === key && (!ctrl || event.ctrlKey) && (!alt || event.altKey)) { if (event.key === key && (!ctrl || event.ctrlKey) && (!alt || event.altKey)) {
callback(); callback();
} }
}; };
// Attach the event listener to the window
window.addEventListener('keydown', handleKeydown); window.addEventListener('keydown', handleKeydown);
el._handleKeydown = handleKeydown; el._handleKeydown = handleKeydown;
}, },
unmounted: function (el) { unmounted(el) {
if (el._handleKeydown) { if (el._handleKeydown) {
window.removeEventListener('keydown', el._handleKeydown); window.removeEventListener('keydown', el._handleKeydown);
} }

View File

@ -31,7 +31,6 @@ const props = defineProps({
const route = useRoute(); const route = useRoute();
const itemTypesOptions = ref([]); const itemTypesOptions = ref([]);
const suppliersOptions = ref([]);
const tagOptions = ref([]); const tagOptions = ref([]);
const tagValues = ref([]); const tagValues = ref([]);
const categoryList = ref(null); const categoryList = ref(null);
@ -40,13 +39,13 @@ const selectedTypeFk = ref(getParamWhere(route.query.table, 'typeFk', false));
const selectedCategory = computed(() => { const selectedCategory = computed(() => {
return (categoryList.value || []).find( return (categoryList.value || []).find(
(category) => category?.id === selectedCategoryFk.value (category) => category?.id === selectedCategoryFk.value,
); );
}); });
const selectedType = computed(() => { const selectedType = computed(() => {
return (itemTypesOptions.value || []).find( return (itemTypesOptions.value || []).find(
(type) => type?.id === selectedTypeFk.value (type) => type?.id === selectedTypeFk.value,
); );
}); });
@ -134,13 +133,6 @@ const setCategoryList = (data) => {
<template> <template>
<FetchData url="ItemCategories" limit="30" auto-load @on-fetch="setCategoryList" /> <FetchData url="ItemCategories" limit="30" auto-load @on-fetch="setCategoryList" />
<FetchData
url="Suppliers"
limit="30"
auto-load
:filter="{ fields: ['id', 'name', 'nickname'], order: 'name ASC', limit: 30 }"
@on-fetch="(data) => (suppliersOptions = data)"
/>
<FetchData <FetchData
url="Tags" url="Tags"
:filter="{ fields: ['id', 'name', 'isFree'] }" :filter="{ fields: ['id', 'name', 'isFree'] }"
@ -290,7 +282,7 @@ const setCategoryList = (data) => {
<QItem class="q-mt-lg"> <QItem class="q-mt-lg">
<QBtn <QBtn
icon="add_circle" icon="add_circle"
shortcut="+" v-shortcut="'+'"
flat flat
class="fill-icon-on-hover q-px-xs" class="fill-icon-on-hover q-px-xs"
color="primary" color="primary"

View File

@ -10,12 +10,13 @@ import routes from 'src/router/modules';
import LeftMenuItem from './LeftMenuItem.vue'; import LeftMenuItem from './LeftMenuItem.vue';
import LeftMenuItemGroup from './LeftMenuItemGroup.vue'; import LeftMenuItemGroup from './LeftMenuItemGroup.vue';
import VnInput from './common/VnInput.vue'; import VnInput from './common/VnInput.vue';
import { useRouter } from 'vue-router';
const { t } = useI18n(); const { t } = useI18n();
const route = useRoute(); const route = useRoute();
const quasar = useQuasar(); const quasar = useQuasar();
const navigation = useNavigationStore(); const navigation = useNavigationStore();
const router = useRouter();
const props = defineProps({ const props = defineProps({
source: { source: {
type: String, type: String,
@ -174,6 +175,10 @@ function normalize(text) {
.replace(/[\u0300-\u036f]/g, '') .replace(/[\u0300-\u036f]/g, '')
.toLowerCase(); .toLowerCase();
} }
const searchModule = () => {
const [item] = filteredItems.value;
if (item) router.push({ name: item.name });
};
</script> </script>
<template> <template>
@ -188,10 +193,11 @@ function normalize(text) {
filled filled
dense dense
autofocus autofocus
@keyup.enter.stop="searchModule()"
/> />
</QItem> </QItem>
<QSeparator /> <QSeparator />
<template v-if="filteredPinnedModules.size"> <template v-if="filteredPinnedModules.size && !search">
<LeftMenuItem <LeftMenuItem
v-for="[key, pinnedModule] of filteredPinnedModules" v-for="[key, pinnedModule] of filteredPinnedModules"
:key="key" :key="key"
@ -215,11 +221,11 @@ function normalize(text) {
</LeftMenuItem> </LeftMenuItem>
<QSeparator /> <QSeparator />
</template> </template>
<template v-for="item in filteredItems" :key="item.name"> <template v-for="(item, index) in filteredItems" :key="item.name">
<template <template
v-if="item.children && !filteredPinnedModules.has(item.name)" v-if="search ||item.children && !filteredPinnedModules.has(item.name)"
> >
<LeftMenuItem :item="item" group="modules"> <LeftMenuItem :item="item" group="modules" :class="search && index === 0 ? 'searched' : ''">
<template #side> <template #side>
<QBtn <QBtn
v-if="item.isPinned === true" v-if="item.isPinned === true"
@ -336,6 +342,9 @@ function normalize(text) {
.header { .header {
color: var(--vn-label-color); color: var(--vn-label-color);
} }
.searched{
background-color: var(--vn-section-hover-color);
}
</style> </style>
<i18n> <i18n>
es: es:

View File

@ -2,26 +2,9 @@
defineProps({ row: { type: Object, required: true } }); defineProps({ row: { type: Object, required: true } });
</script> </script>
<template> <template>
<span> <span class="q-gutter-x-xs">
<QIcon <QIcon
v-if="row.isTaxDataChecked === 0" v-if="row?.risk"
name="vn:no036"
color="primary"
size="xs"
>
<QTooltip>{{ $t('salesTicketsTable.noVerifiedData') }}</QTooltip>
</QIcon>
<QIcon v-if="row.hasTicketRequest" name="vn:buyrequest" color="primary" size="xs">
<QTooltip>{{ $t('salesTicketsTable.purchaseRequest') }}</QTooltip>
</QIcon>
<QIcon v-if="row.itemShortage" name="vn:unavailable" color="primary" size="xs">
<QTooltip>{{ $t('salesTicketsTable.notVisible') }}</QTooltip>
</QIcon>
<QIcon v-if="row.isFreezed" name="vn:frozen" color="primary" size="xs">
<QTooltip>{{ $t('salesTicketsTable.clientFrozen') }}</QTooltip>
</QIcon>
<QIcon
v-if="row.risk"
name="vn:risk" name="vn:risk"
:color="row.hasHighRisk ? 'negative' : 'primary'" :color="row.hasHighRisk ? 'negative' : 'primary'"
size="xs" size="xs"
@ -30,10 +13,57 @@ defineProps({ row: { type: Object, required: true } });
{{ $t('salesTicketsTable.risk') }}: {{ row.risk - row.credit }} {{ $t('salesTicketsTable.risk') }}: {{ row.risk - row.credit }}
</QTooltip> </QTooltip>
</QIcon> </QIcon>
<QIcon v-if="row.hasComponentLack" name="vn:components" color="primary" size="xs"> <QIcon
v-if="row?.hasComponentLack"
name="vn:components"
color="primary"
size="xs"
>
<QTooltip>{{ $t('salesTicketsTable.componentLack') }}</QTooltip> <QTooltip>{{ $t('salesTicketsTable.componentLack') }}</QTooltip>
</QIcon> </QIcon>
<QIcon v-if="row.isTooLittle" name="vn:isTooLittle" color="primary" size="xs"> <QIcon v-if="row?.hasItemDelay" color="primary" size="xs" name="vn:hasItemDelay">
<QTooltip>
{{ $t('ticket.summary.hasItemDelay') }}
</QTooltip>
</QIcon>
<QIcon v-if="row?.hasItemLost" color="primary" size="xs" name="vn:hasItemLost">
<QTooltip>
{{ $t('salesTicketsTable.hasItemLost') }}
</QTooltip>
</QIcon>
<QIcon
v-if="row?.hasItemShortage"
name="vn:unavailable"
color="primary"
size="xs"
>
<QTooltip>{{ $t('salesTicketsTable.notVisible') }}</QTooltip>
</QIcon>
<QIcon v-if="row?.hasRounding" color="primary" name="sync_problem" size="xs">
<QTooltip>
{{ $t('ticketList.rounding') }}
</QTooltip>
</QIcon>
<QIcon
v-if="row?.hasTicketRequest"
name="vn:buyrequest"
color="primary"
size="xs"
>
<QTooltip>{{ $t('salesTicketsTable.purchaseRequest') }}</QTooltip>
</QIcon>
<QIcon
v-if="!row?.isTaxDataChecked === 0"
name="vn:no036"
color="primary"
size="xs"
>
<QTooltip>{{ $t('salesTicketsTable.noVerifiedData') }}</QTooltip>
</QIcon>
<QIcon v-if="row?.isFreezed" name="vn:frozen" color="primary" size="xs">
<QTooltip>{{ $t('salesTicketsTable.clientFrozen') }}</QTooltip>
</QIcon>
<QIcon v-if="row?.isTooLittle" name="vn:isTooLittle" color="primary" size="xs">
<QTooltip>{{ $t('salesTicketsTable.tooLittle') }}</QTooltip> <QTooltip>{{ $t('salesTicketsTable.tooLittle') }}</QTooltip>
</QIcon> </QIcon>
</span> </span>

View File

@ -500,7 +500,7 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
<QCard <QCard
bordered bordered
flat flat
class="row no-wrap justify-between cursor-pointer" class="row no-wrap justify-between cursor-pointer q-pa-sm"
@click=" @click="
(_, row) => { (_, row) => {
$props.rowClick && $props.rowClick(row); $props.rowClick && $props.rowClick(row);
@ -581,7 +581,6 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
<!-- Actions --> <!-- Actions -->
<QCardSection <QCardSection
v-if="colsMap.tableActions" v-if="colsMap.tableActions"
class="column flex-center w-10 no-margin q-pa-xs q-gutter-y-xs"
@click="stopEventPropagation($event)" @click="stopEventPropagation($event)"
> >
<QBtn <QBtn
@ -630,7 +629,7 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
size="md" size="md"
round round
flat flat
shortcut="+" v-shortcut="'+'"
:disabled="!disabledAttr" :disabled="!disabledAttr"
/> />
<QTooltip> <QTooltip>
@ -648,7 +647,7 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
color="primary" color="primary"
fab fab
icon="add" icon="add"
shortcut="+" v-shortcut="'+'"
data-cy="vnTableCreateBtn" data-cy="vnTableCreateBtn"
/> />
<QTooltip self="top right"> <QTooltip self="top right">
@ -807,12 +806,15 @@ es:
.grid-two { .grid-two {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, max-content)); grid-template-columns: 2fr 2fr;
max-width: 100%; .vn-label-value {
margin: 0 auto; flex-direction: column;
overflow: scroll; white-space: nowrap;
white-space: wrap; .fields {
width: 100%; display: flex;
}
}
white-space: nowrap;
} }
.w-80 { .w-80 {

View File

@ -0,0 +1,61 @@
import { vi, describe, expect, it, beforeEach, beforeAll, afterEach } from 'vitest';
import { createWrapper } from 'app/test/vitest/helper';
import UserPanel from 'src/components/UserPanel.vue';
import axios from 'axios';
import { useState } from 'src/composables/useState';
describe('UserPanel', () => {
let wrapper;
let vm;
let state;
beforeEach(() => {
wrapper = createWrapper(UserPanel, {});
state = useState();
state.setUser({
id: 115,
name: 'itmanagement',
nickname: 'itManagementNick',
lang: 'en',
darkMode: false,
companyFk: 442,
warehouseFk: 1,
});
wrapper = wrapper.wrapper;
vm = wrapper.vm;
});
afterEach(() => {
vi.clearAllMocks();
});
it('should fetch warehouses data on mounted', async () => {
const fetchData = wrapper.findComponent({ name: 'FetchData' });
expect(fetchData.props('url')).toBe('Warehouses');
expect(fetchData.props('autoLoad')).toBe(true);
});
it('should toggle dark mode correctly and update preferences', async () => {
await vm.saveDarkMode(true);
expect(axios.patch).toHaveBeenCalledWith('/UserConfigs/115', { darkMode: true });
expect(vm.user.darkMode).toBe(true);
vm.updatePreferences();
expect(vm.darkMode).toBe(true);
});
it('should change user language and update preferences', async () => {
const userLanguage = 'es';
await vm.saveLanguage(userLanguage);
expect(axios.patch).toHaveBeenCalledWith('/VnUsers/115', { lang: userLanguage });
expect(vm.user.lang).toBe(userLanguage);
vm.updatePreferences();
expect(vm.locale).toBe(userLanguage);
});
it('should update user data', async () => {
const key = 'name';
const value = 'itboss';
await vm.saveUserData(key, value);
expect(axios.post).toHaveBeenCalledWith('UserConfigs/setUserConfig', { [key]: value });
});
});

View File

@ -17,7 +17,7 @@ import { useSession } from 'src/composables/useSession';
const route = useRoute(); const route = useRoute();
const quasar = useQuasar(); const quasar = useQuasar();
const { t } = useI18n(); const { t } = useI18n();
const rows = ref(); const rows = ref([]);
const dmsRef = ref(); const dmsRef = ref();
const formDialog = ref({}); const formDialog = ref({});
const token = useSession().getTokenMultimedia(); const token = useSession().getTokenMultimedia();
@ -389,6 +389,14 @@ defineExpose({
</div> </div>
</template> </template>
</QTable> </QTable>
<div
v-else
class="info-row q-pa-md text-center"
>
<h5>
{{ t('No data to display') }}
</h5>
</div>
</template> </template>
</VnPaginate> </VnPaginate>
<QDialog v-model="formDialog.show"> <QDialog v-model="formDialog.show">
@ -405,7 +413,7 @@ defineExpose({
fab fab
color="primary" color="primary"
icon="add" icon="add"
shortcut="+" v-shortcut
@click="showFormDialog()" @click="showFormDialog()"
class="fill-icon" class="fill-icon"
> >

View File

@ -0,0 +1,35 @@
<script setup>
import { computed } from 'vue';
import VnSelect from 'components/common/VnSelect.vue';
const model = defineModel({ type: [String, Number, Object] });
const url = 'Suppliers';
</script>
<template>
<VnSelect
:label="$t('globals.supplier')"
v-bind="$attrs"
v-model="model"
:url="url"
option-value="id"
option-label="nickname"
:fields="['id', 'name', 'nickname', 'nif']"
sort-by="name ASC"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>
{{ scope.opt?.name }}
</QItemLabel>
<QItemLabel caption>
{{
`#${scope.opt?.id} , ${scope.opt?.nickname} (${scope.opt?.nif})`
}}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
</template>

View File

@ -53,6 +53,7 @@ const url = computed(() => {
:fields="['id', 'name', 'nickname', 'code']" :fields="['id', 'name', 'nickname', 'code']"
:filter-options="['id', 'name', 'nickname', 'code']" :filter-options="['id', 'name', 'nickname', 'code']"
sort-by="nickname ASC" sort-by="nickname ASC"
data-cy="vnWorkerSelect"
> >
<template #prepend v-if="$props.hasAvatar"> <template #prepend v-if="$props.hasAvatar">
<VnAvatar :worker-id="value" color="primary" v-bind="$attrs" /> <VnAvatar :worker-id="value" color="primary" v-bind="$attrs" />

View File

@ -1,51 +1,78 @@
import { describe, it, expect, vi, beforeAll, afterEach, beforeEach } from 'vitest'; import {
describe,
it,
expect,
vi,
beforeAll,
afterEach,
beforeEach,
afterAll,
} from 'vitest';
import { createWrapper, axios } from 'app/test/vitest/helper'; import { createWrapper, axios } from 'app/test/vitest/helper';
import VnNotes from 'src/components/ui/VnNotes.vue'; import VnNotes from 'src/components/ui/VnNotes.vue';
import vnDate from 'src/boot/vnDate';
describe('VnNotes', () => { describe('VnNotes', () => {
let vm; let vm;
let wrapper; let wrapper;
let spyFetch; let spyFetch;
let postMock; let postMock;
let expectedBody; let patchMock;
const mockData= {name: 'Tony', lastName: 'Stark', text: 'Test Note', observationTypeFk: 1}; let expectedInsertBody;
let expectedUpdateBody;
function generateExpectedBody() { const defaultOptions = {
expectedBody = {...vm.$props.body, ...{ text: vm.newNote.text, observationTypeFk: vm.newNote.observationTypeFk }}; url: '/test',
} body: { name: 'Tony', lastName: 'Stark' },
selectType: false,
async function setTestParams(text, observationType, type){ saveUrl: null,
vm.newNote.text = text; justInput: false,
vm.newNote.observationTypeFk = observationType; };
wrapper.setProps({ selectType: type }); function generateWrapper(
} options = defaultOptions,
text = null,
beforeAll(async () => { observationType = null,
vi.spyOn(axios, 'get').mockReturnValue({ data: [] }); ) {
vi.spyOn(axios, 'get').mockResolvedValue({ data: [] });
wrapper = createWrapper(VnNotes, { wrapper = createWrapper(VnNotes, {
propsData: { propsData: options,
url: '/test',
body: { name: 'Tony', lastName: 'Stark' },
}
}); });
wrapper = wrapper.wrapper; wrapper = wrapper.wrapper;
vm = wrapper.vm; vm = wrapper.vm;
}); vm.newNote.text = text;
vm.newNote.observationTypeFk = observationType;
}
function createSpyFetch() {
spyFetch = vi.spyOn(vm.$refs.vnPaginateRef, 'fetch');
}
function generateExpectedBody() {
expectedInsertBody = {
...vm.$props.body,
...{ text: vm.newNote.text, observationTypeFk: vm.newNote.observationTypeFk },
};
expectedUpdateBody = { ...vm.$props.body, ...{ notes: vm.newNote.text } };
}
beforeEach(() => { beforeEach(() => {
postMock = vi.spyOn(axios, 'post').mockResolvedValue(mockData); postMock = vi.spyOn(axios, 'post');
spyFetch = vi.spyOn(vm.vnPaginateRef, 'fetch').mockImplementation(() => vi.fn()); patchMock = vi.spyOn(axios, 'patch');
}); });
afterEach(() => { afterEach(() => {
vi.clearAllMocks(); vi.clearAllMocks();
expectedBody = {}; expectedInsertBody = {};
expectedUpdateBody = {};
});
afterAll(() => {
vi.restoreAllMocks();
}); });
describe('insert', () => { describe('insert', () => {
it('should not call axios.post and vnPaginateRef.fetch if newNote.text is null', async () => { it('should not call axios.post and vnPaginateRef.fetch when newNote.text is null', async () => {
await setTestParams( null, null, true ); generateWrapper({ selectType: true });
createSpyFetch();
await vm.insert(); await vm.insert();
@ -53,8 +80,9 @@ describe('VnNotes', () => {
expect(spyFetch).not.toHaveBeenCalled(); expect(spyFetch).not.toHaveBeenCalled();
}); });
it('should not call axios.post and vnPaginateRef.fetch if newNote.text is empty', async () => { it('should not call axios.post and vnPaginateRef.fetch when newNote.text is empty', async () => {
await setTestParams( "", null, false ); generateWrapper(null, '');
createSpyFetch();
await vm.insert(); await vm.insert();
@ -62,8 +90,9 @@ describe('VnNotes', () => {
expect(spyFetch).not.toHaveBeenCalled(); expect(spyFetch).not.toHaveBeenCalled();
}); });
it('should not call axios.post and vnPaginateRef.fetch if observationTypeFk is missing and selectType is true', async () => { it('should not call axios.post and vnPaginateRef.fetch when observationTypeFk is null and selectType is true', async () => {
await setTestParams( "Test Note", null, true ); generateWrapper({ selectType: true }, 'Test Note');
createSpyFetch();
await vm.insert(); await vm.insert();
@ -71,37 +100,57 @@ describe('VnNotes', () => {
expect(spyFetch).not.toHaveBeenCalled(); expect(spyFetch).not.toHaveBeenCalled();
}); });
it('should call axios.post and vnPaginateRef.fetch if observationTypeFk is missing and selectType is false', async () => { it('should call axios.post and vnPaginateRef.fetch when observationTypeFk is missing and selectType is false', async () => {
await setTestParams( "Test Note", null, false ); generateWrapper(null, 'Test Note');
createSpyFetch();
generateExpectedBody(); generateExpectedBody();
await vm.insert(); await vm.insert();
expect(postMock).toHaveBeenCalledWith(vm.$props.url, expectedBody); expect(postMock).toHaveBeenCalledWith(vm.$props.url, expectedInsertBody);
expect(spyFetch).toHaveBeenCalled();
});
it('should call axios.post and vnPaginateRef.fetch if observationTypeFk is setted and selectType is false', async () => {
await setTestParams( "Test Note", 1, false );
generateExpectedBody();
await vm.insert();
expect(postMock).toHaveBeenCalledWith(vm.$props.url, expectedBody);
expect(spyFetch).toHaveBeenCalled(); expect(spyFetch).toHaveBeenCalled();
}); });
it('should call axios.post and vnPaginateRef.fetch when newNote is valid', async () => { it('should call axios.post and vnPaginateRef.fetch when newNote is valid', async () => {
await setTestParams( "Test Note", 1, true ); generateWrapper({ selectType: true }, 'Test Note', 1);
createSpyFetch();
generateExpectedBody(); generateExpectedBody();
await vm.insert(); await vm.insert();
expect(postMock).toHaveBeenCalledWith(vm.$props.url, expectedBody); expect(postMock).toHaveBeenCalledWith(vm.$props.url, expectedInsertBody);
expect(spyFetch).toHaveBeenCalled(); expect(spyFetch).toHaveBeenCalled();
}); });
}); });
});
describe('update', () => {
it('should call axios.patch with saveUrl when saveUrl is set and justInput is true', async () => {
generateWrapper({
url: '/business',
justInput: true,
saveUrl: '/saveUrlTest',
});
generateExpectedBody();
await vm.update();
expect(patchMock).toHaveBeenCalledWith(vm.$props.saveUrl, expectedUpdateBody);
});
it('should call axios.patch with url when saveUrl is not set and justInput is true', async () => {
generateWrapper({
url: '/business',
body: { workerFk: 1110 },
justInput: true,
});
generateExpectedBody();
await vm.update();
expect(patchMock).toHaveBeenCalledWith(
`${vm.$props.url}/${vm.$props.body.workerFk}`,
expectedUpdateBody,
);
});
});
});

View File

@ -15,6 +15,10 @@ const props = defineProps({
type: Object, type: Object,
default: null, default: null,
}, },
userFilter: {
type: Object,
default: null,
},
entityId: { entityId: {
type: [Number, String], type: [Number, String],
default: null, default: null,
@ -34,6 +38,7 @@ const isSummary = ref();
const arrayData = useArrayData(props.dataKey, { const arrayData = useArrayData(props.dataKey, {
url: props.url, url: props.url,
filter: props.filter, filter: props.filter,
userFilter: props.userFilter,
skip: 0, skip: 0,
}); });
const { store } = arrayData; const { store } = arrayData;

View File

@ -1,6 +1,6 @@
<script setup> <script setup>
import axios from 'axios'; import axios from 'axios';
import { ref, reactive } from 'vue'; import { ref, reactive, useAttrs, computed } from 'vue';
import { onBeforeRouteLeave } from 'vue-router'; import { onBeforeRouteLeave } from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
@ -16,12 +16,22 @@ import VnSelect from 'components/common/VnSelect.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnInput from 'components/common/VnInput.vue'; import VnInput from 'components/common/VnInput.vue';
const emit = defineEmits(['onFetch']);
const $attrs = useAttrs();
const isRequired = computed(() => {
return Object.keys($attrs).includes('required')
});
const $props = defineProps({ const $props = defineProps({
url: { type: String, default: null }, url: { type: String, default: null },
saveUrl: {type: String, default: null},
filter: { type: Object, default: () => {} }, filter: { type: Object, default: () => {} },
body: { type: Object, default: () => {} }, body: { type: Object, default: () => {} },
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 },
}); });
const { t } = useI18n(); const { t } = useI18n();
@ -29,6 +39,13 @@ const quasar = useQuasar();
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();
let originalText;
function handleClick(e) {
if (e.shiftKey && e.key === 'Enter') return;
if ($props.justInput) confirmAndUpdate();
else insert();
}
async function insert() { async function insert() {
if (!newNote.text || ($props.selectType && !newNote.observationTypeFk)) return; if (!newNote.text || ($props.selectType && !newNote.observationTypeFk)) return;
@ -41,8 +58,36 @@ async function insert() {
await axios.post($props.url, newBody); await axios.post($props.url, newBody);
await vnPaginateRef.value.fetch(); await vnPaginateRef.value.fetch();
} }
function confirmAndUpdate() {
if(!newNote.text && originalText)
quasar
.dialog({
component: VnConfirm,
componentProps: {
title: t('New note is empty'),
message: t('Are you sure remove this note?'),
},
})
.onOk(update)
.onCancel(() => {
newNote.text = originalText;
});
else update();
}
async function update() {
originalText = newNote.text;
const body = $props.body;
const newBody = {
...body,
...{ notes: newNote.text },
};
await axios.patch(`${$props.saveUrl ?? `${$props.url}/${$props.body.workerFk}`}`, newBody);
}
onBeforeRouteLeave((to, from, next) => { onBeforeRouteLeave((to, from, next) => {
if (newNote.text) if ((newNote.text && !$props.justInput) || (newNote.text !== originalText) && $props.justInput)
quasar.dialog({ quasar.dialog({
component: VnConfirm, component: VnConfirm,
componentProps: { componentProps: {
@ -53,6 +98,13 @@ onBeforeRouteLeave((to, from, next) => {
}); });
else next(); else next();
}); });
function fetchData([ data ]) {
newNote.text = data?.notes;
originalText = data?.notes;
emit('onFetch', data);
}
</script> </script>
<template> <template>
<FetchData <FetchData
@ -62,8 +114,19 @@ onBeforeRouteLeave((to, from, next) => {
auto-load auto-load
@on-fetch="(data) => (observationTypes = data)" @on-fetch="(data) => (observationTypes = data)"
/> />
<QCard class="q-pa-xs q-mb-lg full-width" v-if="$props.addNote"> <FetchData
<QCardSection horizontal> v-if="justInput"
:url="url"
:filter="filter"
@on-fetch="fetchData"
auto-load
/>
<QCard
class="q-pa-xs q-mb-lg full-width"
:class="{ 'just-input': $props.justInput }"
v-if="$props.addNote || $props.justInput"
>
<QCardSection horizontal v-if="!$props.justInput">
{{ t('New note') }} {{ t('New note') }}
</QCardSection> </QCardSection>
<QCardSection class="q-px-xs q-my-none q-py-none"> <QCardSection class="q-px-xs q-my-none q-py-none">
@ -75,19 +138,19 @@ onBeforeRouteLeave((to, from, next) => {
v-model="newNote.observationTypeFk" v-model="newNote.observationTypeFk"
option-label="description" option-label="description"
style="flex: 0.15" style="flex: 0.15"
:required="true" :required="isRequired"
@keyup.enter.stop="insert" @keyup.enter.stop="insert"
/> />
<VnInput <VnInput
v-model.trim="newNote.text" v-model.trim="newNote.text"
type="textarea" type="textarea"
:label="t('Add note here...')" :label="$props.justInput && newNote.text ? '' : t('Add note here...')"
filled filled
size="lg" size="lg"
autogrow autogrow
@keyup.enter.stop="insert" @keyup.enter.stop="handleClick"
:required="isRequired"
clearable clearable
:required="true"
> >
<template #append> <template #append>
<QBtn <QBtn
@ -95,7 +158,7 @@ onBeforeRouteLeave((to, from, next) => {
icon="save" icon="save"
color="primary" color="primary"
flat flat
@click="insert" @click="handleClick"
class="q-mb-xs" class="q-mb-xs"
dense dense
data-cy="saveNote" data-cy="saveNote"
@ -106,6 +169,7 @@ onBeforeRouteLeave((to, from, next) => {
</QCardSection> </QCardSection>
</QCard> </QCard>
<VnPaginate <VnPaginate
v-if="!$props.justInput"
:data-key="$props.url" :data-key="$props.url"
:url="$props.url" :url="$props.url"
order="created DESC" order="created DESC"
@ -198,6 +262,11 @@ onBeforeRouteLeave((to, from, next) => {
} }
} }
} }
.just-input {
padding-right: 18px;
margin-bottom: 2px;
box-shadow: none;
}
</style> </style>
<i18n> <i18n>
es: es:
@ -205,4 +274,6 @@ onBeforeRouteLeave((to, from, next) => {
New note: Nueva nota New note: Nueva nota
Save (Enter): Guardar (Intro) Save (Enter): Guardar (Intro)
Observation type: Tipo de observación Observation type: Tipo de observación
New note is empty: La nueva nota esta vacia
Are you sure remove this note?: Estas seguro de quitar esta nota?
</i18n> </i18n>

View File

@ -123,7 +123,7 @@ watch(
() => props.data, () => props.data,
() => { () => {
store.data = props.data; store.data = props.data;
} },
); );
watch( watch(
@ -132,12 +132,12 @@ watch(
if (!mounted.value) return; if (!mounted.value) return;
emit('onChange', data); emit('onChange', data);
}, },
{ immediate: true } { immediate: true },
); );
watch( watch(
() => [props.url, props.filter], () => [props.url, props.filter],
([url, filter]) => mounted.value && fetch({ url, filter }) ([url, filter]) => mounted.value && fetch({ url, filter }),
); );
const addFilter = async (filter, params) => { const addFilter = async (filter, params) => {
await arrayData.addFilter({ filter, params }); await arrayData.addFilter({ filter, params });
@ -198,7 +198,7 @@ function endPagination() {
async function onLoad(index, done) { async function onLoad(index, done) {
if (!store.data || !mounted.value) return done(); if (!store.data || !mounted.value) return done();
if (store.data.length === 0 || !props.url) return done(false); if (store.data.length === 0 || !arrayData.store.url) return done(false);
pagination.value.page = pagination.value.page + 1; pagination.value.page = pagination.value.page + 1;

View File

@ -19,23 +19,26 @@ onMounted(() => {
const observer = new MutationObserver( const observer = new MutationObserver(
() => () =>
(hasContent.value = (hasContent.value =
actions.value?.childNodes?.length + data.value?.childNodes?.length) actions.value?.childNodes?.length + data.value?.childNodes?.length),
); );
if (actions.value) observer.observe(actions.value, opts); if (actions.value) observer.observe(actions.value, opts);
if (data.value) observer.observe(data.value, opts); if (data.value) observer.observe(data.value, opts);
}); });
onBeforeUnmount(() => stateStore.toggleSubToolbar()); const actionsChildCount = () => !!actions.value?.childNodes?.length;
onBeforeUnmount(() => stateStore.toggleSubToolbar() && hasSubToolbar);
</script> </script>
<template> <template>
<QToolbar <QToolbar
id="subToolbar" id="subToolbar"
class="justify-end sticky"
v-show="hasContent || $slots['st-actions'] || $slots['st-data']" v-show="hasContent || $slots['st-actions'] || $slots['st-data']"
class="justify-end sticky"
> >
<slot name="st-data"> <slot name="st-data">
<div id="st-data"></div> <div id="st-data" :class="{ 'full-width': !actionsChildCount() }">
</div>
</slot> </slot>
<QSpace /> <QSpace />
<slot name="st-actions"> <slot name="st-actions">

View File

@ -93,6 +93,9 @@ export function useArrayData(key, userOptions) {
if (params.filter.where || exprFilter) if (params.filter.where || exprFilter)
params.filter.where = { ...params.filter.where, ...exprFilter }; params.filter.where = { ...params.filter.where, ...exprFilter };
if (!params?.filter?.order?.length) delete params?.filter?.order;
params.filter = JSON.stringify(params.filter); params.filter = JSON.stringify(params.filter);
store.isLoading = true; store.isLoading = true;

Binary file not shown.

View File

@ -127,6 +127,7 @@
<glyph unicode="&#xe975;" glyph-name="stowaway" d="M1006.933 452.267l-260.267 106.667 29.867 29.867c4.267 4.267 4.267 12.8 4.267 17.067-4.267 4.267-8.533 8.533-12.8 8.533h-157.867c0 93.867 76.8 157.867 174.933 157.867 4.267 0 8.533 4.267 12.8 8.533s4.267 8.533 0 17.067l-81.067 153.6c-4.267 0-12.8 4.267-17.067 4.267-46.933 0-93.867-17.067-132.267-42.667-21.333-17.067-42.667-38.4-55.467-59.733-12.8 21.333-29.867 42.667-55.467 59.733-34.133 12.8-81.067 34.133-128 34.133-4.267 0-12.8-4.267-12.8-8.533l-85.333-153.6c-4.267-4.267-4.267-4.267 0-12.8 4.267-4.267 8.533-8.533 12.8-8.533 98.133 0 174.933-59.733 174.933-153.6v0h-140.8c-4.267 0-12.8-4.267-12.8-8.533-8.533-4.267-4.267-17.067 0-21.333l21.333-21.333-277.333-110.933c-8.533-8.533-12.8-12.8-8.533-21.333 0-8.533 8.533-12.8 17.067-12.8v0l98.133 4.267-81.067-85.333c0-4.267-4.267-8.533 0-12.8 0-4.267 4.267-8.533 8.533-8.533l85.333-34.133v-179.2c0-8.533 4.267-12.8 8.533-12.8l358.4-145.067h8.533l358.4 145.067c4.267 4.267 8.533 8.533 8.533 12.8v179.2l85.333 34.133c4.267 0 8.533 4.267 8.533 8.533s0 8.533-4.267 12.8l-68.267 98.133 102.4-4.267c8.533 0 12.8 4.267 17.067 12.8 8.533 0 4.267 4.267-4.267 12.8zM110.933 456.533l196.267 76.8 8.533-8.533-166.4-64-38.4-4.267zM153.6 285.867v0l-68.267 34.133 68.267 98.133 328.533-132.267-68.267-98.133-260.267 98.133zM490.667-29.867l-328.533 132.267v153.6l243.2-98.133h12.8c0 0 0 0 4.267 0v0c0 0 4.267 0 4.267 4.267l64 85.333c0-4.267 0-277.333 0-277.333zM490.667 324.267l-298.667 115.2 149.333 64 153.6-157.867v-17.067h-4.267zM529.067 337.067l157.867 157.867 140.8-55.467-298.667-115.2c0 0 0 12.8 0 12.8zM849.067 102.4l-328.533-132.267v281.6l64-85.333c0 0 0-4.267 4.267-4.267v0h17.067l243.2 98.133v-157.867zM938.667 324.267l-324.267-132.267-68.267 98.133 328.533 132.267 64-98.133zM870.4 460.8l-157.867 64 12.8 8.533 187.733-76.8-42.667 4.267z" /> <glyph unicode="&#xe975;" glyph-name="stowaway" d="M1006.933 452.267l-260.267 106.667 29.867 29.867c4.267 4.267 4.267 12.8 4.267 17.067-4.267 4.267-8.533 8.533-12.8 8.533h-157.867c0 93.867 76.8 157.867 174.933 157.867 4.267 0 8.533 4.267 12.8 8.533s4.267 8.533 0 17.067l-81.067 153.6c-4.267 0-12.8 4.267-17.067 4.267-46.933 0-93.867-17.067-132.267-42.667-21.333-17.067-42.667-38.4-55.467-59.733-12.8 21.333-29.867 42.667-55.467 59.733-34.133 12.8-81.067 34.133-128 34.133-4.267 0-12.8-4.267-12.8-8.533l-85.333-153.6c-4.267-4.267-4.267-4.267 0-12.8 4.267-4.267 8.533-8.533 12.8-8.533 98.133 0 174.933-59.733 174.933-153.6v0h-140.8c-4.267 0-12.8-4.267-12.8-8.533-8.533-4.267-4.267-17.067 0-21.333l21.333-21.333-277.333-110.933c-8.533-8.533-12.8-12.8-8.533-21.333 0-8.533 8.533-12.8 17.067-12.8v0l98.133 4.267-81.067-85.333c0-4.267-4.267-8.533 0-12.8 0-4.267 4.267-8.533 8.533-8.533l85.333-34.133v-179.2c0-8.533 4.267-12.8 8.533-12.8l358.4-145.067h8.533l358.4 145.067c4.267 4.267 8.533 8.533 8.533 12.8v179.2l85.333 34.133c4.267 0 8.533 4.267 8.533 8.533s0 8.533-4.267 12.8l-68.267 98.133 102.4-4.267c8.533 0 12.8 4.267 17.067 12.8 8.533 0 4.267 4.267-4.267 12.8zM110.933 456.533l196.267 76.8 8.533-8.533-166.4-64-38.4-4.267zM153.6 285.867v0l-68.267 34.133 68.267 98.133 328.533-132.267-68.267-98.133-260.267 98.133zM490.667-29.867l-328.533 132.267v153.6l243.2-98.133h12.8c0 0 0 0 4.267 0v0c0 0 4.267 0 4.267 4.267l64 85.333c0-4.267 0-277.333 0-277.333zM490.667 324.267l-298.667 115.2 149.333 64 153.6-157.867v-17.067h-4.267zM529.067 337.067l157.867 157.867 140.8-55.467-298.667-115.2c0 0 0 12.8 0 12.8zM849.067 102.4l-328.533-132.267v281.6l64-85.333c0 0 0-4.267 4.267-4.267v0h17.067l243.2 98.133v-157.867zM938.667 324.267l-324.267-132.267-68.267 98.133 328.533 132.267 64-98.133zM870.4 460.8l-157.867 64 12.8 8.533 187.733-76.8-42.667 4.267z" />
<glyph unicode="&#xe976;" glyph-name="supplier" d="M797.867 405.333l98.133 34.133 21.333-59.733-98.133-34.133-21.333 59.733zM1019.733 341.333c-4.267-8.533-8.533-12.8-17.067-17.067l-332.8-119.467c4.267-4.267 8.533-12.8 12.8-17.067l277.333 102.4 21.333-59.733-277.333-102.4c0-8.533 4.267-12.8 4.267-21.333 0-85.333-68.267-157.867-157.867-157.867-85.333 0-157.867 68.267-157.867 157.867 0 55.467 29.867 106.667 72.533 132.267l-217.6 610.133c-8.533 25.6-38.4 42.667-68.267 29.867l-157.867-55.467-21.333 59.733 157.867 59.733c59.733 17.067 123.733-12.8 149.333-72.533l221.867-614.4c8.533 0 12.8 0 21.333 4.267l-119.467 332.8c-4.267 17.067 4.267 34.133 17.067 38.4l136.533 51.2c0 0 0 0 0 0l115.2 42.667c0 0 0 0 0 0l136.533 51.2c8.533 4.267 17.067 4.267 25.6 0s12.8-8.533 17.067-17.067l145.067-396.8c0-4.267 0-12.8-4.267-21.333zM695.467 657.067l-59.733-21.333 8.533-21.333 59.733 21.333-8.533 21.333zM644.267 106.667c0 51.2-42.667 93.867-93.867 93.867s-93.867-42.667-93.867-93.867c0-51.2 42.667-93.867 93.867-93.867s93.867 38.4 93.867 93.867zM951.467 371.2l-119.467 332.8-76.8-29.867 17.067-51.2c4.267-8.533 4.267-17.067 0-25.6s-8.533-12.8-17.067-17.067l-115.2-42.667c-4.267 0-8.533 0-12.8 0-12.8 0-25.6 8.533-29.867 21.333l-17.067 51.2-76.8-29.867 119.467-332.8 328.533 123.733z" /> <glyph unicode="&#xe976;" glyph-name="supplier" d="M797.867 405.333l98.133 34.133 21.333-59.733-98.133-34.133-21.333 59.733zM1019.733 341.333c-4.267-8.533-8.533-12.8-17.067-17.067l-332.8-119.467c4.267-4.267 8.533-12.8 12.8-17.067l277.333 102.4 21.333-59.733-277.333-102.4c0-8.533 4.267-12.8 4.267-21.333 0-85.333-68.267-157.867-157.867-157.867-85.333 0-157.867 68.267-157.867 157.867 0 55.467 29.867 106.667 72.533 132.267l-217.6 610.133c-8.533 25.6-38.4 42.667-68.267 29.867l-157.867-55.467-21.333 59.733 157.867 59.733c59.733 17.067 123.733-12.8 149.333-72.533l221.867-614.4c8.533 0 12.8 0 21.333 4.267l-119.467 332.8c-4.267 17.067 4.267 34.133 17.067 38.4l136.533 51.2c0 0 0 0 0 0l115.2 42.667c0 0 0 0 0 0l136.533 51.2c8.533 4.267 17.067 4.267 25.6 0s12.8-8.533 17.067-17.067l145.067-396.8c0-4.267 0-12.8-4.267-21.333zM695.467 657.067l-59.733-21.333 8.533-21.333 59.733 21.333-8.533 21.333zM644.267 106.667c0 51.2-42.667 93.867-93.867 93.867s-93.867-42.667-93.867-93.867c0-51.2 42.667-93.867 93.867-93.867s93.867 38.4 93.867 93.867zM951.467 371.2l-119.467 332.8-76.8-29.867 17.067-51.2c4.267-8.533 4.267-17.067 0-25.6s-8.533-12.8-17.067-17.067l-115.2-42.667c-4.267 0-8.533 0-12.8 0-12.8 0-25.6 8.533-29.867 21.333l-17.067 51.2-76.8-29.867 119.467-332.8 328.533 123.733z" />
<glyph unicode="&#xe977;" glyph-name="supplierfalse" d="M198.827 882.773c22.187 0.427 41.813-14.080 48.64-34.133l8.107-22.187 105.813-105.813-54.187 149.333c-25.6 59.733-89.6 89.6-149.333 72.533l-13.653-5.12 54.613-54.613zM708.693 129.28l-173.653 173.653 15.36-43.093c-8.533-4.267-12.8-4.267-21.333-4.267l-29.867 83.2-108.373 108.373 74.24-208.64c-42.667-25.6-72.533-76.8-72.533-132.267 0-89.6 72.533-157.867 157.867-157.867 89.6 0 157.867 72.533 157.867 157.867 0 8.533-4.267 12.8-4.267 21.333l4.693 1.707zM550.4 12.373c-51.2 0-93.867 42.667-93.867 93.867s42.667 93.867 93.867 93.867 93.867-42.667 93.867-93.867c0-55.467-42.667-93.867-93.867-93.867zM960 289.707l-122.453-45.227 49.493-49.067 94.293 34.56zM504.32 577.707l-0.853 2.133 76.8 29.867 17.067-51.2c4.267-12.8 17.067-21.333 29.867-21.333 4.267 0 8.533 0 12.8 0l115.2 42.667c8.533 4.267 12.8 8.533 17.067 17.067s4.267 17.067 0 25.6l-17.067 51.2 76.8 29.867 119.467-332.8-174.507-65.707 45.653-45.653 180.053 64.427c8.533 4.267 12.8 8.533 17.067 17.067s4.267 17.067 4.267 21.333l-145.067 396.8c-4.267 8.533-8.533 12.8-17.067 17.067s-17.067 4.267-25.6 0l-136.533-51.2-115.2-42.667-134.4-50.347 54.187-54.187zM695.467 656.64l8.533-21.333-59.733-21.333-8.533 21.333 59.733 21.333zM896 439.040l-98.133-34.133 21.333-59.733 98.133 34.133zM39.253 960c-9.813 0-20.053-3.84-27.733-11.52-15.36-15.787-15.36-40.533 0-55.893l945.493-945.067c7.68-7.68 17.493-11.52 27.733-11.52 9.813 0 20.053 3.84 27.733 11.52 15.36 15.787 15.36 40.533 0 55.893l-945.493 945.067c-7.68 7.68-17.493 11.52-27.733 11.52z" /> <glyph unicode="&#xe977;" glyph-name="supplierfalse" d="M198.827 882.773c22.187 0.427 41.813-14.080 48.64-34.133l8.107-22.187 105.813-105.813-54.187 149.333c-25.6 59.733-89.6 89.6-149.333 72.533l-13.653-5.12 54.613-54.613zM708.693 129.28l-173.653 173.653 15.36-43.093c-8.533-4.267-12.8-4.267-21.333-4.267l-29.867 83.2-108.373 108.373 74.24-208.64c-42.667-25.6-72.533-76.8-72.533-132.267 0-89.6 72.533-157.867 157.867-157.867 89.6 0 157.867 72.533 157.867 157.867 0 8.533-4.267 12.8-4.267 21.333l4.693 1.707zM550.4 12.373c-51.2 0-93.867 42.667-93.867 93.867s42.667 93.867 93.867 93.867 93.867-42.667 93.867-93.867c0-55.467-42.667-93.867-93.867-93.867zM960 289.707l-122.453-45.227 49.493-49.067 94.293 34.56zM504.32 577.707l-0.853 2.133 76.8 29.867 17.067-51.2c4.267-12.8 17.067-21.333 29.867-21.333 4.267 0 8.533 0 12.8 0l115.2 42.667c8.533 4.267 12.8 8.533 17.067 17.067s4.267 17.067 0 25.6l-17.067 51.2 76.8 29.867 119.467-332.8-174.507-65.707 45.653-45.653 180.053 64.427c8.533 4.267 12.8 8.533 17.067 17.067s4.267 17.067 4.267 21.333l-145.067 396.8c-4.267 8.533-8.533 12.8-17.067 17.067s-17.067 4.267-25.6 0l-136.533-51.2-115.2-42.667-134.4-50.347 54.187-54.187zM695.467 656.64l8.533-21.333-59.733-21.333-8.533 21.333 59.733 21.333zM896 439.040l-98.133-34.133 21.333-59.733 98.133 34.133zM39.253 960c-9.813 0-20.053-3.84-27.733-11.52-15.36-15.787-15.36-40.533 0-55.893l945.493-945.067c7.68-7.68 17.493-11.52 27.733-11.52 9.813 0 20.053 3.84 27.733 11.52 15.36 15.787 15.36 40.533 0 55.893l-945.493 945.067c-7.68 7.68-17.493 11.52-27.733 11.52z" />
<glyph unicode="&#xe978;" glyph-name="inactive-car" d="M1024 900.267l-59.733 59.733-964.267-964.267 59.733-59.733 964.267 964.267zM0 55.893v448.853l119.467 341.333c5.547 17.067 15.787 30.72 30.72 41.387 14.507 10.24 31.147 15.787 49.067 15.787h625.92c6.827 0 13.653-0.853 20.053-2.133l-111.36-111.36h-514.987l-59.733-170.667h403.2l-113.92-113.92h-334.507v-284.587h50.773l-164.693-164.693zM316.587 423.253c8.96-8.96 15.787-19.627 19.627-30.72l-110.080-110.080c-11.52 4.267-21.76 10.667-30.72 19.627-16.64 16.64-24.747 36.693-24.747 60.587s8.107 43.947 24.747 60.587c16.64 16.64 36.693 24.747 60.587 24.747s43.947-8.107 60.587-24.747zM768 277.333c-23.893 0-43.947 8.107-60.587 24.747s-24.747 36.693-24.747 60.587 8.107 43.947 24.747 60.587c16.64 16.64 36.693 24.747 60.587 24.747s43.947-8.107 60.587-24.747c16.64-16.64 24.747-36.693 24.747-60.587s-8.107-43.947-24.747-60.587c-16.64-16.64-36.693-24.747-60.587-24.747zM936.96 753.067l87.040-248.32v-455.253c0-16.213-5.547-29.44-16.213-40.533s-24.32-16.213-40.533-16.213h-56.747c-16.213 0-29.44 5.547-40.533 16.213s-16.213 24.32-16.213 40.533v56.747h-563.2l113.92 113.92h505.6v284.587h-221.44l113.92 113.92h61.867l-16.213 46.080 88.32 88.32z" />
<glyph unicode="&#xe979;" glyph-name="tags" d="M729.6 960c-42.667 0-89.6 0-132.267 0-21.333 0-38.4-8.533-51.2-21.333-140.8-140.8-281.6-281.6-422.4-422.4-25.6-25.6-25.6-51.2 0-76.8 93.867-93.867 187.733-187.733 281.6-281.6 25.6-25.6 51.2-25.6 76.8 0 140.8 140.8 281.6 281.6 422.4 422.4 17.067 12.8 21.333 29.867 21.333 51.2 0 93.867 0 183.467 0 277.333 0 34.133-17.067 51.2-51.2 51.2-51.2 0-98.133 0-145.067 0zM682.667 763.733c0 25.6 17.067 46.933 42.667 46.933s46.933-21.333 46.933-46.933c0-25.6-21.333-46.933-46.933-46.933-21.333 0-42.667 21.333-42.667 46.933zM878.933 482.133c4.267-12.8 0-21.333-8.533-29.867-34.133-51.2-64-98.133-98.133-149.333-76.8-115.2-153.6-234.667-230.4-349.867-12.8-17.067-21.333-21.333-38.4-8.533-115.2 76.8-226.133 149.333-337.067 226.133-17.067 8.533-17.067 21.333-8.533 38.4 12.8 21.333 29.867 46.933 42.667 68.267 8.533 12.8 8.533 12.8 17.067 0 55.467-55.467 115.2-115.2 170.667-170.667 8.533-8.533 17.067-17.067 29.867-21.333 29.867-12.8 55.467-4.267 76.8 21.333 123.733 123.733 247.467 247.467 371.2 371.2 4.267 4.267 4.267 8.533 8.533 12.8 0-8.533 0-8.533 4.267-8.533z" /> <glyph unicode="&#xe979;" glyph-name="tags" d="M729.6 960c-42.667 0-89.6 0-132.267 0-21.333 0-38.4-8.533-51.2-21.333-140.8-140.8-281.6-281.6-422.4-422.4-25.6-25.6-25.6-51.2 0-76.8 93.867-93.867 187.733-187.733 281.6-281.6 25.6-25.6 51.2-25.6 76.8 0 140.8 140.8 281.6 281.6 422.4 422.4 17.067 12.8 21.333 29.867 21.333 51.2 0 93.867 0 183.467 0 277.333 0 34.133-17.067 51.2-51.2 51.2-51.2 0-98.133 0-145.067 0zM682.667 763.733c0 25.6 17.067 46.933 42.667 46.933s46.933-21.333 46.933-46.933c0-25.6-21.333-46.933-46.933-46.933-21.333 0-42.667 21.333-42.667 46.933zM878.933 482.133c4.267-12.8 0-21.333-8.533-29.867-34.133-51.2-64-98.133-98.133-149.333-76.8-115.2-153.6-234.667-230.4-349.867-12.8-17.067-21.333-21.333-38.4-8.533-115.2 76.8-226.133 149.333-337.067 226.133-17.067 8.533-17.067 21.333-8.533 38.4 12.8 21.333 29.867 46.933 42.667 68.267 8.533 12.8 8.533 12.8 17.067 0 55.467-55.467 115.2-115.2 170.667-170.667 8.533-8.533 17.067-17.067 29.867-21.333 29.867-12.8 55.467-4.267 76.8 21.333 123.733 123.733 247.467 247.467 371.2 371.2 4.267 4.267 4.267 8.533 8.533 12.8 0-8.533 0-8.533 4.267-8.533z" />
<glyph unicode="&#xe97a;" glyph-name="tax" d="M448 192c0 174.933 145.067 320 320 320 76.8 0 145.067-25.6 196.267-68.267v324.267c4.267 51.2-38.4 98.133-93.867 98.133h-204.8c-21.333 55.467-72.533 93.867-136.533 93.867s-115.2-38.4-136.533-98.133h-209.067c-55.467 0-98.133-42.667-98.133-93.867v-674.133c0-51.2 42.667-98.133 98.133-98.133h332.8c-42.667 55.467-68.267 123.733-68.267 196.267zM529.067 861.867c29.867 0 46.933-21.333 46.933-46.933 0-29.867-25.6-46.933-46.933-46.933-29.867 0-46.933 21.333-46.933 46.933-4.267 29.867 17.067 46.933 46.933 46.933zM708.267 247.467c-8.533 0-12.8 4.267-17.067 8.533s-8.533 8.533-8.533 17.067v17.067c0 8.533 0 12.8 4.267 17.067s8.533 8.533 17.067 8.533c8.533 0 12.8-4.267 17.067-8.533s4.267-12.8 4.267-17.067v-12.8c4.267-21.333-4.267-29.867-17.067-29.867zM870.4 132.267c4.267-4.267 4.267-12.8 4.267-17.067v-21.333c0-12.8-8.533-21.333-21.333-21.333-8.533 0-12.8 4.267-17.067 8.533s-8.533 12.8-8.533 17.067v17.067c0 8.533 4.267 12.8 8.533 17.067s8.533 8.533 17.067 8.533c8.533 0 12.8-4.267 17.067-8.533zM768 448c-140.8 0-256-115.2-256-256s115.2-256 256-256 256 115.2 256 256-115.2 256-256 256zM635.733 273.067v17.067c0 21.333 4.267 34.133 17.067 46.933s29.867 17.067 51.2 17.067c21.333 0 38.4-4.267 51.2-17.067s17.067-29.867 17.067-46.933v-17.067c0-21.333-4.267-34.133-17.067-46.933s-29.867-17.067-51.2-17.067-38.4 4.267-51.2 17.067c-8.533 12.8-17.067 29.867-17.067 46.933zM721.067 59.733l-34.133 17.067 153.6 243.2 34.133-17.067-153.6-243.2zM925.867 98.133c0-21.333-4.267-34.133-17.067-46.933s-29.867-17.067-51.2-17.067-38.4 4.267-51.2 17.067c-12.8 12.8-21.333 25.6-21.333 46.933v17.067c0 21.333 4.267 34.133 17.067 46.933s29.867 17.067 51.2 17.067 38.4-4.267 51.2-17.067c12.8-12.8 17.067-29.867 17.067-46.933v-17.067h4.267z" /> <glyph unicode="&#xe97a;" glyph-name="tax" d="M448 192c0 174.933 145.067 320 320 320 76.8 0 145.067-25.6 196.267-68.267v324.267c4.267 51.2-38.4 98.133-93.867 98.133h-204.8c-21.333 55.467-72.533 93.867-136.533 93.867s-115.2-38.4-136.533-98.133h-209.067c-55.467 0-98.133-42.667-98.133-93.867v-674.133c0-51.2 42.667-98.133 98.133-98.133h332.8c-42.667 55.467-68.267 123.733-68.267 196.267zM529.067 861.867c29.867 0 46.933-21.333 46.933-46.933 0-29.867-25.6-46.933-46.933-46.933-29.867 0-46.933 21.333-46.933 46.933-4.267 29.867 17.067 46.933 46.933 46.933zM708.267 247.467c-8.533 0-12.8 4.267-17.067 8.533s-8.533 8.533-8.533 17.067v17.067c0 8.533 0 12.8 4.267 17.067s8.533 8.533 17.067 8.533c8.533 0 12.8-4.267 17.067-8.533s4.267-12.8 4.267-17.067v-12.8c4.267-21.333-4.267-29.867-17.067-29.867zM870.4 132.267c4.267-4.267 4.267-12.8 4.267-17.067v-21.333c0-12.8-8.533-21.333-21.333-21.333-8.533 0-12.8 4.267-17.067 8.533s-8.533 12.8-8.533 17.067v17.067c0 8.533 4.267 12.8 8.533 17.067s8.533 8.533 17.067 8.533c8.533 0 12.8-4.267 17.067-8.533zM768 448c-140.8 0-256-115.2-256-256s115.2-256 256-256 256 115.2 256 256-115.2 256-256 256zM635.733 273.067v17.067c0 21.333 4.267 34.133 17.067 46.933s29.867 17.067 51.2 17.067c21.333 0 38.4-4.267 51.2-17.067s17.067-29.867 17.067-46.933v-17.067c0-21.333-4.267-34.133-17.067-46.933s-29.867-17.067-51.2-17.067-38.4 4.267-51.2 17.067c-8.533 12.8-17.067 29.867-17.067 46.933zM721.067 59.733l-34.133 17.067 153.6 243.2 34.133-17.067-153.6-243.2zM925.867 98.133c0-21.333-4.267-34.133-17.067-46.933s-29.867-17.067-51.2-17.067-38.4 4.267-51.2 17.067c-12.8 12.8-21.333 25.6-21.333 46.933v17.067c0 21.333 4.267 34.133 17.067 46.933s29.867 17.067 51.2 17.067 38.4-4.267 51.2-17.067c12.8-12.8 17.067-29.867 17.067-46.933v-17.067h4.267z" />
<glyph unicode="&#xe97b;" glyph-name="thermometer" d="M641.567 326.792v35.527h64.784v25.078h-64.784v119.118h64.784v25.078h-64.784v119.118h64.784v25.078h-64.784v121.208h64.784v25.078h-64.784v8.359c0 71.053-58.514 129.567-129.567 129.567s-129.567-58.514-129.567-129.567v-503.641c-54.335-39.706-87.771-104.49-87.771-173.453 0-119.118 96.131-217.339 217.339-217.339 119.118 0 217.339 96.131 217.339 217.339 0 66.873-33.437 131.657-87.771 173.453zM512-28.473c-100.31 0-179.722 81.502-179.722 179.722 0 64.784 33.437 123.298 87.771 154.645v524.539c0 50.155 41.796 91.951 91.951 91.951s91.951-41.796 91.951-91.951v-522.449c54.335-31.347 87.771-89.861 87.771-154.645 0-100.31-79.412-181.812-179.722-181.812zM652.016 435.461v25.078h35.527v-25.078h-35.527zM652.016 579.657v25.078h35.527v-25.078h-35.527zM652.016 723.853v25.078h35.527v-25.078h-35.527zM568.424 284.996v543.347c0 0 0 0 0 0s0 0 0 0v0 0c0 31.347-25.078 56.424-56.424 56.424s-56.424-25.078-56.424-56.424v0-543.347c-52.245-20.898-87.771-73.143-87.771-131.657 0-79.412 64.784-144.196 144.196-144.196s144.196 64.784 144.196 144.196c0 58.514-35.527 108.669-87.771 131.657zM470.204 824.163v4.18c0 22.988 18.808 41.796 41.796 41.796s41.796-18.808 41.796-41.796v-219.429h-85.682v215.249z" /> <glyph unicode="&#xe97b;" glyph-name="thermometer" d="M641.567 326.792v35.527h64.784v25.078h-64.784v119.118h64.784v25.078h-64.784v119.118h64.784v25.078h-64.784v121.208h64.784v25.078h-64.784v8.359c0 71.053-58.514 129.567-129.567 129.567s-129.567-58.514-129.567-129.567v-503.641c-54.335-39.706-87.771-104.49-87.771-173.453 0-119.118 96.131-217.339 217.339-217.339 119.118 0 217.339 96.131 217.339 217.339 0 66.873-33.437 131.657-87.771 173.453zM512-28.473c-100.31 0-179.722 81.502-179.722 179.722 0 64.784 33.437 123.298 87.771 154.645v524.539c0 50.155 41.796 91.951 91.951 91.951s91.951-41.796 91.951-91.951v-522.449c54.335-31.347 87.771-89.861 87.771-154.645 0-100.31-79.412-181.812-179.722-181.812zM652.016 435.461v25.078h35.527v-25.078h-35.527zM652.016 579.657v25.078h35.527v-25.078h-35.527zM652.016 723.853v25.078h35.527v-25.078h-35.527zM568.424 284.996v543.347c0 0 0 0 0 0s0 0 0 0v0 0c0 31.347-25.078 56.424-56.424 56.424s-56.424-25.078-56.424-56.424v0-543.347c-52.245-20.898-87.771-73.143-87.771-131.657 0-79.412 64.784-144.196 144.196-144.196s144.196 64.784 144.196 144.196c0 58.514-35.527 108.669-87.771 131.657zM470.204 824.163v4.18c0 22.988 18.808 41.796 41.796 41.796s41.796-18.808 41.796-41.796v-219.429h-85.682v215.249z" />

Before

Width:  |  Height:  |  Size: 185 KiB

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -1,10 +1,10 @@
@font-face { @font-face {
font-family: 'icon'; font-family: 'icon';
src: url('fonts/icon.eot?7j3xju'); src: url('fonts/icon.eot?uocffs');
src: url('fonts/icon.eot?7j3xju#iefix') format('embedded-opentype'), src: url('fonts/icon.eot?uocffs#iefix') format('embedded-opentype'),
url('fonts/icon.ttf?7j3xju') format('truetype'), url('fonts/icon.ttf?uocffs') format('truetype'),
url('fonts/icon.woff?7j3xju') format('woff'), url('fonts/icon.woff?uocffs') format('woff'),
url('fonts/icon.svg?7j3xju#icon') format('svg'); url('fonts/icon.svg?uocffs#icon') format('svg');
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
font-display: block; font-display: block;
@ -25,6 +25,9 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-inactive-car:before {
content: "\e978";
}
.icon-hasItemLost:before { .icon-hasItemLost:before {
content: "\e957"; content: "\e957";
} }

View File

@ -326,6 +326,7 @@ globals:
ticketsMonitor: Tickets monitor ticketsMonitor: Tickets monitor
clientsActionsMonitor: Clients and actions clientsActionsMonitor: Clients and actions
serial: Serial serial: Serial
business: Business
medical: Mutual medical: Mutual
pit: IRPF pit: IRPF
wasteRecalc: Waste recaclulate wasteRecalc: Waste recaclulate
@ -377,7 +378,7 @@ login:
loginError: Invalid username or password loginError: Invalid username or password
fieldRequired: This field is required fieldRequired: This field is required
twoFactorRequired: Two-factor verification required twoFactorRequired: Two-factor verification required
twoFactorRequired: twoFactor:
validate: Validate validate: Validate
insert: Enter the verification code insert: Enter the verification code
explanation: >- explanation: >-
@ -456,48 +457,6 @@ ticket:
consigneeStreet: Street consigneeStreet: Street
create: create:
address: Address address: Address
invoiceOut:
card:
issued: Issued
customerCard: Customer card
ticketList: Ticket List
summary:
issued: Issued
dued: Due
booked: Booked
taxBreakdown: Tax breakdown
taxableBase: Taxable base
rate: Rate
fee: Fee
tickets: Tickets
totalWithVat: Amount
globalInvoices:
errors:
chooseValidClient: Choose a valid client
chooseValidCompany: Choose a valid company
chooseValidPrinter: Choose a valid printer
chooseValidSerialType: Choose a serial type
fillDates: Invoice date and the max date should be filled
invoiceDateLessThanMaxDate: Invoice date can not be less than max date
invoiceWithFutureDate: Exists an invoice with a future date
noTicketsToInvoice: There are not tickets to invoice
criticalInvoiceError: 'Critical invoicing error, process stopped'
invalidSerialTypeForAll: The serial type must be global when invoicing all clients
table:
addressId: Address id
streetAddress: Street
statusCard:
percentageText: '{getPercentage}% {getAddressNumber} of {getNAddresses}'
pdfsNumberText: '{nPdfs} of {totalPdfs} PDFs'
negativeBases:
clientId: Client Id
base: Base
active: Active
hasToInvoice: Has to Invoice
verifiedData: Verified Data
comercial: Comercial
errors:
downloadCsvFailed: CSV download failed
department: department:
chat: Chat chat: Chat
bossDepartment: Boss Department bossDepartment: Boss Department
@ -509,6 +468,24 @@ department:
hasToSendMail: Send check-ins by email hasToSendMail: Send check-ins by email
departmentRemoved: Department removed departmentRemoved: Department removed
worker: worker:
pageTitles:
workers: Workers
list: List
basicData: Basic data
summary: Summary
notifications: Notifications
workerCreate: New worker
department: Department
pda: PDA
notes: Notas
dms: My documentation
pbx: Private Branch Exchange
log: Log
calendar: Calendar
timeControl: Time control
locker: Locker
balance: Balance
medical: Medical
list: list:
department: Department department: Department
schedule: Schedule schedule: Schedule
@ -524,15 +501,24 @@ worker:
role: Role role: Role
sipExtension: Extension sipExtension: Extension
locker: Locker locker: Locker
fiDueDate: FI due date fiDueDate: DNI expiration date
sex: Sex sex: Sex
seniority: Seniority seniority: Antiquity
fi: DNI/NIE/NIF fi: DNI/NIE/NIF
birth: Birth birth: Birth
isFreelance: Freelance isFreelance: Freelance
isSsDiscounted: SS Bonification isSsDiscounted: SS Bonification
hasMachineryAuthorized: Machinery authorized hasMachineryAuthorized: Machinery authorized
isDisable: Disable isDisable: Disable
business: Business
started: Started
ended: Ended
reasonEnd: Reason End
department: Departament
workerBusinessCategory: Worker Business Category
notes: Notes
workCenter: Center
professionalCategory: Professional Category
notificationsManager: notificationsManager:
activeNotifications: Active notifications activeNotifications: Active notifications
availableNotifications: Available notifications availableNotifications: Available notifications
@ -591,6 +577,23 @@ worker:
sizeLimit: Size limit sizeLimit: Size limit
isOnReservationMode: Reservation mode isOnReservationMode: Reservation mode
machine: Machine machine: Machine
business:
tableVisibleColumns:
started: Start Date
ended: End Date
company: Company
reasonEnd: Reason for Termination
department: Department
professionalCategory: Professional Category
calendarType: Work Calendar
workCenter: Work Center
payrollCategories: Contract Category
occupationCode: Contribution Code
rate: Rate
businessType: Contract Type
amount: Salary
basicSalary: Transport Workers Salary
notes: Notes
wagon: wagon:
type: type:
submit: Submit submit: Submit

View File

@ -326,6 +326,7 @@ globals:
ticketsMonitor: Monitor de tickets ticketsMonitor: Monitor de tickets
clientsActionsMonitor: Clientes y acciones clientsActionsMonitor: Clientes y acciones
serial: Facturas por serie serial: Facturas por serie
business: Contratos
medical: Mutua medical: Mutua
pit: IRPF pit: IRPF
wasteRecalc: Recalcular mermas wasteRecalc: Recalcular mermas
@ -481,6 +482,26 @@ department:
hasToSendMail: Enviar fichadas por mail hasToSendMail: Enviar fichadas por mail
departmentRemoved: Departamento eliminado departmentRemoved: Departamento eliminado
worker: worker:
pageTitles:
workers: Trabajadores
list: Listado
basicData: Datos básicos
summary: Resumen
notifications: Notificaciones
workerCreate: Nuevo trabajador
department: Departamentos
pda: PDA
notes: Notas
dms: Mi documentación
pbx: Centralita
log: Historial
calendar: Calendario
timeControl: Control de horario
locker: Taquilla
balance: Balance
business: Contrato
formation: Formación
medical: Mutua
list: list:
department: Departamento department: Departamento
schedule: Horario schedule: Horario
@ -505,6 +526,15 @@ worker:
isSsDiscounted: Bonificación SS isSsDiscounted: Bonificación SS
hasMachineryAuthorized: Autorizado para maquinaria hasMachineryAuthorized: Autorizado para maquinaria
isDisable: Deshabilitado isDisable: Deshabilitado
business: Contrato
started: Antigüedad
ended: Fin
reasonEnd: Motivo finalización
department: Departamento
workerBusinessCategory: Categoria profesional
notes: Notas
workCenter: Centro
professionalCategory: Categoria profesional
notificationsManager: notificationsManager:
activeNotifications: Notificaciones activas activeNotifications: Notificaciones activas
availableNotifications: Notificaciones disponibles availableNotifications: Notificaciones disponibles
@ -551,6 +581,23 @@ worker:
debit: Debe debit: Debe
credit: Haber credit: Haber
concept: Concepto concept: Concepto
business:
tableVisibleColumns:
started: Fecha inicio
ended: Fecha fin
company: Empresa
reasonEnd: Motivo finalización
department: Departamento
professionalCategory: Categoria profesional
calendarType: Calendario laboral
workCenter: Centro
payrollCategories: Categoria contrato
occupationCode: Cotización
rate: Tarifa
businessType: Contrato
amount: Salario
basicSalary: Salario transportistas
notes: Notas
operator: operator:
numberOfWagons: Número de vagones numberOfWagons: Número de vagones
train: tren train: tren
@ -563,7 +610,6 @@ worker:
sizeLimit: Tamaño límite sizeLimit: Tamaño límite
isOnReservationMode: Modo de reserva isOnReservationMode: Modo de reserva
machine: Máquina machine: Máquina
wagon: wagon:
type: type:
submit: Guardar submit: Guardar

View File

@ -2,7 +2,7 @@
import Navbar from 'src/components/NavBar.vue'; import Navbar from 'src/components/NavBar.vue';
</script> </script>
<template> <template>
<QLayout view="hHh LpR fFf" v-shortcut> <QLayout view="hHh LpR fFf">
<Navbar /> <Navbar />
<RouterView></RouterView> <RouterView></RouterView>
<QFooter v-if="$q.platform.is.mobile"></QFooter> <QFooter v-if="$q.platform.is.mobile"></QFooter>

View File

@ -86,7 +86,7 @@ watch(
() => route.params.id, () => route.params.id,
() => { () => {
getAccountData(); getAccountData();
} },
); );
onMounted(async () => await getAccountData(false)); onMounted(async () => await getAccountData(false));
@ -130,7 +130,8 @@ onMounted(async () => await getAccountData(false));
openConfirmationModal( openConfirmationModal(
t('User will be removed from alias'), t('User will be removed from alias'),
t('¿Seguro que quieres continuar?'), t('¿Seguro que quieres continuar?'),
() => deleteMailAlias(row, rows, rowIndex) () =>
deleteMailAlias(row, rows, rowIndex),
) )
" "
> >
@ -157,7 +158,7 @@ onMounted(async () => await getAccountData(false));
icon="add" icon="add"
color="primary" color="primary"
@click="openCreateMailAliasForm()" @click="openCreateMailAliasForm()"
shortcut="+" v-shortcut="'+'"
> >
<QTooltip>{{ t('warehouses.add') }}</QTooltip> <QTooltip>{{ t('warehouses.add') }}</QTooltip>
</QBtn> </QBtn>

View File

@ -63,7 +63,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();
@ -109,7 +109,7 @@ const redirectToRoleSummary = (id) =>
openConfirmationModal( openConfirmationModal(
t('El rol va a ser eliminado'), t('El rol va a ser eliminado'),
t('¿Seguro que quieres continuar?'), t('¿Seguro que quieres continuar?'),
() => deleteSubRole(row, rows, rowIndex) () => deleteSubRole(row, rows, rowIndex),
) )
" "
> >
@ -131,7 +131,7 @@ const redirectToRoleSummary = (id) =>
<QBtn <QBtn
fab fab
icon="add" icon="add"
shortcut="+" v-shortcut="'+'"
color="primary" color="primary"
@click="openCreateSubRoleForm()" @click="openCreateSubRoleForm()"
> >

View File

@ -57,7 +57,6 @@ function onFetch(rows, newRows) {
const price = row.quantity * sale.price; const price = row.quantity * sale.price;
const discount = (sale.discount * price) / 100; const discount = (sale.discount * price) / 100;
amountClaimed.value = amountClaimed.value + (price - discount); amountClaimed.value = amountClaimed.value + (price - discount);
} }
} }
@ -208,7 +207,6 @@ async function saveWhenHasChanges() {
selection="multiple" selection="multiple"
v-model:selected="selected" v-model:selected="selected"
:grid="$q.screen.lt.md" :grid="$q.screen.lt.md"
> >
<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">
@ -319,7 +317,13 @@ async function saveWhenHasChanges() {
</div> </div>
<QPageSticky position="bottom-right" :offset="[25, 25]"> <QPageSticky position="bottom-right" :offset="[25, 25]">
<QBtn fab color="primary" shortcut="+" icon="add" @click="showImportDialog()" /> <QBtn
fab
color="primary"
v-shortcut="'+'"
icon="add"
@click="showImportDialog()"
/>
</QPageSticky> </QPageSticky>
</template> </template>
@ -330,9 +334,10 @@ async function saveWhenHasChanges() {
width: 100%; width: 100%;
} }
.grid-style-transition { .grid-style-transition {
transition: transform 0.28s, background-color 0.28s; transition:
transform 0.28s,
background-color 0.28s;
} }
</style> </style>
<i18n> <i18n>

View File

@ -61,7 +61,7 @@ watch(
() => { () => {
claimDmsFilter.value.where.id = router.currentRoute.value.params.id; claimDmsFilter.value.where.id = router.currentRoute.value.params.id;
claimDmsRef.value.fetch(); claimDmsRef.value.fetch();
} },
); );
function openDialog(dmsId) { function openDialog(dmsId) {
@ -249,7 +249,7 @@ function onDrag() {
<QBtn <QBtn
fab fab
@click="inputFile.nativeEl.click()" @click="inputFile.nativeEl.click()"
shortcut="+" v-shortcut="'+'"
icon="add" icon="add"
color="primary" color="primary"
> >

View File

@ -61,7 +61,7 @@ watch(
(newValue) => { (newValue) => {
if (!newValue) return; if (!newValue) return;
getClientData(newValue); getClientData(newValue);
} },
); );
const getClientData = async (id) => { const getClientData = async (id) => {
@ -137,7 +137,7 @@ const toCustomerAddressEdit = (addressId) => {
<QIcon <QIcon
:style="{ :style="{
'font-variation-settings': `'FILL' ${isDefaultAddress( 'font-variation-settings': `'FILL' ${isDefaultAddress(
item item,
)}`, )}`,
}" }"
color="primary" color="primary"
@ -150,7 +150,7 @@ const toCustomerAddressEdit = (addressId) => {
t( t(
isDefaultAddress(item) isDefaultAddress(item)
? 'Default address' ? 'Default address'
: 'Set as default' : 'Set as default',
) )
}} }}
</QTooltip> </QTooltip>
@ -216,7 +216,7 @@ const toCustomerAddressEdit = (addressId) => {
color="primary" color="primary"
fab fab
icon="add" icon="add"
shortcut="+" v-shortcut="'+'"
/> />
<QTooltip> <QTooltip>
{{ t('New consignee') }} {{ t('New consignee') }}

View File

@ -158,7 +158,7 @@ const columns = computed(() => [
openConfirmationModal( openConfirmationModal(
t('Send compensation'), t('Send compensation'),
t('Do you want to report compensation to the client by mail?'), t('Do you want to report compensation to the client by mail?'),
() => sendEmail(`Receipts/${id}/balance-compensation-email`) () => sendEmail(`Receipts/${id}/balance-compensation-email`),
), ),
}, },
], ],
@ -291,7 +291,7 @@ const showBalancePdf = ({ id }) => {
color="primary" color="primary"
fab fab
icon="add" icon="add"
shortcut="+" v-shortcut="'+'"
/> />
<QTooltip> <QTooltip>
{{ t('New payment') }} {{ t('New payment') }}

View File

@ -62,7 +62,7 @@ const customerContactsRef = ref(null);
color="primary" color="primary"
flat flat
icon="add" icon="add"
shortcut="+" v-shortcut="'+'"
> >
<QTooltip> <QTooltip>
{{ t('Add contact') }} {{ t('Add contact') }}

View File

@ -195,7 +195,7 @@ const updateData = () => {
color="primary" color="primary"
fab fab
icon="add" icon="add"
shortcut="+" v-shortcut="'+'"
/> />
<QTooltip> <QTooltip>
{{ t('New contract') }} {{ t('New contract') }}

View File

@ -192,6 +192,7 @@ const debtWarning = computed(() => {
query: { query: {
createForm: JSON.stringify({ createForm: JSON.stringify({
clientFk: entity.id, clientFk: entity.id,
addressId: entity.defaultAddressFk,
}), }),
}, },
}" }"

View File

@ -236,7 +236,7 @@ const toCustomerFileManagementCreate = () => {
@click.stop="toCustomerFileManagementCreate()" @click.stop="toCustomerFileManagementCreate()"
color="primary" color="primary"
fab fab
shortcut="+" v-shortcut="'+'"
icon="add" icon="add"
/> />
<QTooltip> <QTooltip>

View File

@ -23,5 +23,6 @@ const noteFilter = computed(() => {
:body="{ clientFk: route.params.id }" :body="{ clientFk: route.params.id }"
style="overflow-y: auto" style="overflow-y: auto"
:select-type="true" :select-type="true"
required
/> />
</template> </template>

View File

@ -104,7 +104,7 @@ const tableRef = ref();
color="primary" color="primary"
fab fab
icon="add" icon="add"
shortcut="+" v-shortcut="'+'"
/> />
<QTooltip> <QTooltip>
{{ t('Send sample') }} {{ t('Send sample') }}

View File

@ -49,7 +49,7 @@ const getData = async (observations) => {
notes.value = originalNotes notes.value = originalNotes
.map((observation) => { .map((observation) => {
const type = observationTypes.value.find( const type = observationTypes.value.find(
(type) => type.id === observation.observationTypeFk (type) => type.id === observation.observationTypeFk,
); );
return type return type
? { ? {
@ -112,8 +112,8 @@ function getPayload() {
(oNote) => (oNote) =>
oNote.id === note.id && oNote.id === note.id &&
(note.description !== oNote.description || (note.description !== oNote.description ||
note.observationTypeFk !== oNote.observationTypeFk) note.observationTypeFk !== oNote.observationTypeFk),
) ),
) )
.map((note) => ({ .map((note) => ({
data: note, data: note,
@ -130,9 +130,7 @@ async function handleDialog(data) {
.dialog({ .dialog({
component: VnConfirm, component: VnConfirm,
componentProps: { componentProps: {
title: t( title: t('confirmTicket'),
'confirmTicket'
),
message: t('confirmDeletionMessage'), message: t('confirmDeletionMessage'),
}, },
}) })
@ -341,7 +339,7 @@ function handleLocation(data, location) {
class="cursor-pointer add-icon q-mt-md" class="cursor-pointer add-icon q-mt-md"
flat flat
icon="add" icon="add"
shortcut="+" v-shortcut="'+'"
> >
<QTooltip> <QTooltip>
{{ t('Add note') }} {{ t('Add note') }}

View File

@ -1,8 +1,9 @@
import axios from 'axios'; import axios from 'axios';
export async function getAddresses(clientId) { export async function getAddresses(clientId, _filter = {}) {
if (!clientId) return; if (!clientId) return;
const filter = { const filter = {
..._filter,
fields: ['nickname', 'street', 'city', 'id'], fields: ['nickname', 'street', 'city', 'id'],
where: { isActive: true }, where: { isActive: true },
order: 'nickname ASC', order: 'nickname ASC',

View File

@ -1,7 +1,8 @@
import axios from 'axios'; import axios from 'axios';
export async function getClient(clientId) { export async function getClient(clientId, _filter = {}) {
const filter = { const filter = {
..._filter,
include: { include: {
relation: 'defaultAddress', relation: 'defaultAddress',
scope: { scope: {

View File

@ -12,6 +12,7 @@ import VnSelectDialog from 'src/components/common/VnSelectDialog.vue';
import FilterTravelForm from 'src/components/FilterTravelForm.vue'; import FilterTravelForm from 'src/components/FilterTravelForm.vue';
import VnInputNumber from 'src/components/common/VnInputNumber.vue'; import VnInputNumber from 'src/components/common/VnInputNumber.vue';
import { toDate } from 'src/filters'; import { toDate } from 'src/filters';
import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
const route = useRoute(); const route = useRoute();
const { t } = useI18n(); const { t } = useI18n();
@ -51,28 +52,12 @@ const onFilterTravelSelected = (formData, id) => {
> >
<template #form="{ data }"> <template #form="{ data }">
<VnRow> <VnRow>
<VnSelect <VnSelectSupplier
:label="t('globals.supplier')"
v-model="data.supplierFk" v-model="data.supplierFk"
url="Suppliers"
option-value="id"
option-label="nickname"
:fields="['id', 'nickname']"
hide-selected hide-selected
:required="true" :required="true"
map-options map-options
> />
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>{{ scope.opt?.name }}</QItemLabel>
<QItemLabel caption>
{{ scope.opt?.nickname }}, {{ scope.opt?.id }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
<VnSelectDialog <VnSelectDialog
:label="t('entry.basicData.travel')" :label="t('entry.basicData.travel')"
v-model="data.travelFk" v-model="data.travelFk"

View File

@ -17,7 +17,7 @@ const selected = ref([]);
const sortEntryObservationOptions = (data) => { const sortEntryObservationOptions = (data) => {
entryObservationsOptions.value = [...data].sort((a, b) => entryObservationsOptions.value = [...data].sort((a, b) =>
a.description.localeCompare(b.description) a.description.localeCompare(b.description),
); );
}; };
@ -142,7 +142,7 @@ const columns = computed(() => [
fab fab
color="primary" color="primary"
icon="add" icon="add"
shortcut="+" v-shortcut="'+'"
@click="entryObservationsRef.insert()" @click="entryObservationsRef.insert()"
/> />
</QPageSticky> </QPageSticky>

View File

@ -134,6 +134,7 @@ function downloadCSV(rows) {
@click=" @click="
openReport(`Entries/${entityId}/labelSupplier`) openReport(`Entries/${entityId}/labelSupplier`)
" "
data-cy="printLabelsBtn"
/> />
</template> </template>
<template #body="props"> <template #body="props">

View File

@ -13,6 +13,7 @@ import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import { useState } from 'src/composables/useState'; import { useState } from 'src/composables/useState';
import { toDate } from 'src/filters'; import { toDate } from 'src/filters';
import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
const state = useState(); const state = useState();
const { t } = useI18n(); const { t } = useI18n();
@ -26,7 +27,6 @@ const newEntryForm = reactive({
travelFk: Number(route.query?.travelFk) || null, travelFk: Number(route.query?.travelFk) || null,
companyFk: user.value.companyFk || null, companyFk: user.value.companyFk || null,
}); });
const suppliersOptions = ref([]);
const travelsOptions = ref([]); const travelsOptions = ref([]);
const companiesOptions = ref([]); const companiesOptions = ref([]);
@ -36,13 +36,6 @@ const redirectToEntryBasicData = (_, { id }) => {
</script> </script>
<template> <template>
<FetchData
url="Suppliers"
:filter="{ fields: ['id', 'nickname'] }"
order="nickname"
@on-fetch="(data) => (suppliersOptions = data)"
auto-load
/>
<FetchData <FetchData
url="Travels/filter" url="Travels/filter"
:filter="{ fields: ['id', 'warehouseInName'] }" :filter="{ fields: ['id', 'warehouseInName'] }"
@ -79,28 +72,13 @@ const redirectToEntryBasicData = (_, { id }) => {
> >
<template #form="{ data, validate }"> <template #form="{ data, validate }">
<VnRow> <VnRow>
<VnSelect <VnSelectSupplier
:label="t('Supplier')"
class="full-width" class="full-width"
v-model="data.supplierFk" v-model="data.supplierFk"
:options="suppliersOptions"
option-value="id"
option-label="nickname"
hide-selected hide-selected
:required="true" :required="true"
:rules="validate('entry.supplierFk')" :rules="validate('entry.supplierFk')"
> />
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>{{ scope.opt?.nickname }}</QItemLabel>
<QItemLabel caption>
#{{ scope.opt?.id }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
</VnRow> </VnRow>
<VnRow> <VnRow>
<VnSelect <VnSelect
@ -152,7 +130,6 @@ const redirectToEntryBasicData = (_, { id }) => {
<i18n> <i18n>
es: es:
Supplier: Proveedor
Travel: Envío Travel: Envío
Company: Empresa Company: Empresa
</i18n> </i18n>

View File

@ -7,6 +7,7 @@ import VnSelect from 'src/components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue';
import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
const { t } = useI18n(); const { t } = useI18n();
const props = defineProps({ const props = defineProps({
@ -115,34 +116,14 @@ const companiesOptions = ref([]);
</QItem> </QItem>
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnSelect <VnSelectSupplier
:label="t('entryFilter.params.supplierFk')"
v-model="params.supplierFk" v-model="params.supplierFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
url="Suppliers"
option-value="id"
option-label="name"
:fields="['id', 'name', 'nickname']"
:filter-options="['id', 'name', 'nickname']"
sort-by="nickname"
hide-selected hide-selected
dense dense
outlined outlined
rounded rounded
> />
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>
{{ scope.opt?.name}}
</QItemLabel>
<QItemLabel caption>
{{ `#${scope.opt?.id } , ${ scope.opt?.nickname}` }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem> <QItem>
@ -202,4 +183,4 @@ const companiesOptions = ref([]);
</QItem> </QItem>
</template> </template>
</VnFilterPanel> </VnFilterPanel>
</template> </template>

View File

@ -7,6 +7,7 @@ import VnInputDate from 'src/components/common/VnInputDate.vue';
import VnInput from 'components/common/VnInput.vue'; import VnInput from 'components/common/VnInput.vue';
import VnSelect from 'components/common/VnSelect.vue'; import VnSelect from 'components/common/VnSelect.vue';
import ItemsFilterPanel from 'src/components/ItemsFilterPanel.vue'; import ItemsFilterPanel from 'src/components/ItemsFilterPanel.vue';
import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
const { t } = useI18n(); const { t } = useI18n();
@ -18,7 +19,6 @@ defineProps({
}); });
const itemTypeWorkersOptions = ref([]); const itemTypeWorkersOptions = ref([]);
const suppliersOptions = ref([]);
const tagValues = ref([]); const tagValues = ref([]);
</script> </script>
@ -30,13 +30,6 @@ const tagValues = ref([]);
:filter="{ fields: ['id', 'nickname'], order: 'nickname ASC', limit: 30 }" :filter="{ fields: ['id', 'nickname'], order: 'nickname ASC', limit: 30 }"
@on-fetch="(data) => (itemTypeWorkersOptions = data)" @on-fetch="(data) => (itemTypeWorkersOptions = data)"
/> />
<FetchData
url="Suppliers"
limit="30"
auto-load
:filter="{ fields: ['id', 'name', 'nickname'], order: 'name ASC', limit: 30 }"
@on-fetch="(data) => (suppliersOptions = data)"
/>
<ItemsFilterPanel :data-key="dataKey" :custom-tags="['tags']"> <ItemsFilterPanel :data-key="dataKey" :custom-tags="['tags']">
<template #body="{ params, searchFn }"> <template #body="{ params, searchFn }">
<QItem class="q-my-md"> <QItem class="q-my-md">
@ -57,31 +50,14 @@ const tagValues = ref([]);
</QItem> </QItem>
<QItem class="q-my-md"> <QItem class="q-my-md">
<QItemSection> <QItemSection>
<VnSelect <VnSelectSupplier
:label="t('globals.params.supplierFk')"
v-model="params.supplierFk" v-model="params.supplierFk"
:options="suppliersOptions" @update:model-value="searchFn()"
option-value="id" hide-selected
option-label="name"
dense dense
outlined outlined
rounded rounded
use-input />
@update:model-value="searchFn()"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>
{{ scope.opt?.name}}
</QItemLabel>
<QItemLabel caption>
{{ `#${scope.opt?.id } , ${ scope.opt?.nickname}` }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem class="q-my-md"> <QItem class="q-my-md">

View File

@ -13,6 +13,7 @@ import VnInput from 'src/components/common/VnInput.vue';
import VnDms from 'src/components/common/VnDms.vue'; import VnDms from 'src/components/common/VnDms.vue';
import VnConfirm from 'src/components/ui/VnConfirm.vue'; import VnConfirm from 'src/components/ui/VnConfirm.vue';
import axios from 'axios'; import axios from 'axios';
import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
const { t } = useI18n(); const { t } = useI18n();
@ -115,27 +116,12 @@ function deleteFile(dmsFk) {
> >
<template #form="{ data }"> <template #form="{ data }">
<VnRow> <VnRow>
<VnSelect <VnSelectSupplier
:required="true"
:label="t('supplierFk')"
v-model="data.supplierFk" v-model="data.supplierFk"
option-value="id" hide-selected
option-label="nickname"
url="Suppliers"
:fields="['id', 'nickname']"
sort-by="nickname"
:is-clearable="false" :is-clearable="false"
> :required="true"
<template #option="scope"> />
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>{{
`${scope.opt.id} - ${scope.opt.nickname}`
}}</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
<VnInput <VnInput
clearable clearable
clear-icon="close" clear-icon="close"
@ -229,7 +215,7 @@ function deleteFile(dmsFk) {
v-else v-else
icon="add_circle" icon="add_circle"
round round
shortcut="+" v-shortcut="'+'"
padding="xs" padding="xs"
@click=" @click="
() => { () => {

View File

@ -226,6 +226,7 @@ const createInvoiceInCorrection = async () => {
v-ripple v-ripple
clickable clickable
@click="triggerMenu('correct')" @click="triggerMenu('correct')"
data-cy="createCorrectiveItem"
> >
<QItemSection <QItemSection
>{{ t('invoiceIn.descriptorMenu.createCorrective') }}...</QItemSection >{{ t('invoiceIn.descriptorMenu.createCorrective') }}...</QItemSection

View File

@ -232,7 +232,7 @@ async function insert() {
<QBtn <QBtn
color="primary" color="primary"
icon="add" icon="add"
shortcut="+" v-shortcut="'+'"
size="lg" size="lg"
round round
@click="!areRows ? insert() : invoiceInFormRef.insert()" @click="!areRows ? insert() : invoiceInFormRef.insert()"

View File

@ -218,7 +218,7 @@ const columns = computed(() => [
<QBtn <QBtn
color="primary" color="primary"
icon="add" icon="add"
shortcut="+" v-shortcut="'+'"
size="lg" size="lg"
round round
@click="invoiceInFormRef.insert()" @click="invoiceInFormRef.insert()"

View File

@ -117,7 +117,7 @@ const isNotEuro = (code) => code != 'EUR';
function taxRate(invoiceInTax) { function taxRate(invoiceInTax) {
const sageTaxTypeId = invoiceInTax.taxTypeSageFk; const sageTaxTypeId = invoiceInTax.taxTypeSageFk;
const taxRateSelection = sageTaxTypes.value.find( const taxRateSelection = sageTaxTypes.value.find(
(transaction) => transaction.id == sageTaxTypeId (transaction) => transaction.id == sageTaxTypeId,
); );
const taxTypeSage = taxRateSelection?.rate ?? 0; const taxTypeSage = taxRateSelection?.rate ?? 0;
const taxableBase = invoiceInTax?.taxableBase ?? 0; const taxableBase = invoiceInTax?.taxableBase ?? 0;
@ -131,14 +131,14 @@ function autocompleteExpense(evt, row, col) {
const param = isNaN(val) ? row[col.model] : val; const param = isNaN(val) ? row[col.model] : val;
const lookup = expenses.value.find( const lookup = expenses.value.find(
({ id }) => id == useAccountShortToStandard(param) ({ id }) => id == useAccountShortToStandard(param),
); );
expenseRef.value.vnSelectDialogRef.vnSelectRef.toggleOption(lookup); expenseRef.value.vnSelectDialogRef.vnSelectRef.toggleOption(lookup);
} }
const taxableBaseTotal = computed(() => { const taxableBaseTotal = computed(() => {
return getTotal(invoiceInFormRef.value.formData, 'taxableBase', ); return getTotal(invoiceInFormRef.value.formData, 'taxableBase');
}); });
const taxRateTotal = computed(() => { const taxRateTotal = computed(() => {
@ -147,13 +147,9 @@ const taxRateTotal = computed(() => {
}); });
}); });
const combinedTotal = computed(() => { const combinedTotal = computed(() => {
return +taxableBaseTotal.value + +taxRateTotal.value; return +taxableBaseTotal.value + +taxRateTotal.value;
}); });
</script> </script>
<template> <template>
<FetchData <FetchData
@ -283,7 +279,7 @@ const combinedTotal = computed(() => {
row.taxableBase = await getExchange( row.taxableBase = await getExchange(
val, val,
row.currencyFk, row.currencyFk,
invoiceIn.issued invoiceIn.issued,
); );
} }
" "
@ -426,7 +422,7 @@ const combinedTotal = computed(() => {
color="primary" color="primary"
icon="add" icon="add"
size="lg" size="lg"
shortcut="+" v-shortcut="'+'"
round round
@click="invoiceInFormRef.insert()" @click="invoiceInFormRef.insert()"
> >

View File

@ -12,6 +12,7 @@ import { useStateStore } from 'stores/useStateStore';
import { useState } from 'src/composables/useState'; import { useState } from 'src/composables/useState';
import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
const state = useState(); const state = useState();
const { t } = useI18n(); const { t } = useI18n();
@ -60,28 +61,12 @@ const redirectToInvoiceInBasicData = (__, { id }) => {
> >
<template #form="{ data, validate }"> <template #form="{ data, validate }">
<VnRow> <VnRow>
<VnSelect <VnSelectSupplier
url="Suppliers"
:fields="['id', 'nickname']"
:label="t('Supplier')"
v-model="data.supplierFk" v-model="data.supplierFk"
option-value="id"
option-label="nickname"
hide-selected hide-selected
:required="true" :required="true"
:rules="validate('entry.supplierFk')" :rules="validate('entry.supplierFk')"
> />
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>{{ scope.opt?.nickname }}</QItemLabel>
<QItemLabel caption>
#{{ scope.opt?.id }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
<VnInput <VnInput
:label="t('invoiceIn.list.supplierRef')" :label="t('invoiceIn.list.supplierRef')"
v-model="data.supplierRef" v-model="data.supplierRef"
@ -111,7 +96,6 @@ const redirectToInvoiceInBasicData = (__, { id }) => {
<i18n> <i18n>
es: es:
Supplier: Proveedor
Travel: Envío Travel: Envío
Company: Empresa Company: Empresa
</i18n> </i18n>

View File

@ -6,6 +6,7 @@ import VnInputDate from 'components/common/VnInputDate.vue';
import VnInputNumber from 'src/components/common/VnInputNumber.vue'; import VnInputNumber from 'src/components/common/VnInputNumber.vue';
import { dateRange } from 'src/filters'; import { dateRange } from 'src/filters';
import { date } from 'quasar'; import { date } from 'quasar';
import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
defineProps({ dataKey: { type: String, required: true } }); defineProps({ dataKey: { type: String, required: true } });
const dateFormat = 'YYYY-MM-DDTHH:mm:ss.SSSZ'; const dateFormat = 'YYYY-MM-DDTHH:mm:ss.SSSZ';
@ -65,29 +66,12 @@ function handleDaysAgo(params, daysAgo) {
</QItem> </QItem>
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnSelect <VnSelectSupplier
v-model="params.supplierFk" v-model="params.supplierFk"
url="Suppliers"
:fields="['id', 'nickname', 'name']"
:label="getLocale('supplierFk')"
option-label="nickname"
dense dense
outlined outlined
rounded rounded
> />
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>
{{ scope.opt?.name}}
</QItemLabel>
<QItemLabel caption>
{{ `#${scope.opt?.id } , ${ scope.opt?.nickname}` }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem> <QItem>

View File

@ -14,6 +14,7 @@ import VnInput from 'src/components/common/VnInput.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue';
import FetchData from 'src/components/FetchData.vue'; import FetchData from 'src/components/FetchData.vue';
import VnSection from 'src/components/common/VnSection.vue'; import VnSection from 'src/components/common/VnSection.vue';
import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
const user = useState().getUser(); const user = useState().getUser();
const { viewSummary } = useSummaryDialog(); const { viewSummary } = useSummaryDialog();
@ -148,7 +149,9 @@ const cols = computed(() => [
<template #body> <template #body>
<VnTable <VnTable
ref="tableRef" ref="tableRef"
:data-key data-key="InvoiceInList"
url="InvoiceIns/filter"
:order="['issued DESC', 'id DESC']"
:create="{ :create="{
urlCreate: 'InvoiceIns', urlCreate: 'InvoiceIns',
title: t('globals.createInvoiceIn'), title: t('globals.createInvoiceIn'),
@ -168,27 +171,11 @@ const cols = computed(() => [
</span> </span>
</template> </template>
<template #more-create-dialog="{ data }"> <template #more-create-dialog="{ data }">
<VnSelect <VnSelectSupplier
v-model="data.supplierFk" v-model="data.supplierFk"
url="Suppliers" hide-selected
:fields="['id', 'name', 'nickname']"
:label="t('globals.supplier')"
option-value="id"
option-label="nickname"
:filter-options="['id', 'name', 'nickname']"
:required="true" :required="true"
> />
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>{{ scope.opt?.nickname }}</QItemLabel>
<QItemLabel caption>
#{{ scope.opt?.id }}, {{ scope.opt?.name }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
<VnInput <VnInput
:label="t('invoiceIn.list.supplierRef')" :label="t('invoiceIn.list.supplierRef')"
v-model="data.supplierRef" v-model="data.supplierRef"

View File

@ -92,7 +92,7 @@ const submit = async (rows) => {
class="cursor-pointer fill-icon-on-hover" class="cursor-pointer fill-icon-on-hover"
color="primary" color="primary"
icon="add_circle" icon="add_circle"
shortcut="+" v-shortcut="'+'"
flat flat
> >
<QTooltip> <QTooltip>

View File

@ -125,7 +125,7 @@ onMounted(async () => {
inventoriedDate.value = inventoriedDate.value =
(await axios.get('Configs/findOne')).data?.inventoried || today; (await axios.get('Configs/findOne')).data?.inventoried || today;
if (query.warehouseFk) ref.warehouseFk = query.warehouseFk; if (query.warehouseFk) ref.warehouseFk = +query.warehouseFk;
else if (!ref.warehouseFk && user.value) ref.warehouseFk = user.value.warehouseFk; else if (!ref.warehouseFk && user.value) ref.warehouseFk = user.value.warehouseFk;
if (ref.date) showWhatsBeforeInventory.value = true; if (ref.date) showWhatsBeforeInventory.value = true;
ref.itemFk = route.params.id; ref.itemFk = route.params.id;
@ -143,7 +143,7 @@ onMounted(async () => {
const fetchItemBalances = async () => await arrayDataItemBalances.fetch({}); const fetchItemBalances = async () => await arrayDataItemBalances.fetch({});
const getBadgeAttrs = (_date) => { const getBadgeAttrs = (_date) => {
const isSameDate = date.isSameDate(today.value, _date); const isSameDate = date.isSameDate(today, _date);
const attrs = { const attrs = {
'text-color': isSameDate ? 'black' : 'white', 'text-color': isSameDate ? 'black' : 'white',
color: isSameDate ? 'warning' : 'transparent', color: isSameDate ? 'warning' : 'transparent',
@ -153,8 +153,6 @@ const getBadgeAttrs = (_date) => {
const scrollToToday = async () => { const scrollToToday = async () => {
await nextTick(); await nextTick();
const today = Date.vnNew();
today.setHours(0, 0, 0, 0);
const todayCell = document.querySelector(`td[data-date="${today.toISOString()}"]`); const todayCell = document.querySelector(`td[data-date="${today.toISOString()}"]`);
if (todayCell) { if (todayCell) {
todayCell.scrollIntoView({ behavior: 'smooth', block: 'center' }); todayCell.scrollIntoView({ behavior: 'smooth', block: 'center' });

View File

@ -175,7 +175,7 @@ const insertTag = (rows) => {
@click="insertTag(rows)" @click="insertTag(rows)"
color="primary" color="primary"
icon="add" icon="add"
shortcut="+" v-shortcut="'+'"
fab fab
> >
<QTooltip> <QTooltip>

View File

@ -12,6 +12,7 @@ import { QCheckbox } from 'quasar';
import { useArrayData } from 'composables/useArrayData'; import { useArrayData } from 'composables/useArrayData';
import { useValidator } from 'src/composables/useValidator'; import { useValidator } from 'src/composables/useValidator';
import axios from 'axios'; import axios from 'axios';
import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
const { t } = useI18n(); const { t } = useI18n();
@ -246,34 +247,14 @@ onMounted(async () => {
</QItem> </QItem>
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnSelect <VnSelectSupplier
:label="t('params.supplierFk')"
v-model="params.supplierFk" v-model="params.supplierFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
url="Suppliers"
option-value="id"
option-label="name"
:fields="['id', 'name', 'nickname']"
:filter-options="['id', 'name', 'nickname']"
sort-by="name ASC"
hide-selected hide-selected
dense dense
outlined outlined
rounded rounded
> />
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>
{{ scope.opt?.name }}
</QItemLabel>
<QItemLabel caption>
{{ `#${scope.opt?.id} , ${scope.opt?.nickname}` }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
</QItemSection> </QItemSection>
</QItem> </QItem>
<!-- Tags filter --> <!-- Tags filter -->

View File

@ -5,6 +5,9 @@ import VnTable from 'components/VnTable/VnTable.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import WorkerDescriptorProxy from '../Worker/Card/WorkerDescriptorProxy.vue'; import WorkerDescriptorProxy from '../Worker/Card/WorkerDescriptorProxy.vue';
import VnSection from 'src/components/common/VnSection.vue'; import VnSection from 'src/components/common/VnSection.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnSelectWorker from 'src/components/common/VnSelectWorker.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
const { t } = useI18n(); const { t } = useI18n();
const tableRef = ref(); const tableRef = ref();
@ -60,20 +63,17 @@ const columns = computed(() => [
label: t('code'), label: t('code'),
isTitle: true, isTitle: true,
cardVisible: true, cardVisible: true,
create: true,
}, },
{ {
align: 'left', align: 'left',
name: 'name', name: 'name',
label: t('globals.name'), label: t('globals.name'),
cardVisible: true, cardVisible: true,
create: true,
}, },
{ {
align: 'left', align: 'left',
label: t('worker'), label: t('worker'),
name: 'workerFk', name: 'workerFk',
create: true,
component: 'select', component: 'select',
attrs: { attrs: {
url: 'Workers/search', url: 'Workers/search',
@ -100,7 +100,6 @@ const columns = computed(() => [
align: 'left', align: 'left',
name: 'categoryFk', name: 'categoryFk',
label: t('ItemCategory'), label: t('ItemCategory'),
create: true,
component: 'select', component: 'select',
attrs: { attrs: {
options: itemCategoriesOptions.value, options: itemCategoriesOptions.value,
@ -112,7 +111,6 @@ const columns = computed(() => [
align: 'left', align: 'left',
name: 'Temperature', name: 'Temperature',
label: t('Temperature'), label: t('Temperature'),
create: true,
component: 'select', component: 'select',
attrs: { attrs: {
options: temperatureOptions.value, options: temperatureOptions.value,
@ -180,6 +178,29 @@ const columns = computed(() => [
<WorkerDescriptorProxy :id="row.workerFk" /> <WorkerDescriptorProxy :id="row.workerFk" />
</span> </span>
</template> </template>
<template #more-create-dialog="{ data }">
<VnInput v-model="data.code" :label="t('code')" data-cy="codeInput" />
<VnInput
v-model="data.name"
:label="t('globals.name')"
data-cy="nameInput"
/>
<VnSelectWorker v-model="data.workerFk" />
<VnSelect
:label="t('ItemCategory')"
v-model="data.categoryFk"
:options="itemCategoriesOptions"
hide-selected
data-cy="itemCategorySelect"
/>
<VnSelect
:label="t('Temperature')"
v-model="data.temperatureFk"
:options="temperatureOptions"
hide-selected
data-cy="temperatureSelect"
/>
</template>
</VnTable> </VnTable>
</template> </template>
</VnSection> </VnSection>

View File

@ -293,7 +293,7 @@ const columns = computed(() => [
title: t('globals.preview'), title: t('globals.preview'),
icon: 'preview', icon: 'preview',
color: 'primary', color: 'primary',
action: (row) => viewSummary(row.id, TicketSummary), action: (row) => viewSummary(row.id, TicketSummary, 'lg-width'),
isPrimary: true, isPrimary: true,
attrs: { attrs: {
flat: true, flat: true,

View File

@ -38,6 +38,7 @@ salesTicketsTable:
payMethod: Pay method payMethod: Pay method
department: Department department: Department
packing: ITP packing: ITP
hasItemLost: Item lost
searchBar: searchBar:
label: Search tickets label: Search tickets
info: Search tickets by id or alias info: Search tickets by id or alias

View File

@ -39,6 +39,7 @@ salesTicketsTable:
payMethod: Método de pago payMethod: Método de pago
department: Departamento department: Departamento
packing: ITP packing: ITP
hasItemLost: Artículo perdido
searchBar: searchBar:
label: Buscar tickets label: Buscar tickets
info: Buscar tickets por identificador o alias info: Buscar tickets por identificador o alias

View File

@ -110,7 +110,7 @@ const getSelectedTagValues = async (tag) => {
</div> </div>
<QBtn <QBtn
icon="add_circle" icon="add_circle"
shortcut="+" v-shortcut="'+'"
flat flat
class="filter-icon q-mb-md" class="filter-icon q-mb-md"
size="md" size="md"

View File

@ -22,7 +22,6 @@ const catalogParams = {
}; };
const arrayData = useArrayData(dataKey, { const arrayData = useArrayData(dataKey, {
url: 'Orders/CatalogFilter', url: 'Orders/CatalogFilter',
limit: 50,
userParams: catalogParams, userParams: catalogParams,
}); });
const store = arrayData.store; const store = arrayData.store;
@ -66,7 +65,7 @@ function extractValueTags(items) {
.filter((k) => /^value\d+$/.test(k)) .filter((k) => /^value\d+$/.test(k))
.map((v) => x[v]) .map((v) => x[v])
.filter((v) => v) .filter((v) => v)
.sort() .sort(),
); );
tagValue.value = resultValueTags; tagValue.value = resultValueTags;
} }
@ -76,7 +75,7 @@ watch(
(val) => { (val) => {
extractTags(val); extractTags(val);
}, },
{ immediate: true } { immediate: true },
); );
</script> </script>

View File

@ -184,7 +184,7 @@ function addOrder(value, field, params) {
{{ {{
t( t(
categoryList.find((c) => c.id == customTag.value)?.name || categoryList.find((c) => c.id == customTag.value)?.name ||
'' '',
) )
}} }}
</strong> </strong>
@ -296,7 +296,7 @@ function addOrder(value, field, params) {
<template #append> <template #append>
<QBtn <QBtn
icon="add_circle" icon="add_circle"
shortcut="+" v-shortcut="'+'"
flat flat
color="primary" color="primary"
size="md" size="md"

View File

@ -81,7 +81,7 @@ const columns = computed(() => [
label: t('module.created'), label: t('module.created'),
component: 'date', component: 'date',
cardVisible: true, cardVisible: true,
format: (row) => toDateTimeFormat(row?.landed), format: (row) => toDateTimeFormat(row?.created),
columnField: { columnField: {
component: null, component: null,
}, },

View File

@ -88,7 +88,7 @@ async function deleteWorCenter(id) {
</VnPaginate> </VnPaginate>
</div> </div>
<QPageSticky :offset="[18, 18]"> <QPageSticky :offset="[18, 18]">
<QBtn @click.stop="dialog.show()" color="primary" fab shortcut="+" icon="add"> <QBtn @click.stop="dialog.show()" color="primary" fab v-shortcut="'+'" icon="add">
<QDialog ref="dialog"> <QDialog ref="dialog">
<FormModelPopup <FormModelPopup
:title="t('Add work center')" :title="t('Add work center')"

View File

@ -3,14 +3,17 @@ import axios from 'axios';
import { getAgencies } from 'src/pages/Route/Agency/composables/getAgencies'; import { getAgencies } from 'src/pages/Route/Agency/composables/getAgencies';
vi.mock('axios'); vi.mock('axios');
const response = { data: [{ agencyModeFk: 'Agency1' }, { agencyModeFk: 'Agency2' }] };
axios.get.mockResolvedValue(response);
describe('getAgencies', () => { describe('getAgencies', () => {
afterEach(() => { afterEach(() => {
vi.clearAllMocks(); vi.clearAllMocks();
}); });
const generateParams = (formData) => ({ const generateParams = (formData, filter = {}) => ({
params: { params: {
filter: JSON.stringify(filter),
warehouseFk: formData.warehouseId, warehouseFk: formData.warehouseId,
addressFk: formData.addressId, addressFk: formData.addressId,
landed: formData.landed, landed: formData.landed,
@ -23,10 +26,15 @@ describe('getAgencies', () => {
addressId: '456', addressId: '456',
landed: 'true', landed: 'true',
}; };
const filter = {
fields: ['nickname', 'street', 'city', 'id'],
where: { isActive: true },
order: 'nickname ASC',
};
await getAgencies(formData); await getAgencies(formData, null, filter);
expect(axios.get).toHaveBeenCalledWith('Agencies/getAgenciesWithWarehouse', generateParams(formData)); expect(axios.get).toHaveBeenCalledWith('Agencies/getAgenciesWithWarehouse', generateParams(formData, filter));
}); });
it('should not call API when formData is missing required landed field', async () => { it('should not call API when formData is missing required landed field', async () => {
@ -52,4 +60,23 @@ describe('getAgencies', () => {
expect(axios.get).not.toHaveBeenCalled(); expect(axios.get).not.toHaveBeenCalled();
}); });
it('should return options and agency when default agency is found', async () => {
const formData = { warehouseId: '123', addressId: '456', landed: 'true' };
const client = { defaultAddress: { agencyModeFk: 'Agency1' } };
const { options, agency } = await getAgencies(formData, client);
expect(options).toEqual(response.data);
expect(agency).toEqual(response.data[0]);
});
it('should return options and agency when client is not provided', async () => {
const formData = { warehouseId: '123', addressId: '456', landed: 'true' };
const { options, agency } = await getAgencies(formData);
expect(options).toEqual(response.data);
expect(agency).toBeNull();
});
}); });

View File

@ -1,12 +1,26 @@
import axios from 'axios'; import axios from 'axios';
import agency from 'src/router/modules/agency';
export async function getAgencies(formData) { export async function getAgencies(formData, client, _filter = {}) {
if (!formData.warehouseId || !formData.addressId || !formData.landed) return; if (!formData.warehouseId || !formData.addressId || !formData.landed) return;
const filter = {
..._filter
};
let defaultAgency = null;
let params = { let params = {
filter: JSON.stringify(filter),
warehouseFk: formData.warehouseId, warehouseFk: formData.warehouseId,
addressFk: formData.addressId, addressFk: formData.addressId,
landed: formData.landed, landed: formData.landed,
}; };
return await axios.get('Agencies/getAgenciesWithWarehouse', { params }); const { data } = await axios.get('Agencies/getAgenciesWithWarehouse', { params });
if(data && client) {
defaultAgency = data.find((agency) => agency.agencyModeFk === client.defaultAddress.agencyModeFk );
};
return {options: data, agency: defaultAgency}
} }

View File

@ -6,6 +6,7 @@ import VnFilterPanel from 'components/ui/VnFilterPanel.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 VnInput from 'components/common/VnInput.vue'; import VnInput from 'components/common/VnInput.vue';
import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
const { t } = useI18n(); const { t } = useI18n();
const props = defineProps({ const props = defineProps({
@ -19,7 +20,6 @@ const emit = defineEmits(['search']);
const agencyList = ref([]); const agencyList = ref([]);
const agencyAgreementList = ref([]); const agencyAgreementList = ref([]);
const supplierList = ref([]);
const exprBuilder = (param, value) => { const exprBuilder = (param, value) => {
switch (param) { switch (param) {
@ -58,14 +58,6 @@ const exprBuilder = (param, value) => {
@on-fetch="(data) => (agencyAgreementList = data)" @on-fetch="(data) => (agencyAgreementList = data)"
auto-load auto-load
/> />
<FetchData
url="Suppliers"
:filter="{ fields: ['name'] }"
sort-by="name ASC"
limit="30"
@on-fetch="(data) => (supplierList = data)"
auto-load
/>
<VnFilterPanel <VnFilterPanel
:data-key="props.dataKey" :data-key="props.dataKey"
:expr-builder="exprBuilder" :expr-builder="exprBuilder"
@ -123,14 +115,12 @@ const exprBuilder = (param, value) => {
/> />
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem class="q-my-sm" v-if="supplierList"> <QItem class="q-my-sm">
<QItemSection> <QItemSection>
<VnSelect <VnSelectSupplier
:label="t('Autonomous')" :label="t('Autonomous')"
v-model="params.supplierFk" v-model="params.supplierFk"
:options="supplierList" hide-selected
option-value="name"
option-label="name"
dense dense
outlined outlined
rounded rounded

View File

@ -6,29 +6,18 @@ import FormModel from 'components/FormModel.vue';
import VnInputDate from 'components/common/VnInputDate.vue'; import VnInputDate from 'components/common/VnInputDate.vue';
import VnInput from 'components/common/VnInput.vue'; import VnInput from 'components/common/VnInput.vue';
import VnInputTime from 'components/common/VnInputTime.vue'; import VnInputTime from 'components/common/VnInputTime.vue';
import VnSelect from 'components/common/VnSelect.vue'; import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
import FetchData from 'components/FetchData.vue';
import { ref } from 'vue';
const { t } = useI18n(); const { t } = useI18n();
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
const supplierList = ref([]);
const filter = { include: [{ relation: 'supplier' }] }; const filter = { include: [{ relation: 'supplier' }] };
const onSave = (data, response) => { const onSave = (data, response) => {
router.push({ name: 'RoadmapSummary', params: { id: response?.id } }); router.push({ name: 'RoadmapSummary', params: { id: response?.id } });
}; };
</script> </script>
<template> <template>
<FetchData
url="Suppliers"
auto-load
:filter="{ fields: ['id', 'nickname'] }"
sort-by="nickname"
limit="30"
@on-fetch="(data) => (supplierList = data)"
/>
<FormModel <FormModel
:url="`Roadmaps/${route.params?.id}`" :url="`Roadmaps/${route.params?.id}`"
observe-form-changes observe-form-changes
@ -56,27 +45,14 @@ const onSave = (data, response) => {
/> />
</VnRow> </VnRow>
<VnRow> <VnRow>
<VnSelect <VnSelectSupplier
:label="t('Carrier')" :label="t('Carrier')"
v-model="data.supplierFk" v-model="data.supplierFk"
:options="supplierList"
option-value="id"
option-label="nickname"
emit-value emit-value
map-options map-options
use-input use-input
:input-debounce="0" :input-debounce="0"
> />
<template #option="{ itemProps, opt }">
<QItem v-bind="itemProps">
<QItemSection>
<QItemLabel
>{{ opt.id }} - {{ opt.nickname }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
<VnInput <VnInput
v-model="data.price" v-model="data.price"
:label="t('Price')" :label="t('Price')"

View File

@ -3,9 +3,9 @@ import { ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnFilterPanel from 'components/ui/VnFilterPanel.vue'; import VnFilterPanel from 'components/ui/VnFilterPanel.vue';
import VnSelect from 'components/common/VnSelect.vue';
import VnInputDate from 'components/common/VnInputDate.vue'; import VnInputDate from 'components/common/VnInputDate.vue';
import VnInput from 'components/common/VnInput.vue'; import VnInput from 'components/common/VnInput.vue';
import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
const { t } = useI18n(); const { t } = useI18n();
const props = defineProps({ const props = defineProps({
@ -16,19 +16,9 @@ const props = defineProps({
}); });
const emit = defineEmits(['search']); const emit = defineEmits(['search']);
const supplierList = ref([]);
</script> </script>
<template> <template>
<FetchData
url="Suppliers"
:filter="{ fields: ['id', 'nickname'] }"
sort-by="nickname"
limit="30"
@on-fetch="(data) => (supplierList = data)"
auto-load
/>
<VnFilterPanel <VnFilterPanel
:data-key="props.dataKey" :data-key="props.dataKey"
:search-button="true" :search-button="true"
@ -71,14 +61,11 @@ const supplierList = ref([]);
/> />
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem v-if="supplierList" class="q-my-sm"> <QItem class="q-my-sm">
<QItemSection> <QItemSection>
<VnSelect <VnSelectSupplier
:label="t('Carrier')" :label="t('Carrier')"
v-model="params.supplierFk" v-model="params.supplierFk"
:options="supplierList"
option-value="id"
option-label="nickname"
dense dense
outlined outlined
rounded rounded
@ -86,17 +73,7 @@ const supplierList = ref([]);
map-options map-options
use-input use-input
:input-debounce="0" :input-debounce="0"
> />
<template #option="{ itemProps, opt }">
<QItem v-bind="itemProps">
<QItemSection>
<QItemLabel
>{{ opt.id }} - {{ opt.nickname }}</QItemLabel
>
</QItemSection>
</QItem>
</template>
</VnSelect>
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem class="q-my-sm"> <QItem class="q-my-sm">

View File

@ -68,7 +68,7 @@ const updateDefaultStop = (data) => {
<QBtn <QBtn
flat flat
icon="add" icon="add"
shortcut="+" v-shortcut="'+'"
class="cursor-pointer" class="cursor-pointer"
color="primary" color="primary"
@click="roadmapStopsCrudRef.insert()" @click="roadmapStopsCrudRef.insert()"

View File

@ -120,8 +120,8 @@ const deletePriorities = async () => {
try { try {
await Promise.all( await Promise.all(
selectedRows.value.map((ticket) => selectedRows.value.map((ticket) =>
axios.patch(`Tickets/${ticket?.id}/`, { priority: null }) axios.patch(`Tickets/${ticket?.id}/`, { priority: null }),
) ),
); );
} finally { } finally {
refreshKey.value++; refreshKey.value++;
@ -132,8 +132,8 @@ const setOrderedPriority = async () => {
try { try {
await Promise.all( await Promise.all(
ticketList.value.map((ticket, index) => ticketList.value.map((ticket, index) =>
axios.patch(`Tickets/${ticket?.id}/`, { priority: index + 1 }) axios.patch(`Tickets/${ticket?.id}/`, { priority: index + 1 }),
) ),
); );
} finally { } finally {
refreshKey.value++; refreshKey.value++;
@ -162,7 +162,7 @@ const setHighestPriority = async (ticket, ticketList) => {
const goToBuscaman = async (ticket = null) => { const goToBuscaman = async (ticket = null) => {
await openBuscaman( await openBuscaman(
routeEntity.value?.vehicleFk, routeEntity.value?.vehicleFk,
ticket ? [ticket] : selectedRows.value ticket ? [ticket] : selectedRows.value,
); );
}; };
@ -393,7 +393,13 @@ const openSmsDialog = async () => {
</VnPaginate> </VnPaginate>
</div> </div>
<QPageSticky :offset="[20, 20]"> <QPageSticky :offset="[20, 20]">
<QBtn fab icon="add" shortcut="+" color="primary" @click="openTicketsDialog"> <QBtn
fab
icon="add"
v-shortcut="'+'"
color="primary"
@click="openTicketsDialog"
>
<QTooltip> <QTooltip>
{{ t('Add ticket') }} {{ t('Add ticket') }}
</QTooltip> </QTooltip>

View File

@ -84,7 +84,7 @@ function exprBuilder(param, value) {
</div> </div>
<QPageSticky :offset="[20, 20]"> <QPageSticky :offset="[20, 20]">
<RouterLink :to="{ name: 'ShelvingCreate' }"> <RouterLink :to="{ name: 'ShelvingCreate' }">
<QBtn fab icon="add" color="primary" shortcut="+" /> <QBtn fab icon="add" color="primary" v-shortcut="'+'" />
<QTooltip> <QTooltip>
{{ t('shelving.list.newShelving') }} {{ t('shelving.list.newShelving') }}
</QTooltip> </QTooltip>

View File

@ -71,7 +71,7 @@ function bankEntityFilter(val, update) {
filteredBankEntitiesOptions.value = bankEntitiesOptions.value.filter( filteredBankEntitiesOptions.value = bankEntitiesOptions.value.filter(
(bank) => (bank) =>
bank.bic.toLowerCase().startsWith(needle) || bank.bic.toLowerCase().startsWith(needle) ||
bank.name.toLowerCase().includes(needle) bank.name.toLowerCase().includes(needle),
); );
}); });
} }
@ -170,7 +170,7 @@ function bankEntityFilter(val, update) {
<QIcon name="info" class="cursor-pointer"> <QIcon name="info" class="cursor-pointer">
<QTooltip>{{ <QTooltip>{{
t( t(
'Name of the bank account holder if different from the provider' 'Name of the bank account holder if different from the provider',
) )
}}</QTooltip> }}</QTooltip>
</QIcon> </QIcon>
@ -194,7 +194,7 @@ function bankEntityFilter(val, update) {
<QBtn <QBtn
flat flat
icon="add" icon="add"
shortcut="+" v-shortcut
class="cursor-pointer" class="cursor-pointer"
color="primary" color="primary"
@click="supplierAccountRef.insert()" @click="supplierAccountRef.insert()"

View File

@ -89,7 +89,7 @@ const redirectToUpdateView = (addressData) => {
icon="add" icon="add"
color="primary" color="primary"
@click="redirectToCreateView()" @click="redirectToCreateView()"
shortcut="+" v-shortcut="'+'"
/> />
<QTooltip> <QTooltip>
{{ t('New address') }} {{ t('New address') }}

View File

@ -114,7 +114,7 @@ const redirectToCreateView = () => {
icon="add" icon="add"
color="primary" color="primary"
@click="redirectToCreateView()" @click="redirectToCreateView()"
shortcut="+" v-shortcut="'+'"
/> />
<QTooltip> <QTooltip>
{{ t('supplier.agencyTerms.addRow') }} {{ t('supplier.agencyTerms.addRow') }}

View File

@ -78,7 +78,7 @@ const insertRow = () => {
<QBtn <QBtn
flat flat
icon="add" icon="add"
shortcut="+" v-shortcut="'+'"
class="cursor-pointer" class="cursor-pointer"
color="primary" color="primary"
@click="insertRow()" @click="insertRow()"

View File

@ -9,6 +9,8 @@ import VnLv from 'src/components/ui/VnLv.vue';
import useCardDescription from 'src/composables/useCardDescription'; import useCardDescription from 'src/composables/useCardDescription';
import VnUserLink from 'src/components/ui/VnUserLink.vue'; import VnUserLink from 'src/components/ui/VnUserLink.vue';
import { toDateTimeFormat } from 'src/filters/date'; import { toDateTimeFormat } from 'src/filters/date';
import FetchData from 'src/components/FetchData.vue';
import TicketProblems from 'src/components/TicketProblems.vue';
const $props = defineProps({ const $props = defineProps({
id: { id: {
@ -28,6 +30,7 @@ const { t } = useI18n();
const entityId = computed(() => { const entityId = computed(() => {
return $props.id || route.params.id; return $props.id || route.params.id;
}); });
const problems = ref({});
const filter = { const filter = {
include: [ include: [
@ -113,6 +116,11 @@ const setData = (entity) => {
</script> </script>
<template> <template>
<FetchData
:url="`Tickets/${entityId}/getTicketProblems`"
auto-load
@on-fetch="(data) => ([problems] = data)"
/>
<CardDescriptor <CardDescriptor
module="Ticket" module="Ticket"
:url="`Tickets/${entityId}`" :url="`Tickets/${entityId}`"
@ -167,48 +175,9 @@ const setData = (entity) => {
<VnLv :label="t('globals.warehouse')" :value="entity.warehouse?.name" /> <VnLv :label="t('globals.warehouse')" :value="entity.warehouse?.name" />
<VnLv :label="t('globals.alias')" :value="entity.nickname" /> <VnLv :label="t('globals.alias')" :value="entity.nickname" />
</template> </template>
<template #icons="{ entity }"> <template #icons>
<QCardActions class="q-gutter-x-md"> <QCardActions class="q-gutter-x-xs">
<QIcon <TicketProblems :row="problems" />
v-if="entity.client.isActive == false"
name="vn:disabled"
size="xs"
color="primary"
>
<QTooltip>{{ t('Client inactive') }}</QTooltip>
</QIcon>
<QIcon
v-if="entity.client.isFreezed == true"
name="vn:frozen"
size="xs"
color="primary"
>
<QTooltip>{{ t('Client Frozen') }}</QTooltip>
</QIcon>
<QIcon
v-if="entity?.problem?.includes('hasRisk')"
name="vn:risk"
size="xs"
color="primary"
>
<QTooltip>{{ t('Client has debt') }}</QTooltip>
</QIcon>
<QIcon
v-if="entity.client.isTaxDataChecked == false"
name="vn:no036"
size="xs"
color="primary"
>
<QTooltip>{{ t('Client not checked') }}</QTooltip>
</QIcon>
<QIcon
v-if="entity.isDeleted == true"
name="vn:deletedTicket"
size="xs"
color="primary"
>
<QTooltip>{{ t('This ticket is deleted') }}</QTooltip>
</QIcon>
</QCardActions> </QCardActions>
</template> </template>
<template #actions="{ entity }"> <template #actions="{ entity }">

View File

@ -69,14 +69,16 @@ const onAddressSelected = (addressId) => {
} }
const fetchClient = async () => { const fetchClient = async () => {
const { data } = await getClient(client.value) const response = await getClient(client.value)
const [retrievedClient] = data; if (!response) return;
const [retrievedClient] = response.data;
selectedClient.value = retrievedClient; selectedClient.value = retrievedClient;
}; };
const fetchAddresses = async () => { const fetchAddresses = async () => {
const { data } = await getAddresses(client.value); const response = await getAddresses(client.value);
addressesOptions.value = data; if (!response) return;
addressesOptions.value = response.data;
const { defaultAddress } = selectedClient.value; const { defaultAddress } = selectedClient.value;
address.value = defaultAddress.id; address.value = defaultAddress.id;

View File

@ -32,7 +32,7 @@ watch(
crudModelFilter.where.ticketFk = route.params.id; crudModelFilter.where.ticketFk = route.params.id;
store.filter = crudModelFilter; store.filter = crudModelFilter;
await ticketNotesCrudRef.value.reload(); await ticketNotesCrudRef.value.reload();
} },
); );
function handleDelete(row) { function handleDelete(row) {
ticketNotesCrudRef.value.remove([row]); ticketNotesCrudRef.value.remove([row]);
@ -105,7 +105,7 @@ async function handleSave() {
<VnRow v-if="observationTypes.length > rows.length"> <VnRow v-if="observationTypes.length > rows.length">
<QBtn <QBtn
icon="add_circle" icon="add_circle"
shortcut="+" v-shortcut="'+'"
flat flat
class="fill-icon-on-hover q-ml-md" class="fill-icon-on-hover q-ml-md"
color="primary" color="primary"

View File

@ -41,7 +41,7 @@ watch(
crudModelFilter.where.ticketFk = route.params.id; crudModelFilter.where.ticketFk = route.params.id;
store.filter = crudModelFilter; store.filter = crudModelFilter;
await ticketPackagingsCrudRef.value.reload(); await ticketPackagingsCrudRef.value.reload();
} },
); );
</script> </script>
@ -118,7 +118,7 @@ watch(
<VnRow> <VnRow>
<QBtn <QBtn
icon="add_circle" icon="add_circle"
shortcut="+" v-shortcut="'+'"
flat flat
class="fill-icon-on-hover q-ml-md" class="fill-icon-on-hover q-ml-md"
color="primary" color="primary"

View File

@ -24,6 +24,7 @@ import axios from 'axios';
import VnTable from 'src/components/VnTable/VnTable.vue'; import VnTable from 'src/components/VnTable/VnTable.vue';
import VnUsesMana from 'src/components/ui/VnUsesMana.vue'; import VnUsesMana from 'src/components/ui/VnUsesMana.vue';
import VnConfirm from 'src/components/ui/VnConfirm.vue'; import VnConfirm from 'src/components/ui/VnConfirm.vue';
import TicketProblems from 'src/components/TicketProblems.vue';
import RightMenu from 'src/components/common/RightMenu.vue'; import RightMenu from 'src/components/common/RightMenu.vue';
const route = useRoute(); const route = useRoute();
@ -56,7 +57,7 @@ const canProceed = ref();
watch( watch(
() => route.params.id, () => route.params.id,
() => tableRef.value.reload() () => tableRef.value.reload(),
); );
const columns = computed(() => [ const columns = computed(() => [
@ -199,7 +200,7 @@ const changeQuantity = async (sale) => {
await updateQuantity(sale); await updateQuantity(sale);
} catch (e) { } catch (e) {
const { quantity } = tableRef.value.CrudModelRef.originalData.find( const { quantity } = tableRef.value.CrudModelRef.originalData.find(
(s) => s.id === sale.id (s) => s.id === sale.id,
); );
sale.quantity = quantity; sale.quantity = quantity;
throw e; throw e;
@ -503,7 +504,7 @@ async function isSalePrepared(item) {
componentProps: { componentProps: {
title: t('Item prepared'), title: t('Item prepared'),
message: t( message: t(
'This item is already prepared. Do you want to continue?' 'This item is already prepared. Do you want to continue?',
), ),
data: item, data: item,
}, },
@ -525,7 +526,7 @@ watch(
if (newItemFk) { if (newItemFk) {
updateItem(newRow.value); updateItem(newRow.value);
} }
} },
); );
</script> </script>
@ -595,7 +596,7 @@ watch(
openConfirmationModal( openConfirmationModal(
t('Continue anyway?'), t('Continue anyway?'),
t('You are going to delete lines of the ticket'), t('You are going to delete lines of the ticket'),
removeSales removeSales,
) )
" "
> >
@ -679,53 +680,7 @@ watch(
:disabled-attr="isTicketEditable" :disabled-attr="isTicketEditable"
> >
<template #column-statusIcons="{ row }"> <template #column-statusIcons="{ row }">
<router-link <TicketProblems :row="row" />
v-if="row.claim?.claimFk"
:to="{ name: 'ClaimBasicData', params: { id: row.claim?.claimFk } }"
>
<QIcon color="primary" name="vn:claims" size="xs">
<QTooltip>
{{ t('ticketSale.claim') }}:
{{ row.claim?.claimFk }}
</QTooltip>
</QIcon>
</router-link>
<QIcon v-if="row.visible < 0" color="primary" name="warning" size="xs">
<QTooltip>
{{ t('ticketSale.visible') }}: {{ row.visible || 0 }}
</QTooltip>
</QIcon>
<QIcon
v-if="row.reserved"
color="primary"
name="vn:reserva"
size="xs"
data-cy="ticketSaleReservedIcon"
>
<QTooltip>
{{ t('ticketSale.reserved') }}
</QTooltip>
</QIcon>
<QIcon
v-if="row.itemShortage"
color="primary"
name="vn:unavailable"
size="xs"
>
<QTooltip>
{{ t('ticketSale.noVisible') }}
</QTooltip>
</QIcon>
<QIcon
v-if="row.hasComponentLack"
color="primary"
name="vn:components"
size="xs"
>
<QTooltip>
{{ t('ticketSale.hasComponentLack') }}
</QTooltip>
</QIcon>
</template> </template>
<template #body-cell-picture="{ row }"> <template #body-cell-picture="{ row }">
<QTd> <QTd>
@ -856,7 +811,7 @@ watch(
color="primary" color="primary"
fab fab
icon="add" icon="add"
shortcut="+" v-shortcut="'+'"
data-cy="ticketSaleAddToBasketBtn" data-cy="ticketSaleAddToBasketBtn"
/> />
<QTooltip class="text-no-wrap"> <QTooltip class="text-no-wrap">

View File

@ -40,7 +40,7 @@ watch(
async () => { async () => {
store.filter = crudModelFilter.value; store.filter = crudModelFilter.value;
await ticketServiceCrudRef.value.reload(); await ticketServiceCrudRef.value.reload();
} },
); );
onMounted(async () => await getDefaultTaxClass()); onMounted(async () => await getDefaultTaxClass());
@ -59,7 +59,7 @@ const createRefund = async () => {
t('service.createRefundSuccess', { t('service.createRefundSuccess', {
ticketId: refundTicket.id, ticketId: refundTicket.id,
}), }),
'positive' 'positive',
); );
router.push({ name: 'TicketSale', params: { id: refundTicket.id } }); router.push({ name: 'TicketSale', params: { id: refundTicket.id } });
}; };
@ -225,7 +225,7 @@ async function handleSave() {
color="primary" color="primary"
icon="add" icon="add"
@click="ticketServiceCrudRef.insert()" @click="ticketServiceCrudRef.insert()"
shortcut="+" v-shortcut="'+'"
/> />
</QPageSticky> </QPageSticky>
</template> </template>

View File

@ -20,6 +20,7 @@ import ZoneDescriptorProxy from 'src/pages/Zone/Card/ZoneDescriptorProxy.vue';
import VnSelect from 'src/components/common/VnSelect.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnToSummary from 'src/components/ui/VnToSummary.vue'; import VnToSummary from 'src/components/ui/VnToSummary.vue';
import TicketDescriptorMenu from './TicketDescriptorMenu.vue'; import TicketDescriptorMenu from './TicketDescriptorMenu.vue';
import TicketProblems from 'src/components/TicketProblems.vue';
const route = useRoute(); const route = useRoute();
const { notify } = useNotify(); const { notify } = useNotify();
@ -243,7 +244,7 @@ onMounted(async () => {
<QCard class="vn-one" v-if="entity.notes.length"> <QCard class="vn-one" v-if="entity.notes.length">
<VnTitle <VnTitle
:url="toTicketUrl('observation')" :url="toTicketUrl('observation')"
:text="t('ticket.pageTitles.notes')" :text="t('globals.pageTitles.notes')"
/> />
<QVirtualScroll <QVirtualScroll
:items="entity.notes" :items="entity.notes"
@ -311,83 +312,7 @@ onMounted(async () => {
<template #body="props"> <template #body="props">
<QTr :props="props"> <QTr :props="props">
<QTd class="q-gutter-x-xs"> <QTd class="q-gutter-x-xs">
<QBtn <TicketProblems :row="props.row" />
flat
round
icon="vn:claims"
v-if="props.row.claim"
color="primary"
:to="{
name: 'ClaimCard',
params: {
id: props.row.claim.claimFk,
},
}"
>
<QTooltip>
{{ t('ticket.summary.claim') }}:
{{ props.row.claim.claimFk }}
</QTooltip>
</QBtn>
<QBtn
flat
round
icon="vn:claims"
v-if="props.row.claimBeginning"
color="primary"
:to="{
name: 'ClaimCard',
params: {
id: props.row.claimBeginning.claimFk,
},
}"
>
<QTooltip>
{{ t('ticket.summary.claim') }}:
{{ props.row.claimBeginning.claimFk }}
</QTooltip>
</QBtn>
<QIcon
name="warning"
v-show="props.row.visible < 0"
color="primary"
size="xs"
>
<QTooltip>
{{ t('globals.visible') }}:
{{ props.row.visible }}
</QTooltip>
</QIcon>
<QIcon
name="vn:reserved"
v-show="props.row.reserved"
color="primary"
size="xs"
>
<QTooltip>
{{ t('ticket.summary.reserved') }}
</QTooltip>
</QIcon>
<QIcon
name="vn:unavailable"
v-show="props.row.itemShortage"
color="primary"
size="xs"
>
<QTooltip>
{{ t('ticket.summary.itemShortage') }}
</QTooltip>
</QIcon>
<QIcon
name="vn:components"
v-show="props.row.hasComponentLack"
color="primary"
size="xs"
>
<QTooltip>
{{ t('ticket.summary.hasComponentLack') }}
</QTooltip>
</QIcon>
</QTd> </QTd>
<QTd> <QTd>
<QBtn class="link" flat> <QBtn class="link" flat>

View File

@ -19,7 +19,7 @@ watch(
async (val) => { async (val) => {
paginateFilter.where.ticketFk = val; paginateFilter.where.ticketFk = val;
paginateRef.value.fetch(); paginateRef.value.fetch();
} },
); );
const paginateFilter = reactive({ const paginateFilter = reactive({
@ -119,7 +119,7 @@ const openCreateModal = () => createTrackingDialogRef.value.show();
color="primary" color="primary"
fab fab
icon="add" icon="add"
shortcut="+" v-shortcut="'+'"
/> />
<QTooltip class="text-no-wrap"> <QTooltip class="text-no-wrap">
{{ t('tracking.addState') }} {{ t('tracking.addState') }}

View File

@ -38,35 +38,43 @@ onBeforeMount(async () => {
await onClientSelected(initialFormState); await onClientSelected(initialFormState);
}); });
function resetAgenciesSelector(formData) {
agenciesOptions.value = [];
formData.agencyModeId = null;
}
const fetchClient = async (formData) => { const fetchClient = async (formData) => {
const { data } = await getClient(formData.clientId); const response = await getClient(formData.clientId);
const [client] = data; if (!response) return;
const [client] = response.data;
selectedClient.value = client; selectedClient.value = client;
}; };
const fetchAddresses = async (formData) => { const fetchAddresses = async (formData) => {
const { data } = await getAddresses(formData.clientId); const response = await getAddresses(formData.clientId);
addressesOptions.value = data; if (!response) return;
addressesOptions.value = response.data;
const { defaultAddress } = selectedClient.value; const { defaultAddress } = selectedClient.value;
formData.addressId = defaultAddress.id; formData.addressId = defaultAddress.id;
}; };
const onClientSelected = async (formData) => { const onClientSelected = async (formData) => {
resetAgenciesSelector(formData);
await fetchClient(formData); await fetchClient(formData);
await fetchAddresses(formData); await fetchAddresses(formData);
}; };
const fetchAvailableAgencies = async (formData) => { const fetchAvailableAgencies = async (formData) => {
const { data } = await getAgencies(formData); resetAgenciesSelector(formData);
agenciesOptions.value = data; const response= await getAgencies(formData, selectedClient.value);
if (!response) return;
const defaultAgency = agenciesOptions.value.find(
(agency) => const { options, agency } = response
agency.agencyModeFk === selectedClient.value.defaultAddress.agencyModeFk if(options)
); agenciesOptions.value = options;
if(agency)
if (defaultAgency) formData.agencyModeId = defaultAgency.agencyModeFk; formData.agencyModeId = agency;
}; };
const redirectToTicketList = (_, { id }) => { const redirectToTicketList = (_, { id }) => {

View File

@ -38,35 +38,43 @@ onBeforeMount(async () => {
await onClientSelected(initialFormState); await onClientSelected(initialFormState);
}); });
function resetAgenciesSelector(formData) {
agenciesOptions.value = [];
if(formData) formData.agencyModeId = null;
}
const fetchClient = async (formData) => { const fetchClient = async (formData) => {
const { data } = await getClient(formData.clientId); const response = await getClient(formData.clientId);
const [client] = data; if (!response) return;
const [client] = response.data;
selectedClient.value = client; selectedClient.value = client;
}; };
const fetchAddresses = async (formData) => { const fetchAddresses = async (formData) => {
const { data } = await getAddresses(formData.clientId); const response = await getAddresses(formData.clientId);
addressesOptions.value = data; if (!response) return;
addressesOptions.value = response.data;
const { defaultAddress } = selectedClient.value; const { defaultAddress } = selectedClient.value;
formData.addressId = defaultAddress.id; formData.addressId = defaultAddress.id;
}; };
const onClientSelected = async (formData) => { const onClientSelected = async (formData) => {
resetAgenciesSelector(formData);
await fetchClient(formData); await fetchClient(formData);
await fetchAddresses(formData); await fetchAddresses(formData);
}; };
const fetchAvailableAgencies = async (formData) => { const fetchAvailableAgencies = async (formData) => {
const { data } = await getAgencies(formData); resetAgenciesSelector(formData);
agenciesOptions.value = data; const response= await getAgencies(formData, selectedClient.value);
if (!response) return;
const defaultAgency = agenciesOptions.value.find(
(agency) => const { options, agency } = response
agency.agencyModeFk === selectedClient.value.defaultAddress.agencyModeFk if(options)
); agenciesOptions.value = options;
if(agency)
if (defaultAgency) formData.agencyModeId = defaultAgency.agencyModeFk; formData.agencyModeId = agency;
}; };
const redirectToTicketList = (_, { id }) => { const redirectToTicketList = (_, { id }) => {

View File

@ -229,37 +229,46 @@ const columns = computed(() => [
], ],
}, },
]); ]);
function resetAgenciesSelector(formData) {
agenciesOptions.value = [];
if(formData) formData.agencyModeId = null;
}
function redirectToLines(id) { function redirectToLines(id) {
const url = `#/ticket/${id}/sale`; const url = `#/ticket/${id}/sale`;
window.open(url, '_blank'); window.open(url, '_blank');
} }
const onClientSelected = async (formData) => { const onClientSelected = async (formData) => {
resetAgenciesSelector(formData);
await fetchClient(formData); await fetchClient(formData);
await fetchAddresses(formData); await fetchAddresses(formData);
}; };
const fetchAvailableAgencies = async (formData) => { const fetchAvailableAgencies = async (formData) => {
const { data } = await getAgencies(formData); resetAgenciesSelector(formData);
agenciesOptions.value = data; const response= await getAgencies(formData, selectedClient.value);
if (!response) return;
const defaultAgency = agenciesOptions.value.find(
(agency) => const { options, agency } = response
agency.agencyModeFk === selectedClient.value.defaultAddress.agencyModeFk if(options)
); agenciesOptions.value = options;
if(agency)
if (defaultAgency) formData.agencyModeId = defaultAgency.agencyModeFk; formData.agencyModeId = agency;
}; };
const fetchClient = async (formData) => { const fetchClient = async (formData) => {
const { data } = await getClient(formData.clientId); const response = await getClient(formData.clientId);
const [client] = data; if (!response) return;
const [client] = response.data;
selectedClient.value = client; selectedClient.value = client;
}; };
const fetchAddresses = async (formData) => { const fetchAddresses = async (formData) => {
const { data } = await getAddresses(formData.clientId); const response = await getAddresses(formData.clientId);
addressesOptions.value = data; if (!response) return;
addressesOptions.value = response.data;
const { defaultAddress } = selectedClient.value; const { defaultAddress } = selectedClient.value;
formData.addressId = defaultAddress.id; formData.addressId = defaultAddress.id;

View File

@ -208,3 +208,9 @@ ticketList:
toLines: Go to lines toLines: Go to lines
addressNickname: Address nickname addressNickname: Address nickname
ref: Reference ref: Reference
rounding: Rounding
noVerifiedData: No verified data
purchaseRequest: Purchase request
notVisible: Not visible
clientFrozen: Client frozen
componentLack: Component lack

View File

@ -213,3 +213,9 @@ ticketList:
toLines: Ir a lineas toLines: Ir a lineas
addressNickname: Alias consignatario addressNickname: Alias consignatario
ref: Referencia ref: Referencia
rounding: Redondeo
noVerifiedData: Sin datos comprobados
purchaseRequest: Petición de compra
notVisible: No visible
clientFrozen: Cliente congelado
componentLack: Faltan componentes

View File

@ -217,7 +217,7 @@ const removeThermograph = async (id) => {
icon="add" icon="add"
color="primary" color="primary"
@click="redirectToThermographForm('create')" @click="redirectToThermographForm('create')"
shortcut="+" v-shortcut="'+'"
/> />
<QTooltip class="text-no-wrap"> <QTooltip class="text-no-wrap">
{{ t('Add thermograph') }} {{ t('Add thermograph') }}

View File

@ -8,6 +8,7 @@ import VnSelect from 'src/components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnInputDate from 'components/common/VnInputDate.vue'; import VnInputDate from 'components/common/VnInputDate.vue';
import axios from 'axios'; import axios from 'axios';
import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
const { t } = useI18n(); const { t } = useI18n();
const props = defineProps({ const props = defineProps({
@ -48,11 +49,11 @@ const warehouses = async () => {
continentsResponse.data.forEach((continent) => { continentsResponse.data.forEach((continent) => {
const countriesInContinent = Object.keys(countryContinentMap).filter( const countriesInContinent = Object.keys(countryContinentMap).filter(
(countryId) => countryContinentMap[countryId] === continent.id.toString() (countryId) => countryContinentMap[countryId] === continent.id.toString(),
); );
warehousesByContinent.value[continent.code] = warehousesResponse.data.filter( warehousesByContinent.value[continent.code] = warehousesResponse.data.filter(
(warehouse) => countriesInContinent.includes(warehouse.countryFk.toString()) (warehouse) => countriesInContinent.includes(warehouse.countryFk.toString()),
); );
}); });
}; };
@ -112,7 +113,7 @@ warehouses();
<template #append> <template #append>
<QBtn <QBtn
icon="add" icon="add"
shortcut="+" v-shortcut="'+'"
flat flat
dense dense
size="12px" size="12px"
@ -211,30 +212,13 @@ warehouses();
</QItem> </QItem>
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnSelect <VnSelectSupplier
:label="t('globals.pageTitles.supplier')"
v-model="params.cargoSupplierFk" v-model="params.cargoSupplierFk"
url="Suppliers"
option-value="id"
option-label="name"
hide-selected hide-selected
dense dense
outlined outlined
rounded rounded
> />
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>
{{ scope.opt?.name}}
</QItemLabel>
<QItemLabel caption>
{{ `#${scope.opt?.id } , ${ scope.opt?.nickname}` }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem> <QItem>

View File

@ -96,7 +96,13 @@ async function remove(row) {
> >
</VnTable> </VnTable>
<QPageSticky :offset="[18, 18]"> <QPageSticky :offset="[18, 18]">
<QBtn @click.stop="dialog.show()" color="primary" fab icon="add" shortcut="+"> <QBtn
@click.stop="dialog.show()"
color="primary"
fab
icon="add"
v-shortcut="'+'"
>
<QDialog ref="dialog"> <QDialog ref="dialog">
<FormModelPopup <FormModelPopup
:title="t('Create new Wagon type')" :title="t('Create new Wagon type')"

View File

@ -0,0 +1,234 @@
<script setup>
import { ref, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import VnTable from 'components/VnTable/VnTable.vue';
import { toDate } from 'src/filters';
import { useQuasar } from 'quasar';
import axios from 'axios';
const { t } = useI18n();
const route = useRoute();
const tableRef = ref();
const entityId = computed(() => route.params.id);
const quasar = useQuasar();
async function reactivateWorker() {
const hasToReactive = tableRef.value.CrudModelRef.formData.find(
(data) => !data.ended
);
if (hasToReactive) {
quasar
.dialog({
message: t('Do you want to reactivate the user?'),
ok: {
push: true,
color: 'primary',
},
cancel: true,
})
.onOk(async () => {
await axios.patch(`Workers/${entityId.value}`, {
isDisable: false,
});
});
}
}
const columns = computed(() => [
{
name: 'started',
label: t('worker.business.tableVisibleColumns.started'),
align: 'left',
format: ({ started }) => toDate(started),
component: 'date',
cardVisible: true,
create: true,
},
{
name: 'ended',
label: t('worker.business.tableVisibleColumns.ended'),
align: 'left',
format: ({ ended }) => toDate(ended),
component: 'date',
cardVisible: true,
create: true,
},
{
label: t('worker.business.tableVisibleColumns.company'),
align: 'left',
name: 'companyCodeFk',
component: 'select',
attrs: {
url: 'Companies',
fields: ['code'],
optionLabel: 'code',
optionValue: 'code',
},
cardVisible: true,
create: true,
},
{
align: 'left',
name: 'reasonEndFk',
component: 'select',
label: t('worker.business.tableVisibleColumns.reasonEnd'),
attrs: {
url: 'BusinessReasonEnds',
fields: ['id', 'reason'],
optionLabel: 'reason',
},
cardVisible: true,
},
{
align: 'left',
name: 'departmentFk',
component: 'select',
label: t('worker.business.tableVisibleColumns.department'),
attrs: {
url: 'Departments',
fields: ['id', 'name'],
optionLabel: 'name',
},
cardVisible: true,
create: true,
},
{
align: 'left',
name: 'workerBusinessProfessionalCategoryFk',
component: 'select',
label: t('worker.business.tableVisibleColumns.professionalCategory'),
attrs: {
url: 'WorkerBusinessProfessionalCategories',
fields: ['id', 'description', 'code'],
optionLabel: 'description',
},
cardVisible: true,
create: true,
},
{
align: 'left',
name: 'calendarTypeFk',
component: 'select',
label: t('worker.business.tableVisibleColumns.calendarType'),
attrs: {
url: 'CalendarTypes',
fields: ['id', 'description'],
optionLabel: 'description',
},
cardVisible: true,
create: true,
},
{
align: 'left',
name: 'workcenterFk',
component: 'select',
label: t('worker.business.tableVisibleColumns.workCenter'),
attrs: {
url: 'WorkCenters',
fields: ['id', 'name'],
optionLabel: 'name',
},
cardVisible: true,
create: true,
},
{
align: 'left',
name: 'workerBusinessCategoryFk',
component: 'select',
label: t('worker.business.tableVisibleColumns.payrollCategories'),
attrs: {
url: 'payrollCategories',
fields: ['id', 'description'],
optionLabel: 'description',
},
cardVisible: true,
create: true,
},
{
align: 'left',
name: 'occupationCodeFk',
component: 'select',
label: t('worker.business.tableVisibleColumns.occupationCode'),
attrs: {
url: 'OccupationCodes',
fields: ['code', 'name'],
optionValue: 'code',
},
cardVisible: true,
create: true,
},
{
align: 'left',
name: 'rate',
label: t('worker.business.tableVisibleColumns.rate'),
component: 'input',
cardVisible: true,
create: true,
},
{
align: 'left',
name: 'workerBusinessTypeFk',
component: 'select',
label: t('worker.business.tableVisibleColumns.businessType'),
attrs: {
url: 'WorkerBusinessTypes',
fields: ['id', 'name'],
},
cardVisible: true,
create: true,
},
{
align: 'left',
label: t('worker.business.tableVisibleColumns.amount'),
name: 'amount',
component: 'input',
cardVisible: true,
create: true,
},
{
align: 'left',
label: t('worker.business.tableVisibleColumns.basicSalary'),
name: 'basicSalary',
component: 'input',
cardVisible: true,
create: true,
},
{
name: 'notes',
label: t('worker.business.tableVisibleColumns.notes'),
align: 'left',
component: 'input',
cardVisible: true,
},
]);
</script>
<template>
<VnTable
ref="tableRef"
data-key="WorkerBusiness"
:url="`Workers/${entityId}/Business`"
save-url="/Businesses/crud"
:create="{
urlCreate: `Workers/${entityId}/Business`,
title: 'Create business',
onDataSaved: () => tableRef.reload(),
formInitialData: {},
}"
order="id DESC"
:columns="columns"
default-mode="card"
auto-load
:disable-option="{ table: true }"
:right-search="false"
card-class="grid-two q-gutter-x-xl q-gutter-y-md q-pr-lg q-py-lg"
:is-editable="true"
:use-model="true"
@save-changes="(data) => reactivateWorker(data)"
/>
</template>
<i18n>
es:
Do you want to reactivate the user?: desea reactivar el usuario?
</i18n>

View File

@ -1,7 +1,8 @@
<script setup> <script setup>
import { nextTick, ref, watch } from 'vue'; import { nextTick, ref, watch, computed } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { useAcl } from 'src/composables/useAcl';
import WorkerCalendarFilter from 'pages/Worker/Card/WorkerCalendarFilter.vue'; import WorkerCalendarFilter from 'pages/Worker/Card/WorkerCalendarFilter.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
@ -9,10 +10,17 @@ import WorkerCalendarItem from 'pages/Worker/Card/WorkerCalendarItem.vue';
import RightMenu from 'src/components/common/RightMenu.vue'; import RightMenu from 'src/components/common/RightMenu.vue';
import axios from 'axios'; import axios from 'axios';
import VnNotes from 'src/components/ui/VnNotes.vue';
import { useStateStore } from 'src/stores/useStateStore';
const stateStore = useStateStore();
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
const { t } = useI18n(); const { t } = useI18n();
const acl = useAcl();
const canSeeNotes = computed(() =>
acl.hasAny([{ model: 'Worker', props: '__get__business', accessType: 'READ' }]),
);
const workerIsFreelance = ref(); const workerIsFreelance = ref();
const WorkerFreelanceRef = ref(); const WorkerFreelanceRef = ref();
const workerCalendarFilterRef = ref(null); const workerCalendarFilterRef = ref(null);
@ -26,6 +34,10 @@ const contractHolidays = ref(null);
const yearHolidays = ref(null); const yearHolidays = ref(null);
const eventsMap = ref({}); const eventsMap = ref({});
const festiveEventsMap = ref({}); const festiveEventsMap = ref({});
const saveUrl = ref();
const body = {
workerFk: route.params.id,
};
const onFetchActiveContract = (data) => { const onFetchActiveContract = (data) => {
if (!data) return; if (!data) return;
@ -67,7 +79,7 @@ const onFetchAbsences = (data) => {
name: holidayName, name: holidayName,
isFestive: true, isFestive: true,
}, },
true true,
); );
}); });
} }
@ -146,7 +158,7 @@ watch(
async () => { async () => {
await nextTick(); await nextTick();
await activeContractRef.value.fetch(); await activeContractRef.value.fetch();
} },
); );
watch([year, businessFk], () => refreshData()); watch([year, businessFk], () => refreshData());
</script> </script>
@ -181,6 +193,20 @@ watch([year, businessFk], () => refreshData());
/> />
</template> </template>
</RightMenu> </RightMenu>
<Teleport to="#st-data" v-if="stateStore.isSubToolbarShown() && canSeeNotes">
<VnNotes
:just-input="true"
:url="`Workers/${route.params.id}/business`"
:filter="{ fields: ['id', 'notes', 'workerFk'] }"
:save-url="saveUrl"
@on-fetch="
(data) => {
saveUrl = `Businesses/${data.id}`;
}
"
:body="body"
/>
</Teleport>
<QPage class="column items-center"> <QPage class="column items-center">
<QCard v-if="workerIsFreelance"> <QCard v-if="workerIsFreelance">
<QCardSection class="text-center"> <QCardSection class="text-center">

View File

@ -180,8 +180,6 @@ const yearList = ref(generateYears());
:is-clearable="false" :is-clearable="false"
/> />
</QItemSection> </QItemSection>
</QItem>
<QItem>
<QItemSection> <QItemSection>
<VnSelect <VnSelect
:label="t('Contract')" :label="t('Contract')"

View File

@ -1,7 +1,7 @@
<script setup> <script setup>
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { ref, computed } from 'vue'; import { ref, computed, watch } from 'vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import VnSelect from 'src/components/common/VnSelect.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
@ -19,6 +19,7 @@ const trainsData = ref([]);
const machinesData = ref([]); const machinesData = ref([]);
const route = useRoute(); const route = useRoute();
const routeId = computed(() => route.params.id); const routeId = computed(() => route.params.id);
const selected = ref([]);
const initialData = computed(() => { const initialData = computed(() => {
return { return {
@ -41,6 +42,21 @@ async function insert() {
await axios.post('Operators', initialData.value); await axios.post('Operators', initialData.value);
crudModelRef.value.reload(); crudModelRef.value.reload();
} }
watch(
() => crudModelRef.value?.formData,
(formData) => {
if (formData && formData.length) {
if (JSON.stringify(selected.value) !== JSON.stringify(formData)) {
selected.value = formData;
}
} else if (selected.value.length > 0) {
selected.value = [];
}
},
{ immediate: true, deep: true }
);
</script> </script>
<template> <template>
@ -67,6 +83,7 @@ async function insert() {
:data-required="{ workerFk: route.params.id }" :data-required="{ workerFk: route.params.id }"
ref="crudModelRef" ref="crudModelRef"
search-url="operator" search-url="operator"
:selected="selected"
auto-load auto-load
> >
<template #body="{ rows }"> <template #body="{ rows }">

View File

@ -101,7 +101,7 @@ function reloadData() {
openConfirmationModal( openConfirmationModal(
t(`Remove PDA`), t(`Remove PDA`),
t('Do you want to remove this PDA?'), t('Do you want to remove this PDA?'),
() => deallocatePDA(row.deviceProductionFk) () => deallocatePDA(row.deviceProductionFk),
) )
" "
> >
@ -114,7 +114,13 @@ function reloadData() {
</template> </template>
</VnPaginate> </VnPaginate>
<QPageSticky :offset="[18, 18]"> <QPageSticky :offset="[18, 18]">
<QBtn @click.stop="dialog.show()" color="primary" fab icon="add" shortcut="+"> <QBtn
@click.stop="dialog.show()"
color="primary"
fab
icon="add"
v-shortcut="'+'"
>
<QDialog ref="dialog"> <QDialog ref="dialog">
<FormModelPopup <FormModelPopup
:title="t('Add new device')" :title="t('Add new device')"

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