Merge branch 'dev' of https: refs #8326//gitea.verdnatura.es/verdnatura/salix-front into 8326-UnifyCardTitles
gitea/salix-front/pipeline/pr-dev This commit is unstable Details

This commit is contained in:
Jon Elias 2025-03-21 08:14:36 +01:00
commit bd8c610ce8
222 changed files with 3339 additions and 2873 deletions

4
Jenkinsfile vendored
View File

@ -115,11 +115,14 @@ pipeline {
steps { steps {
script { script {
sh 'rm -f junit/e2e-*.xml' sh 'rm -f junit/e2e-*.xml'
sh 'rm -rf test/cypress/screenshots'
env.COMPOSE_TAG = PROTECTED_BRANCH.contains(env.CHANGE_TARGET) ? env.CHANGE_TARGET : 'dev' env.COMPOSE_TAG = PROTECTED_BRANCH.contains(env.CHANGE_TARGET) ? env.CHANGE_TARGET : 'dev'
def image = docker.build('lilium-dev', '-f docs/Dockerfile.dev docs') def image = docker.build('lilium-dev', '-f docs/Dockerfile.dev docs')
sh 'docker login --username $CREDS_USR --password $CREDS_PSW $REGISTRY' sh 'docker login --username $CREDS_USR --password $CREDS_PSW $REGISTRY'
sh "docker-compose ${env.COMPOSE_PARAMS} pull back"
sh "docker-compose ${env.COMPOSE_PARAMS} pull db"
sh "docker-compose ${env.COMPOSE_PARAMS} up -d" sh "docker-compose ${env.COMPOSE_PARAMS} up -d"
image.inside("--network ${env.COMPOSE_PROJECT}_default -e CI -e TZ --init") { image.inside("--network ${env.COMPOSE_PROJECT}_default -e CI -e TZ --init") {
@ -130,6 +133,7 @@ pipeline {
post { post {
always { always {
sh "docker-compose ${env.COMPOSE_PARAMS} down -v" sh "docker-compose ${env.COMPOSE_PARAMS} down -v"
archiveArtifacts artifacts: 'test/cypress/screenshots/**/*', allowEmptyArchive: true
junit( junit(
testResults: 'junit/e2e-*.xml', testResults: 'junit/e2e-*.xml',
allowEmptyResults: true allowEmptyResults: true

View File

@ -32,6 +32,18 @@ pnpm run test:front
pnpm run test:e2e pnpm run test:e2e
``` ```
### Run e2e parallel
```bash
pnpm run test:e2e:parallel
```
### View e2e parallel report
```bash
pnpm run test:e2e:summary
```
### Build the app for production ### Build the app for production
```bash ```bash

View File

@ -25,6 +25,8 @@ RUN apt-get update \
libnss3 \ libnss3 \
libxss1 \ libxss1 \
libxtst6 \ libxtst6 \
mesa-vulkan-drivers \
vulkan-tools \
xauth \ xauth \
xvfb \ xvfb \
&& apt-get clean \ && apt-get clean \

View File

@ -1,6 +1,6 @@
{ {
"name": "salix-front", "name": "salix-front",
"version": "25.12.0", "version": "25.14.0",
"description": "Salix frontend", "description": "Salix frontend",
"productName": "Salix", "productName": "Salix",
"author": "Verdnatura", "author": "Verdnatura",
@ -13,7 +13,7 @@
"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:e2e:parallel": "bash ./test/cypress/cypressParallel.sh", "test:e2e:parallel": "bash ./test/cypress/run.sh",
"test:e2e:summary": "bash ./test/cypress/summary.sh", "test:e2e:summary": "bash ./test/cypress/summary.sh",
"test": "echo \"See package.json => scripts for available tests.\" && exit 0", "test": "echo \"See package.json => scripts for available tests.\" && exit 0",
"test:front": "vitest", "test:front": "vitest",
@ -56,6 +56,7 @@
"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",
"junit-merge": "^2.0.0",
"mocha": "^11.1.0", "mocha": "^11.1.0",
"postcss": "^8.4.23", "postcss": "^8.4.23",
"prettier": "^3.4.2", "prettier": "^3.4.2",

View File

@ -91,6 +91,9 @@ devDependencies:
husky: husky:
specifier: ^8.0.0 specifier: ^8.0.0
version: 8.0.3 version: 8.0.3
junit-merge:
specifier: ^2.0.0
version: 2.0.0
mocha: mocha:
specifier: ^11.1.0 specifier: ^11.1.0
version: 11.1.0 version: 11.1.0
@ -4860,6 +4863,10 @@ packages:
universalify: 2.0.1 universalify: 2.0.1
dev: true dev: true
/fs-readdir-recursive@1.1.0:
resolution: {integrity: sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==}
dev: true
/fs.realpath@1.0.0: /fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
@ -5640,6 +5647,16 @@ packages:
verror: 1.10.0 verror: 1.10.0
dev: true dev: true
/junit-merge@2.0.0:
resolution: {integrity: sha512-qwENzBWcdHPazNqPO0fKyFIqEyaSKyO0iyBeIU4Y/scjkXYpwTi88P2S/PWecqgMhzG2MOCwXk8QB9ucvXeIPw==}
hasBin: true
dependencies:
commander: 2.20.3
fs-readdir-recursive: 1.1.0
mkdirp: 0.5.6
xmldoc: 1.3.0
dev: true
/keyv@4.5.4: /keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
dependencies: dependencies:
@ -6037,7 +6054,6 @@ packages:
hasBin: true hasBin: true
dependencies: dependencies:
minimist: 1.2.8 minimist: 1.2.8
dev: false
/mlly@1.7.4: /mlly@1.7.4:
resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==}
@ -7253,6 +7269,10 @@ packages:
resolution: {integrity: sha512-5f3k2PbGGp+YtKJjOItpg3P99IMD84E4HOvcfleTb5joCHNXYLsR9yWFPOYGgaeMPDubQILTCMdsFb2OMeOjtg==} resolution: {integrity: sha512-5f3k2PbGGp+YtKJjOItpg3P99IMD84E4HOvcfleTb5joCHNXYLsR9yWFPOYGgaeMPDubQILTCMdsFb2OMeOjtg==}
dev: true dev: true
/sax@1.4.1:
resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==}
dev: true
/scheduler@0.25.0: /scheduler@0.25.0:
resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==} resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==}
dev: true dev: true
@ -8689,6 +8709,12 @@ packages:
engines: {node: '>=4.0'} engines: {node: '>=4.0'}
dev: true dev: true
/xmldoc@1.3.0:
resolution: {integrity: sha512-y7IRWW6PvEnYQZNZFMRLNJw+p3pezM4nKYPfr15g4OOW9i8VpeydycFuipE2297OvZnh3jSb2pxOt9QpkZUVng==}
dependencies:
sax: 1.4.1
dev: true
/xunit-viewer@10.6.1(@babel/runtime@7.26.9)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.3)(codemirror@6.0.1)(react-dom@19.0.0)(react@19.0.0): /xunit-viewer@10.6.1(@babel/runtime@7.26.9)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.3)(codemirror@6.0.1)(react-dom@19.0.0)(react@19.0.0):
resolution: {integrity: sha512-ZMprLPVhCQJf2KD56tv2hlOjc4T+KnUe1E9DkEBHnuliOq7IOXWJf61pxyBMo/7H83B7Ln0DIeWNMMbx/3I7Jg==} resolution: {integrity: sha512-ZMprLPVhCQJf2KD56tv2hlOjc4T+KnUe1E9DkEBHnuliOq7IOXWJf61pxyBMo/7H83B7Ln0DIeWNMMbx/3I7Jg==}
hasBin: true hasBin: true

View File

@ -181,9 +181,8 @@ async function saveChanges(data) {
return; return;
} }
let changes = data || getChanges(); let changes = data || getChanges();
if ($props.beforeSaveFn) { if ($props.beforeSaveFn) changes = await $props.beforeSaveFn(changes, getChanges);
changes = await $props.beforeSaveFn(changes, getChanges);
}
try { try {
if (changes?.creates?.length === 0 && changes?.updates?.length === 0) { if (changes?.creates?.length === 0 && changes?.updates?.length === 0) {
return; return;
@ -194,7 +193,7 @@ async function saveChanges(data) {
isLoading.value = false; isLoading.value = false;
} }
originalData.value = JSON.parse(JSON.stringify(formData.value)); originalData.value = JSON.parse(JSON.stringify(formData.value));
if (changes.creates?.length) await vnPaginateRef.value.fetch(); if (changes?.creates?.length) await vnPaginateRef.value.fetch();
hasChanges.value = false; hasChanges.value = false;
emit('saveChanges', data); emit('saveChanges', data);

View File

@ -188,7 +188,7 @@ const selectItem = ({ id }) => {
> >
<template #body-cell-id="{ row }"> <template #body-cell-id="{ row }">
<QTd auto-width @click.stop> <QTd auto-width @click.stop>
<QBtn flat color="blue">{{ row.id }}</QBtn> <QBtn flat class="link">{{ row.id }}</QBtn>
<ItemDescriptorProxy :id="row.id" /> <ItemDescriptorProxy :id="row.id" />
</QTd> </QTd>
</template> </template>

View File

@ -196,7 +196,7 @@ const selectTravel = ({ id }) => {
> >
<template #body-cell-id="{ row }"> <template #body-cell-id="{ row }">
<QTd auto-width @click.stop data-cy="travelFk-travel-form"> <QTd auto-width @click.stop data-cy="travelFk-travel-form">
<QBtn flat color="blue">{{ row.id }}</QBtn> <QBtn flat class="link">{{ row.id }}</QBtn>
<TravelDescriptorProxy :id="row.id" /> <TravelDescriptorProxy :id="row.id" />
</QTd> </QTd>
</template> </template>

View File

@ -77,6 +77,7 @@ watch(
function findMatches(search, item) { function findMatches(search, item) {
const matches = []; const matches = [];
function findRoute(search, item) { function findRoute(search, item) {
if (!item?.children) return;
for (const child of item.children) { for (const child of item.children) {
if (search?.indexOf(child.name) > -1) { if (search?.indexOf(child.name) > -1) {
matches.push(child); matches.push(child);
@ -92,7 +93,7 @@ function findMatches(search, item) {
} }
function addChildren(module, route, parent) { function addChildren(module, route, parent) {
const menus = route?.meta?.menu ?? route?.menus?.[props.source]; //backwards compatible const menus = route?.meta?.menu;
if (!menus) return; if (!menus) return;
const matches = findMatches(menus, route); const matches = findMatches(menus, route);
@ -107,11 +108,7 @@ function getRoutes() {
main: getMainRoutes, main: getMainRoutes,
card: getCardRoutes, card: getCardRoutes,
}; };
try { handleRoutes[props.source]();
handleRoutes[props.source]();
} catch (error) {
throw new Error(`Method is not defined`);
}
} }
function getMainRoutes() { function getMainRoutes() {
const modules = Object.assign([], navigation.getModules().value); const modules = Object.assign([], navigation.getModules().value);
@ -122,7 +119,6 @@ function getMainRoutes() {
); );
if (!moduleDef) continue; if (!moduleDef) continue;
item.children = []; item.children = [];
addChildren(item.module, moduleDef, item.children); addChildren(item.module, moduleDef, item.children);
} }
@ -132,21 +128,16 @@ function getMainRoutes() {
function getCardRoutes() { function getCardRoutes() {
const currentRoute = route.matched[1]; const currentRoute = route.matched[1];
const currentModule = toLowerCamel(currentRoute.name); const currentModule = toLowerCamel(currentRoute.name);
let moduleDef = routes.find((route) => toLowerCamel(route.name) === currentModule); let moduleDef;
if (!moduleDef) return;
if (!moduleDef?.menus) moduleDef = betaGetRoutes();
addChildren(currentModule, moduleDef, items.value);
}
function betaGetRoutes() {
let menuRoute;
let index = route.matched.length - 1; let index = route.matched.length - 1;
while (!menuRoute && index > 0) { while (!moduleDef && index > 0) {
if (route.matched[index]?.meta?.menu) menuRoute = route.matched[index]; if (route.matched[index]?.meta?.menu) moduleDef = route.matched[index];
index--; index--;
} }
return menuRoute;
if (!moduleDef) return;
addChildren(currentModule, moduleDef, items.value);
} }
async function togglePinned(item, event) { async function togglePinned(item, event) {

View File

@ -28,6 +28,17 @@ defineProps({ row: { type: Object, required: true } });
{{ t('ticketSale.reserved') }} {{ t('ticketSale.reserved') }}
</QTooltip> </QTooltip>
</QIcon> </QIcon>
<QIcon
v-if="row?.isDeleted"
color="primary"
name="vn:deletedTicket"
size="xs"
data-cy="ticketDeletedIcon"
>
<QTooltip>
{{ t('Ticket deleted') }}
</QTooltip>
</QIcon>
<QIcon <QIcon
v-if="row?.hasRisk" v-if="row?.hasRisk"
name="vn:risk" name="vn:risk"

View File

@ -6,6 +6,7 @@ import VnSelect from 'components/common/VnSelect.vue';
import VnInput from 'components/common/VnInput.vue'; import VnInput from 'components/common/VnInput.vue';
import VnInputDate from 'components/common/VnInputDate.vue'; import VnInputDate from 'components/common/VnInputDate.vue';
import VnInputTime from 'components/common/VnInputTime.vue'; import VnInputTime from 'components/common/VnInputTime.vue';
import VnCheckbox from 'components/common/VnCheckbox.vue';
import VnColumn from 'components/VnTable/VnColumn.vue'; import VnColumn from 'components/VnTable/VnColumn.vue';
const $props = defineProps({ const $props = defineProps({
@ -106,7 +107,7 @@ const components = {
}, },
}, },
checkbox: { checkbox: {
component: markRaw(QCheckbox), component: markRaw(VnCheckbox),
event: updateEvent, event: updateEvent,
attrs: { attrs: {
class: $props.showTitle ? 'q-py-sm' : 'q-px-md q-py-xs fit', class: $props.showTitle ? 'q-py-sm' : 'q-px-md q-py-xs fit',

View File

@ -140,7 +140,7 @@ const $props = defineProps({
}, },
dataCy: { dataCy: {
type: String, type: String,
default: 'vn-table', default: 'vnTable',
}, },
}); });
@ -595,18 +595,17 @@ function cardClick(_, row) {
function removeTextValue(data, getChanges) { function removeTextValue(data, getChanges) {
let changes = data.updates; let changes = data.updates;
if (!changes) return data; if (changes) {
for (const change of changes) {
for (const change of changes) { for (const key in change.data) {
for (const key in change.data) { if (key.endsWith('VnTableTextValue')) {
if (key.endsWith('VnTableTextValue')) { delete change.data[key];
delete change.data[key]; }
} }
} }
data.updates = changes.filter((change) => Object.keys(change.data).length > 0);
} }
data.updates = changes.filter((change) => Object.keys(change.data).length > 0);
if ($attrs?.beforeSaveFn) data = $attrs.beforeSaveFn(data, getChanges); if ($attrs?.beforeSaveFn) data = $attrs.beforeSaveFn(data, getChanges);
return data; return data;
@ -685,7 +684,7 @@ const rowCtrlClickFunction = computed(() => {
@update:selected="emit('update:selected', $event)" @update:selected="emit('update:selected', $event)"
@selection="(details) => handleSelection(details, rows)" @selection="(details) => handleSelection(details, rows)"
:hide-selected-banner="true" :hide-selected-banner="true"
:data-cy="$props.dataCy ?? 'vnTable'" :data-cy
> >
<template #top-left v-if="!$props.withoutHeader"> <template #top-left v-if="!$props.withoutHeader">
<slot name="top-left"> </slot> <slot name="top-left"> </slot>
@ -776,12 +775,13 @@ const rowCtrlClickFunction = computed(() => {
:data-col-field="col?.name" :data-col-field="col?.name"
> >
<div <div
class="no-padding no-margin peter" class="no-padding no-margin"
style=" style="
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
" "
:data-cy="`vnTableCell_${col.name}`"
> >
<slot <slot
:name="`column-${col.name}`" :name="`column-${col.name}`"
@ -920,12 +920,24 @@ const rowCtrlClickFunction = computed(() => {
:row-index="index" :row-index="index"
> >
<VnColumn <VnColumn
:column="col" :column="{
...col,
disable:
col?.component ===
'checkbox'
? true
: false,
}"
:row="row" :row="row"
:is-editable="false" :is-editable="false"
v-model="row[col.name]" v-model="row[col.name]"
component-prop="columnField" component-prop="columnField"
:show-label="true" :show-label="
col?.component ===
'checkbox'
? false
: true
"
/> />
</slot> </slot>
</span> </span>
@ -966,6 +978,8 @@ const rowCtrlClickFunction = computed(() => {
v-for="col of cols.filter((cols) => cols.visible ?? true)" v-for="col of cols.filter((cols) => cols.visible ?? true)"
:key="col?.id" :key="col?.id"
:class="getColAlign(col)" :class="getColAlign(col)"
:style="col?.width ? `max-width: ${col?.width}` : ''"
style="font-size: small"
> >
<slot <slot
:name="`column-footer-${col.name}`" :name="`column-footer-${col.name}`"
@ -1028,38 +1042,43 @@ const rowCtrlClickFunction = computed(() => {
@on-data-saved="(_, res) => createForm.onDataSaved(res)" @on-data-saved="(_, res) => createForm.onDataSaved(res)"
> >
<template #form-inputs="{ data }"> <template #form-inputs="{ data }">
<div :style="createComplement?.containerStyle"> <slot name="alter-create" :data="data">
<div <div :style="createComplement?.containerStyle">
:style="createComplement?.previousStyle" <div
v-if="!quasar.screen.xs" :style="createComplement?.previousStyle"
> v-if="!quasar.screen.xs"
<slot name="previous-create-dialog" :data="data" />
</div>
<div class="grid-create" :style="createComplement?.columnGridStyle">
<slot
v-for="column of splittedColumns.create"
:key="column.name"
:name="`column-create-${column.name}`"
:data="data"
:column-name="column.name"
:label="column.label"
> >
<VnColumn <slot name="previous-create-dialog" :data="data" />
:column="{ </div>
...column, <div
...{ disable: column?.createDisable ?? false }, class="grid-create"
}" :style="createComplement?.columnGridStyle"
:row="{}" >
default="input" <slot
v-model="data[column.name]" v-for="column of splittedColumns.create"
:show-label="true" :key="column.name"
component-prop="columnCreate" :name="`column-create-${column.name}`"
:data-cy="`${column.name}-create-popup`" :data="data"
/> :column-name="column.name"
</slot> :label="column.label"
<slot name="more-create-dialog" :data="data" /> >
<VnColumn
:column="{
...column,
...column?.createAttrs,
}"
:row="{}"
default="input"
v-model="data[column.name]"
:show-label="true"
component-prop="columnCreate"
:data-cy="`${column.name}-create-popup`"
/>
</slot>
<slot name="more-create-dialog" :data="data" />
</div>
</div> </div>
</div> </slot>
</template> </template>
</FormModelPopup> </FormModelPopup>
</QDialog> </QDialog>

View File

@ -15,10 +15,7 @@ vi.mock('src/router/modules', () => ({
meta: { meta: {
title: 'customers', title: 'customers',
icon: 'vn:client', icon: 'vn:client',
}, menu: ['CustomerList', 'CustomerCreate'],
menus: {
main: ['CustomerList', 'CustomerCreate'],
card: ['CustomerBasicData'],
}, },
children: [ children: [
{ {
@ -50,14 +47,6 @@ vi.mock('src/router/modules', () => ({
], ],
}, },
}, },
{
path: 'create',
name: 'CustomerCreate',
meta: {
title: 'createCustomer',
icon: 'vn:addperson',
},
},
], ],
}, },
], ],
@ -98,7 +87,7 @@ vi.spyOn(vueRouter, 'useRoute').mockReturnValue({
icon: 'vn:client', icon: 'vn:client',
moduleName: 'Customer', moduleName: 'Customer',
keyBinding: 'c', keyBinding: 'c',
menu: 'customer', menu: ['customer'],
}, },
}, },
], ],
@ -260,15 +249,6 @@ describe('Leftmenu as main', () => {
}); });
}); });
it('should handle a single matched route with a menu', () => {
const route = {
matched: [{ meta: { menu: 'customer' } }],
};
const result = vm.betaGetRoutes();
expect(result.meta.menu).toEqual(route.matched[0].meta.menu);
});
it('should get routes for main source', () => { it('should get routes for main source', () => {
vm.props.source = 'main'; vm.props.source = 'main';
vm.getRoutes(); vm.getRoutes();
@ -351,8 +331,9 @@ describe('addChildren', () => {
it('should handle routes with no meta menu', () => { it('should handle routes with no meta menu', () => {
const route = { const route = {
meta: {}, meta: {
menus: {}, menu: [],
},
}; };
const parent = []; const parent = [];

View File

@ -27,7 +27,11 @@ const checkboxModel = computed({
</script> </script>
<template> <template>
<div> <div>
<QCheckbox v-bind="$attrs" v-model="checkboxModel" /> <QCheckbox
v-bind="$attrs"
v-model="checkboxModel"
:data-cy="$attrs['data-cy'] ?? `vnCheckbox${$attrs['label'] ?? ''}`"
/>
<QIcon <QIcon
v-if="info" v-if="info"
v-bind="$attrs" v-bind="$attrs"

View File

@ -177,6 +177,7 @@ function addDefaultData(data) {
name="vn:attach" name="vn:attach"
class="cursor-pointer" class="cursor-pointer"
@click="inputFileRef.pickFiles()" @click="inputFileRef.pickFiles()"
data-cy="attachFile"
> >
<QTooltip>{{ t('globals.selectFile') }}</QTooltip> <QTooltip>{{ t('globals.selectFile') }}</QTooltip>
</QIcon> </QIcon>

View File

@ -389,10 +389,7 @@ defineExpose({
</div> </div>
</template> </template>
</QTable> </QTable>
<div <div v-else class="info-row q-pa-md text-center">
v-else
class="info-row q-pa-md text-center"
>
<h5> <h5>
{{ t('No data to display') }} {{ t('No data to display') }}
</h5> </h5>
@ -416,6 +413,7 @@ defineExpose({
v-shortcut v-shortcut
@click="showFormDialog()" @click="showFormDialog()"
class="fill-icon" class="fill-icon"
data-cy="addButton"
> >
<QTooltip> <QTooltip>
{{ t('Upload file') }} {{ t('Upload file') }}

View File

@ -107,7 +107,7 @@ const manageDate = (date) => {
@click="isPopupOpen = !isPopupOpen" @click="isPopupOpen = !isPopupOpen"
@keydown="isPopupOpen = false" @keydown="isPopupOpen = false"
hide-bottom-space hide-bottom-space
:data-cy="$attrs.dataCy ?? $attrs.label + '_inputDate'" :data-cy="($attrs['data-cy'] ?? $attrs.label) + '_inputDate'"
> >
<template #append> <template #append>
<QIcon <QIcon

View File

@ -10,7 +10,7 @@ import { useColor } from 'src/composables/useColor';
import { useCapitalize } from 'src/composables/useCapitalize'; import { useCapitalize } from 'src/composables/useCapitalize';
import { useValidator } from 'src/composables/useValidator'; import { useValidator } from 'src/composables/useValidator';
import VnAvatar from '../ui/VnAvatar.vue'; import VnAvatar from '../ui/VnAvatar.vue';
import VnJsonValue from '../common/VnJsonValue.vue'; import VnLogValue from './VnLogValue.vue';
import FetchData from '../FetchData.vue'; import FetchData from '../FetchData.vue';
import VnSelect from './VnSelect.vue'; import VnSelect from './VnSelect.vue';
import VnUserLink from '../ui/VnUserLink.vue'; import VnUserLink from '../ui/VnUserLink.vue';
@ -560,10 +560,11 @@ watch(
value.nameI18n value.nameI18n
}}: }}:
</span> </span>
<VnJsonValue <VnLogValue
:value=" :value="
value.val.val value.val.val
" "
:name="value.name"
/> />
</QItem> </QItem>
</QCardSection> </QCardSection>
@ -614,7 +615,11 @@ watch(
> >
{{ prop.nameI18n }}: {{ prop.nameI18n }}:
</span> </span>
<VnJsonValue :value="prop.val.val" /> <VnLogValue
:value="prop.val.val"
:name="prop.name"
/>
<VnIconLink />
<span <span
v-if=" v-if="
propIndex < propIndex <
@ -642,8 +647,9 @@ watch(
{{ prop.nameI18n }}: {{ prop.nameI18n }}:
</span> </span>
<span v-if="log.action == 'update'"> <span v-if="log.action == 'update'">
<VnJsonValue <VnLogValue
:value="prop.old.val" :value="prop.old.val"
:name="prop.name"
/> />
<span <span
v-if="prop.old.id" v-if="prop.old.id"
@ -652,8 +658,9 @@ watch(
#{{ prop.old.id }} #{{ prop.old.id }}
</span> </span>
<VnJsonValue <VnLogValue
:value="prop.val.val" :value="prop.val.val"
:name="prop.name"
/> />
<span <span
v-if="prop.val.id" v-if="prop.val.id"
@ -663,8 +670,9 @@ watch(
</span> </span>
</span> </span>
<span v-else="prop.old.val"> <span v-else="prop.old.val">
<VnJsonValue <VnLogValue
:value="prop.val.val" :value="prop.val.val"
:name="prop.name"
/> />
<span <span
v-if="prop.old.id" v-if="prop.old.id"

View File

@ -0,0 +1,22 @@
<script setup>
import { useDescriptorStore } from 'src/stores/useDescriptorStore';
import VnJsonValue from './VnJsonValue.vue';
import { computed } from 'vue';
const descriptorStore = useDescriptorStore();
const $props = defineProps({
name: { type: [String], default: undefined },
});
const descriptor = computed(() => descriptorStore.has($props.name));
</script>
<template>
<VnJsonValue v-bind="$attrs" />
<QIcon
name="launch"
class="link"
v-if="$attrs.value && descriptor"
:data-cy="'iconLaunch-' + $props.name"
/>
<component :is="descriptor" :id="$attrs.value" v-if="$attrs.value && descriptor" />
</template>

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { ref, toRefs, computed, watch, onMounted, useAttrs } from 'vue'; import { ref, toRefs, computed, watch, onMounted, useAttrs, nextTick } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useArrayData } from 'src/composables/useArrayData'; import { useArrayData } from 'src/composables/useArrayData';
import { useRequired } from 'src/composables/useRequired'; import { useRequired } from 'src/composables/useRequired';
@ -247,6 +247,7 @@ async function fetchFilter(val) {
} }
async function filterHandler(val, update) { async function filterHandler(val, update) {
if (isLoading.value) return update();
if (!val && lastVal.value === val) { if (!val && lastVal.value === val) {
lastVal.value = val; lastVal.value = val;
return update(); return update();
@ -294,6 +295,7 @@ async function onScroll({ to, direction, from, index }) {
await arrayData.loadMore(); await arrayData.loadMore();
setOptions(arrayData.store.data); setOptions(arrayData.store.data);
vnSelectRef.value.scrollTo(lastIndex); vnSelectRef.value.scrollTo(lastIndex);
await nextTick();
isLoading.value = false; isLoading.value = false;
} }
} }

View File

@ -0,0 +1,26 @@
import { describe, it, expect } from 'vitest';
import VnLogValue from 'src/components/common/VnLogValue.vue';
import { createWrapper } from 'app/test/vitest/helper';
const buildComponent = (props) => {
return createWrapper(VnLogValue, {
props,
global: {},
}).wrapper;
};
describe('VnLogValue', () => {
const id = 1;
it('renders without descriptor', async () => {
expect(getIcon('inventFk').exists()).toBe(false);
});
it('renders with descriptor', async () => {
expect(getIcon('claimFk').text()).toBe('launch');
});
function getIcon(name) {
const wrapper = buildComponent({ value: { val: id }, name });
return wrapper.find('.q-icon');
}
});

View File

@ -38,6 +38,10 @@ const $props = defineProps({
type: String, type: String,
default: 'md-width', default: 'md-width',
}, },
toModule: {
type: String,
default: null,
},
}); });
const state = useState(); const state = useState();
@ -129,6 +133,9 @@ const emit = defineEmits(['onFetch']);
const iconModule = computed(() => { const iconModule = computed(() => {
moduleName.value = getName(); moduleName.value = getName();
if ($props.toModule) {
return router.getRoutes().find((r) => r.name === $props.toModule.name).meta.icon;
}
if (isSameModuleName) { if (isSameModuleName) {
return router.options.routes[1].children.find((r) => r.name === moduleName.value) return router.options.routes[1].children.find((r) => r.name === moduleName.value)
?.meta?.icon; ?.meta?.icon;
@ -139,9 +146,10 @@ const iconModule = computed(() => {
const toModule = computed(() => { const toModule = computed(() => {
moduleName.value = getName(); moduleName.value = getName();
if ($props.toModule) return $props.toModule;
if (isSameModuleName) { if (isSameModuleName) {
return router.options.routes[1].children.find((r) => r.name === moduleName.value) return router.options.routes[1].children.find((r) => r.name === moduleName.value)
?.children[0]?.redirect; ?.redirect;
} else { } else {
return route.matched[1].path.split('/').length > 2 return route.matched[1].path.split('/').length > 2
? route.matched[1].redirect ? route.matched[1].redirect
@ -151,7 +159,7 @@ const toModule = computed(() => {
</script> </script>
<template> <template>
<div class="descriptor"> <div class="descriptor" data-cy="cardDescriptor">
<template v-if="entity && !isLoading"> <template v-if="entity && !isLoading">
<div class="header bg-primary q-pa-sm justify-between"> <div class="header bg-primary q-pa-sm justify-between">
<slot name="header-extra-action"> <slot name="header-extra-action">
@ -180,6 +188,7 @@ const toModule = computed(() => {
color="white" color="white"
class="link" class="link"
v-if="summary" v-if="summary"
data-cy="openSummaryBtn"
> >
<QTooltip> <QTooltip>
{{ t('components.smartCard.openSummary') }} {{ t('components.smartCard.openSummary') }}
@ -194,6 +203,7 @@ const toModule = computed(() => {
icon="launch" icon="launch"
round round
size="md" size="md"
data-cy="goToSummaryBtn"
> >
<QTooltip> <QTooltip>
{{ t('components.cardDescriptor.summary') }} {{ t('components.cardDescriptor.summary') }}
@ -211,18 +221,27 @@ const toModule = computed(() => {
<QList dense> <QList dense>
<QItemLabel header class="ellipsis text-h5" :lines="1"> <QItemLabel header class="ellipsis text-h5" :lines="1">
<div class="title"> <div class="title">
<span v-if="$props.title" :title="getValueFromPath(title)"> <span
v-if="$props.title"
:title="getValueFromPath(title)"
:data-cy="`${$attrs['data-cy'] ?? 'cardDescriptor'}_title`"
>
{{ getValueFromPath(title) ?? $props.title }} {{ getValueFromPath(title) ?? $props.title }}
</span> </span>
<slot v-else name="description" :entity="entity"> <slot v-else name="description" :entity="entity">
<span :title="entity.name"> <span
{{ entity.name }} :title="entity.name"
</span> :data-cy="`${$attrs['data-cy'] ?? 'cardDescriptor'}_description`"
v-text="entity.name"
/>
</slot> </slot>
</div> </div>
</QItemLabel> </QItemLabel>
<QItem> <QItem>
<QItemLabel class="subtitle"> <QItemLabel
class="subtitle"
:data-cy="`${$attrs['data-cy'] ?? 'cardDescriptor'}_subtitle`"
>
#{{ getValueFromPath(subtitle) ?? entity.id }} #{{ getValueFromPath(subtitle) ?? entity.id }}
</QItemLabel> </QItemLabel>
<QBtn <QBtn
@ -240,7 +259,10 @@ const toModule = computed(() => {
</QBtn> </QBtn>
</QItem> </QItem>
</QList> </QList>
<div class="list-box q-mt-xs"> <div
class="list-box q-mt-xs"
:data-cy="`${$attrs['data-cy'] ?? 'cardDescriptor'}_listbox`"
>
<slot name="body" :entity="entity" /> <slot name="body" :entity="entity" />
</div> </div>
</div> </div>

View File

@ -81,6 +81,7 @@ async function fetch() {
name: `${moduleName ?? route.meta.moduleName}Summary`, name: `${moduleName ?? route.meta.moduleName}Summary`,
params: { id: entityId || entity.id }, params: { id: entityId || entity.id },
}" }"
data-cy="goToSummaryBtn"
> >
<QIcon name="open_in_new" color="white" size="sm" /> <QIcon name="open_in_new" color="white" size="sm" />
</router-link> </router-link>

View File

@ -54,7 +54,7 @@ const $props = defineProps({
default: 'table', default: 'table',
}, },
redirect: { redirect: {
type: Boolean, type: [String, Boolean],
default: true, default: true,
}, },
arrayData: { arrayData: {
@ -249,7 +249,7 @@ const getLocale = (label) => {
:key="chip.label" :key="chip.label"
:removable="!unremovableParams?.includes(chip.label)" :removable="!unremovableParams?.includes(chip.label)"
@remove="remove(chip.label)" @remove="remove(chip.label)"
data-cy="vnFilterPanelChip" :data-cy="`vnFilterPanelChip_${chip.label}`"
> >
<slot <slot
name="tags" name="tags"

View File

@ -28,7 +28,7 @@ function copyValueText() {
const val = computed(() => $props.value); const val = computed(() => $props.value);
</script> </script>
<template> <template>
<div class="vn-label-value"> <div class="vn-label-value" :data-cy="`${$attrs['data-cy'] ?? 'vnLv'}${label ?? ''}`">
<QCheckbox <QCheckbox
v-if="typeof value === 'boolean'" v-if="typeof value === 'boolean'"
v-model="val" v-model="val"

View File

@ -12,7 +12,7 @@
{{ $t('components.cardDescriptor.moreOptions') }} {{ $t('components.cardDescriptor.moreOptions') }}
</QTooltip> </QTooltip>
<QMenu ref="menuRef" data-cy="descriptor-more-opts-menu"> <QMenu ref="menuRef" data-cy="descriptor-more-opts-menu">
<QList> <QList data-cy="descriptor-more-opts_list">
<slot name="menu" :menu-ref="$refs.menuRef" /> <slot name="menu" :menu-ref="$refs.menuRef" />
</QList> </QList>
</QMenu> </QMenu>

View File

@ -9,6 +9,8 @@ export function getColAlign(col) {
case 'number': case 'number':
align = 'right'; align = 'right';
break; break;
case 'time':
case 'date':
case 'checkbox': case 'checkbox':
align = 'center'; align = 'center';
break; break;

View File

@ -15,6 +15,7 @@ body.body--light {
--vn-empty-tag: #acacac; --vn-empty-tag: #acacac;
--vn-black-text-color: black; --vn-black-text-color: black;
--vn-text-color-contrast: white; --vn-text-color-contrast: white;
--vn-link-color: #1e90ff;
background-color: var(--vn-page-color); background-color: var(--vn-page-color);
@ -38,6 +39,7 @@ body.body--dark {
--vn-empty-tag: #2d2d2d; --vn-empty-tag: #2d2d2d;
--vn-black-text-color: black; --vn-black-text-color: black;
--vn-text-color-contrast: black; --vn-text-color-contrast: black;
--vn-link-color: #66bfff;
background-color: var(--vn-page-color); background-color: var(--vn-page-color);
@ -49,7 +51,7 @@ a {
} }
.link { .link {
color: $color-link; color: var(--vn-link-color);
cursor: pointer; cursor: pointer;
&--white { &--white {
@ -58,14 +60,14 @@ a {
} }
.tx-color-link { .tx-color-link {
color: $color-link !important; color: var(--vn-link-color) !important;
} }
.tx-color-font { .tx-color-font {
color: $color-link !important; color: var(--vn-link-color) !important;
} }
.header-link { .header-link {
color: $color-link !important; color: var(--vn-link-color) !important;
cursor: pointer; cursor: pointer;
border-bottom: solid $primary; border-bottom: solid $primary;
border-width: 2px; border-width: 2px;

View File

@ -24,7 +24,6 @@ $alert: $negative;
$white: #fff; $white: #fff;
$dark: #3d3d3d; $dark: #3d3d3d;
// custom // custom
$color-link: #66bfff;
$color-spacer-light: #a3a3a31f; $color-spacer-light: #a3a3a31f;
$color-spacer: #7979794d; $color-spacer: #7979794d;
$border-thin-light: 1px solid $color-spacer-light; $border-thin-light: 1px solid $color-spacer-light;

View File

@ -99,7 +99,6 @@ globals:
file: File file: File
selectFile: Select a file selectFile: Select a file
copyClipboard: Copy on clipboard copyClipboard: Copy on clipboard
salesPerson: SalesPerson
send: Send send: Send
code: Code code: Code
since: Since since: Since
@ -158,7 +157,9 @@ globals:
changeState: Change state changeState: Change state
raid: 'Raid {daysInForward} days' raid: 'Raid {daysInForward} days'
isVies: Vies isVies: Vies
department: Department
noData: No data available noData: No data available
vehicle: Vehicle
pageTitles: pageTitles:
logIn: Login logIn: Login
addressEdit: Update address addressEdit: Update address
@ -346,7 +347,6 @@ globals:
params: params:
description: Description description: Description
clientFk: Client id clientFk: Client id
salesPersonFk: Sales person
warehouseFk: Warehouse warehouseFk: Warehouse
provinceFk: Province provinceFk: Province
stateFk: State stateFk: State
@ -604,7 +604,6 @@ worker:
balance: Balance balance: Balance
medical: Medical medical: Medical
list: list:
department: Department
schedule: Schedule schedule: Schedule
newWorker: New worker newWorker: New worker
summary: summary:
@ -863,7 +862,6 @@ components:
mine: For me mine: For me
hasMinPrice: Minimum price hasMinPrice: Minimum price
# LatestBuysFilter # LatestBuysFilter
salesPersonFk: Buyer
supplierFk: Supplier supplierFk: Supplier
from: From from: From
to: To to: To
@ -894,6 +892,8 @@ components:
VnLv: VnLv:
copyText: '{copyValue} has been copied to the clipboard' copyText: '{copyValue} has been copied to the clipboard'
iban_tooltip: 'IBAN: ES21 1234 5678 90 0123456789' iban_tooltip: 'IBAN: ES21 1234 5678 90 0123456789'
VnNotes:
clientWithoutPhone: 'The following clients do not have a phone number and the message will not be sent to them: {clientWithoutPhone}'
weekdays: weekdays:
sun: Sunday sun: Sunday
mon: Monday mon: Monday

View File

@ -103,7 +103,6 @@ globals:
file: Fichero file: Fichero
selectFile: Seleccione un fichero selectFile: Seleccione un fichero
copyClipboard: Copiar en portapapeles copyClipboard: Copiar en portapapeles
salesPerson: Comercial
send: Enviar send: Enviar
code: Código code: Código
since: Desde since: Desde
@ -163,6 +162,8 @@ globals:
raid: 'Redada {daysInForward} días' raid: 'Redada {daysInForward} días'
isVies: Vies isVies: Vies
noData: Datos no disponibles noData: Datos no disponibles
department: Departamento
vehicle: Vehículo
pageTitles: pageTitles:
logIn: Inicio de sesión logIn: Inicio de sesión
addressEdit: Modificar consignatario addressEdit: Modificar consignatario
@ -349,7 +350,6 @@ globals:
params: params:
description: Descripción description: Descripción
clientFk: Id cliente clientFk: Id cliente
salesPersonFk: Comercial
warehouseFk: Almacén warehouseFk: Almacén
provinceFk: Provincia provinceFk: Provincia
stateFk: Estado stateFk: Estado
@ -531,7 +531,6 @@ ticket:
state: Estado state: Estado
shipped: Enviado shipped: Enviado
landed: Entregado landed: Entregado
salesPerson: Comercial
total: Total total: Total
card: card:
customerId: ID cliente customerId: ID cliente
@ -623,8 +622,6 @@ invoiceOut:
errors: errors:
downloadCsvFailed: Error al descargar CSV downloadCsvFailed: Error al descargar CSV
order: order:
field:
salesPersonFk: Comercial
form: form:
clientFk: Cliente clientFk: Cliente
addressFk: Dirección addressFk: Dirección
@ -692,7 +689,6 @@ worker:
formation: Formación formation: Formación
medical: Mutua medical: Mutua
list: list:
department: Departamento
schedule: Horario schedule: Horario
newWorker: Nuevo trabajador newWorker: Nuevo trabajador
summary: summary:
@ -950,7 +946,6 @@ components:
hasMinPrice: Precio mínimo hasMinPrice: Precio mínimo
wareHouseFk: Almacén wareHouseFk: Almacén
# LatestBuysFilter # LatestBuysFilter
salesPersonFk: Comprador
supplierFk: Proveedor supplierFk: Proveedor
visible: Visible visible: Visible
active: Activo active: Activo
@ -981,6 +976,8 @@ components:
VnLv: VnLv:
copyText: '{copyValue} se ha copiado al portapepeles' copyText: '{copyValue} se ha copiado al portapepeles'
iban_tooltip: 'IBAN: ES21 1234 5678 90 0123456789' iban_tooltip: 'IBAN: ES21 1234 5678 90 0123456789'
VnNotes:
clientWithoutPhone: 'Estos clientes no tienen asociado número de télefono y el sms no les será enviado: {clientWithoutPhone}'
weekdays: weekdays:
sun: Domingo sun: Domingo
mon: Lunes mon: Lunes

View File

@ -53,6 +53,7 @@ const removeAlias = () => {
:url="`MailAliases/${entityId}`" :url="`MailAliases/${entityId}`"
data-key="Alias" data-key="Alias"
title="alias" title="alias"
:to-module="{ name: 'AccountAlias' }"
> >
<template #menu> <template #menu>
<QItem v-ripple clickable @click="removeAlias()"> <QItem v-ripple clickable @click="removeAlias()">

View File

@ -5,6 +5,7 @@ import { useI18n } from 'vue-i18n';
import CardSummary from 'components/ui/CardSummary.vue'; import CardSummary from 'components/ui/CardSummary.vue';
import VnLv from 'src/components/ui/VnLv.vue'; import VnLv from 'src/components/ui/VnLv.vue';
import VnTitle from 'src/components/common/VnTitle.vue';
const route = useRoute(); const route = useRoute();
const { t } = useI18n(); const { t } = useI18n();
@ -27,13 +28,10 @@ const entityId = computed(() => $props.id || route.params.id);
<template #body="{ entity: alias }"> <template #body="{ entity: alias }">
<QCard class="vn-one"> <QCard class="vn-one">
<QCardSection class="q-pa-none"> <QCardSection class="q-pa-none">
<router-link <VnTitle
:to="{ name: 'AliasBasicData', params: { id: entityId } }" :url="`#/account/alias/${entityId}/basic-data`"
class="header header-link" :text="t('globals.summary.basicData')"
> />
{{ t('globals.summary.basicData') }}
<QIcon name="open_in_new" />
</router-link>
</QCardSection> </QCardSection>
<VnLv :label="t('role.id')" :value="alias.id" /> <VnLv :label="t('role.id')" :value="alias.id" />
<VnLv :label="t('role.description')" :value="alias.description" /> <VnLv :label="t('role.description')" :value="alias.description" />

View File

@ -0,0 +1,14 @@
<script setup>
import AccountDescriptor from './AccountDescriptor.vue';
import AccountSummary from './AccountSummary.vue';
</script>
<template>
<QPopupProxy style="max-width: 10px">
<AccountDescriptor
v-if="$attrs.id"
v-bind="$attrs.id"
:summary="AccountSummary"
:proxy-render="true"
/>
</QPopupProxy>
</template>

View File

@ -5,6 +5,7 @@ import CardSummary from 'components/ui/CardSummary.vue';
import VnLv from 'src/components/ui/VnLv.vue'; import VnLv from 'src/components/ui/VnLv.vue';
import filter from './AccountFilter.js'; import filter from './AccountFilter.js';
import AccountDescriptorMenu from './AccountDescriptorMenu.vue'; import AccountDescriptorMenu from './AccountDescriptorMenu.vue';
import VnTitle from 'src/components/common/VnTitle.vue';
const $props = defineProps({ id: { type: Number, default: 0 } }); const $props = defineProps({ id: { type: Number, default: 0 } });
@ -26,13 +27,10 @@ const entityId = computed(() => $props.id || route.params.id);
<template #body="{ entity }"> <template #body="{ entity }">
<QCard class="vn-one"> <QCard class="vn-one">
<QCardSection class="q-pa-none"> <QCardSection class="q-pa-none">
<router-link <VnTitle
:to="{ name: 'AccountBasicData', params: { id: entityId } }" :url="`#/account/${entityId}/basic-data`"
class="header header-link" :text="$t('globals.pageTitles.basicData')"
> />
{{ $t('globals.pageTitles.basicData') }}
<QIcon name="open_in_new" />
</router-link>
</QCardSection> </QCardSection>
<VnLv :label="$t('account.card.nickname')" :value="entity.name" /> <VnLv :label="$t('account.card.nickname')" :value="entity.name" />
<VnLv :label="$t('account.card.role')" :value="entity.role?.name" /> <VnLv :label="$t('account.card.role')" :value="entity.role?.name" />

View File

@ -37,6 +37,7 @@ const removeRole = async () => {
:filter="{ where: { id: entityId } }" :filter="{ where: { id: entityId } }"
data-key="Role" data-key="Role"
:summary="$props.summary" :summary="$props.summary"
:to-module="{ name: 'AccountRoles' }"
> >
<template #menu> <template #menu>
<QItem v-ripple clickable @click="removeRole()"> <QItem v-ripple clickable @click="removeRole()">

View File

@ -4,6 +4,7 @@ import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import CardSummary from 'components/ui/CardSummary.vue'; import CardSummary from 'components/ui/CardSummary.vue';
import VnLv from 'src/components/ui/VnLv.vue'; import VnLv from 'src/components/ui/VnLv.vue';
import VnTitle from 'src/components/common/VnTitle.vue';
const route = useRoute(); const route = useRoute();
const { t } = useI18n(); const { t } = useI18n();
@ -29,13 +30,10 @@ const entityId = computed(() => $props.id || route.params.id);
<template #body="{ entity }"> <template #body="{ entity }">
<QCard class="vn-one"> <QCard class="vn-one">
<QCardSection class="q-pa-none"> <QCardSection class="q-pa-none">
<a <VnTitle
class="header header-link" :url="`#/account/role/${entityId}/basic-data`"
:href="`#/VnUser/${entityId}/basic-data`" :text="$t('globals.pageTitles.basicData')"
> />
{{ t('globals.pageTitles.basicData') }}
<QIcon name="open_in_new" />
</a>
</QCardSection> </QCardSection>
<VnLv :label="t('role.id')" :value="entity.id" /> <VnLv :label="t('role.id')" :value="entity.id" />
<VnLv :label="t('globals.name')" :value="entity.name" /> <VnLv :label="t('globals.name')" :value="entity.name" />

View File

@ -5,6 +5,7 @@ import { useI18n } from 'vue-i18n';
import { toDateHourMinSec, toPercentage } from 'src/filters'; import { toDateHourMinSec, toPercentage } from 'src/filters';
import TicketDescriptorProxy from 'pages/Ticket/Card/TicketDescriptorProxy.vue'; import TicketDescriptorProxy from 'pages/Ticket/Card/TicketDescriptorProxy.vue';
import ClaimDescriptorMenu from 'pages/Claim/Card/ClaimDescriptorMenu.vue'; import ClaimDescriptorMenu from 'pages/Claim/Card/ClaimDescriptorMenu.vue';
import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue';
import CardDescriptor from 'components/ui/CardDescriptor.vue'; import CardDescriptor from 'components/ui/CardDescriptor.vue';
import VnLv from 'src/components/ui/VnLv.vue'; import VnLv from 'src/components/ui/VnLv.vue';
import VnUserLink from 'src/components/ui/VnUserLink.vue'; import VnUserLink from 'src/components/ui/VnUserLink.vue';
@ -65,12 +66,12 @@ onMounted(async () => {
</template> </template>
</VnLv> </VnLv>
<VnLv :label="t('claim.created')" :value="toDateHourMinSec(entity.created)" /> <VnLv :label="t('claim.created')" :value="toDateHourMinSec(entity.created)" />
<VnLv :label="t('claim.commercial')"> <VnLv :label="t('globals.department')">
<template #value> <template #value>
<VnUserLink <span class="link">
:name="entity.client?.salesPersonUser?.name" {{ entity?.client?.department?.name || '-' }}
:worker-id="entity.client?.salesPersonFk" <DepartmentDescriptorProxy :id="entity?.client?.departmentFk" />
/> </span>
</template> </template>
</VnLv> </VnLv>
<VnLv <VnLv

View File

@ -0,0 +1,14 @@
<script setup>
import ClaimDescriptor from './ClaimDescriptor.vue';
import ClaimSummary from './ClaimSummary.vue';
</script>
<template>
<QPopupProxy style="max-width: 10px">
<ClaimDescriptor
v-if="$attrs.id"
v-bind="$attrs.id"
:summary="ClaimSummary"
:proxy-render="true"
/>
</QPopupProxy>
</template>

View File

@ -14,7 +14,7 @@ export default {
relation: 'client', relation: 'client',
scope: { scope: {
include: [ include: [
{ relation: 'salesPersonUser' }, { relation: 'department' },
{ {
relation: 'claimsRatio', relation: 'claimsRatio',
scope: { scope: {

View File

@ -117,7 +117,7 @@ const selected = ref([]);
const mana = ref(0); const mana = ref(0);
async function fetchMana() { async function fetchMana() {
const ticketId = claim.value.ticketFk; const ticketId = claim.value.ticketFk;
const response = await axios.get(`Tickets/${ticketId}/getSalesPersonMana`); const response = await axios.get(`Tickets/${ticketId}/getDepartmentMana`);
mana.value = response.data; mana.value = response.data;
} }

View File

@ -19,6 +19,7 @@ import ClaimNotes from 'src/pages/Claim/Card/ClaimNotes.vue';
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue'; import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue'; import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue';
import ClaimDescriptorMenu from './ClaimDescriptorMenu.vue'; import ClaimDescriptorMenu from './ClaimDescriptorMenu.vue';
const route = useRoute(); const route = useRoute();
@ -252,13 +253,15 @@ function claimUrl(section) {
</VnLv> </VnLv>
<VnLv <VnLv
v-if="$route.name != 'ClaimSummary'" v-if="$route.name != 'ClaimSummary'"
:label="t('globals.salesPerson')" :label="t('customer.summary.team')"
> >
<template #value> <template #value>
<VnUserLink <span class="link">
:name="claim.client?.salesPersonUser?.name" {{ claim?.client?.department?.name || '-' }}
:worker-id="claim.client?.salesPersonFk" <DepartmentDescriptorProxy
/> :id="claim?.client?.departmentFk"
/>
</span>
</template> </template>
</VnLv> </VnLv>
<VnLv v-if="$route.name != 'ClaimSummary'" :label="t('claim.attendedBy')"> <VnLv v-if="$route.name != 'ClaimSummary'" :label="t('claim.attendedBy')">
@ -271,7 +274,7 @@ function claimUrl(section) {
</VnLv> </VnLv>
<VnLv v-if="$route.name != 'ClaimSummary'" :label="t('claim.customer')"> <VnLv v-if="$route.name != 'ClaimSummary'" :label="t('claim.customer')">
<template #value> <template #value>
<span class="link cursor-pointer"> <span class="link">
{{ claim.client?.name }} {{ claim.client?.name }}
<CustomerDescriptorProxy :id="claim.clientFk" /> <CustomerDescriptorProxy :id="claim.clientFk" />
</span> </span>

View File

@ -44,15 +44,14 @@ const props = defineProps({
is-outlined is-outlined
/> />
<VnSelect <VnSelect
:label="t('Salesperson')"
v-model="params.salesPersonFk"
url="Workers/activeWithInheritedRole"
:filter="{ where: { role: 'salesPerson' } }"
:use-like="false"
option-filter="firstName"
dense
outlined outlined
dense
rounded rounded
:label="t('globals.params.departmentFk')"
v-model="params.departmentFk"
option-value="id"
option-label="name"
url="Departments"
/> />
<VnSelect <VnSelect
:label="t('claim.attendedBy')" :label="t('claim.attendedBy')"
@ -126,7 +125,6 @@ en:
search: Contains search: Contains
clientFk: Customer clientFk: Customer
clientName: Customer clientName: Customer
salesPersonFk: Salesperson
attenderFk: Attender attenderFk: Attender
claimResponsibleFk: Responsible claimResponsibleFk: Responsible
claimStateFk: State claimStateFk: State
@ -139,7 +137,6 @@ es:
search: Contiene search: Contiene
clientFk: Cliente clientFk: Cliente
clientName: Cliente clientName: Cliente
salesPersonFk: Comercial
attenderFk: Asistente attenderFk: Asistente
claimResponsibleFk: Responsable claimResponsibleFk: Responsable
claimStateFk: Estado claimStateFk: Estado
@ -148,6 +145,5 @@ es:
itemFk: Artículo itemFk: Artículo
zoneFk: Zona zoneFk: Zona
Client Name: Nombre del cliente Client Name: Nombre del cliente
Salesperson: Comercial
Item: Artículo Item: Artículo
</i18n> </i18n>

View File

@ -4,6 +4,7 @@ import { useI18n } from 'vue-i18n';
import { toDate } from 'filters/index'; import { toDate } from 'filters/index';
import ClaimFilter from './ClaimFilter.vue'; import ClaimFilter from './ClaimFilter.vue';
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue'; import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue';
import VnUserLink from 'src/components/ui/VnUserLink.vue'; import VnUserLink from 'src/components/ui/VnUserLink.vue';
import ClaimSummary from './Card/ClaimSummary.vue'; import ClaimSummary from './Card/ClaimSummary.vue';
import { useSummaryDialog } from 'src/composables/useSummaryDialog'; import { useSummaryDialog } from 'src/composables/useSummaryDialog';
@ -48,6 +49,20 @@ const columns = computed(() => [
}, },
columnClass: 'expand', columnClass: 'expand',
}, },
{
align: 'left',
name: 'departmentFk',
label: t('customer.summary.team'),
component: 'select',
attrs: {
url: 'Departments',
},
create: true,
columnField: {
component: null,
},
format: (row, dashIfEmpty) => dashIfEmpty(row.departmentName),
},
{ {
align: 'left', align: 'left',
label: t('claim.attendedBy'), label: t('claim.attendedBy'),
@ -152,6 +167,12 @@ const STATE_COLOR = {
<CustomerDescriptorProxy :id="row.clientFk" /> <CustomerDescriptorProxy :id="row.clientFk" />
</span> </span>
</template> </template>
<template #column-departmentFk="{ row }">
<span class="link" @click.stop>
{{ row.departmentName || '-' }}
<DepartmentDescriptorProxy :id="row?.departmentFk" />
</span>
</template>
<template #column-attendedBy="{ row }"> <template #column-attendedBy="{ row }">
<span @click.stop> <span @click.stop>
<VnUserLink :name="row.workerName" :worker-id="row.workerFk" /> <VnUserLink :name="row.workerName" :worker-id="row.workerFk" />

View File

@ -8,7 +8,6 @@ import FormModel from 'components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnSelect from 'src/components/common/VnSelect.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnSelectWorker from 'src/components/common/VnSelectWorker.vue';
import { getDifferences, getUpdatedValues } from 'src/filters'; import { getDifferences, getUpdatedValues } from 'src/filters';
const route = useRoute(); const route = useRoute();
@ -37,7 +36,7 @@ const exprBuilder = (param, value) => {
function onBeforeSave(formData, originalData) { function onBeforeSave(formData, originalData) {
return getUpdatedValues( return getUpdatedValues(
Object.keys(getDifferences(formData, originalData)), Object.keys(getDifferences(formData, originalData)),
formData formData,
); );
} }
</script> </script>
@ -119,16 +118,11 @@ function onBeforeSave(formData, originalData) {
/> />
</VnRow> </VnRow>
<VnRow> <VnRow>
<VnSelectWorker <VnSelect
:label="t('customer.summary.salesPerson')" :label="t('globals.department')"
v-model="data.salesPersonFk" v-model="data.departmentFk"
:params="{ url="Departments"
departmentCodes: ['VT', 'shopping'], :fields="['id', 'name']"
}"
:has-avatar="true"
:rules="validate('client.salesPersonFk')"
:expr-builder="exprBuilder"
emit-value
/> />
<VnSelect <VnSelect
v-model="data.contactChannelFk" v-model="data.contactChannelFk"
@ -160,7 +154,7 @@ function onBeforeSave(formData, originalData) {
<QIcon name="info" class="cursor-pointer"> <QIcon name="info" class="cursor-pointer">
<QTooltip>{{ <QTooltip>{{
t( t(
'In case of a company succession, specify the grantor company' 'In case of a company succession, specify the grantor company',
) )
}}</QTooltip> }}</QTooltip>
</QIcon> </QIcon>

View File

@ -3,14 +3,14 @@ import { onMounted, ref, computed } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { dashIfEmpty, toCurrency, toDate } from 'src/filters'; import { toCurrency, toDate } from 'src/filters';
import useCardDescription from 'src/composables/useCardDescription'; import useCardDescription from 'src/composables/useCardDescription';
import CardDescriptor from 'components/ui/CardDescriptor.vue'; import CardDescriptor from 'components/ui/CardDescriptor.vue';
import VnLv from 'src/components/ui/VnLv.vue'; import VnLv from 'src/components/ui/VnLv.vue';
import VnUserLink from 'src/components/ui/VnUserLink.vue';
import CustomerDescriptorMenu from './CustomerDescriptorMenu.vue'; import CustomerDescriptorMenu from './CustomerDescriptorMenu.vue';
import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue';
import { useState } from 'src/composables/useState'; import { useState } from 'src/composables/useState';
const state = useState(); const state = useState();
@ -84,14 +84,10 @@ const debtWarning = computed(() => {
:value="toCurrency(entity.debt)" :value="toCurrency(entity.debt)"
:info="t('customer.summary.riskInfo')" :info="t('customer.summary.riskInfo')"
/> />
<VnLv :label="t('customer.summary.salesPerson')"> <VnLv :label="t('globals.department')">
<template #value> <template #value>
<VnUserLink <span class="link" v-text="entity.department?.name" />
v-if="entity.salesPersonUser" <DepartmentDescriptorProxy :id="entity.department?.id" />
:name="entity.salesPersonUser.name"
:worker-id="entity.salesPersonFk"
/>
<span v-else>{{ dashIfEmpty(entity.salesPersonUser) }}</span>
</template> </template>
</VnLv> </VnLv>
<VnLv <VnLv

View File

@ -86,12 +86,12 @@ const tableColumnComponents = {
}, },
file: { file: {
component: QBtn, component: QBtn,
props: () => ({ flat: true, color: 'blue' }), props: () => ({ flat: true }),
event: ({ row }) => downloadFile(row.dmsFk), event: ({ row }) => downloadFile(row.dmsFk),
}, },
employee: { employee: {
component: QBtn, component: QBtn,
props: () => ({ flat: true, color: 'blue' }), props: () => ({ flat: true }),
event: () => {}, event: () => {},
}, },
created: { created: {
@ -214,8 +214,17 @@ const toCustomerFileManagementCreate = () => {
v-bind="tableColumnComponents[props.col.name].props(props)" v-bind="tableColumnComponents[props.col.name].props(props)"
> >
<template v-if="props.col.name !== 'original'"> <template v-if="props.col.name !== 'original'">
{{ props.value }} <span
:class="{
link:
props.col.name === 'employee' ||
props.col.name === 'file',
}"
>
{{ props.value }}
</span>
</template> </template>
<WorkerDescriptorProxy <WorkerDescriptorProxy
:id="props.row.dms.workerFk" :id="props.row.dms.workerFk"
v-if="props.col.name === 'employee'" v-if="props.col.name === 'employee'"

View File

@ -2,7 +2,6 @@
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import VnUserLink from 'src/components/ui/VnUserLink.vue';
import { toCurrency, toPercentage, toDate, dashOrCurrency } from 'src/filters'; import { toCurrency, toPercentage, toDate, dashOrCurrency } from 'src/filters';
import CardSummary from 'components/ui/CardSummary.vue'; import CardSummary from 'components/ui/CardSummary.vue';
@ -13,6 +12,8 @@ import CustomerSummaryTable from 'src/pages/Customer/components/CustomerSummaryT
import VnTitle from 'src/components/common/VnTitle.vue'; import VnTitle from 'src/components/common/VnTitle.vue';
import VnRow from 'src/components/ui/VnRow.vue'; import VnRow from 'src/components/ui/VnRow.vue';
import CustomerDescriptorMenu from './CustomerDescriptorMenu.vue'; import CustomerDescriptorMenu from './CustomerDescriptorMenu.vue';
import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue';
const route = useRoute(); const route = useRoute();
const { t } = useI18n(); const { t } = useI18n();
const grafanaUrl = 'https://grafana.verdnatura.es'; const grafanaUrl = 'https://grafana.verdnatura.es';
@ -106,16 +107,12 @@ const sumRisk = ({ clientRisks }) => {
{{ t('globals.params.email') }} {{ t('globals.params.email') }}
<VnLinkMail email="entity.email"></VnLinkMail> </template <VnLinkMail email="entity.email"></VnLinkMail> </template
></VnLv> ></VnLv>
<VnLv <VnLv :label="t('globals.department')">
:label="t('customer.summary.salesPerson')"
:value="entity?.salesPersonUser?.name"
>
<template #value> <template #value>
<VnUserLink <span class="link" v-text="entity.department?.name" />
:name="entity.salesPersonUser?.name" <DepartmentDescriptorProxy :id="entity?.department?.id" />
:worker-id="entity.salesPersonFk" </template>
/> </template </VnLv>
></VnLv>
<VnLv <VnLv
:label="t('customer.summary.contactChannel')" :label="t('customer.summary.contactChannel')"
:value="entity?.contactChannel?.name" :value="entity?.contactChannel?.name"

View File

@ -1,146 +0,0 @@
<script setup>
import { reactive, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue';
import FormModel from 'components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import VnLocation from 'src/components/common/VnLocation.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
const { t } = useI18n();
const initialData = reactive({
active: true,
isEqualizated: false,
});
const workersOptions = ref([]);
const businessTypesOptions = ref([]);
function handleLocation(data, location) {
const { town, code, provinceFk, countryFk } = location ?? {};
data.postcode = code;
data.city = town;
data.provinceFk = provinceFk;
data.countryFk = countryFk;
}
</script>
<template>
<FetchData
@on-fetch="(data) => (workersOptions = data)"
auto-load
url="Workers/search?departmentCodes"
/>
<FetchData
@on-fetch="(data) => (businessTypesOptions = data)"
auto-load
url="BusinessTypes"
/>
<QPage>
<VnSubToolbar />
<FormModel
:form-initial-data="initialData"
model="client"
url-create="Clients/createWithUser"
>
<template #form="{ data, validate }">
<VnRow>
<QInput :label="t('Comercial name')" v-model="data.name" />
<VnSelect
:label="t('Salesperson')"
:options="workersOptions"
hide-selected
option-label="name"
option-value="id"
v-model="data.salesPersonFk"
/>
</VnRow>
<VnRow>
<VnSelect
:label="t('Business type')"
:options="businessTypesOptions"
hide-selected
option-label="description"
option-value="code"
v-model="data.businessTypeFk"
/>
<QInput v-model="data.fi" :label="t('Tax number')" />
</VnRow>
<VnRow>
<QInput
:label="t('Business name')"
:rules="validate('client.socialName')"
v-model="data.socialName"
/>
</VnRow>
<VnRow>
<QInput
:label="t('Street')"
:rules="validate('client.street')"
v-model="data.street"
/>
</VnRow>
<VnRow>
<VnLocation
:rules="validate('Worker.postcode')"
:acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]"
v-model="data.location"
@update:model-value="(location) => handleLocation(data, location)"
>
</VnLocation>
</VnRow>
<VnRow>
<QInput v-model="data.userName" :label="t('Web user')" />
<QInput
:label="t('Email')"
:rules="validate('client.email')"
clearable
type="email"
v-model="data.email"
>
<template #append>
<QIcon name="info" class="cursor-info">
<QTooltip max-width="400px">{{
t('customer.basicData.youCanSaveMultipleEmails')
}}</QTooltip>
</QIcon>
</template>
</QInput>
</VnRow>
<QCheckbox
:label="t('Is equalizated')"
v-model="initialData.isEqualizated"
/>
</template>
</FormModel>
</QPage>
</template>
<style lang="scss" scoped>
.card {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
grid-gap: 20px;
}
</style>
<i18n>
es:
Comercial name: Nombre comercial
Salesperson: Comercial
Business type: Tipo de negocio
Tax number: NIF / CIF
Business name: Razón social
Street: Dirección fiscal
Postcode: Código postal
City: Población
Province: Provincia
Country: País
Web user: Usuario web
Email: Email
Is equalizated: Recargo de equivalencia
</i18n>

View File

@ -3,7 +3,6 @@ import { useI18n } from 'vue-i18n';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnSelect from 'components/common/VnSelect.vue'; import VnSelect from 'components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnSelectWorker from 'src/components/common/VnSelectWorker.vue';
const { t } = useI18n(); const { t } = useI18n();
defineProps({ defineProps({
@ -65,22 +64,15 @@ const exprBuilder = (param, value) => {
</QItem> </QItem>
<QItem class="q-mb-sm"> <QItem class="q-mb-sm">
<QItemSection> <QItemSection>
<VnSelectWorker <VnSelect
:label="t('Salesperson')"
v-model="params.salesPersonFk"
:params="{
departmentCodes: ['VT'],
}"
:expr-builder="exprBuilder"
@update:model-value="searchFn()"
emit-value
map-options
use-input
hide-selected
dense
outlined outlined
dense
rounded rounded
:input-debounce="0" :label="t('globals.params.departmentFk')"
v-model="params.departmentFk"
option-value="id"
option-label="name"
url="Departments"
/> />
</QItemSection> </QItemSection>
</QItem> </QItem>
@ -164,7 +156,6 @@ en:
params: params:
search: Contains search: Contains
fi: FI fi: FI
salesPersonFk: Salesperson
provinceFk: Province provinceFk: Province
isActive: Is active isActive: Is active
city: City city: City
@ -191,7 +182,6 @@ es:
sageTaxTypeFk: Tipo de impuesto Sage sageTaxTypeFk: Tipo de impuesto Sage
sageTransactionTypeFk: Tipo de impuesto Sage sageTransactionTypeFk: Tipo de impuesto Sage
payMethodFk: Forma de pago payMethodFk: Forma de pago
salesPersonFk: Comercial
provinceFk: Provincia provinceFk: Provincia
city: Ciudad city: Ciudad
phone: Teléfono phone: Teléfono
@ -201,7 +191,6 @@ es:
name: Nombre name: Nombre
postcode: CP postcode: CP
FI: NIF FI: NIF
Salesperson: Comercial
Province: Provincia Province: Provincia
City: Ciudad City: Ciudad
Phone: Teléfono Phone: Teléfono

View File

@ -10,7 +10,6 @@ import CustomerFilter from './CustomerFilter.vue';
import VnTable from 'components/VnTable/VnTable.vue'; import VnTable from 'components/VnTable/VnTable.vue';
import VnLocation from 'src/components/common/VnLocation.vue'; import VnLocation from 'src/components/common/VnLocation.vue';
import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue'; import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue';
import VnSelectWorker from 'src/components/common/VnSelectWorker.vue';
import VnSection from 'src/components/common/VnSection.vue'; import VnSection from 'src/components/common/VnSection.vue';
const { t } = useI18n(); const { t } = useI18n();
@ -73,30 +72,17 @@ const columns = computed(() => [
}, },
{ {
align: 'left', align: 'left',
name: 'salesPersonFk', name: 'departmentFk',
label: t('customer.extendedList.tableVisibleColumns.salesPersonFk'), label: t('customer.summary.team'),
component: 'select', component: 'select',
attrs: { attrs: {
url: 'Workers/activeWithInheritedRole', url: 'Departments',
fields: ['id', 'name', 'firstName'],
where: { role: 'salesPerson' },
optionFilter: 'firstName',
}, },
columnFilter: { create: true,
component: 'select',
attrs: {
url: 'Workers/activeWithInheritedRole',
fields: ['id', 'name', 'firstName'],
where: { role: 'salesPerson' },
optionLabel: 'firstName',
optionValue: 'id',
},
},
create: false,
columnField: { columnField: {
component: null, component: null,
}, },
format: (row, dashIfEmpty) => dashIfEmpty(row.salesPerson), format: (row, dashIfEmpty) => dashIfEmpty(row.departmentName),
}, },
{ {
align: 'left', align: 'left',
@ -155,6 +141,9 @@ const columns = computed(() => [
inWhere: true, inWhere: true,
}, },
columnClass: 'expand', columnClass: 'expand',
attrs: {
uppercase: true,
},
}, },
{ {
align: 'left', align: 'left',
@ -446,36 +435,6 @@ function handleLocation(data, location) {
redirect="customer" redirect="customer"
> >
<template #more-create-dialog="{ data }"> <template #more-create-dialog="{ data }">
<VnSelectWorker
:label="t('customer.summary.salesPerson')"
v-model="data.salesPersonFk"
:params="{
departmentCodes: ['VT', 'shopping'],
}"
:has-avatar="true"
:id-value="data.salesPersonFk"
emit-value
auto-load
>
<template #prepend>
<VnAvatar
:worker-id="data.salesPersonFk"
color="primary"
:title="title"
/>
</template>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>{{ scope.opt?.name }}</QItemLabel>
<QItemLabel caption
>{{ scope.opt?.nickname }},
{{ scope.opt?.code }}</QItemLabel
>
</QItemSection>
</QItem>
</template>
</VnSelectWorker>
<VnLocation <VnLocation
:acls="[{ model: 'Province', props: '*', accessType: 'WRITE' }]" :acls="[{ model: 'Province', props: '*', accessType: 'WRITE' }]"
v-model="data.location" v-model="data.location"

View File

@ -32,28 +32,6 @@ const columns = computed(() => [
}, },
}, },
}, },
{
align: 'left',
name: 'isWorker',
label: t('Is worker'),
},
{
align: 'left',
name: 'salesPersonFk',
label: t('Salesperson'),
columnFilter: {
component: 'select',
attrs: {
url: 'Workers/activeWithInheritedRole',
fields: ['id', 'name'],
where: { role: 'salesPerson' },
useLike: false,
optionValue: 'id',
optionLabel: 'name',
optionFilter: 'firstName',
},
},
},
{ {
align: 'left', align: 'left',
name: 'departmentFk', name: 'departmentFk',
@ -153,6 +131,11 @@ const columns = computed(() => [
label: t('Has recovery'), label: t('Has recovery'),
name: 'hasRecovery', name: 'hasRecovery',
}, },
{
align: 'left',
name: 'isWorker',
label: t('customer.params.isWorker'),
},
]); ]);
const viewAddObservation = (rowsSelected) => { const viewAddObservation = (rowsSelected) => {
@ -167,7 +150,6 @@ const viewAddObservation = (rowsSelected) => {
function exprBuilder(param, value) { function exprBuilder(param, value) {
switch (param) { switch (param) {
case 'salesPersonFk':
case 'creditInsurance': case 'creditInsurance':
case 'countryFk': case 'countryFk':
return { [`c.${param}`]: value }; return { [`c.${param}`]: value };
@ -176,7 +158,7 @@ function exprBuilder(param, value) {
case 'workerFk': case 'workerFk':
return { [`co.${param}`]: value }; return { [`co.${param}`]: value };
case 'departmentFk': case 'departmentFk':
return { [`wd.${param}`]: value }; return { [`c.${param}`]: value };
case 'amount': case 'amount':
case 'clientFk': case 'clientFk':
return { [`d.${param}`]: value }; return { [`d.${param}`]: value };
@ -241,12 +223,6 @@ function exprBuilder(param, value) {
<template #column-observation="{ row }"> <template #column-observation="{ row }">
<VnInput type="textarea" v-model="row.observation" readonly dense rows="2" /> <VnInput type="textarea" v-model="row.observation" readonly dense rows="2" />
</template> </template>
<template #column-salesPersonFk="{ row }">
<span class="link" @click.stop>
{{ row.salesPersonName }}
<WorkerDescriptorProxy :id="row.salesPersonFk" />
</span>
</template>
<template #column-departmentFk="{ row }"> <template #column-departmentFk="{ row }">
<span class="link" @click.stop> <span class="link" @click.stop>
{{ row.departmentName }} {{ row.departmentName }}
@ -265,8 +241,6 @@ function exprBuilder(param, value) {
es: es:
Add observation: Añadir observación Add observation: Añadir observación
Client: Cliente Client: Cliente
Is worker: Es trabajador
Salesperson: Comercial
Department: Departamento Department: Departamento
Country: País Country: País
P. Method: F. Pago P. Method: F. Pago
@ -281,5 +255,5 @@ es:
Credit I.: Crédito A. Credit I.: Crédito A.
Credit insurance: Crédito asegurado Credit insurance: Crédito asegurado
From: Desde From: Desde
Has recovery: Tiene recobro Has recovery: Recobro
</i18n> </i18n>

View File

@ -15,19 +15,12 @@ const props = defineProps({
}, },
}); });
const salespersons = ref();
const countries = ref(); const countries = ref();
const authors = ref(); const authors = ref();
const departments = ref(); const departments = ref();
</script> </script>
<template> <template>
<FetchData
:filter="{ where: { role: 'salesPerson' } }"
@on-fetch="(data) => (salespersons = data)"
auto-load
url="Workers/activeWithInheritedRole"
/>
<FetchData @on-fetch="(data) => (countries = data)" auto-load url="Countries" /> <FetchData @on-fetch="(data) => (countries = data)" auto-load url="Countries" />
<FetchData <FetchData
@on-fetch="(data) => (authors = data)" @on-fetch="(data) => (authors = data)"
@ -62,29 +55,6 @@ const departments = ref();
@update:model-value="searchFn()" @update:model-value="searchFn()"
/> />
</QItem> </QItem>
<QItem class="q-mb-sm">
<QItemSection v-if="salespersons">
<VnSelect
:input-debounce="0"
:label="t('Salesperson')"
:options="salespersons"
dense
emit-value
hide-selected
map-options
option-label="name"
option-value="id"
outlined
rounded
use-input
v-model="params.salesPersonFk"
@update:model-value="searchFn()"
/>
</QItemSection>
<QItemSection v-else>
<QSkeleton class="full-width" type="QInput" />
</QItemSection>
</QItem>
<QItem class="q-mb-sm"> <QItem class="q-mb-sm">
<QItemSection v-if="departments"> <QItemSection v-if="departments">
<VnSelect <VnSelect
@ -219,7 +189,6 @@ const departments = ref();
en: en:
params: params:
clientFk: Client clientFk: Client
salesPersonFk: Salesperson
countryFk: Country countryFk: Country
paymentMethod: P. Method paymentMethod: P. Method
balance: Balance D. balance: Balance D.
@ -230,7 +199,6 @@ en:
es: es:
params: params:
clientFk: Cliente clientFk: Cliente
salesPersonFk: Comercial
countryFk: País countryFk: País
paymentMethod: F. Pago paymentMethod: F. Pago
balance: Saldo V. balance: Saldo V.
@ -239,7 +207,6 @@ es:
credit: Crédito A. credit: Crédito A.
defaulterSinced: Desde defaulterSinced: Desde
Client: Cliente Client: Cliente
Salesperson: Comercial
Departments: Departamentos Departments: Departamentos
Country: País Country: País
P. Method: F. Pago P. Method: F. Pago

View File

@ -69,17 +69,16 @@ const columns = computed(() => [
}, },
{ {
align: 'left', align: 'left',
label: t('customer.extendedList.tableVisibleColumns.salesPersonFk'), name: 'departmentFk',
name: 'salesPersonFk', label: t('customer.summary.team'),
component: 'select', component: 'select',
attrs: { attrs: {
url: 'Workers/activeWithInheritedRole', url: 'Departments',
fields: ['id', 'name'],
where: { role: 'salesPerson' },
optionFilter: 'firstName',
useLike: false,
}, },
visible: false, columnField: {
component: null,
},
format: (row, dashIfEmpty) => dashIfEmpty(row.departmentName),
}, },
]); ]);
</script> </script>
@ -96,7 +95,7 @@ const columns = computed(() => [
</VnSubToolbar> </VnSubToolbar>
<VnTable <VnTable
:data-key="dataKey" :data-key="dataKey"
url="Clients" url="Clients/filter"
:table="{ :table="{
'row-key': 'id', 'row-key': 'id',
selection: 'multiple', selection: 'multiple',
@ -127,7 +126,6 @@ const columns = computed(() => [
es: es:
Identifier: Identificador Identifier: Identificador
Social name: Razón social Social name: Razón social
Salesperson: Comercial
Phone: Teléfono Phone: Teléfono
City: Población City: Población
Email: Email Email: Email

View File

@ -20,7 +20,7 @@ customer:
name: Name name: Name
contact: Contact contact: Contact
mobile: Mobile mobile: Mobile
salesPerson: Sales person team: Team
contactChannel: Contact channel contactChannel: Contact channel
socialName: Social name socialName: Social name
fiscalId: Fiscal ID fiscalId: Fiscal ID
@ -78,7 +78,6 @@ customer:
id: Identifier id: Identifier
socialName: Social name socialName: Social name
fi: Tax number fi: Tax number
salesPersonFk: Salesperson
creditInsurance: Credit insurance creditInsurance: Credit insurance
phone: Phone phone: Phone
street: Street street: Street

View File

@ -20,7 +20,7 @@ customer:
name: Nombre name: Nombre
contact: Contacto contact: Contacto
mobile: Móvil mobile: Móvil
salesPerson: Comercial team: Equipo
contactChannel: Canal de contacto contactChannel: Canal de contacto
socialName: Razón social socialName: Razón social
fiscalId: NIF/CIF fiscalId: NIF/CIF
@ -78,7 +78,6 @@ customer:
id: Identificador id: Identificador
socialName: Razón social socialName: Razón social
fi: NIF / CIF fi: NIF / CIF
salesPersonFk: Comercial
creditInsurance: Crédito asegurado creditInsurance: Crédito asegurado
phone: Teléfono phone: Teléfono
street: Dirección fiscal street: Dirección fiscal

View File

@ -13,6 +13,7 @@ import VnSelect from 'src/components/common/VnSelect.vue';
import VnInputNumber from 'src/components/common/VnInputNumber.vue'; import VnInputNumber from 'src/components/common/VnInputNumber.vue';
import VnSelectTravelExtended from 'src/components/common/VnSelectTravelExtended.vue'; import VnSelectTravelExtended from 'src/components/common/VnSelectTravelExtended.vue';
import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue'; import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
import VnCheckbox from 'src/components/common/VnCheckbox.vue';
const route = useRoute(); const route = useRoute();
const { t } = useI18n(); const { t } = useI18n();
@ -53,7 +54,7 @@ onMounted(() => {
:clear-store-on-unmount="false" :clear-store-on-unmount="false"
> >
<template #form="{ data }"> <template #form="{ data }">
<VnRow> <VnRow class="q-py-sm">
<VnSelectTravelExtended <VnSelectTravelExtended
:data="data" :data="data"
v-model="data.travelFk" v-model="data.travelFk"
@ -65,7 +66,7 @@ onMounted(() => {
:required="true" :required="true"
/> />
</VnRow> </VnRow>
<VnRow> <VnRow class="q-py-sm">
<VnInput v-model="data.reference" :label="t('globals.reference')" /> <VnInput v-model="data.reference" :label="t('globals.reference')" />
<VnInputNumber <VnInputNumber
v-model="data.invoiceAmount" v-model="data.invoiceAmount"
@ -73,7 +74,7 @@ onMounted(() => {
:positive="false" :positive="false"
/> />
</VnRow> </VnRow>
<VnRow> <VnRow class="q-py-sm">
<VnInput <VnInput
v-model="data.invoiceNumber" v-model="data.invoiceNumber"
:label="t('entry.summary.invoiceNumber')" :label="t('entry.summary.invoiceNumber')"
@ -84,12 +85,13 @@ onMounted(() => {
:options="companiesOptions" :options="companiesOptions"
option-value="id" option-value="id"
option-label="code" option-label="code"
sort-by="code"
map-options map-options
hide-selected hide-selected
:required="true" :required="true"
/> />
</VnRow> </VnRow>
<VnRow> <VnRow class="q-py-sm">
<VnInputNumber <VnInputNumber
:label="t('entry.summary.commission')" :label="t('entry.summary.commission')"
v-model="data.commission" v-model="data.commission"
@ -102,9 +104,10 @@ onMounted(() => {
:options="currenciesOptions" :options="currenciesOptions"
option-value="id" option-value="id"
option-label="code" option-label="code"
sort-by="code"
/> />
</VnRow> </VnRow>
<VnRow> <VnRow class="q-py-sm">
<VnInputNumber <VnInputNumber
v-model="data.initialTemperature" v-model="data.initialTemperature"
name="initialTemperature" name="initialTemperature"
@ -121,8 +124,16 @@ onMounted(() => {
:decimal-places="2" :decimal-places="2"
:positive="false" :positive="false"
/> />
<VnSelect
v-model="data.typeFk"
url="entryTypes"
:fields="['code', 'description']"
option-value="code"
optionLabel="description"
sortBy="description"
/>
</VnRow> </VnRow>
<VnRow> <VnRow class="q-py-sm">
<QInput <QInput
:label="t('entry.basicData.observation')" :label="t('entry.basicData.observation')"
type="textarea" type="textarea"
@ -132,14 +143,20 @@ onMounted(() => {
fill-input fill-input
/> />
</VnRow> </VnRow>
<VnRow> <VnRow class="q-py-sm">
<QCheckbox v-model="data.isOrdered" :label="t('entry.summary.ordered')" /> <VnCheckbox
<QCheckbox v-model="data.isConfirmed" :label="t('globals.confirmed')" /> v-model="data.isOrdered"
<QCheckbox :label="t('entry.list.tableVisibleColumns.isOrdered')"
v-model="data.isExcludedFromAvailable"
:label="t('entry.summary.excludedFromAvailable')"
/> />
<QCheckbox <VnCheckbox
v-model="data.isConfirmed"
:label="t('entry.list.tableVisibleColumns.isConfirmed')"
/>
<VnCheckbox
v-model="data.isExcludedFromAvailable"
:label="t('entry.list.tableVisibleColumns.isExcludedFromAvailable')"
/>
<VnCheckbox
:disable="!isAdministrative()" :disable="!isAdministrative()"
v-model="data.isBooked" v-model="data.isBooked"
:label="t('entry.basicData.booked')" :label="t('entry.basicData.booked')"

View File

@ -2,7 +2,7 @@
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { onMounted, ref } from 'vue'; import { onMounted, ref, computed } from 'vue';
import { useState } from 'src/composables/useState'; import { useState } from 'src/composables/useState';
@ -16,6 +16,8 @@ import ItemDescriptor from 'src/pages/Item/Card/ItemDescriptor.vue';
import axios from 'axios'; import axios from 'axios';
import VnSelectEnum from 'src/components/common/VnSelectEnum.vue'; import VnSelectEnum from 'src/components/common/VnSelectEnum.vue';
import { checkEntryLock } from 'src/composables/checkEntryLock'; import { checkEntryLock } from 'src/composables/checkEntryLock';
import VnRow from 'src/components/ui/VnRow.vue';
import VnInput from 'src/components/common/VnInput.vue';
const $props = defineProps({ const $props = defineProps({
id: { id: {
@ -57,31 +59,6 @@ const columns = [
createOrder: 12, createOrder: 12,
width: '25px', width: '25px',
}, },
{
label: t('Buyer'),
name: 'workerFk',
component: 'select',
attrs: {
url: 'TicketRequests/getItemTypeWorker',
fields: ['id', 'nickname'],
optionLabel: 'nickname',
sortBy: 'nickname ASC',
optionValue: 'id',
},
visible: false,
},
{
label: t('Family'),
name: 'itemTypeFk',
component: 'select',
attrs: {
url: 'itemTypes',
fields: ['id', 'name'],
optionLabel: 'name',
optionValue: 'id',
},
visible: false,
},
{ {
name: 'id', name: 'id',
isId: true, isId: true,
@ -111,16 +88,10 @@ const columns = [
}, },
}, },
{ {
align: 'center', align: 'left',
label: t('Article'), label: t('Article'),
component: 'input',
name: 'name', name: 'name',
component: 'select',
attrs: {
url: 'Items',
fields: ['id', 'name'],
optionLabel: 'name',
optionValue: 'id',
},
width: '85px', width: '85px',
isEditable: false, isEditable: false,
}, },
@ -212,7 +183,6 @@ const columns = [
}, },
}, },
{ {
align: 'center',
labelAbbreviation: 'GM', labelAbbreviation: 'GM',
label: t('Grouping selector'), label: t('Grouping selector'),
toolTip: t('Grouping selector'), toolTip: t('Grouping selector'),
@ -240,7 +210,6 @@ const columns = [
}, },
}, },
{ {
align: 'center',
labelAbbreviation: 'G', labelAbbreviation: 'G',
label: 'Grouping', label: 'Grouping',
toolTip: 'Grouping', toolTip: 'Grouping',
@ -294,7 +263,7 @@ const columns = [
align: 'center', align: 'center',
label: t('Amount'), label: t('Amount'),
name: 'amount', name: 'amount',
width: '45px', width: '75px',
component: 'number', component: 'number',
attrs: { attrs: {
positive: false, positive: false,
@ -310,7 +279,9 @@ const columns = [
toolTip: t('Package'), toolTip: t('Package'),
name: 'price2', name: 'price2',
component: 'number', component: 'number',
createDisable: true, createAttrs: {
disable: true,
},
width: '35px', width: '35px',
create: true, create: true,
format: (row) => parseFloat(row['price2']).toFixed(2), format: (row) => parseFloat(row['price2']).toFixed(2),
@ -320,7 +291,9 @@ const columns = [
label: t('Box'), label: t('Box'),
name: 'price3', name: 'price3',
component: 'number', component: 'number',
createDisable: true, createAttrs: {
disable: true,
},
cellEvent: { cellEvent: {
'update:modelValue': async (value, oldValue, row) => { 'update:modelValue': async (value, oldValue, row) => {
row['price2'] = row['price2'] * (value / oldValue); row['price2'] = row['price2'] * (value / oldValue);
@ -340,13 +313,6 @@ const columns = [
toggleIndeterminate: false, toggleIndeterminate: false,
}, },
component: 'checkbox', component: 'checkbox',
cellEvent: {
'update:modelValue': async (value, oldValue, row) => {
await axios.patch(`Items/${row['itemFk']}`, {
hasMinPrice: value,
});
},
},
width: '25px', width: '25px',
}, },
{ {
@ -356,13 +322,6 @@ const columns = [
toolTip: t('Minimum price'), toolTip: t('Minimum price'),
name: 'minPrice', name: 'minPrice',
component: 'number', component: 'number',
cellEvent: {
'update:modelValue': async (value, oldValue, row) => {
await axios.patch(`Items/${row['itemFk']}`, {
minPrice: value,
});
},
},
width: '35px', width: '35px',
style: (row) => { style: (row) => {
if (!row?.hasMinPrice) return { color: 'var(--vn-label-color)' }; if (!row?.hasMinPrice) return { color: 'var(--vn-label-color)' };
@ -425,6 +384,23 @@ const columns = [
}, },
}, },
]; ];
const buyerFk = ref(null);
const itemTypeFk = ref(null);
const inkFk = ref(null);
const tag1 = ref(null);
const tag2 = ref(null);
const tag1Filter = ref(null);
const tag2Filter = ref(null);
const filter = computed(() => {
const where = {};
where.workerFk = buyerFk.value;
where.itemTypeFk = itemTypeFk.value;
where.inkFk = inkFk.value;
where.tag1 = tag1.value;
where.tag2 = tag2.value;
return { where };
});
function getQuantityStyle(row) { function getQuantityStyle(row) {
if (row?.quantity !== row?.stickers * row?.packing) if (row?.quantity !== row?.stickers * row?.packing)
@ -610,6 +586,7 @@ onMounted(() => {
:url="`Entries/${entityId}/getBuyList`" :url="`Entries/${entityId}/getBuyList`"
search-url="EntryBuys" search-url="EntryBuys"
save-url="Buys/crud" save-url="Buys/crud"
:filter="filter"
:disable-option="{ card: true }" :disable-option="{ card: true }"
v-model:selected="selectedRows" v-model:selected="selectedRows"
@on-fetch="() => footerFetchDataRef.fetch()" @on-fetch="() => footerFetchDataRef.fetch()"
@ -655,7 +632,7 @@ onMounted(() => {
:is-editable="editableMode" :is-editable="editableMode"
:without-header="!editableMode" :without-header="!editableMode"
:with-filters="editableMode" :with-filters="editableMode"
:right-search="editableMode" :right-search="false"
:row-click="false" :row-click="false"
:columns="columns" :columns="columns"
:beforeSaveFn="beforeSave" :beforeSaveFn="beforeSave"
@ -666,6 +643,46 @@ onMounted(() => {
data-cy="entry-buys" data-cy="entry-buys"
overlay overlay
> >
<template #top-left>
<VnRow>
<VnSelect
:label="t('Buyer')"
v-model="buyerFk"
url="TicketRequests/getItemTypeWorker"
:fields="['id', 'nickname']"
option-label="nickname"
sort-by="nickname ASC"
/>
<VnSelect
:label="t('Family')"
v-model="itemTypeFk"
url="ItemTypes"
:fields="['id', 'name']"
option-label="name"
sort-by="name ASC"
/>
<VnSelect
:label="t('Color')"
v-model="inkFk"
url="Inks"
:fields="['id', 'name']"
option-label="name"
sort-by="name ASC"
/>
<VnInput
v-model="tag1Filter"
:label="t('Tag')"
@keyup.enter="tag1 = tag1Filter"
@remove="tag1 = null"
/>
<VnInput
v-model="tag2Filter"
:label="t('Tag')"
@keyup.enter="tag2 = tag2Filter"
@remove="tag2 = null"
/>
</VnRow>
</template>
<template #column-hex="{ row }"> <template #column-hex="{ row }">
<VnColor :colors="row?.hexJson" style="height: 100%; min-width: 2000px" /> <VnColor :colors="row?.hexJson" style="height: 100%; min-width: 2000px" />
</template> </template>
@ -696,7 +713,7 @@ onMounted(() => {
</div> </div>
</template> </template>
<template #column-footer-weight> <template #column-footer-weight>
{{ footer?.weight }} <span class="q-pr-xs">{{ footer?.weight }}</span>
</template> </template>
<template #column-footer-quantity> <template #column-footer-quantity>
<span :style="getQuantityStyle(footer)" data-cy="footer-quantity"> <span :style="getQuantityStyle(footer)" data-cy="footer-quantity">
@ -704,9 +721,8 @@ onMounted(() => {
</span> </span>
</template> </template>
<template #column-footer-amount> <template #column-footer-amount>
<span :style="getAmountStyle(footer)" data-cy="footer-amount"> <span data-cy="footer-amount">{{ footer?.amount }} / </span>
{{ footer?.amount }} <span style="color: var(--q-positive)">{{ footer?.checkedAmount }}</span>
</span>
</template> </template>
<template #column-create-itemFk="{ data }"> <template #column-create-itemFk="{ data }">
<VnSelect <VnSelect
@ -767,6 +783,8 @@ onMounted(() => {
</template> </template>
<i18n> <i18n>
es: es:
Buyer: Comprador
Family: Familia
Article: Artículo Article: Artículo
Siz.: Med. Siz.: Med.
Size: Medida Size: Medida

View File

@ -2,153 +2,82 @@
import { ref, computed } from 'vue'; import { ref, computed } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import VnTable from 'src/components/VnTable/VnTable.vue';
import FetchData from 'components/FetchData.vue';
import CrudModel from 'components/CrudModel.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
const { params } = useRoute(); const { params } = useRoute();
const { t } = useI18n(); const { t } = useI18n();
const selectedRows = ref([]);
const entryObservationsRef = ref(null); const entryObservationsRef = ref(null);
const entryObservationsOptions = ref([]); const entityId = ref(params.id);
const selected = ref([]);
const sortEntryObservationOptions = (data) => {
entryObservationsOptions.value = [...data].sort((a, b) =>
a.description.localeCompare(b.description),
);
};
const columns = computed(() => [ const columns = computed(() => [
{ {
name: 'observationType', name: 'id',
label: t('entry.notes.observationType'), isId: true,
field: (row) => row.observationTypeFk, visible: false,
sortable: true, isEditable: false,
options: entryObservationsOptions.value, columnFilter: false,
required: true,
model: 'observationTypeFk',
optionValue: 'id',
optionLabel: 'description',
tabIndex: 1,
align: 'left',
}, },
{ {
name: 'observationTypeFk',
label: t('entry.notes.observationType'),
component: 'select',
columnFilter: { inWhere: true },
attrs: {
inWhere: true,
url: 'ObservationTypes',
fields: ['id', 'description'],
optionValue: 'id',
optionLabel: 'description',
sortBy: 'description',
},
width: '30px',
create: true,
},
{
align: 'left',
name: 'description', name: 'description',
label: t('globals.description'), label: t('globals.description'),
field: (row) => row.description, component: 'input',
tabIndex: 2, columnFilter: false,
align: 'left', attrs: { autogrow: true },
create: true,
}, },
]); ]);
const filter = computed(() => ({
fields: ['id', 'entryFk', 'observationTypeFk', 'description'],
include: ['observationType'],
where: { entryFk: entityId },
}));
</script> </script>
<template> <template>
<FetchData <VnTable
url="ObservationTypes" ref="entryObservationsRef"
@on-fetch="(data) => sortEntryObservationOptions(data)" data-key="EntryObservations"
:columns="columns"
url="EntryObservations"
:user-filter="filter"
order="id ASC"
:disable-option="{ card: true }"
:is-editable="true"
:right-search="true"
v-model:selected="selectedRows"
:create="{
urlCreate: 'EntryObservations',
title: t('Create note'),
onDataSaved: () => {
entryObservationsRef.reload();
},
formInitialData: { entryFk: entityId },
}"
:table="{
'row-key': 'id',
selection: 'multiple',
}"
auto-load auto-load
/> />
<CrudModel
data-key="EntryAccount"
url="EntryObservations"
model="EntryAccount"
:filter="{
fields: ['id', 'entryFk', 'observationTypeFk', 'description'],
where: { entryFk: params.id },
}"
ref="entryObservationsRef"
:data-required="{ entryFk: params.id }"
v-model:selected="selected"
auto-load
>
<template #body="{ rows, validate }">
<QTable
v-model:selected="selected"
:columns="columns"
:rows="rows"
:pagination="{ rowsPerPage: 0 }"
row-key="$index"
selection="multiple"
hide-pagination
:grid="$q.screen.lt.md"
table-header-class="text-left"
>
<template #body-cell-observationType="{ row, col }">
<QTd>
<VnSelect
v-model="row[col.model]"
:options="col.options"
:option-value="col.optionValue"
:option-label="col.optionLabel"
:autofocus="col.tabIndex == 1"
input-debounce="0"
hide-selected
:required="true"
/>
</QTd>
</template>
<template #body-cell-description="{ row, col }">
<QTd>
<VnInput
:label="t('globals.description')"
v-model="row[col.name]"
:rules="validate('EntryObservation.description')"
/>
</QTd>
</template>
<template #item="props">
<div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition">
<QCard bordered flat>
<QCardSection>
<QCheckbox v-model="props.selected" dense />
</QCardSection>
<QSeparator />
<QList dense>
<QItem>
<QItemSection>
<VnSelect
v-model="props.row.observationTypeFk"
:options="entryObservationsOptions"
option-value="id"
option-label="description"
input-debounce="0"
hide-selected
:required="true"
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInput
:label="t('globals.description')"
v-model="props.row.description"
:rules="
validate('EntryObservation.description')
"
/>
</QItemSection>
</QItem>
</QList>
</QCard>
</div>
</template>
</QTable>
</template>
</CrudModel>
<QPageSticky position="bottom-right" :offset="[25, 25]">
<QBtn
fab
color="primary"
icon="add"
v-shortcut="'+'"
@click="entryObservationsRef.insert()"
/>
</QPageSticky>
</template> </template>
<i18n> <i18n>
es: es:
Add note: Añadir nota Create note: Crear nota
Remove note: Quitar nota
</i18n> </i18n>

View File

@ -92,13 +92,13 @@ onMounted(async () => {
</div> </div>
<div class="card-content"> <div class="card-content">
<VnCheckbox <VnCheckbox
:label="t('entry.summary.ordered')" :label="t('entry.list.tableVisibleColumns.isOrdered')"
v-model="entry.isOrdered" v-model="entry.isOrdered"
:disable="true" :disable="true"
size="xs" size="xs"
/> />
<VnCheckbox <VnCheckbox
:label="t('globals.confirmed')" :label="t('entry.list.tableVisibleColumns.isConfirmed')"
v-model="entry.isConfirmed" v-model="entry.isConfirmed"
:disable="true" :disable="true"
size="xs" size="xs"
@ -110,7 +110,11 @@ onMounted(async () => {
size="xs" size="xs"
/> />
<VnCheckbox <VnCheckbox
:label="t('entry.summary.excludedFromAvailable')" :label="
t(
'entry.list.tableVisibleColumns.isExcludedFromAvailable',
)
"
v-model="entry.isExcludedFromAvailable" v-model="entry.isExcludedFromAvailable"
:disable="true" :disable="true"
size="xs" size="xs"

View File

@ -85,7 +85,7 @@ const entryFilterPanel = ref();
</QItemSection> </QItemSection>
<QItemSection> <QItemSection>
<QCheckbox <QCheckbox
:label="t('entry.list.tableVisibleColumns.isConfirmed')" label="LE"
v-model="params.isConfirmed" v-model="params.isConfirmed"
toggle-indeterminate toggle-indeterminate
> >
@ -102,6 +102,7 @@ const entryFilterPanel = ref();
v-model="params.landed" v-model="params.landed"
@update:model-value="searchFn()" @update:model-value="searchFn()"
is-outlined is-outlined
data-cy="landed"
/> />
</QItemSection> </QItemSection>
</QItem> </QItem>
@ -121,13 +122,6 @@ const entryFilterPanel = ref();
rounded rounded
/> />
</QItemSection> </QItemSection>
<QItemSection>
<VnInput
v-model="params.invoiceNumber"
:label="t('params.invoiceNumber')"
is-outlined
/>
</QItemSection>
</QItem> </QItem>
<QItem> <QItem>
<QItemSection> <QItemSection>
@ -171,6 +165,7 @@ const entryFilterPanel = ref();
@update:model-value="searchFn()" @update:model-value="searchFn()"
url="Warehouses" url="Warehouses"
:fields="['id', 'name']" :fields="['id', 'name']"
sort-by="name ASC"
hide-selected hide-selected
dense dense
outlined outlined
@ -186,6 +181,7 @@ const entryFilterPanel = ref();
@update:model-value="searchFn()" @update:model-value="searchFn()"
url="Warehouses" url="Warehouses"
:fields="['id', 'name']" :fields="['id', 'name']"
sort-by="name ASC"
hide-selected hide-selected
dense dense
outlined outlined
@ -233,15 +229,6 @@ const entryFilterPanel = ref();
/> />
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem>
<QItemSection>
<VnInput
v-model="params.evaNotes"
:label="t('params.evaNotes')"
is-outlined
/>
</QItemSection>
</QItem>
</template> </template>
</VnFilterPanel> </VnFilterPanel>
</template> </template>
@ -267,7 +254,7 @@ en:
hasToShowDeletedEntries: Show deleted entries hasToShowDeletedEntries: Show deleted entries
es: es:
params: params:
isExcludedFromAvailable: Inventario isExcludedFromAvailable: Excluida
isOrdered: Pedida isOrdered: Pedida
isConfirmed: Confirmado isConfirmed: Confirmado
isReceived: Recibida isReceived: Recibida

View File

@ -1,264 +0,0 @@
<script setup>
import { onMounted, onUnmounted, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useStateStore } from 'stores/useStateStore';
import { toDate } from 'src/filters';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import RightMenu from 'src/components/common/RightMenu.vue';
import EntryLatestBuysFilter from './EntryLatestBuysFilter.vue';
import VnTable from 'components/VnTable/VnTable.vue';
import VnImg from 'src/components/ui/VnImg.vue';
const stateStore = useStateStore();
const { t } = useI18n();
const tableRef = ref();
const columns = [
{
align: 'center',
label: t('entry.latestBuys.tableVisibleColumns.image'),
name: 'itemFk',
columnField: {
component: VnImg,
attrs: ({ row }) => {
return {
id: row.id,
size: '50x50',
};
},
},
columnFilter: false,
},
{
align: 'left',
label: t('entry.latestBuys.tableVisibleColumns.itemFk'),
name: 'itemFk',
isTitle: true,
columnFilter: {
component: 'number',
inWhere: true,
},
},
{
align: 'left',
label: t('entry.summary.packing'),
name: 'packing',
columnFilter: {
component: 'number',
inWhere: true,
},
},
{
align: 'left',
label: t('entry.summary.grouping'),
name: 'grouping',
columnFilter: {
component: 'number',
inWhere: true,
},
},
{
align: 'left',
label: t('globals.quantity'),
name: 'quantity',
columnFilter: {
component: 'number',
inWhere: true,
},
},
{
align: 'left',
label: t('globals.description'),
name: 'description',
},
{
align: 'left',
label: t('globals.size'),
name: 'size',
columnFilter: {
component: 'number',
inWhere: true,
},
},
{
align: 'left',
label: t('globals.tags'),
name: 'tags',
},
{
align: 'left',
label: t('globals.type'),
name: 'type',
},
{
align: 'left',
label: t('globals.intrastat'),
name: 'intrastat',
},
{
align: 'left',
label: t('globals.origin'),
name: 'origin',
},
{
align: 'left',
label: t('entry.latestBuys.tableVisibleColumns.weightByPiece'),
name: 'weightByPiece',
columnFilter: {
component: 'number',
inWhere: true,
},
},
{
align: 'left',
label: t('entry.latestBuys.tableVisibleColumns.isActive'),
name: 'isActive',
},
{
align: 'left',
label: t('entry.latestBuys.tableVisibleColumns.family'),
name: 'family',
},
{
align: 'left',
label: t('entry.latestBuys.tableVisibleColumns.entryFk'),
name: 'entryFk',
columnFilter: {
component: 'number',
inWhere: true,
},
},
{
align: 'left',
label: t('entry.summary.buyingValue'),
name: 'buyingValue',
columnFilter: {
component: 'number',
inWhere: true,
},
},
{
align: 'left',
label: t('entry.latestBuys.tableVisibleColumns.freightValue'),
name: 'freightValue',
columnFilter: {
component: 'number',
inWhere: true,
},
},
{
align: 'left',
label: t('entry.latestBuys.tableVisibleColumns.comissionValue'),
name: 'comissionValue',
columnFilter: {
component: 'number',
inWhere: true,
},
},
{
align: 'left',
label: t('entry.latestBuys.tableVisibleColumns.packageValue'),
name: 'packageValue',
columnFilter: {
component: 'number',
inWhere: true,
},
},
{
align: 'left',
label: t('entry.latestBuys.tableVisibleColumns.isIgnored'),
name: 'isIgnored',
},
{
align: 'left',
label: t('entry.latestBuys.tableVisibleColumns.price2'),
name: 'price2',
columnFilter: {
component: 'number',
inWhere: true,
},
},
{
align: 'left',
label: t('entry.latestBuys.tableVisibleColumns.price3'),
name: 'price3',
columnFilter: {
component: 'number',
inWhere: true,
},
},
{
align: 'left',
label: t('entry.latestBuys.tableVisibleColumns.minPrice'),
name: 'minPrice',
columnFilter: {
component: 'number',
inWhere: true,
},
},
{
align: 'left',
label: t('entry.latestBuys.tableVisibleColumns.ektFk'),
name: 'ektFk',
},
{
align: 'left',
label: t('globals.weight'),
name: 'weight',
columnFilter: {
component: 'number',
inWhere: true,
},
},
{
align: 'left',
label: t('entry.buys.packagingFk'),
name: 'packagingFk',
columnFilter: {
component: 'number',
inWhere: true,
},
},
{
align: 'left',
label: t('entry.latestBuys.tableVisibleColumns.packingOut'),
name: 'packingOut',
},
{
align: 'left',
label: t('entry.latestBuys.tableVisibleColumns.landing'),
name: 'landing',
component: 'date',
columnField: {
component: null,
},
format: (row, dashIfEmpty) => dashIfEmpty(toDate(row.landing)),
},
];
onMounted(async () => {
stateStore.rightDrawer = true;
});
onUnmounted(() => (stateStore.rightDrawer = false));
</script>
<template>
<RightMenu>
<template #right-panel>
<EntryLatestBuysFilter data-key="LatestBuys" />
</template>
</RightMenu>
<VnSubToolbar />
<VnTable
ref="tableRef"
data-key="LatestBuys"
url="Buys/latestBuysFilter"
order="id DESC"
:columns="columns"
redirect="entry"
:row-click="({ entryFk }) => tableRef.redirect(entryFk)"
auto-load
:right-search="false"
/>
</template>

View File

@ -1,161 +0,0 @@
<script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import VnInputDate from 'src/components/common/VnInputDate.vue';
import VnInput from 'components/common/VnInput.vue';
import VnSelect from 'components/common/VnSelect.vue';
import ItemsFilterPanel from 'src/components/ItemsFilterPanel.vue';
import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
const { t } = useI18n();
defineProps({
dataKey: {
type: String,
required: true,
},
});
const tagValues = ref([]);
</script>
<template>
<ItemsFilterPanel :data-key="dataKey" :custom-tags="['tags']">
<template #body="{ params, searchFn }">
<QItem class="q-my-md">
<QItemSection>
<VnSelect
:label="t('components.itemsFilterPanel.salesPersonFk')"
v-model="params.salesPersonFk"
url="TicketRequests/getItemTypeWorker"
option-label="nickname"
:fields="['id', 'nickname']"
sort-by="nickname ASC"
dense
outlined
rounded
use-input
@update:model-value="searchFn()"
/>
</QItemSection>
</QItem>
<QItem class="q-my-md">
<QItemSection>
<VnSelectSupplier
v-model="params.supplierFk"
url="Suppliers"
:fields="['id', 'name', 'nickname']"
sort-by="name ASC"
dense
outlined
rounded
/>
</QItemSection>
</QItem>
<QItem class="q-my-md">
<QItemSection>
<VnInputDate
:label="t('components.itemsFilterPanel.started')"
v-model="params.from"
is-outlined
@update:model-value="searchFn()"
/>
</QItemSection>
</QItem>
<QItem class="q-my-md">
<QItemSection>
<VnInputDate
:label="t('components.itemsFilterPanel.ended')"
v-model="params.to"
is-outlined
@update:model-value="searchFn()"
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QCheckbox
:label="t('components.itemsFilterPanel.active')"
v-model="params.active"
toggle-indeterminate
@update:model-value="searchFn()"
/>
</QItemSection>
<QItemSection>
<QCheckbox
:label="t('globals.visible')"
v-model="params.visible"
toggle-indeterminate
@update:model-value="searchFn()"
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QCheckbox
:label="t('components.itemsFilterPanel.floramondo')"
v-model="params.floramondo"
toggle-indeterminate
@update:model-value="searchFn()"
/>
</QItemSection>
</QItem>
<QItem
v-for="(value, index) in tagValues"
:key="value"
class="q-mt-md filter-value"
>
<QItemSection class="col">
<VnSelect
:label="t('params.tag')"
v-model="value.selectedTag"
:options="tagOptions"
option-label="name"
dense
outlined
rounded
:emit-value="false"
use-input
:is-clearable="false"
@update:model-value="getSelectedTagValues(value)"
/>
</QItemSection>
<QItemSection class="col">
<VnSelect
v-if="!value?.selectedTag?.isFree && value.valueOptions"
:label="t('params.value')"
v-model="value.value"
:options="value.valueOptions || []"
option-value="value"
option-label="value"
dense
outlined
rounded
emit-value
use-input
:disable="!value"
:is-clearable="false"
class="filter-input"
@update:model-value="applyTags(params, searchFn)"
/>
<VnInput
v-else
v-model="value.value"
:label="t('params.value')"
:disable="!value"
is-outlined
class="filter-input"
:is-clearable="false"
@keyup.enter="applyTags(params, searchFn)"
/>
</QItemSection>
<QIcon
name="delete"
class="filter-icon"
@click="removeTag(index, params, searchFn)"
/>
</QItem>
</template>
</ItemsFilterPanel>
</template>

View File

@ -107,9 +107,8 @@ const columns = computed(() => [
attrs: { attrs: {
url: 'suppliers', url: 'suppliers',
fields: ['id', 'name'], fields: ['id', 'name'],
where: { order: 'name DESC' }, sortBy: 'name ASC',
}, },
format: (row, dashIfEmpty) => dashIfEmpty(row.supplierName),
width: '110px', width: '110px',
}, },
{ {
@ -145,6 +144,7 @@ const columns = computed(() => [
attrs: { attrs: {
url: 'agencyModes', url: 'agencyModes',
fields: ['id', 'name'], fields: ['id', 'name'],
sortBy: 'name ASC',
}, },
columnField: { columnField: {
component: null, component: null,
@ -158,7 +158,6 @@ const columns = computed(() => [
component: 'input', component: 'input',
}, },
{ {
align: 'left',
label: t('entry.list.tableVisibleColumns.warehouseOutFk'), label: t('entry.list.tableVisibleColumns.warehouseOutFk'),
name: 'warehouseOutFk', name: 'warehouseOutFk',
cardVisible: true, cardVisible: true,
@ -166,6 +165,7 @@ const columns = computed(() => [
attrs: { attrs: {
url: 'warehouses', url: 'warehouses',
fields: ['id', 'name'], fields: ['id', 'name'],
sortBy: 'name ASC',
}, },
columnField: { columnField: {
component: null, component: null,
@ -174,7 +174,6 @@ const columns = computed(() => [
width: '65px', width: '65px',
}, },
{ {
align: 'left',
label: t('entry.list.tableVisibleColumns.warehouseInFk'), label: t('entry.list.tableVisibleColumns.warehouseInFk'),
name: 'warehouseInFk', name: 'warehouseInFk',
cardVisible: true, cardVisible: true,
@ -182,6 +181,7 @@ const columns = computed(() => [
attrs: { attrs: {
url: 'warehouses', url: 'warehouses',
fields: ['id', 'name'], fields: ['id', 'name'],
sortBy: 'name ASC',
}, },
columnField: { columnField: {
component: null, component: null,
@ -190,7 +190,6 @@ const columns = computed(() => [
width: '65px', width: '65px',
}, },
{ {
align: 'left',
labelAbbreviation: t('Type'), labelAbbreviation: t('Type'),
label: t('entry.list.tableVisibleColumns.entryTypeDescription'), label: t('entry.list.tableVisibleColumns.entryTypeDescription'),
toolTip: t('entry.list.tableVisibleColumns.entryTypeDescription'), toolTip: t('entry.list.tableVisibleColumns.entryTypeDescription'),
@ -201,6 +200,7 @@ const columns = computed(() => [
fields: ['code', 'description'], fields: ['code', 'description'],
optionValue: 'code', optionValue: 'code',
optionLabel: 'description', optionLabel: 'description',
sortBy: 'description',
}, },
width: '65px', width: '65px',
format: (row, dashIfEmpty) => dashIfEmpty(row.entryTypeDescription), format: (row, dashIfEmpty) => dashIfEmpty(row.entryTypeDescription),

View File

@ -1,24 +1,23 @@
<script setup> <script setup>
import { ref, computed } from 'vue'; import { ref, computed } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useState } from 'src/composables/useState'; import { useQuasar, date } from 'quasar';
import { useQuasar } from 'quasar';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import FormModelPopup from 'components/FormModelPopup.vue'; import FormModelPopup from 'components/FormModelPopup.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import RightMenu from 'src/components/common/RightMenu.vue';
import EntryStockBoughtFilter from './EntryStockBoughtFilter.vue';
import VnTable from 'components/VnTable/VnTable.vue'; import VnTable from 'components/VnTable/VnTable.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import EntryStockBoughtDetail from 'src/pages/Entry/EntryStockBoughtDetail.vue'; import EntryStockBoughtDetail from 'src/pages/Entry/EntryStockBoughtDetail.vue';
import TravelDescriptorProxy from '../Travel/Card/TravelDescriptorProxy.vue';
import { useFilterParams } from 'src/composables/useFilterParams';
import axios from 'axios';
const { t } = useI18n(); const { t } = useI18n();
const quasar = useQuasar(); const quasar = useQuasar();
const state = useState(); const filterDate = ref(useFilterParams('StockBoughts').params);
const user = state.getUser();
const footer = ref({ bought: 0, reserve: 0 }); const footer = ref({ bought: 0, reserve: 0 });
const columns = computed(() => [ const columns = computed(() => [
{ {
@ -46,7 +45,7 @@ const columns = computed(() => [
optionValue: 'id', optionValue: 'id',
}, },
columnFilter: false, columnFilter: false,
width: '50px', width: '60%',
}, },
{ {
align: 'center', align: 'center',
@ -56,20 +55,20 @@ const columns = computed(() => [
create: true, create: true,
component: 'number', component: 'number',
summation: true, summation: true,
width: '50px',
format: ({ reserve }, dashIfEmpty) => dashIfEmpty(round(reserve)), format: ({ reserve }, dashIfEmpty) => dashIfEmpty(round(reserve)),
width: '20%',
}, },
{ {
align: 'center', align: 'right',
label: t('entryStockBought.bought'), label: t('entryStockBought.bought'),
name: 'bought', name: 'bought',
summation: true, summation: true,
cardVisible: true, cardVisible: true,
style: ({ reserve, bought }) => boughtStyle(bought, reserve), style: ({ reserve, bought }) => boughtStyle(bought, reserve),
columnFilter: false, columnFilter: false,
width: '20%',
}, },
{ {
align: 'left',
label: t('entryStockBought.date'), label: t('entryStockBought.date'),
name: 'dated', name: 'dated',
component: 'date', component: 'date',
@ -77,7 +76,7 @@ const columns = computed(() => [
create: true, create: true,
}, },
{ {
align: 'left', align: 'center',
name: 'tableActions', name: 'tableActions',
actions: [ actions: [
{ {
@ -90,7 +89,7 @@ const columns = computed(() => [
component: EntryStockBoughtDetail, component: EntryStockBoughtDetail,
componentProps: { componentProps: {
workerFk: row.workerFk, workerFk: row.workerFk,
dated: userParams.value.dated, dated: filterDate.value.dated,
}, },
}); });
}, },
@ -98,39 +97,29 @@ const columns = computed(() => [
], ],
}, },
]); ]);
const fetchDataRef = ref(); const fetchDataRef = ref();
const travelDialogRef = ref(false); const travelDialogRef = ref(false);
const tableRef = ref(); const tableRef = ref();
const travel = ref(null); const travel = ref(null);
const userParams = ref({ const filter = computed(() => ({
dated: Date.vnNew().toJSON(), fields: ['id', 'm3', 'ref', 'warehouseInFk'],
});
const filter = ref({
fields: ['id', 'm3', 'warehouseInFk'],
include: [ include: [
{ {
relation: 'warehouseIn', relation: 'warehouseIn',
scope: { scope: {
fields: ['code'], fields: ['code', 'name'],
}, },
}, },
], ],
where: { where: {
shipped: (userParams.value.dated shipped: date.adjustDate(filterDate.value.dated, {
? new Date(userParams.value.dated) hour: 0,
: Date.vnNew() minute: 0,
).setHours(0, 0, 0, 0), second: 0,
}),
m3: { neq: null }, m3: { neq: null },
}, },
}); }));
const setUserParams = async ({ dated }) => {
const shipped = (dated ? new Date(dated) : Date.vnNew()).setHours(0, 0, 0, 0);
filter.value.where.shipped = shipped;
fetchDataRef.value?.fetch();
};
function openDialog() { function openDialog() {
travelDialogRef.value = true; travelDialogRef.value = true;
@ -151,6 +140,31 @@ function round(value) {
function boughtStyle(bought, reserve) { function boughtStyle(bought, reserve) {
return reserve < bought ? { color: 'var(--q-negative)' } : ''; return reserve < bought ? { color: 'var(--q-negative)' } : '';
} }
async function beforeSave(data, getChanges) {
const changes = data.creates;
if (!changes) return data;
const patchPromises = [];
for (const change of changes) {
if (change?.isReal === false && change?.reserve > 0) {
const postData = {
workerFk: change.workerFk,
reserve: change.reserve,
dated: filterDate.value.dated,
};
const promise = axios.post('StockBoughts', postData).catch((error) => {
console.error('Error processing change: ', change, error);
});
patchPromises.push(promise);
}
}
await Promise.all(patchPromises);
const filteredChanges = changes.filter((change) => change?.isReal !== false);
data.creates = filteredChanges;
}
</script> </script>
<template> <template>
<VnSubToolbar> <VnSubToolbar>
@ -158,18 +172,17 @@ function boughtStyle(bought, reserve) {
<FetchData <FetchData
ref="fetchDataRef" ref="fetchDataRef"
url="Travels" url="Travels"
auto-load
:filter="filter" :filter="filter"
@on-fetch=" @on-fetch="
(data) => { (data) => {
travel = data.find( travel = data.find(
(data) => data.warehouseIn?.code.toLowerCase() === 'vnh', (data) => data.warehouseIn?.code?.toLowerCase() === 'vnh',
); );
} }
" "
/> />
<VnRow class="travel"> <VnRow class="travel">
<div v-if="travel"> <div v-show="travel">
<span style="color: var(--vn-label-color)"> <span style="color: var(--vn-label-color)">
{{ t('entryStockBought.purchaseSpaces') }}: {{ t('entryStockBought.purchaseSpaces') }}:
</span> </span>
@ -180,7 +193,7 @@ function boughtStyle(bought, reserve) {
v-if="travel?.m3" v-if="travel?.m3"
style="max-width: 20%" style="max-width: 20%"
flat flat
icon="edit" icon="search"
@click="openDialog()" @click="openDialog()"
:title="t('entryStockBought.editTravel')" :title="t('entryStockBought.editTravel')"
color="primary" color="primary"
@ -195,57 +208,42 @@ function boughtStyle(bought, reserve) {
:url-update="`Travels/${travel?.id}`" :url-update="`Travels/${travel?.id}`"
model="travel" model="travel"
:title="t('Travel m3')" :title="t('Travel m3')"
:form-initial-data="{ id: travel?.id, m3: travel?.m3 }" :form-initial-data="travel"
@on-data-saved="fetchDataRef.fetch()" @on-data-saved="fetchDataRef.fetch()"
> >
<template #form-inputs="{ data }"> <template #form-inputs="{ data }">
<VnInput <span class="link">
v-model="data.id" {{ data.ref }}
:label="t('id')" <TravelDescriptorProxy :id="data.id" />
type="number" </span>
disable
readonly
/>
<VnInput v-model="data.m3" :label="t('m3')" type="number" /> <VnInput v-model="data.m3" :label="t('m3')" type="number" />
</template> </template>
</FormModelPopup> </FormModelPopup>
</QDialog> </QDialog>
<RightMenu>
<template #right-panel>
<EntryStockBoughtFilter
data-key="StockBoughts"
@set-user-params="setUserParams"
/>
</template>
</RightMenu>
<div class="table-container"> <div class="table-container">
<div class="column items-center"> <div class="column items-center">
<VnTable <VnTable
ref="tableRef" ref="tableRef"
data-key="StockBoughts" data-key="StockBoughts"
url="StockBoughts/getStockBought" url="StockBoughts/getStockBought"
:beforeSaveFn="beforeSave"
save-url="StockBoughts/crud" save-url="StockBoughts/crud"
search-url="StockBoughts" search-url="StockBoughts"
order="reserve DESC" order="bought DESC"
:right-search="false"
:is-editable="true" :is-editable="true"
@on-fetch="(data) => setFooter(data)" @on-fetch="
:create="{ async (data) => {
urlCreate: 'StockBoughts', setFooter(data);
title: t('entryStockBought.reserveSomeSpace'), await fetchDataRef.fetch();
onDataSaved: () => tableRef.reload(), }
formInitialData: { "
workerFk: user.id,
dated: Date.vnNow(),
},
}"
:columns="columns" :columns="columns"
:user-params="userParams"
:footer="true" :footer="true"
table-height="80vh" table-height="80vh"
auto-load
:column-search="false" :column-search="false"
:without-header="true" :without-header="true"
:user-params="{ dated: Date.vnNew() }"
auto-load
> >
<template #column-workerFk="{ row }"> <template #column-workerFk="{ row }">
<span class="link" @click.stop> <span class="link" @click.stop>
@ -278,9 +276,6 @@ function boughtStyle(bought, reserve) {
.column { .column {
min-width: 35%; min-width: 35%;
margin-top: 5%; margin-top: 5%;
display: flex;
flex-direction: column;
align-items: center;
} }
.text-negative { .text-negative {
color: $negative !important; color: $negative !important;

View File

@ -1,70 +0,0 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { onMounted } from 'vue';
import { useStateStore } from 'stores/useStateStore';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue';
const { t } = useI18n();
const props = defineProps({
dataKey: {
type: String,
required: true,
},
});
const stateStore = useStateStore();
const emit = defineEmits(['set-user-params']);
const setUserParams = (params) => {
emit('set-user-params', params);
};
onMounted(async () => {
stateStore.rightDrawer = true;
});
</script>
<template>
<VnFilterPanel
:data-key="props.dataKey"
:search-button="true"
search-url="StockBoughts"
@set-user-params="setUserParams"
>
<template #tags="{ tag, formatFn }">
<div class="q-gutter-x-xs">
<strong>{{ t(`params.${tag.label}`) }}: </strong>
<span>{{ formatFn(tag.value) }}</span>
</div>
</template>
<template #body="{ params, searchFn }">
<QItem class="q-my-sm">
<QItemSection>
<VnInputDate
id="date"
v-model="params.dated"
@update:model-value="
(value) => {
params.dated = value;
setUserParams(params);
searchFn();
}
"
:label="t('Date')"
is-outlined
/>
</QItemSection>
</QItem>
</template>
</VnFilterPanel>
</template>
<i18n>
en:
params:
dated: Date
workerFk: Worker
es:
Date: Fecha
params:
dated: Fecha
workerFk: Trabajador
</i18n>

View File

@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import { toDate } from 'src/filters/index'; import { toDate } from 'src/filters/index';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import EntryBuysTableDialog from './EntryBuysTableDialog.vue'; import EntrySupplierlDetail from './EntrySupplierlDetail.vue';
import VnTable from 'components/VnTable/VnTable.vue'; import VnTable from 'components/VnTable/VnTable.vue';
const { t } = useI18n(); const { t } = useI18n();
@ -18,18 +18,28 @@ const columns = computed(() => [
{ {
align: 'left', align: 'left',
name: 'id', name: 'id',
label: t('myEntries.id'), label: t('entrySupplier.id'),
columnFilter: false, columnFilter: false,
isId: true,
chip: {
condition: () => true,
},
},
{
align: 'left',
name: 'supplierName',
label: t('entrySupplier.supplierName'),
cardVisible: true,
isTitle: true, isTitle: true,
}, },
{ {
visible: false, visible: false,
align: 'right', align: 'right',
label: t('myEntries.shipped'), label: t('entrySupplier.shipped'),
name: 'shipped', name: 'shipped',
columnFilter: { columnFilter: {
name: 'fromShipped', name: 'fromShipped',
label: t('myEntries.fromShipped'), label: t('entrySupplier.fromShipped'),
component: 'date', component: 'date',
}, },
format: ({ shipped }) => toDate(shipped), format: ({ shipped }) => toDate(shipped),
@ -37,26 +47,26 @@ const columns = computed(() => [
{ {
visible: false, visible: false,
align: 'left', align: 'left',
label: t('myEntries.shipped'), label: t('entrySupplier.shipped'),
name: 'shipped', name: 'shipped',
columnFilter: { columnFilter: {
name: 'toShipped', name: 'toShipped',
label: t('myEntries.toShipped'), label: t('entrySupplier.toShipped'),
component: 'date', component: 'date',
}, },
format: ({ shipped }) => toDate(shipped), format: ({ shipped }) => toDate(shipped),
cardVisible: true, cardVisible: true,
}, },
{ {
align: 'right', align: 'left',
label: t('myEntries.shipped'), label: t('entrySupplier.shipped'),
name: 'shipped', name: 'shipped',
columnFilter: false, columnFilter: false,
format: ({ shipped }) => toDate(shipped), format: ({ shipped }) => toDate(shipped),
}, },
{ {
align: 'right', align: 'left',
label: t('myEntries.landed'), label: t('entrySupplier.landed'),
name: 'landed', name: 'landed',
columnFilter: false, columnFilter: false,
format: ({ landed }) => toDate(landed), format: ({ landed }) => toDate(landed),
@ -64,15 +74,13 @@ const columns = computed(() => [
{ {
align: 'right', align: 'right',
label: t('myEntries.wareHouseIn'), label: t('entrySupplier.wareHouseIn'),
name: 'warehouseInFk', name: 'warehouseInFk',
format: (row) => { format: ({ warehouseInName }) => warehouseInName,
row.warehouseInName;
},
cardVisible: true, cardVisible: true,
columnFilter: { columnFilter: {
name: 'warehouseInFk', name: 'warehouseInFk',
label: t('myEntries.warehouseInFk'), label: t('entrySupplier.warehouseInFk'),
component: 'select', component: 'select',
attrs: { attrs: {
url: 'warehouses', url: 'warehouses',
@ -86,13 +94,13 @@ const columns = computed(() => [
}, },
{ {
align: 'left', align: 'left',
label: t('myEntries.daysOnward'), label: t('entrySupplier.daysOnward'),
name: 'daysOnward', name: 'daysOnward',
visible: false, visible: false,
}, },
{ {
align: 'left', align: 'left',
label: t('myEntries.daysAgo'), label: t('entrySupplier.daysAgo'),
name: 'daysAgo', name: 'daysAgo',
visible: false, visible: false,
}, },
@ -101,8 +109,8 @@ const columns = computed(() => [
name: 'tableActions', name: 'tableActions',
actions: [ actions: [
{ {
title: t('myEntries.printLabels'), title: t('entrySupplier.printLabels'),
icon: 'move_item', icon: 'search',
isPrimary: true, isPrimary: true,
action: (row) => printBuys(row.id), action: (row) => printBuys(row.id),
}, },
@ -112,7 +120,7 @@ const columns = computed(() => [
const printBuys = (rowId) => { const printBuys = (rowId) => {
quasar.dialog({ quasar.dialog({
component: EntryBuysTableDialog, component: EntrySupplierlDetail,
componentProps: { componentProps: {
id: rowId, id: rowId,
}, },
@ -121,19 +129,18 @@ const printBuys = (rowId) => {
</script> </script>
<template> <template>
<VnSearchbar <VnSearchbar
data-key="myEntriesList" data-key="entrySupplierList"
url="Entries/filter" url="Entries/filter"
:label="t('myEntries.search')" :label="t('entrySupplier.search')"
:info="t('myEntries.searchInfo')" :info="t('entrySupplier.searchInfo')"
/> />
<VnTable <VnTable
data-key="myEntriesList" data-key="entrySupplierList"
url="Entries/filter" url="Entries/filter"
:columns="columns" :columns="columns"
:user-params="params" :user-params="params"
default-mode="card" default-mode="card"
order="shipped DESC" order="shipped DESC"
auto-load auto-load
chip-locale="myEntries"
/> />
</template> </template>

View File

@ -30,7 +30,7 @@ const entriesTableColumns = computed(() => [
align: 'left', align: 'left',
name: 'itemFk', name: 'itemFk',
field: 'itemFk', field: 'itemFk',
label: t('entry.latestBuys.tableVisibleColumns.itemFk'), label: t('entrySupplier.itemId'),
}, },
{ {
align: 'left', align: 'left',
@ -65,7 +65,15 @@ const entriesTableColumns = computed(() => [
]); ]);
function downloadCSV(rows) { function downloadCSV(rows) {
const headers = ['id', 'itemFk', 'name', 'stickers', 'packing', 'grouping', 'comment']; const headers = [
'id',
'itemFk',
'name',
'stickers',
'packing',
'grouping',
'comment',
];
const csvRows = rows.map((row) => { const csvRows = rows.map((row) => {
const buy = row; const buy = row;
@ -119,17 +127,18 @@ function downloadCSV(rows) {
> >
<template #top-left> <template #top-left>
<QBtn <QBtn
:label="t('myEntries.downloadCsv')" :label="t('entrySupplier.downloadCsv')"
color="primary" color="primary"
icon="csv" icon="csv"
@click="downloadCSV(rows)" @click="downloadCSV(rows)"
unelevated unelevated
data-cy="downloadCsvBtn"
/> />
</template> </template>
<template #top-right> <template #top-right>
<QBtn <QBtn
class="q-mr-lg" class="q-mr-lg"
:label="t('myEntries.printLabels')" :label="t('entrySupplier.printLabels')"
color="primary" color="primary"
icon="print" icon="print"
@click=" @click="
@ -148,13 +157,18 @@ function downloadCSV(rows) {
v-if="props.row.stickers > 0" v-if="props.row.stickers > 0"
@click=" @click="
openReport( openReport(
`Entries/${props.row.id}/buy-label-supplier` `Entries/${props.row.id}/buy-label-supplier`,
{},
true,
) )
" "
unelevated unelevated
color="primary"
flat
data-cy="seeLabelBtn"
> >
<QTooltip>{{ <QTooltip>{{
t('myEntries.viewLabel') t('entrySupplier.viewLabel')
}}</QTooltip> }}</QTooltip>
</QBtn> </QBtn>
</QTr> </QTr>

View File

@ -38,7 +38,7 @@ const recalc = async () => {
<template> <template>
<div class="q-pa-lg row justify-center"> <div class="q-pa-lg row justify-center">
<QCard class="bg-light" style="width: 300px"> <QCard class="bg-light" style="width: 300px" data-cy="wasteRecalc">
<QCardSection> <QCardSection>
<VnInputDate <VnInputDate
class="q-mb-lg" class="q-mb-lg"
@ -46,6 +46,7 @@ const recalc = async () => {
:label="$t('globals.from')" :label="$t('globals.from')"
rounded rounded
dense dense
data-cy="dateFrom"
/> />
<VnInputDate <VnInputDate
class="q-mb-lg" class="q-mb-lg"
@ -55,6 +56,7 @@ const recalc = async () => {
:disable="!dateFrom" :disable="!dateFrom"
rounded rounded
dense dense
data-cy="dateTo"
/> />
<QBtn <QBtn
color="primary" color="primary"
@ -63,6 +65,7 @@ const recalc = async () => {
:loading="isLoading" :loading="isLoading"
:disable="isLoading || !(dateFrom && dateTo)" :disable="isLoading || !(dateFrom && dateTo)"
@click="recalc()" @click="recalc()"
data-cy="recalc"
/> />
</QCardSection> </QCardSection>
</QCard> </QCard>

View File

@ -6,7 +6,7 @@ entry:
list: list:
newEntry: New entry newEntry: New entry
tableVisibleColumns: tableVisibleColumns:
isExcludedFromAvailable: Exclude from inventory isExcludedFromAvailable: Excluded from available
isOrdered: Ordered isOrdered: Ordered
isConfirmed: Ready to label isConfirmed: Ready to label
isReceived: Received isReceived: Received
@ -33,7 +33,7 @@ entry:
invoiceAmount: Invoice amount invoiceAmount: Invoice amount
ordered: Ordered ordered: Ordered
booked: Booked booked: Booked
excludedFromAvailable: Inventory excludedFromAvailable: Excluded
travelReference: Reference travelReference: Reference
travelAgency: Agency travelAgency: Agency
travelShipped: Shipped travelShipped: Shipped
@ -55,7 +55,7 @@ entry:
commission: Commission commission: Commission
observation: Observation observation: Observation
booked: Booked booked: Booked
excludedFromAvailable: Inventory excludedFromAvailable: Excluded
initialTemperature: Ini °C initialTemperature: Ini °C
finalTemperature: Fin °C finalTemperature: Fin °C
buys: buys:
@ -65,27 +65,10 @@ entry:
printedStickers: Printed stickers printedStickers: Printed stickers
notes: notes:
observationType: Observation type observationType: Observation type
latestBuys:
tableVisibleColumns:
image: Picture
itemFk: Item ID
weightByPiece: Weight/Piece
isActive: Active
family: Family
entryFk: Entry
freightValue: Freight value
comissionValue: Commission value
packageValue: Package value
isIgnored: Is ignored
price2: Grouping
price3: Packing
minPrice: Min
ektFk: Ekt
packingOut: Package out
landing: Landing
isExcludedFromAvailable: Es inventory
params: params:
isExcludedFromAvailable: Exclude from inventory entryFk: Entry
observationTypeFk: Observation type
isExcludedFromAvailable: Excluded from available
isOrdered: Ordered isOrdered: Ordered
isConfirmed: Ready to label isConfirmed: Ready to label
isReceived: Received isReceived: Received
@ -127,13 +110,17 @@ entry:
company_name: Company name company_name: Company name
itemTypeFk: Item type itemTypeFk: Item type
workerFk: Worker id workerFk: Worker id
daysAgo: Days ago
toShipped: T. shipped
fromShipped: F. shipped
supplierName: Supplier
search: Search entries search: Search entries
searchInfo: You can search by entry reference searchInfo: You can search by entry reference
descriptorMenu: descriptorMenu:
showEntryReport: Show entry report showEntryReport: Show entry report
entryFilter: entryFilter:
params: params:
isExcludedFromAvailable: Exclude from inventory isExcludedFromAvailable: Excluded from available
invoiceNumber: Invoice number invoiceNumber: Invoice number
travelFk: Travel travelFk: Travel
companyFk: Company companyFk: Company
@ -155,7 +142,7 @@ entryFilter:
warehouseOutFk: Origin warehouseOutFk: Origin
warehouseInFk: Destiny warehouseInFk: Destiny
entryTypeCode: Entry type entryTypeCode: Entry type
myEntries: entrySupplier:
id: ID id: ID
landed: Landed landed: Landed
shipped: Shipped shipped: Shipped
@ -170,6 +157,8 @@ myEntries:
downloadCsv: Download CSV downloadCsv: Download CSV
search: Search entries search: Search entries
searchInfo: You can search by entry reference searchInfo: You can search by entry reference
supplierName: Supplier
itemId: Item id
entryStockBought: entryStockBought:
travel: Travel travel: Travel
editTravel: Edit travel editTravel: Edit travel

View File

@ -6,7 +6,7 @@ entry:
list: list:
newEntry: Nueva entrada newEntry: Nueva entrada
tableVisibleColumns: tableVisibleColumns:
isExcludedFromAvailable: Excluir del inventario isExcludedFromAvailable: Excluir del disponible
isOrdered: Pedida isOrdered: Pedida
isConfirmed: Lista para etiquetar isConfirmed: Lista para etiquetar
isReceived: Recibida isReceived: Recibida
@ -33,7 +33,7 @@ entry:
invoiceAmount: Importe invoiceAmount: Importe
ordered: Pedida ordered: Pedida
booked: Contabilizada booked: Contabilizada
excludedFromAvailable: Inventario excludedFromAvailable: Excluido
travelReference: Referencia travelReference: Referencia
travelAgency: Agencia travelAgency: Agencia
travelShipped: F. envio travelShipped: F. envio
@ -56,7 +56,7 @@ entry:
observation: Observación observation: Observación
commission: Comisión commission: Comisión
booked: Contabilizada booked: Contabilizada
excludedFromAvailable: Inventario excludedFromAvailable: Excluido
initialTemperature: Ini °C initialTemperature: Ini °C
finalTemperature: Fin °C finalTemperature: Fin °C
buys: buys:
@ -66,30 +66,12 @@ entry:
printedStickers: Etiquetas impresas printedStickers: Etiquetas impresas
notes: notes:
observationType: Tipo de observación observationType: Tipo de observación
latestBuys:
tableVisibleColumns:
image: Foto
itemFk: Id Artículo
weightByPiece: Peso (gramos)/tallo
isActive: Activo
family: Familia
entryFk: Entrada
freightValue: Porte
comissionValue: Comisión
packageValue: Embalaje
isIgnored: Ignorado
price2: Grouping
price3: Packing
minPrice: Min
ektFk: Ekt
packingOut: Embalaje envíos
landing: Llegada
isExcludedFromAvailable: Es inventario
search: Buscar entradas search: Buscar entradas
searchInfo: Puedes buscar por referencia de entrada searchInfo: Puedes buscar por referencia de entrada
params: params:
isExcludedFromAvailable: Excluir del inventario entryFk: Entrada
observationTypeFk: Tipo de observación
isExcludedFromAvailable: Excluir del disponible
isOrdered: Pedida isOrdered: Pedida
isConfirmed: Lista para etiquetar isConfirmed: Lista para etiquetar
isReceived: Recibida isReceived: Recibida
@ -131,9 +113,13 @@ entry:
company_name: Nombre empresa company_name: Nombre empresa
itemTypeFk: Familia itemTypeFk: Familia
workerFk: Comprador workerFk: Comprador
daysAgo: Días atras
toShipped: F. salida(hasta)
fromShipped: F. salida(desde)
supplierName: Proveedor
entryFilter: entryFilter:
params: params:
isExcludedFromAvailable: Inventario isExcludedFromAvailable: Excluido
isOrdered: Pedida isOrdered: Pedida
isConfirmed: Confirmado isConfirmed: Confirmado
isReceived: Recibida isReceived: Recibida
@ -149,7 +135,7 @@ entryFilter:
warehouseInFk: Destino warehouseInFk: Destino
entryTypeCode: Tipo de entrada entryTypeCode: Tipo de entrada
hasToShowDeletedEntries: Mostrar entradas eliminadas hasToShowDeletedEntries: Mostrar entradas eliminadas
myEntries: entrySupplier:
id: ID id: ID
landed: F. llegada landed: F. llegada
shipped: F. salida shipped: F. salida
@ -164,10 +150,12 @@ myEntries:
downloadCsv: Descargar CSV downloadCsv: Descargar CSV
search: Buscar entradas search: Buscar entradas
searchInfo: Puedes buscar por referencia de la entrada searchInfo: Puedes buscar por referencia de la entrada
supplierName: Proveedor
itemId: Id artículo
entryStockBought: entryStockBought:
travel: Envío travel: Envío
editTravel: Editar envío editTravel: Editar envío
purchaseSpaces: Espacios de compra purchaseSpaces: Camiones reservados
buyer: Comprador buyer: Comprador
reserve: Reservado reserve: Reservado
bought: Comprado bought: Comprado

View File

@ -121,25 +121,40 @@ function deleteFile(dmsFk) {
hide-selected hide-selected
:is-clearable="false" :is-clearable="false"
:required="true" :required="true"
data-cy="invoiceInBasicDataSupplier"
/> />
<VnInput <VnInput
clearable clearable
clear-icon="close" clear-icon="close"
:label="t('invoiceIn.supplierRef')" :label="t('invoiceIn.supplierRef')"
v-model="data.supplierRef" v-model="data.supplierRef"
data-cy="invoiceInBasicDataSupplierRef"
/> />
</VnRow> </VnRow>
<VnRow> <VnRow>
<VnInputDate :label="t('Expedition date')" v-model="data.issued" /> <VnInputDate
:label="t('Expedition date')"
v-model="data.issued"
data-cy="invoiceInBasicDataIssued"
/>
<VnInputDate <VnInputDate
:label="t('Operation date')" :label="t('Operation date')"
v-model="data.operated" v-model="data.operated"
autofocus autofocus
data-cy="invoiceInBasicDataOperated"
/> />
</VnRow> </VnRow>
<VnRow> <VnRow>
<VnInputDate :label="t('Entry date')" v-model="data.bookEntried" /> <VnInputDate
<VnInputDate :label="t('Accounted date')" v-model="data.booked" /> :label="t('Entry date')"
v-model="data.bookEntried"
data-cy="invoiceInBasicDatabookEntried"
/>
<VnInputDate
:label="t('Accounted date')"
v-model="data.booked"
data-cy="invoiceInBasicDataBooked"
/>
</VnRow> </VnRow>
<VnRow> <VnRow>
<VnSelect <VnSelect
@ -149,7 +164,7 @@ function deleteFile(dmsFk) {
option-value="id" option-value="id"
option-label="id" option-label="id"
:filter-options="['id', 'name']" :filter-options="['id', 'name']"
data-cy="UnDeductibleVatSelect" data-cy="invoiceInBasicDataDeductibleExpenseFk"
> >
<template #option="scope"> <template #option="scope">
<QItem v-bind="scope.itemProps"> <QItem v-bind="scope.itemProps">
@ -182,6 +197,7 @@ function deleteFile(dmsFk) {
padding="xs" padding="xs"
round round
@click="downloadFile(data.dmsFk)" @click="downloadFile(data.dmsFk)"
data-cy="invoiceInBasicDataDmsDownload"
/> />
<QBtn <QBtn
:class="{ :class="{
@ -197,6 +213,7 @@ function deleteFile(dmsFk) {
documentDialogRef.dms = data.dms; documentDialogRef.dms = data.dms;
} }
" "
data-cy="invoiceInBasicDataDmsEdit"
> >
<QTooltip>{{ t('Edit document') }}</QTooltip> <QTooltip>{{ t('Edit document') }}</QTooltip>
</QBtn> </QBtn>
@ -210,6 +227,7 @@ function deleteFile(dmsFk) {
padding="xs" padding="xs"
round round
@click="deleteFile(data.dmsFk)" @click="deleteFile(data.dmsFk)"
data-cy="invoiceInBasicDataDmsDelete"
/> />
</div> </div>
<QBtn <QBtn
@ -224,7 +242,7 @@ function deleteFile(dmsFk) {
delete documentDialogRef.dms; delete documentDialogRef.dms;
} }
" "
data-cy="dms-create" data-cy="invoiceInBasicDataDmsAdd"
> >
<QTooltip>{{ t('Create document') }}</QTooltip> <QTooltip>{{ t('Create document') }}</QTooltip>
</QBtn> </QBtn>
@ -237,9 +255,9 @@ function deleteFile(dmsFk) {
:label="t('Currency')" :label="t('Currency')"
v-model="data.currencyFk" v-model="data.currencyFk"
:options="currencies" :options="currencies"
option-value="id"
option-label="code" option-label="code"
sort-by="id" sort-by="id"
data-cy="invoiceInBasicDataCurrencyFk"
/> />
<VnSelect <VnSelect
@ -249,8 +267,8 @@ function deleteFile(dmsFk) {
:label="t('Company')" :label="t('Company')"
v-model="data.companyFk" v-model="data.companyFk"
:options="companies" :options="companies"
option-value="id"
option-label="code" option-label="code"
data-cy="invoiceInBasicDataCompanyFk"
/> />
</VnRow> </VnRow>
<VnRow> <VnRow>
@ -260,6 +278,7 @@ function deleteFile(dmsFk) {
:options="sageWithholdings" :options="sageWithholdings"
option-value="id" option-value="id"
option-label="withholding" option-label="withholding"
data-cy="invoiceInBasicDataWithholdingSageFk"
/> />
</VnRow> </VnRow>
</template> </template>

View File

@ -1,22 +1,16 @@
<script setup> <script setup>
import { ref, computed, capitalize } from 'vue'; import { ref, computed, capitalize } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useArrayData } from 'src/composables/useArrayData'; import { useArrayData } from 'src/composables/useArrayData';
import CrudModel from 'src/components/CrudModel.vue'; import CrudModel from 'src/components/CrudModel.vue';
import FetchData from 'src/components/FetchData.vue'; import FetchData from 'src/components/FetchData.vue';
import VnSelect from 'src/components/common/VnSelect.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
const route = useRoute();
const { t } = useI18n(); const { t } = useI18n();
const arrayData = useArrayData(); const arrayData = useArrayData();
const invoiceIn = computed(() => arrayData.store.data); const invoiceIn = computed(() => arrayData.store.data);
const invoiceInCorrectionRef = ref(); const invoiceInCorrectionRef = ref();
const filter = {
include: { relation: 'invoiceIn' },
where: { correctingFk: route.params.id },
};
const columns = computed(() => [ const columns = computed(() => [
{ {
name: 'origin', name: 'origin',
@ -92,7 +86,8 @@ const requiredFieldRule = (val) => val || t('globals.requiredField');
v-if="invoiceIn" v-if="invoiceIn"
data-key="InvoiceInCorrection" data-key="InvoiceInCorrection"
url="InvoiceInCorrections" url="InvoiceInCorrections"
:filter="filter" :user-filter="{ include: { relation: 'invoiceIn' } }"
:filter="{ where: { correctingFk: $route.params.id } }"
auto-load auto-load
primary-key="correctingFk" primary-key="correctingFk"
:default-remove="false" :default-remove="false"
@ -115,6 +110,7 @@ const requiredFieldRule = (val) => val || t('globals.requiredField');
:option-label="col.optionLabel" :option-label="col.optionLabel"
:disable="row.invoiceIn.isBooked" :disable="row.invoiceIn.isBooked"
:filter-options="['description']" :filter-options="['description']"
data-cy="invoiceInCorrective_type"
> >
<template #option="{ opt, itemProps }"> <template #option="{ opt, itemProps }">
<QItem v-bind="itemProps"> <QItem v-bind="itemProps">
@ -137,6 +133,7 @@ const requiredFieldRule = (val) => val || t('globals.requiredField');
:rules="[requiredFieldRule]" :rules="[requiredFieldRule]"
:filter-options="['code', 'description']" :filter-options="['code', 'description']"
:disable="row.invoiceIn.isBooked" :disable="row.invoiceIn.isBooked"
data-cy="invoiceInCorrective_class"
> >
<template #option="{ opt, itemProps }"> <template #option="{ opt, itemProps }">
<QItem v-bind="itemProps"> <QItem v-bind="itemProps">
@ -161,6 +158,7 @@ const requiredFieldRule = (val) => val || t('globals.requiredField');
:option-label="col.optionLabel" :option-label="col.optionLabel"
:rules="[requiredFieldRule]" :rules="[requiredFieldRule]"
:disable="row.invoiceIn.isBooked" :disable="row.invoiceIn.isBooked"
data-cy="invoiceInCorrective_reason"
/> />
</QTd> </QTd>
</template> </template>

View File

@ -17,10 +17,6 @@ const { t } = useI18n();
const cardDescriptorRef = ref(); const cardDescriptorRef = ref();
const entityId = computed(() => $props.id || +currentRoute.value.params.id); const entityId = computed(() => $props.id || +currentRoute.value.params.id);
const totalAmount = ref(); const totalAmount = ref();
const config = ref();
const cplusRectificationTypes = ref([]);
const siiTypeInvoiceIns = ref([]);
const invoiceCorrectionTypes = ref([]);
const invoiceInCorrection = reactive({ correcting: [], corrected: null }); const invoiceInCorrection = reactive({ correcting: [], corrected: null });
const routes = reactive({ const routes = reactive({
getSupplier: (id) => { getSupplier: (id) => {
@ -30,7 +26,7 @@ const routes = reactive({
return { return {
name: 'InvoiceInList', name: 'InvoiceInList',
query: { query: {
params: JSON.stringify({ supplierFk: id }), table: JSON.stringify({ supplierFk: id }),
}, },
}; };
}, },
@ -39,7 +35,7 @@ const routes = reactive({
return { return {
name: 'InvoiceInList', name: 'InvoiceInList',
query: { query: {
params: JSON.stringify({ correctedFk: entityId.value }), table: JSON.stringify({ correctedFk: entityId.value }),
}, },
}; };
} }
@ -108,7 +104,7 @@ async function setInvoiceCorrection(id) {
<VnLv :label="t('invoiceIn.list.amount')" :value="toCurrency(totalAmount)" /> <VnLv :label="t('invoiceIn.list.amount')" :value="toCurrency(totalAmount)" />
<VnLv :label="t('invoiceIn.list.supplier')"> <VnLv :label="t('invoiceIn.list.supplier')">
<template #value> <template #value>
<span class="link"> <span class="link" data-cy="invoiceInDescriptor_supplier">
{{ entity?.supplier?.nickname }} {{ entity?.supplier?.nickname }}
<SupplierDescriptorProxy :id="entity?.supplierFk" /> <SupplierDescriptorProxy :id="entity?.supplierFk" />
</span> </span>

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { ref, computed, toRefs, reactive } from 'vue'; import { ref, computed, toRefs, reactive, onBeforeMount } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
@ -111,10 +111,9 @@ async function cloneInvoice() {
} }
const isAgricultural = () => { const isAgricultural = () => {
if (!config.value) return false;
return ( return (
invoiceIn.value?.supplier?.sageFarmerWithholdingFk === invoiceIn.value?.supplier?.sageWithholdingFk ==
config?.value[0]?.sageWithholdingFk config.value?.sageFarmerWithholdingFk
); );
}; };
function showPdfInvoice() { function showPdfInvoice() {
@ -153,162 +152,183 @@ const createInvoiceInCorrection = async () => {
); );
push({ path: `/invoice-in/${correctingId}/summary` }); push({ path: `/invoice-in/${correctingId}/summary` });
}; };
onBeforeMount(async () => {
config.value = (
await axios.get('invoiceinConfigs/findOne', {
params: { fields: ['sageFarmerWithholdingFk'] },
})
).data;
});
</script> </script>
<template> <template>
<FetchData <template v-if="config">
url="InvoiceCorrectionTypes" <FetchData
@on-fetch="(data) => (invoiceCorrectionTypes = data)" url="InvoiceCorrectionTypes"
auto-load @on-fetch="(data) => (invoiceCorrectionTypes = data)"
/> auto-load
<FetchData />
url="CplusRectificationTypes" <FetchData
@on-fetch="(data) => (cplusRectificationTypes = data)" url="CplusRectificationTypes"
auto-load @on-fetch="(data) => (cplusRectificationTypes = data)"
/> auto-load
<FetchData />
url="SiiTypeInvoiceIns" <FetchData
:where="{ code: { like: 'R%' } }" url="SiiTypeInvoiceIns"
@on-fetch="(data) => (siiTypeInvoiceIns = data)" :where="{ code: { like: 'R%' } }"
auto-load @on-fetch="(data) => (siiTypeInvoiceIns = data)"
/> auto-load
<FetchData />
url="InvoiceInConfigs" <InvoiceInToBook>
:where="{ fields: ['sageWithholdingFk'] }" <template #content="{ book }">
auto-load <QItem
@on-fetch="(data) => (config = data)" v-if="!invoice?.isBooked && canEditProp('toBook')"
/> v-ripple
<InvoiceInToBook> clickable
<template #content="{ book }"> @click="book(entityId)"
<QItem >
v-if="!invoice?.isBooked && canEditProp('toBook')" <QItemSection>{{ t('invoiceIn.descriptorMenu.book') }}</QItemSection>
v-ripple </QItem>
clickable </template>
@click="book(entityId)" </InvoiceInToBook>
<QItem
v-if="invoice?.isBooked && canEditProp('toUnbook')"
v-ripple
clickable
@click="triggerMenu('unbook')"
>
<QItemSection>
{{ t('invoiceIn.descriptorMenu.unbook') }}
</QItemSection>
</QItem>
<QItem
v-if="canEditProp('deleteById')"
v-ripple
clickable
@click="triggerMenu('delete')"
>
<QItemSection>{{ t('invoiceIn.descriptorMenu.deleteInvoice') }}</QItemSection>
</QItem>
<QItem
v-if="canEditProp('clone')"
v-ripple
clickable
@click="triggerMenu('clone')"
>
<QItemSection>{{ t('invoiceIn.descriptorMenu.cloneInvoice') }}</QItemSection>
</QItem>
<QItem v-if="isAgricultural()" v-ripple clickable @click="triggerMenu('showPdf')">
<QItemSection>{{
t('invoiceIn.descriptorMenu.showAgriculturalPdf')
}}</QItemSection>
</QItem>
<QItem v-if="isAgricultural()" v-ripple clickable @click="triggerMenu('sendPdf')">
<QItemSection
>{{ t('invoiceIn.descriptorMenu.sendAgriculturalPdf') }}...</QItemSection
> >
<QItemSection>{{ t('invoiceIn.descriptorMenu.book') }}</QItemSection> </QItem>
</QItem> <QItem
</template> v-if="!invoiceInCorrection.corrected"
</InvoiceInToBook> v-ripple
<QItem clickable
v-if="invoice?.isBooked && canEditProp('toUnbook')" @click="triggerMenu('correct')"
v-ripple data-cy="createCorrectiveItem"
clickable
@click="triggerMenu('unbook')"
>
<QItemSection>
{{ t('invoiceIn.descriptorMenu.unbook') }}
</QItemSection>
</QItem>
<QItem
v-if="canEditProp('deleteById')"
v-ripple
clickable
@click="triggerMenu('delete')"
>
<QItemSection>{{ t('invoiceIn.descriptorMenu.deleteInvoice') }}</QItemSection>
</QItem>
<QItem v-if="canEditProp('clone')" v-ripple clickable @click="triggerMenu('clone')">
<QItemSection>{{ t('invoiceIn.descriptorMenu.cloneInvoice') }}</QItemSection>
</QItem>
<QItem v-if="isAgricultural()" v-ripple clickable @click="triggerMenu('showPdf')">
<QItemSection>{{
t('invoiceIn.descriptorMenu.showAgriculturalPdf')
}}</QItemSection>
</QItem>
<QItem v-if="isAgricultural()" v-ripple clickable @click="triggerMenu('sendPdf')">
<QItemSection
>{{ t('invoiceIn.descriptorMenu.sendAgriculturalPdf') }}...</QItemSection
> >
</QItem> <QItemSection
<QItem >{{ t('invoiceIn.descriptorMenu.createCorrective') }}...</QItemSection
v-if="!invoiceInCorrection.corrected" >
v-ripple </QItem>
clickable <QItem
@click="triggerMenu('correct')" v-if="invoice.dmsFk"
data-cy="createCorrectiveItem" v-ripple
> clickable
<QItemSection @click="downloadFile(invoice.dmsFk)"
>{{ t('invoiceIn.descriptorMenu.createCorrective') }}...</QItemSection
> >
</QItem> <QItemSection>{{ t('components.smartCard.downloadFile') }}</QItemSection>
<QItem v-if="invoice.dmsFk" v-ripple clickable @click="downloadFile(invoice.dmsFk)"> </QItem>
<QItemSection>{{ t('components.smartCard.downloadFile') }}</QItemSection> <QDialog ref="correctionDialogRef">
</QItem> <QCard data-cy="correctiveInvoiceDialog">
<QDialog ref="correctionDialogRef"> <QCardSection>
<QCard> <QItem class="q-px-none">
<QCardSection> <span class="text-primary text-h6 full-width">
<QItem class="q-px-none"> {{ t('Create rectificative invoice') }}
<span class="text-primary text-h6 full-width"> </span>
{{ t('Create rectificative invoice') }} <QBtn icon="close" flat round dense v-close-popup />
</span> </QItem>
<QBtn icon="close" flat round dense v-close-popup /> </QCardSection>
</QItem> <QCardSection>
</QCardSection> <QItem>
<QCardSection> <QItemSection>
<QItem> <QInput
<QItemSection> :label="t('Original invoice')"
<QInput v-model="entityId"
:label="t('Original invoice')" readonly
v-model="entityId" />
readonly <VnSelect
/> :label="`${useCapitalize(t('globals.class'))}`"
<VnSelect v-model="correctionFormData.invoiceClass"
:label="`${useCapitalize(t('globals.class'))}`" :options="siiTypeInvoiceIns"
v-model="correctionFormData.invoiceClass" option-value="id"
:options="siiTypeInvoiceIns" option-label="code"
option-value="id" :required="true"
option-label="code" data-cy="invoiceInDescriptorMenu_class"
:required="true" />
/> </QItemSection>
</QItemSection> <QItemSection>
<QItemSection> <VnSelect
<VnSelect :label="`${useCapitalize(t('globals.type'))}`"
:label="`${useCapitalize(t('globals.type'))}`" v-model="correctionFormData.invoiceType"
v-model="correctionFormData.invoiceType" :options="cplusRectificationTypes"
:options="cplusRectificationTypes" option-value="id"
option-value="id" option-label="description"
option-label="description" :required="true"
:required="true" data-cy="invoiceInDescriptorMenu_type"
> >
<template #option="{ itemProps, opt }"> <template #option="{ itemProps, opt }">
<QItem v-bind="itemProps"> <QItem v-bind="itemProps">
<QItemSection> <QItemSection>
<QItemLabel <QItemLabel
>{{ opt.id }} - >{{ opt.id }} -
{{ opt.description }}</QItemLabel {{ opt.description }}</QItemLabel
> >
</QItemSection> </QItemSection>
</QItem> </QItem>
<div></div> <div></div>
</template> </template>
</VnSelect> </VnSelect>
<VnSelect <VnSelect
:label="`${useCapitalize(t('globals.reason'))}`" :label="`${useCapitalize(t('globals.reason'))}`"
v-model="correctionFormData.invoiceReason" v-model="correctionFormData.invoiceReason"
:options="invoiceCorrectionTypes" :options="invoiceCorrectionTypes"
option-value="id" option-value="id"
option-label="description" option-label="description"
:required="true" :required="true"
/> data-cy="invoiceInDescriptorMenu_reason"
</QItemSection> />
</QItem> </QItemSection>
</QCardSection> </QItem>
<QCardActions class="justify-end q-mr-sm"> </QCardSection>
<QBtn flat :label="t('globals.close')" color="primary" v-close-popup /> <QCardActions class="justify-end q-mr-sm">
<QBtn <QBtn
:label="t('globals.save')" flat
color="primary" :label="t('globals.close')"
v-close-popup color="primary"
@click="createInvoiceInCorrection" v-close-popup
:disable="isNotFilled" />
/> <QBtn
</QCardActions> :label="t('globals.save')"
</QCard> color="primary"
</QDialog> v-close-popup
@click="createInvoiceInCorrection"
:disable="isNotFilled"
data-cy="saveCorrectiveInvoice"
/>
</QCardActions>
</QCard>
</QDialog>
</template>
</template> </template>
<i18n> <i18n>
en: en:
isNotLinked: The entry {bookEntry} has been deleted with {accountingEntries} entries isNotLinked: The entry {bookEntry} has been deleted with {accountingEntries} entries

View File

@ -198,6 +198,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
color="orange-11" color="orange-11"
text-color="black" text-color="black"
@click="book(entityId)" @click="book(entityId)"
data-cy="invoiceInSummary_book"
/> />
</template> </template>
</InvoiceIntoBook> </InvoiceIntoBook>
@ -218,7 +219,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
:value="entity.supplier?.name" :value="entity.supplier?.name"
> >
<template #value> <template #value>
<span class="link"> <span class="link" data-cy="invoiceInSummary_supplier">
{{ entity.supplier?.name }} {{ entity.supplier?.name }}
<SupplierDescriptorProxy :id="entity.supplierFk" /> <SupplierDescriptorProxy :id="entity.supplierFk" />
</span> </span>

View File

@ -202,6 +202,9 @@ function setCursor(ref) {
:option-label="col.optionLabel" :option-label="col.optionLabel"
:filter-options="['id', 'name']" :filter-options="['id', 'name']"
:tooltip="t('Create a new expense')" :tooltip="t('Create a new expense')"
:acls="[
{ model: 'Expense', props: '*', accessType: 'WRITE' },
]"
@keydown.tab.prevent=" @keydown.tab.prevent="
autocompleteExpense( autocompleteExpense(
$event, $event,

View File

@ -7,6 +7,7 @@ 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'; import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
import VnCheckbox from 'src/components/common/VnCheckbox.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';
@ -147,13 +148,13 @@ function handleDaysAgo(params, daysAgo) {
</QItem> </QItem>
<QItem> <QItem>
<QItemSection> <QItemSection>
<QCheckbox <VnCheckbox
:label="$t('invoiceIn.isBooked')" :label="$t('invoiceIn.isBooked')"
v-model="params.isBooked" v-model="params.isBooked"
@update:model-value="searchFn()" @update:model-value="searchFn()"
toggle-indeterminate toggle-indeterminate
/> />
<QCheckbox <VnCheckbox
:label="getLocale('params.correctingFk')" :label="getLocale('params.correctingFk')"
v-model="params.correctingFk" v-model="params.correctingFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"

View File

@ -4,7 +4,7 @@ import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import VnConfirm from 'src/components/ui/VnConfirm.vue'; import VnConfirm from 'src/components/ui/VnConfirm.vue';
import { useArrayData } from 'src/composables/useArrayData'; import { useArrayData } from 'src/composables/useArrayData';
import qs from 'qs';
const { notify, dialog } = useQuasar(); const { notify, dialog } = useQuasar();
const { t } = useI18n(); const { t } = useI18n();
@ -61,17 +61,15 @@ async function checkToBook(id) {
} }
async function toBook(id) { async function toBook(id) {
let type = 'positive'; let err = false;
let message = t('globals.dataSaved');
try { try {
await axios.post(`InvoiceIns/${id}/toBook`); await axios.post(`InvoiceIns/${id}/toBook`);
store.data.isBooked = true; store.data.isBooked = true;
} catch (e) { } catch (e) {
type = 'negative'; err = true;
message = t('It was not able to book the invoice'); throw e;
} finally { } finally {
notify({ type, message }); if (!err) notify({ type: 'positive', message: t('globals.dataSaved') });
} }
} }
</script> </script>

View File

@ -46,6 +46,11 @@ function ticketFilter(invoice) {
<InvoiceOutDescriptorMenu :invoice-out-data="entity" :menu-ref="menuRef" /> <InvoiceOutDescriptorMenu :invoice-out-data="entity" :menu-ref="menuRef" />
</template> </template>
<template #body="{ entity }"> <template #body="{ entity }">
<VnLv
v-if="entity.externalRef"
:label="t('invoiceOut.externalRef')"
:value="entity.externalRef"
/>
<VnLv :label="t('invoiceOut.card.issued')" :value="toDate(entity.issued)" /> <VnLv :label="t('invoiceOut.card.issued')" :value="toDate(entity.issued)" />
<VnLv :label="t('globals.amount')" :value="toCurrency(entity.amount)" /> <VnLv :label="t('globals.amount')" :value="toCurrency(entity.amount)" />
<VnLv v-if="entity.client" :label="t('globals.client')"> <VnLv v-if="entity.client" :label="t('globals.client')">

View File

@ -7,6 +7,7 @@ import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnInputDate from 'components/common/VnInputDate.vue'; import VnInputDate from 'components/common/VnInputDate.vue';
import VnInputNumber from 'src/components/common/VnInputNumber.vue'; import VnInputNumber from 'src/components/common/VnInputNumber.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
const { t } = useI18n(); const { t } = useI18n();
const props = defineProps({ const props = defineProps({
@ -30,7 +31,7 @@ const states = ref();
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnInput <VnInput
:label="t('Customer ID')" :label="t('globals.params.clientFk')"
v-model="params.clientFk" v-model="params.clientFk"
is-outlined is-outlined
/> />
@ -38,13 +39,17 @@ const states = ref();
</QItem> </QItem>
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnInput v-model="params.fi" :label="t('FI')" is-outlined /> <VnInput
v-model="params.fi"
:label="t('globals.params.fi')"
is-outlined
/>
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnInputNumber <VnInputNumber
:label="t('Amount')" :label="t('globals.amount')"
v-model="params.amount" v-model="params.amount"
is-outlined is-outlined
data-cy="InvoiceOutFilterAmountBtn" data-cy="InvoiceOutFilterAmountBtn"
@ -54,7 +59,7 @@ const states = ref();
<QItem> <QItem>
<QItemSection> <QItemSection>
<QInput <QInput
:label="t('Min')" :label="t('invoiceOut.params.min')"
dense dense
lazy-rules lazy-rules
outlined outlined
@ -65,7 +70,7 @@ const states = ref();
</QItemSection> </QItemSection>
<QItemSection> <QItemSection>
<QInput <QInput
:label="t('Max')" :label="t('invoiceOut.params.max')"
dense dense
lazy-rules lazy-rules
outlined outlined
@ -78,7 +83,7 @@ const states = ref();
<QItem> <QItem>
<QItemSection> <QItemSection>
<QCheckbox <QCheckbox
:label="t('Has PDF')" :label="t('invoiceOut.params.hasPdf')"
toggle-indeterminate toggle-indeterminate
v-model="params.hasPdf" v-model="params.hasPdf"
/> />
@ -88,14 +93,31 @@ const states = ref();
<QItemSection> <QItemSection>
<VnInputDate <VnInputDate
v-model="params.created" v-model="params.created"
:label="t('Created')" :label="t('invoiceOut.params.created')"
is-outlined is-outlined
/> />
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnInputDate v-model="params.dued" :label="t('Dued')" is-outlined /> <VnInputDate
v-model="params.dued"
:label="t('invoiceOut.params.dued')"
is-outlined
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnSelect
outlined
rounded
:label="t('globals.params.departmentFk')"
v-model="params.departmentFk"
option-value="id"
option-label="name"
url="Departments"
/>
</QItemSection> </QItemSection>
</QItem> </QItem>
</template> </template>

View File

@ -8,7 +8,7 @@ import { useSummaryDialog } from 'src/composables/useSummaryDialog';
import { usePrintService } from 'src/composables/usePrintService'; import { usePrintService } from 'src/composables/usePrintService';
import VnTable from 'src/components/VnTable/VnTable.vue'; import VnTable from 'src/components/VnTable/VnTable.vue';
import InvoiceOutSummary from './Card/InvoiceOutSummary.vue'; import InvoiceOutSummary from './Card/InvoiceOutSummary.vue';
import { toCurrency, toDate } from 'src/filters/index'; import { toCurrency, toDate, dashIfEmpty } from 'src/filters/index';
import { QBtn } from 'quasar'; import { QBtn } from 'quasar';
import axios from 'axios'; import axios from 'axios';
import InvoiceOutFilter from './InvoiceOutFilter.vue'; import InvoiceOutFilter from './InvoiceOutFilter.vue';
@ -16,6 +16,7 @@ import VnRow from 'src/components/ui/VnRow.vue';
import VnRadio from 'src/components/common/VnRadio.vue'; import VnRadio from 'src/components/common/VnRadio.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vue'; import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vue';
import DepartmentDescriptorProxy from '../Worker/Department/Card/DepartmentDescriptorProxy.vue';
import VnSection from 'src/components/common/VnSection.vue'; import VnSection from 'src/components/common/VnSection.vue';
const { t } = useI18n(); const { t } = useI18n();
@ -54,6 +55,14 @@ const columns = computed(() => [
name: 'id', name: 'id',
}, },
}, },
{
align: 'left',
name: 'issued',
label: t('invoiceOut.summary.issued'),
component: 'date',
format: (row) => toDate(row.issued),
columnField: { component: null },
},
{ {
align: 'left', align: 'left',
name: 'ref', name: 'ref',
@ -86,6 +95,20 @@ const columns = computed(() => [
component: null, component: null,
}, },
}, },
{
align: 'left',
name: 'departmentFk',
label: t('customer.summary.team'),
cardVisible: true,
component: 'select',
attrs: {
url: 'Departments',
},
columnField: {
component: null,
},
format: (row, dashIfEmpty) => dashIfEmpty(row.departmentName),
},
{ {
align: 'left', align: 'left',
name: 'companyFk', name: 'companyFk',
@ -229,6 +252,12 @@ watchEffect(selectedRows);
<CustomerDescriptorProxy :id="row.clientFk" /> <CustomerDescriptorProxy :id="row.clientFk" />
</span> </span>
</template> </template>
<template #column-departmentFk="{ row }">
<span class="link" @click.stop>
{{ dashIfEmpty(row.departmentName) }}
<DepartmentDescriptorProxy :id="row?.departmentFk" />
</span>
</template>
<template #more-create-dialog="{ data }"> <template #more-create-dialog="{ data }">
<div class="row q-col-gutter-xs col-span-2"> <div class="row q-col-gutter-xs col-span-2">
<div class="col-12"> <div class="col-12">

View File

@ -8,7 +8,7 @@ import { useInvoiceOutGlobalStore } from 'src/stores/invoiceOutGlobal.js';
import { useArrayData } from 'src/composables/useArrayData'; import { useArrayData } from 'src/composables/useArrayData';
import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vue'; import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vue';
import TicketDescriptorProxy from '../Ticket/Card/TicketDescriptorProxy.vue'; import TicketDescriptorProxy from '../Ticket/Card/TicketDescriptorProxy.vue';
import WorkerDescriptorProxy from '../Worker/Card/WorkerDescriptorProxy.vue'; import DepartmentDescriptorProxy from '../Worker/Department/Card/DepartmentDescriptorProxy.vue';
import VnInputDate from 'components/common/VnInputDate.vue'; import VnInputDate from 'components/common/VnInputDate.vue';
import InvoiceOutNegativeBasesFilter from './InvoiceOutNegativeBasesFilter.vue'; import InvoiceOutNegativeBasesFilter from './InvoiceOutNegativeBasesFilter.vue';
import RightMenu from 'src/components/common/RightMenu.vue'; import RightMenu from 'src/components/common/RightMenu.vue';
@ -115,18 +115,16 @@ const columns = computed(() => [
}, },
{ {
align: 'left', align: 'left',
label: t('customer.extendedList.tableVisibleColumns.salesPersonFk'), name: 'departmentFk',
name: 'workerName', label: t('customer.summary.team'),
component: 'select', component: 'select',
attrs: { attrs: {
url: 'Workers/activeWithInheritedRole', url: 'Departments',
fields: ['id', 'name'],
where: { role: 'salesPerson' },
}, },
columnField: { columnField: {
component: null, component: null,
}, },
format: (row, dashIfEmpty) => dashIfEmpty(row.workerName), format: (row, dashIfEmpty) => dashIfEmpty(row.departmentName),
}, },
]); ]);
@ -198,10 +196,10 @@ const downloadCSV = async () => {
<TicketDescriptorProxy :id="row.ticketFk" /> <TicketDescriptorProxy :id="row.ticketFk" />
</span> </span>
</template> </template>
<template #column-workerName="{ row }"> <template #column-departmentFk="{ row }">
<span class="link" @click.stop> <span class="link" @click.stop>
{{ row.workerName }} {{ row.departmentName }}
<WorkerDescriptorProxy :id="row.comercialId" /> <DepartmentDescriptorProxy :id="row.departmentFk" />
</span> </span>
</template> </template>
<template #moreFilterPanel="{ params }"> <template #moreFilterPanel="{ params }">

View File

@ -129,12 +129,15 @@ const props = defineProps({
</QItem> </QItem>
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnSelectWorker <VnSelect
:label="t('invoiceOut.negativeBases.comercial')" outlined
v-model="params.workerName" dense
option-value="name" rounded
is-outlined :label="t('globals.params.departmentFk')"
@update:model-value="searchFn()" v-model="params.departmentFk"
option-value="id"
option-label="name"
url="Departments"
/> />
</QItemSection> </QItemSection>
</QItem> </QItem>

View File

@ -1,6 +1,7 @@
invoiceOut: invoiceOut:
search: Search invoice search: Search invoice
searchInfo: You can search by invoice reference searchInfo: You can search by invoice reference
externalRef: External Ref.
params: params:
id: ID id: ID
company: Company company: Company
@ -12,7 +13,6 @@ invoiceOut:
isActive: Active isActive: Active
hasToInvoice: Has to invoice hasToInvoice: Has to invoice
hasVerifiedData: Verified data hasVerifiedData: Verified data
workerName: Worker
isTaxDataChecked: Verified data isTaxDataChecked: Verified data
amount: Amount amount: Amount
clientFk: Client clientFk: Client
@ -26,6 +26,7 @@ invoiceOut:
max: Max max: Max
hasPdf: Has PDF hasPdf: Has PDF
search: Contains search: Contains
departmentFk: Department
card: card:
issued: Issued issued: Issued
customerCard: Customer card customerCard: Customer card

View File

@ -1,6 +1,7 @@
invoiceOut: invoiceOut:
search: Buscar factura emitida search: Buscar factura emitida
searchInfo: Puedes buscar por referencia de la factura searchInfo: Puedes buscar por referencia de la factura
externalRef: Ref. externa
params: params:
id: ID id: ID
company: Empresa company: Empresa
@ -12,7 +13,6 @@ invoiceOut:
isActive: Activo isActive: Activo
hasToInvoice: Debe facturar hasToInvoice: Debe facturar
hasVerifiedData: Datos verificados hasVerifiedData: Datos verificados
workerName: Comercial
isTaxDataChecked: Datos comprobados isTaxDataChecked: Datos comprobados
amount: Importe amount: Importe
clientFk: Cliente clientFk: Cliente
@ -26,6 +26,7 @@ invoiceOut:
max: Max max: Max
hasPdf: Tiene PDF hasPdf: Tiene PDF
search: Contiene search: Contiene
departmentFk: Departamento
card: card:
issued: Fecha emisión issued: Fecha emisión
customerCard: Ficha del cliente customerCard: Ficha del cliente

View File

@ -3,6 +3,7 @@ import { ref, computed, onMounted } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue'; import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import { toCurrency } from 'filters/index'; import { toCurrency } from 'filters/index';
import useNotify from 'src/composables/useNotify.js'; import useNotify from 'src/composables/useNotify.js';
@ -61,6 +62,7 @@ const columns = computed(() => [
columnClass: 'expand', columnClass: 'expand',
}, },
{ {
align: 'left',
label: t('item.buyRequest.requester'), label: t('item.buyRequest.requester'),
name: 'requesterName', name: 'requesterName',
component: 'select', component: 'select',
@ -77,6 +79,19 @@ const columns = computed(() => [
}, },
columnClass: 'shrink', columnClass: 'shrink',
}, },
{
align: 'left',
name: 'departmentFk',
label: t('customer.summary.team'),
component: 'select',
attrs: {
url: 'Departments',
},
columnField: {
component: null,
},
format: (row, dashIfEmpty) => dashIfEmpty(row.departmentName),
},
{ {
label: t('item.buyRequest.requested'), label: t('item.buyRequest.requested'),
name: 'quantity', name: 'quantity',
@ -107,6 +122,7 @@ const columns = computed(() => [
}, },
columnClass: 'shrink', columnClass: 'shrink',
}, },
{ {
label: t('globals.item'), label: t('globals.item'),
name: 'item', name: 'item',
@ -262,6 +278,12 @@ const onDenyAccept = (_, responseData) => {
<WorkerDescriptorProxy :id="row.requesterFk" /> <WorkerDescriptorProxy :id="row.requesterFk" />
</span> </span>
</template> </template>
<template #column-departmentFk="{ row }">
<span class="link" @click.stop>
{{ row.departmentName }}
<DepartmentDescriptorProxy :id="row.departmentFk" />
</span>
</template>
<template #column-item="{ row }"> <template #column-item="{ row }">
<span> <span>

View File

@ -221,7 +221,7 @@ en:
attenderFk: Atender attenderFk: Atender
clientFk: Client id clientFk: Client id
warehouseFk: Warehouse warehouseFk: Warehouse
requesterFk: Salesperson requesterFk: Requester
from: From from: From
to: To to: To
mine: For me mine: For me
@ -239,7 +239,7 @@ es:
attenderFk: Comprador attenderFk: Comprador
clientFk: Id cliente clientFk: Id cliente
warehouseFk: Almacén warehouseFk: Almacén
requesterFk: Comercial requesterFk: Solicitante
from: Desde from: Desde
to: Hasta to: Hasta
mine: Para mi mine: Para mi

View File

@ -30,6 +30,7 @@ const entityId = computed(() => {
:filter="filter" :filter="filter"
title="code" title="code"
data-key="ItemType" data-key="ItemType"
:to-module="{ name: 'ItemTypeList' }"
> >
<template #body="{ entity }"> <template #body="{ entity }">
<VnLv :label="$t('itemType.shared.code')" :value="entity.code" /> <VnLv :label="$t('itemType.shared.code')" :value="entity.code" />

View File

@ -7,6 +7,7 @@ import filter from './ItemTypeFilter.js';
import CardSummary from 'components/ui/CardSummary.vue'; import CardSummary from 'components/ui/CardSummary.vue';
import VnLv from 'src/components/ui/VnLv.vue'; import VnLv from 'src/components/ui/VnLv.vue';
import VnToSummary from 'src/components/ui/VnToSummary.vue'; import VnToSummary from 'src/components/ui/VnToSummary.vue';
import VnTitle from 'src/components/common/VnTitle.vue';
onUpdated(() => summaryRef.value.fetch()); onUpdated(() => summaryRef.value.fetch());
@ -62,13 +63,10 @@ async function setItemTypeData(data) {
</template> </template>
<template #body> <template #body>
<QCard class="vn-one"> <QCard class="vn-one">
<router-link <VnTitle
:to="{ name: 'ItemTypeBasicData', params: { id: entityId } }" :url="`#/item/item-type/${entityId}/basic-data`"
class="header header-link" :text="$t('globals.summary.basicData')"
> />
{{ t('globals.summary.basicData') }}
<QIcon name="open_in_new" />
</router-link>
<VnLv :label="t('itemType.summary.id')" :value="itemType.id" /> <VnLv :label="t('itemType.summary.id')" :value="itemType.id" />
<VnLv :label="t('itemType.shared.code')" :value="itemType.code" /> <VnLv :label="t('itemType.shared.code')" :value="itemType.code" />
<VnLv :label="t('itemType.shared.name')" :value="itemType.name" /> <VnLv :label="t('itemType.shared.name')" :value="itemType.name" />

View File

@ -84,7 +84,7 @@ item:
attenderFk: Atender attenderFk: Atender
clientFk: Client id clientFk: Client id
warehouseFk: Warehouse warehouseFk: Warehouse
requesterFk: Salesperson requesterFk: Requester
from: From from: From
to: To to: To
mine: For me mine: For me

View File

@ -93,7 +93,7 @@ item:
attenderFk: Comprador attenderFk: Comprador
clientFk: Id cliente clientFk: Id cliente
warehouseFk: Almacén warehouseFk: Almacén
requesterFk: Comercial requesterFk: Solicitante
from: Desde from: Desde
to: Hasta to: Hasta
mine: Para mi mine: Para mi

View File

@ -31,7 +31,7 @@ function exprBuilder(param, value) {
switch (param) { switch (param) {
case 'clientFk': case 'clientFk':
return { [`c.id`]: value }; return { [`c.id`]: value };
case 'salesPersonFk': case 'departmentFk':
return { [`c.${param}`]: value }; return { [`c.${param}`]: value };
} }
} }
@ -62,25 +62,17 @@ const columns = computed(() => [
columnFilter: false, columnFilter: false,
}, },
{ {
label: t('salesClientsTable.salesPerson'),
name: 'salesPersonFk',
field: 'salesPerson',
align: 'left', align: 'left',
name: 'departmentFk',
label: t('customer.summary.team'),
component: 'select',
attrs: {
url: 'Departments',
},
columnField: { columnField: {
component: null, component: null,
}, },
optionFilter: 'firstName', format: (row, dashIfEmpty) => dashIfEmpty(row.departmentName),
columnFilter: {
component: 'select',
attrs: {
url: 'Workers/activeWithInheritedRole',
fields: ['id', 'name'],
sortBy: 'nickname ASC',
where: { role: 'salesPerson' },
useLike: false,
},
},
columnClass: 'no-padding',
}, },
{ {
label: t('salesClientsTable.client'), label: t('salesClientsTable.client'),
@ -128,9 +120,9 @@ const columns = computed(() => [
<VnInputDate v-model="to" :label="$t('globals.to')" dense /> <VnInputDate v-model="to" :label="$t('globals.to')" dense />
</VnRow> </VnRow>
</template> </template>
<template #column-salesPersonFk="{ row }"> <template #column-departmentFk="{ row }">
<span class="link" :title="row.salesPerson" v-text="row.salesPerson" /> <span class="link" :title="row.department" v-text="row.department" />
<WorkerDescriptorProxy :id="row.salesPersonFk" dense /> <WorkerDescriptorProxy :id="row.departmentFk" dense />
</template> </template>
<template #column-clientFk="{ row }"> <template #column-clientFk="{ row }">
<span class="link" :title="row.clientName" v-text="row.clientName" /> <span class="link" :title="row.clientName" v-text="row.clientName" />

View File

@ -1,9 +1,9 @@
<script setup> <script setup>
import { ref, computed } from 'vue'; import { ref, computed } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue'; import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
import VnTable from 'components/VnTable/VnTable.vue'; import VnTable from 'components/VnTable/VnTable.vue';
import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue';
import { toDateFormat, toDateTimeFormat } from 'src/filters/date.js'; import { toDateFormat, toDateTimeFormat } from 'src/filters/date.js';
import { toCurrency } from 'src/filters'; import { toCurrency } from 'src/filters';
@ -20,8 +20,8 @@ function exprBuilder(param, value) {
switch (param) { switch (param) {
case 'clientFk': case 'clientFk':
return { [`c.id`]: value }; return { [`c.id`]: value };
case 'salesPersonFk': case 'departmentFk':
return { [`c.salesPersonFk`]: value }; return { [`c.departmentFk`]: value };
} }
} }
@ -63,20 +63,18 @@ const columns = computed(() => [
columnFilter: false, columnFilter: false,
}, },
{ {
label: t('salesClientsTable.salesPerson'),
name: 'salesPersonFk',
align: 'left', align: 'left',
optionFilter: 'firstName', name: 'departmentFk',
columnFilter: { label: t('customer.summary.team'),
component: 'select', component: 'select',
attrs: { attrs: {
url: 'Workers/activeWithInheritedRole', url: 'Departments',
fields: ['id', 'name'],
sortBy: 'nickname ASC',
where: { role: 'salesPerson' },
useLike: false,
},
}, },
create: true,
columnField: {
component: null,
},
format: (row, dashIfEmpty) => dashIfEmpty(row.departmentName),
}, },
{ {
label: t('salesOrdersTable.import'), label: t('salesOrdersTable.import'),
@ -184,11 +182,10 @@ const openTab = (id) =>
<CustomerDescriptorProxy :id="row.clientFk" /> <CustomerDescriptorProxy :id="row.clientFk" />
</QTd> </QTd>
</template> </template>
<template #column-departmentFk="{ row }">
<template #column-salesPersonFk="{ row }">
<QTd @click.stop> <QTd @click.stop>
<span class="link" v-text="row.salesPerson" /> <span class="link" v-text="row.departmentName" />
<WorkerDescriptorProxy :id="row.salesPersonFk" dense /> <DepartmentDescriptorProxy :id="row.departmentFk" dense />
</QTd> </QTd>
</template> </template>
</VnTable> </VnTable>

View File

@ -9,7 +9,6 @@ import VnInput from 'src/components/common/VnInput.vue';
import VnInputNumber from 'src/components/common/VnInputNumber.vue'; import VnInputNumber from 'src/components/common/VnInputNumber.vue';
import FetchData from 'src/components/FetchData.vue'; import FetchData from 'src/components/FetchData.vue';
import { dateRange } from 'src/filters'; import { dateRange } from 'src/filters';
import VnSelectWorker from 'src/components/common/VnSelectWorker.vue';
defineProps({ dataKey: { type: String, required: true } }); defineProps({ dataKey: { type: String, required: true } });
const { t, te } = useI18n(); const { t, te } = useI18n();
@ -113,16 +112,16 @@ const getLocale = (label) => {
</QItem> </QItem>
<QItem> <QItem>
<QItemSection> <QItemSection>
<VnSelectWorker <VnSelect
outlined outlined
dense dense
rounded rounded
:label="t('globals.params.salesPersonFk')" :label="t('globals.params.departmentFk')"
v-model="params.salesPersonFk" v-model="params.departmentFk"
:params="{ departmentCodes: ['VT'] }" option-value="id"
:no-one="true" option-label="name"
> url="Departments"
</VnSelectWorker> />
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem> <QItem>

View File

@ -2,7 +2,7 @@
import { ref, computed, onMounted } from 'vue'; import { ref, computed, onMounted } 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 WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue';
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue'; import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue'; import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
import InvoiceOutDescriptorProxy from 'src/pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue'; import InvoiceOutDescriptorProxy from 'src/pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue';
@ -49,8 +49,8 @@ function exprBuilder(param, value) {
switch (param) { switch (param) {
case 'stateFk': case 'stateFk':
return { 'ts.stateFk': value }; return { 'ts.stateFk': value };
case 'salesPersonFk': case 'departmentFk':
return { 'c.salesPersonFk': !value ? null : value }; return { 'c.departmentFk': !value ? null : value };
case 'provinceFk': case 'provinceFk':
return { 'a.provinceFk': value }; return { 'a.provinceFk': value };
case 'theoreticalHour': case 'theoreticalHour':
@ -108,19 +108,18 @@ const columns = computed(() => [
}, },
}, },
{ {
label: t('salesClientsTable.salesPerson'),
name: 'salesPersonFk',
field: 'userName',
align: 'left', align: 'left',
columnFilter: { name: 'departmentFk',
component: 'select', label: t('customer.summary.team'),
attrs: { component: 'select',
url: 'Workers/search?departmentCodes=["VT"]', attrs: {
fields: ['id', 'name', 'nickname', 'code'], url: 'Departments',
sortBy: 'nickname ASC',
optionLabel: 'nickname',
},
}, },
create: true,
columnField: {
component: null,
},
format: (row, dashIfEmpty) => dashIfEmpty(row.departmentName),
}, },
{ {
label: t('salesClientsTable.date'), label: t('salesClientsTable.date'),
@ -437,10 +436,10 @@ const openTab = (id) => useOpenURL(`#/ticket/${id}/sale`);
<CustomerDescriptorProxy :id="row.clientFk" /> <CustomerDescriptorProxy :id="row.clientFk" />
</div> </div>
</template> </template>
<template #column-salesPersonFk="{ row }"> <template #column-departmentFk="{ row }">
<div @click.stop :title="row.userName"> <div @click.stop :title="row.departmentName">
<span class="link" v-text="dashIfEmpty(row.userName)" /> <span class="link" v-text="dashIfEmpty(row.departmentName)" />
<WorkerDescriptorProxy :id="row.salesPersonFk" /> <DepartmentDescriptorProxy :id="row.departmentFk" />
</div> </div>
</template> </template>
<template #column-shippedDate="{ row }"> <template #column-shippedDate="{ row }">

View File

@ -7,7 +7,6 @@ salesClientsTable:
to: To to: To
date: Date date: Date
hour: Hour hour: Hour
salesPerson: Salesperson
client: Client client: Client
salesOrdersTable: salesOrdersTable:
delete: Delete delete: Delete

View File

@ -7,7 +7,6 @@ salesClientsTable:
to: Hasta to: Hasta
date: Fecha date: Fecha
hour: Hora hour: Hora
salesPerson: Comercial
client: Cliente client: Cliente
salesOrdersTable: salesOrdersTable:
delete: Eliminar delete: Eliminar

View File

@ -64,17 +64,7 @@ const orderFilter = {
{ {
relation: 'client', relation: 'client',
scope: { scope: {
fields: [ fields: ['name', 'isActive', 'isFreezed', 'isTaxDataChecked'],
'salesPersonFk',
'name',
'isActive',
'isFreezed',
'isTaxDataChecked',
],
include: {
relation: 'salesPersonUser',
scope: { fields: ['id', 'name'] },
},
}, },
}, },
], ],
@ -167,7 +157,7 @@ const onClientChange = async (clientId) => {
!data.isConfirmed && !data.isConfirmed &&
agencyList?.length && agencyList?.length &&
agencyList.some( agencyList.some(
(agency) => agency.agencyModeFk === data.agency_id (agency) => agency.agencyModeFk === data.agency_id,
) )
? data.agencyModeFk ? data.agencyModeFk
: null : null

View File

@ -8,7 +8,7 @@ import filter from './OrderFilter.js';
import CardDescriptor from 'components/ui/CardDescriptor.vue'; import CardDescriptor from 'components/ui/CardDescriptor.vue';
import VnLv from 'src/components/ui/VnLv.vue'; import VnLv from 'src/components/ui/VnLv.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue';
const DEFAULT_ITEMS = 0; const DEFAULT_ITEMS = 0;
@ -66,11 +66,11 @@ const total = ref(0);
:label="t('globals.state')" :label="t('globals.state')"
:value="getConfirmationValue(entity.isConfirmed)" :value="getConfirmationValue(entity.isConfirmed)"
/> />
<VnLv :label="t('order.field.salesPersonFk')"> <VnLv :label="t('customer.summary.team')">
<template #value> <template #value>
<span class="link"> <span class="link">
{{ entity?.client?.salesPersonUser?.name || '-' }} {{ entity?.client?.department?.name || '-' }}
<WorkerDescriptorProxy :id="entity?.client?.salesPersonFk" /> <DepartmentDescriptorProxy :id="entity?.client?.departmentFk" />
</span> </span>
</template> </template>
</VnLv> </VnLv>

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