Merge branch 'test' of https://gitea.verdnatura.es/verdnatura/salix-front into Warmfix-DepartmentIcon
gitea/salix-front/pipeline/pr-test There was a failure building this commit Details

This commit is contained in:
Jon Elias 2025-03-18 14:01:53 +01:00
commit fb912725b3
269 changed files with 4737 additions and 3969 deletions

1
.gitignore vendored
View File

@ -31,6 +31,7 @@ yarn-error.log*
# Cypress directories and files # Cypress directories and files
/test/cypress/videos /test/cypress/videos
/test/cypress/screenshots /test/cypress/screenshots
/junit
# VitePress directories and files # VitePress directories and files
/docs/.vitepress/cache /docs/.vitepress/cache

9
Jenkinsfile vendored
View File

@ -114,15 +114,18 @@ pipeline {
} }
steps { steps {
script { script {
sh 'rm junit/e2e-*.xml || true' sh 'rm -f junit/e2e-*.xml'
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')
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"
def image = docker.build('lilium-dev', '-f docs/Dockerfile.dev docs')
image.inside("--network ${env.COMPOSE_PROJECT}_default -e CI -e TZ --init") { image.inside("--network ${env.COMPOSE_PROJECT}_default -e CI -e TZ --init") {
sh 'cypress run --browser chromium || true' sh 'sh test/cypress/cypressParallel.sh 2'
} }
} }
} }

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

@ -1,13 +1,18 @@
import { defineConfig } from 'cypress'; import { defineConfig } from 'cypress';
let urlHost, reporter, reporterOptions; let urlHost, reporter, reporterOptions, timeouts;
if (process.env.CI) { if (process.env.CI) {
urlHost = 'front'; urlHost = 'front';
reporter = 'junit'; reporter = 'junit';
reporterOptions = { reporterOptions = {
mochaFile: 'junit/e2e-[hash].xml', mochaFile: 'junit/e2e-[hash].xml',
toConsole: false, };
timeouts = {
defaultCommandTimeout: 30000,
requestTimeout: 30000,
responseTimeout: 60000,
pageLoadTimeout: 60000,
}; };
} else { } else {
urlHost = 'localhost'; urlHost = 'localhost';
@ -20,17 +25,19 @@ if (process.env.CI) {
reportDir: 'test/cypress/reports', reportDir: 'test/cypress/reports',
inlineAssets: true, inlineAssets: true,
}; };
timeouts = {
defaultCommandTimeout: 10000,
requestTimeout: 10000,
responseTimeout: 30000,
pageLoadTimeout: 60000,
};
} }
export default defineConfig({ export default defineConfig({
e2e: { e2e: {
baseUrl: `http://${urlHost}:9000`, baseUrl: `http://${urlHost}:9000`,
experimentalStudio: false, experimentalStudio: false,
defaultCommandTimeout: 10000,
trashAssetsBeforeRuns: false, trashAssetsBeforeRuns: false,
requestTimeout: 10000,
responseTimeout: 30000,
pageLoadTimeout: 60000,
defaultBrowser: 'chromium', defaultBrowser: 'chromium',
fixturesFolder: 'test/cypress/fixtures', fixturesFolder: 'test/cypress/fixtures',
screenshotsFolder: 'test/cypress/screenshots', screenshotsFolder: 'test/cypress/screenshots',
@ -50,8 +57,8 @@ export default defineConfig({
}, },
viewportWidth: 1280, viewportWidth: 1280,
viewportHeight: 720, viewportHeight: 720,
...timeouts,
includeShadowDom: true,
waitForAnimations: true,
}, },
experimentalMemoryManagement: true,
defaultCommandTimeout: 10000,
numTestsKeptInMemory: 2,
}); });

View File

@ -1,6 +1,6 @@
{ {
"name": "salix-front", "name": "salix-front",
"version": "25.10.0", "version": "25.12.0",
"description": "Salix frontend", "description": "Salix frontend",
"productName": "Salix", "productName": "Salix",
"author": "Verdnatura", "author": "Verdnatura",
@ -13,6 +13,8 @@
"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/run.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",
"test:front:ci": "vitest run", "test:front:ci": "vitest run",
@ -54,11 +56,14 @@
"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",
"postcss": "^8.4.23", "postcss": "^8.4.23",
"prettier": "^3.4.2", "prettier": "^3.4.2",
"sass": "^1.83.4", "sass": "^1.83.4",
"vitepress": "^1.6.3", "vitepress": "^1.6.3",
"vitest": "^0.34.0" "vitest": "^0.34.0",
"xunit-viewer": "^10.6.1"
}, },
"engines": { "engines": {
"node": "^20 || ^18 || ^16", "node": "^20 || ^18 || ^16",

File diff suppressed because it is too large Load Diff

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

@ -57,7 +57,7 @@ const refresh = () => window.location.reload();
:class="{ :class="{
'no-visible': !stateQuery.isLoading().value, 'no-visible': !stateQuery.isLoading().value,
}" }"
size="xs" size="sm"
data-cy="loading-spinner" data-cy="loading-spinner"
/> />
<QSpace /> <QSpace />

View File

@ -12,7 +12,7 @@ defineProps({ row: { type: Object, required: true } });
> >
<QIcon name="vn:claims" size="xs"> <QIcon name="vn:claims" size="xs">
<QTooltip> <QTooltip>
{{ t('ticketSale.claim') }}: {{ $t('ticketSale.claim') }}:
{{ row.claim?.claimFk }} {{ row.claim?.claimFk }}
</QTooltip> </QTooltip>
</QIcon> </QIcon>

View File

@ -87,7 +87,7 @@ const makeInvoice = async () => {
(data) => ( (data) => (
(rectificativeTypeOptions = data), (rectificativeTypeOptions = data),
(transferInvoiceParams.cplusRectificationTypeFk = data.filter( (transferInvoiceParams.cplusRectificationTypeFk = data.filter(
(type) => type.description == 'I Por diferencias' (type) => type.description == 'I Por diferencias',
)[0].id) )[0].id)
) )
" "
@ -100,7 +100,7 @@ const makeInvoice = async () => {
(data) => ( (data) => (
(siiTypeInvoiceOutsOptions = data), (siiTypeInvoiceOutsOptions = data),
(transferInvoiceParams.siiTypeInvoiceOutFk = data.filter( (transferInvoiceParams.siiTypeInvoiceOutFk = data.filter(
(type) => type.code == 'R4' (type) => type.code == 'R4',
)[0].id) )[0].id)
) )
" "
@ -122,7 +122,6 @@ const makeInvoice = async () => {
<VnRow> <VnRow>
<VnSelect <VnSelect
:label="t('Client')" :label="t('Client')"
:options="clientsOptions"
hide-selected hide-selected
option-label="name" option-label="name"
option-value="id" option-value="id"

View File

@ -595,8 +595,7 @@ 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')) {
@ -606,7 +605,7 @@ function removeTextValue(data, getChanges) {
} }
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;
@ -776,7 +775,7 @@ 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;
@ -978,6 +977,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}`"
@ -1040,6 +1041,7 @@ 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 }">
<slot name="alter-create" :data="data">
<div :style="createComplement?.containerStyle"> <div :style="createComplement?.containerStyle">
<div <div
:style="createComplement?.previousStyle" :style="createComplement?.previousStyle"
@ -1047,7 +1049,10 @@ const rowCtrlClickFunction = computed(() => {
> >
<slot name="previous-create-dialog" :data="data" /> <slot name="previous-create-dialog" :data="data" />
</div> </div>
<div class="grid-create" :style="createComplement?.columnGridStyle"> <div
class="grid-create"
:style="createComplement?.columnGridStyle"
>
<slot <slot
v-for="column of splittedColumns.create" v-for="column of splittedColumns.create"
:key="column.name" :key="column.name"
@ -1059,7 +1064,7 @@ const rowCtrlClickFunction = computed(() => {
<VnColumn <VnColumn
:column="{ :column="{
...column, ...column,
...{ disable: column?.createDisable ?? false }, ...column?.createAttrs,
}" }"
:row="{}" :row="{}"
default="input" default="input"
@ -1072,6 +1077,7 @@ const rowCtrlClickFunction = computed(() => {
<slot name="more-create-dialog" :data="data" /> <slot name="more-create-dialog" :data="data" />
</div> </div>
</div> </div>
</slot>
</template> </template>
</FormModelPopup> </FormModelPopup>
</QDialog> </QDialog>
@ -1148,9 +1154,13 @@ es:
.grid-create { .grid-create {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, max-content)); grid-template-columns: 1fr 1fr;
max-width: 100%;
grid-gap: 20px; grid-gap: 20px;
margin: 0 auto; margin: 0 auto;
.col-span-2 {
grid-column: span 2;
}
} }
.flex-one { .flex-one {

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

@ -56,7 +56,12 @@ async function confirm() {
{{ t('The notification will be sent to the following address') }} {{ t('The notification will be sent to the following address') }}
</QCardSection> </QCardSection>
<QCardSection class="q-pt-none"> <QCardSection class="q-pt-none">
<VnInput v-model="address" is-outlined autofocus /> <VnInput
v-model="address"
is-outlined
autofocus
data-cy="SendEmailNotifiactionDialogInput"
/>
</QCardSection> </QCardSection>
<QCardActions align="right"> <QCardActions align="right">
<QBtn :label="t('globals.cancel')" color="primary" flat v-close-popup /> <QBtn :label="t('globals.cancel')" color="primary" flat v-close-popup />

View File

@ -1,50 +1,56 @@
<script setup> <script setup>
import { onBeforeMount, computed } from 'vue'; import { onBeforeMount } from 'vue';
import { useRoute, useRouter, onBeforeRouteUpdate } from 'vue-router'; import { useRouter, onBeforeRouteUpdate, onBeforeRouteLeave } from 'vue-router';
import { useArrayData } from 'src/composables/useArrayData'; import { useArrayData } from 'src/composables/useArrayData';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import useCardSize from 'src/composables/useCardSize'; import useCardSize from 'src/composables/useCardSize';
import VnSubToolbar from '../ui/VnSubToolbar.vue'; import VnSubToolbar from '../ui/VnSubToolbar.vue';
import VnSearchbar from 'components/ui/VnSearchbar.vue';
import LeftMenu from 'components/LeftMenu.vue';
import RightMenu from 'components/common/RightMenu.vue';
const props = defineProps({ const props = defineProps({
dataKey: { type: String, required: true }, dataKey: { type: String, required: true },
url: { type: String, default: undefined }, url: { type: String, default: undefined },
idInWhere: { type: Boolean, default: false },
filter: { type: Object, default: () => {} }, filter: { type: Object, default: () => {} },
descriptor: { type: Object, required: true }, descriptor: { type: Object, required: true },
filterPanel: { type: Object, default: undefined }, filterPanel: { type: Object, default: undefined },
idInWhere: { type: Boolean, default: false },
searchDataKey: { type: String, default: undefined }, searchDataKey: { type: String, default: undefined },
searchbarProps: { type: Object, default: undefined }, searchbarProps: { type: Object, default: undefined },
redirectOnError: { type: Boolean, default: false }, redirectOnError: { type: Boolean, default: false },
}); });
const stateStore = useStateStore(); const stateStore = useStateStore();
const route = useRoute();
const router = useRouter(); const router = useRouter();
const searchRightDataKey = computed(() => {
if (!props.searchDataKey) return route.name;
return props.searchDataKey;
});
const arrayData = useArrayData(props.dataKey, { const arrayData = useArrayData(props.dataKey, {
url: props.url, url: props.url,
userFilter: props.filter, userFilter: props.filter,
oneRecord: true, oneRecord: true,
}); });
onBeforeRouteLeave(() => {
stateStore.cardDescriptorChangeValue(null);
});
onBeforeMount(async () => { onBeforeMount(async () => {
stateStore.cardDescriptorChangeValue(props.descriptor);
const route = router.currentRoute.value;
try { try {
await fetch(route.params.id); await fetch(route.params.id);
} catch { } catch {
const { matched: matches } = router.currentRoute.value; const { matched: matches } = route;
const { path } = matches.at(-1); const { path } = matches.at(-1);
router.push({ path: path.replace(/:id.*/, '') }); router.push({ path: path.replace(/:id.*/, '') });
} }
}); });
onBeforeRouteUpdate(async (to, from) => { onBeforeRouteUpdate(async (to, from) => {
if (hasRouteParam(to.params)) {
const { matched } = router.currentRoute.value;
const { name } = matched.at(-3);
if (name) {
router.push({ name, params: to.params });
}
}
const id = to.params.id; const id = to.params.id;
if (id !== from.params.id) await fetch(id, true); if (id !== from.params.id) await fetch(id, true);
}); });
@ -56,34 +62,13 @@ async function fetch(id, append = false) {
else arrayData.store.url = props.url.replace(regex, `/${id}`); else arrayData.store.url = props.url.replace(regex, `/${id}`);
await arrayData.fetch({ append, updateRouter: false }); await arrayData.fetch({ append, updateRouter: false });
} }
function hasRouteParam(params, valueToCheck = ':addressId') {
return Object.values(params).includes(valueToCheck);
}
</script> </script>
<template> <template>
<QDrawer
v-model="stateStore.leftDrawer"
show-if-above
:width="256"
v-if="stateStore.isHeaderMounted()"
>
<QScrollArea class="fit">
<component :is="descriptor" />
<QSeparator />
<LeftMenu source="card" />
</QScrollArea>
</QDrawer>
<slot name="searchbar" v-if="props.searchDataKey">
<VnSearchbar :data-key="props.searchDataKey" v-bind="props.searchbarProps" />
</slot>
<RightMenu>
<template #right-panel v-if="props.filterPanel">
<component :is="props.filterPanel" :data-key="searchRightDataKey" />
</template>
</RightMenu>
<QPageContainer>
<QPage>
<VnSubToolbar /> <VnSubToolbar />
<div :class="[useCardSize(), $attrs.class]"> <div :class="[useCardSize(), $attrs.class]">
<RouterView :key="$route.path" /> <RouterView :key="$route.path" />
</div> </div>
</QPage>
</QPageContainer>
</template> </template>

View File

@ -1,74 +0,0 @@
<script setup>
import { onBeforeMount } from 'vue';
import { useRouter, onBeforeRouteUpdate, onBeforeRouteLeave } from 'vue-router';
import { useArrayData } from 'src/composables/useArrayData';
import { useStateStore } from 'stores/useStateStore';
import useCardSize from 'src/composables/useCardSize';
import VnSubToolbar from '../ui/VnSubToolbar.vue';
const props = defineProps({
dataKey: { type: String, required: true },
url: { type: String, default: undefined },
idInWhere: { type: Boolean, default: false },
filter: { type: Object, default: () => {} },
descriptor: { type: Object, required: true },
filterPanel: { type: Object, default: undefined },
searchDataKey: { type: String, default: undefined },
searchbarProps: { type: Object, default: undefined },
redirectOnError: { type: Boolean, default: false },
});
const stateStore = useStateStore();
const router = useRouter();
const arrayData = useArrayData(props.dataKey, {
url: props.url,
userFilter: props.filter,
oneRecord: true,
});
onBeforeRouteLeave(() => {
stateStore.cardDescriptorChangeValue(null);
});
onBeforeMount(async () => {
stateStore.cardDescriptorChangeValue(props.descriptor);
const route = router.currentRoute.value;
try {
await fetch(route.params.id);
} catch {
const { matched: matches } = route;
const { path } = matches.at(-1);
router.push({ path: path.replace(/:id.*/, '') });
}
});
onBeforeRouteUpdate(async (to, from) => {
if (hasRouteParam(to.params)) {
const { matched } = router.currentRoute.value;
const { name } = matched.at(-3);
if (name) {
router.push({ name, params: to.params });
}
}
const id = to.params.id;
if (id !== from.params.id) await fetch(id, true);
});
async function fetch(id, append = false) {
const regex = /\/(\d+)/;
if (props.idInWhere) arrayData.store.filter.where = { id };
else if (!regex.test(props.url)) arrayData.store.url = `${props.url}/${id}`;
else arrayData.store.url = props.url.replace(regex, `/${id}`);
await arrayData.fetch({ append, updateRouter: false });
}
function hasRouteParam(params, valueToCheck = ':addressId') {
return Object.values(params).includes(valueToCheck);
}
</script>
<template>
<VnSubToolbar />
<div :class="[useCardSize(), $attrs.class]">
<RouterView :key="$route.path" />
</div>
</template>

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

@ -6,6 +6,7 @@ import { useArrayData } from 'composables/useArrayData';
import { useSummaryDialog } from 'src/composables/useSummaryDialog'; import { useSummaryDialog } from 'src/composables/useSummaryDialog';
import { useState } from 'src/composables/useState'; import { useState } from 'src/composables/useState';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { useRoute, useRouter } from 'vue-router';
import { useClipboard } from 'src/composables/useClipboard'; import { useClipboard } from 'src/composables/useClipboard';
import VnMoreOptions from './VnMoreOptions.vue'; import VnMoreOptions from './VnMoreOptions.vue';
@ -47,6 +48,7 @@ const $props = defineProps({
const state = useState(); const state = useState();
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
const router = useRouter();
const { t } = useI18n(); const { t } = useI18n();
const { copyText } = useClipboard(); const { copyText } = useClipboard();
const { viewSummary } = useSummaryDialog(); const { viewSummary } = useSummaryDialog();
@ -58,6 +60,9 @@ const isSameDataKey = computed(() => $props.dataKey === route.meta.moduleName);
const DESCRIPTOR_PROXY = 'DescriptorProxy'; const DESCRIPTOR_PROXY = 'DescriptorProxy';
const moduleName = ref(); const moduleName = ref();
const isSameModuleName = route.matched[1].meta.moduleName !== moduleName.value; const isSameModuleName = route.matched[1].meta.moduleName !== moduleName.value;
const DESCRIPTOR_PROXY = 'DescriptorProxy';
const moduleName = ref();
const isSameModuleName = route.matched[1].meta.moduleName !== moduleName.value;
defineExpose({ getData }); defineExpose({ getData });
onBeforeMount(async () => { onBeforeMount(async () => {
@ -84,6 +89,7 @@ onBeforeMount(async () => {
); );
}); });
function getName() {
function getName() { function getName() {
let name = $props.dataKey; let name = $props.dataKey;
if ($props.dataKey.includes(DESCRIPTOR_PROXY)) { if ($props.dataKey.includes(DESCRIPTOR_PROXY)) {
@ -91,11 +97,17 @@ function getName() {
} }
return name; return name;
} }
const routeName = computed(() => {
let routeName = getName();
return `${routeName}Summary`;
return name;
}
const routeName = computed(() => { const routeName = computed(() => {
let routeName = getName(); let routeName = getName();
return `${routeName}Summary`; return `${routeName}Summary`;
}); });
async function getData() { async function getData() {
store.url = $props.url; store.url = $props.url;
store.filter = $props.filter ?? {}; store.filter = $props.filter ?? {};
@ -164,6 +176,8 @@ const toModule = computed(() => {
<div class="descriptor"> <div class="descriptor">
<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">
<QBtn
<slot name="header-extra-action"> <slot name="header-extra-action">
<QBtn <QBtn
round round
@ -174,11 +188,14 @@ const toModule = computed(() => {
color="white" color="white"
class="link" class="link"
:to="toModule" :to="toModule"
:to="toModule"
> >
<QTooltip> <QTooltip>
{{ t('globals.goToModuleIndex') }} {{ t('globals.goToModuleIndex') }}
</QTooltip> </QTooltip>
</QBtn> </QBtn>
</slot>
</QBtn>
</slot> </slot>
<QBtn <QBtn
@click.stop="viewSummary(entity.id, $props.summary, $props.width)" @click.stop="viewSummary(entity.id, $props.summary, $props.width)"
@ -191,6 +208,7 @@ const toModule = computed(() => {
class="link" class="link"
v-if="summary" v-if="summary"
data-cy="openSummaryBtn" data-cy="openSummaryBtn"
data-cy="openSummaryBtn"
> >
<QTooltip> <QTooltip>
{{ t('components.smartCard.openSummary') }} {{ t('components.smartCard.openSummary') }}
@ -206,6 +224,7 @@ const toModule = computed(() => {
round round
size="md" size="md"
data-cy="goToSummaryBtn" data-cy="goToSummaryBtn"
data-cy="goToSummaryBtn"
> >
<QTooltip> <QTooltip>
{{ t('components.cardDescriptor.summary') }} {{ t('components.cardDescriptor.summary') }}

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: {

View File

@ -186,7 +186,7 @@ function fetchData([data]) {
ref="vnPaginateRef" ref="vnPaginateRef"
class="show" class="show"
v-bind="$attrs" v-bind="$attrs"
search-url="notes" :search-url="false"
@on-fetch=" @on-fetch="
newNote.text = ''; newNote.text = '';
newNote.observationTypeFk = null; newNote.observationTypeFk = null;

View File

@ -33,6 +33,10 @@ const props = defineProps({
type: String, type: String,
default: '', default: '',
}, },
userFilter: {
type: Object,
default: null,
},
filter: { filter: {
type: Object, type: Object,
default: null, default: null,

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

@ -148,8 +148,7 @@ export function useArrayData(key, userOptions) {
} }
async function applyFilter({ filter, params }, fetchOptions = {}) { async function applyFilter({ filter, params }, fetchOptions = {}) {
if (filter) store.userFilter = filter; if (filter) store.filter = filter;
store.filter = {};
if (params) store.userParams = { ...params }; if (params) store.userParams = { ...params };
const response = await fetch(fetchOptions); const response = await fetch(fetchOptions);

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;
@ -337,5 +339,5 @@ input::-webkit-inner-spin-button {
} }
.containerShrinked { .containerShrinked {
width: 80%; width: 70%;
} }

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
@ -531,6 +531,7 @@ ticket:
customerCard: Customer card customerCard: Customer card
ticketList: Ticket List ticketList: Ticket List
newOrder: New Order newOrder: New Order
ticketClaimed: Claimed ticket
boxing: boxing:
expedition: Expedition expedition: Expedition
created: Created created: Created
@ -603,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:
@ -862,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

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,13 +531,13 @@ 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
customerCard: Ficha del cliente customerCard: Ficha del cliente
ticketList: Listado de tickets ticketList: Listado de tickets
newOrder: Nuevo pedido newOrder: Nuevo pedido
ticketClaimed: Ticket reclamado
boxing: boxing:
expedition: Expedición expedition: Expedición
created: Creado created: Creado
@ -622,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
@ -691,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:
@ -949,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

View File

@ -149,14 +149,12 @@ const columns = computed(() => [
:right-search="false" :right-search="false"
> >
<template #more-create-dialog="{ data }"> <template #more-create-dialog="{ data }">
<QCardSection>
<VnInputPassword <VnInputPassword
:label="t('Password')" :label="t('Password')"
v-model="data.password" v-model="data.password"
:required="true" :required="true"
autocomplete="new-password" autocomplete="new-password"
/> />
</QCardSection>
</template> </template>
</VnTable> </VnTable>
</template> </template>

View File

@ -1,10 +1,10 @@
<script setup> <script setup>
import VnCardBeta from 'components/common/VnCardBeta.vue'; import VnCard from 'components/common/VnCard.vue';
import AliasDescriptor from './AliasDescriptor.vue'; import AliasDescriptor from './AliasDescriptor.vue';
</script> </script>
<template> <template>
<VnCardBeta <VnCard
data-key="Alias" data-key="Alias"
url="MailAliases" url="MailAliases"
:descriptor="AliasDescriptor" :descriptor="AliasDescriptor"

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

@ -1,10 +1,10 @@
<script setup> <script setup>
import VnCardBeta from 'components/common/VnCardBeta.vue'; import VnCard from 'components/common/VnCard.vue';
import AccountDescriptor from './AccountDescriptor.vue'; import AccountDescriptor from './AccountDescriptor.vue';
import filter from './AccountFilter.js'; import filter from './AccountFilter.js';
</script> </script>
<template> <template>
<VnCardBeta <VnCard
url="VnUsers/preview" url="VnUsers/preview"
:id-in-where="true" :id-in-where="true"
data-key="Account" data-key="Account"

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

@ -1,9 +1,9 @@
<script setup> <script setup>
import VnCardBeta from 'components/common/VnCardBeta.vue'; import VnCard from 'components/common/VnCard.vue';
import RoleDescriptor from './RoleDescriptor.vue'; import RoleDescriptor from './RoleDescriptor.vue';
</script> </script>
<template> <template>
<VnCardBeta <VnCard
url="VnRoles" url="VnRoles"
data-key="Role" data-key="Role"
:id-in-where="true" :id-in-where="true"

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

@ -1,10 +1,10 @@
<script setup> <script setup>
import VnCardBeta from 'components/common/VnCardBeta.vue'; import VnCard from 'components/common/VnCard.vue';
import ClaimDescriptor from './ClaimDescriptor.vue'; import ClaimDescriptor from './ClaimDescriptor.vue';
import filter from './ClaimFilter.js'; import filter from './ClaimFilter.js';
</script> </script>
<template> <template>
<VnCardBeta <VnCard
data-key="Claim" data-key="Claim"
url="Claims" url="Claims"
:descriptor="ClaimDescriptor" :descriptor="ClaimDescriptor"

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

@ -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

@ -210,6 +210,7 @@ function onDrag() {
class="all-pointer-events absolute delete-button zindex" class="all-pointer-events absolute delete-button zindex"
@click.stop="viewDeleteDms(index)" @click.stop="viewDeleteDms(index)"
round round
:data-cy="`delete-button-${index+1}`"
/> />
<QIcon <QIcon
name="play_circle" name="play_circle"
@ -227,6 +228,7 @@ function onDrag() {
class="rounded-borders cursor-pointer fit" class="rounded-borders cursor-pointer fit"
@click="openDialog(media.dmsFk)" @click="openDialog(media.dmsFk)"
v-if="!media.isVideo" v-if="!media.isVideo"
:data-cy="`file-${index+1}`"
> >
</QImg> </QImg>
<video <video
@ -235,6 +237,7 @@ function onDrag() {
muted="muted" muted="muted"
v-if="media.isVideo" v-if="media.isVideo"
@click="openDialog(media.dmsFk)" @click="openDialog(media.dmsFk)"
:data-cy="`file-${index+1}`"
/> />
</QCard> </QCard>
</div> </div>

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

@ -80,7 +80,7 @@ const columns = [
:right-search="false" :right-search="false"
:column-search="false" :column-search="false"
:disable-option="{ card: true, table: true }" :disable-option="{ card: true, table: true }"
search-url="actions" :search-url="false"
:filter="{ where: { claimFk: $props.id } }" :filter="{ where: { claimFk: $props.id } }"
:columns="columns" :columns="columns"
:limit="0" :limit="0"

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

@ -1,10 +1,10 @@
<script setup> <script setup>
import VnCardBeta from 'components/common/VnCardBeta.vue'; import VnCard from 'components/common/VnCard.vue';
import CustomerDescriptor from './CustomerDescriptor.vue'; import CustomerDescriptor from './CustomerDescriptor.vue';
</script> </script>
<template> <template>
<VnCardBeta <VnCard
data-key="Customer" data-key="Customer"
:url="`Clients/${$route.params.id}/getCard`" :url="`Clients/${$route.params.id}/getCard`"
:descriptor="CustomerDescriptor" :descriptor="CustomerDescriptor"

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'">
<span
:class="{
link:
props.col.name === 'employee' ||
props.col.name === 'file',
}"
>
{{ props.value }} {{ 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

@ -1,10 +1,10 @@
<script setup> <script setup>
import VnCardBeta from 'components/common/VnCardBeta.vue'; import VnCard from 'components/common/VnCard.vue';
import EntryDescriptor from './EntryDescriptor.vue'; import EntryDescriptor from './EntryDescriptor.vue';
import filter from './EntryFilter.js'; import filter from './EntryFilter.js';
</script> </script>
<template> <template>
<VnCardBeta <VnCard
data-key="Entry" data-key="Entry"
url="Entries" url="Entries"
:descriptor="EntryDescriptor" :descriptor="EntryDescriptor"

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"
@on-fetch="(data) => sortEntryObservationOptions(data)"
auto-load
/>
<CrudModel
data-key="EntryAccount"
url="EntryObservations"
model="EntryAccount"
:filter="{
fields: ['id', 'entryFk', 'observationTypeFk', 'description'],
where: { entryFk: params.id },
}"
ref="entryObservationsRef" ref="entryObservationsRef"
:data-required="{ entryFk: params.id }" data-key="EntryObservations"
v-model:selected="selected"
auto-load
>
<template #body="{ rows, validate }">
<QTable
v-model:selected="selected"
:columns="columns" :columns="columns"
:rows="rows" url="EntryObservations"
:pagination="{ rowsPerPage: 0 }" :user-filter="filter"
row-key="$index" order="id ASC"
selection="multiple" :disable-option="{ card: true }"
hide-pagination :is-editable="true"
:grid="$q.screen.lt.md" :right-search="true"
table-header-class="text-left" v-model:selected="selectedRows"
> :create="{
<template #body-cell-observationType="{ row, col }"> urlCreate: 'EntryObservations',
<QTd> title: t('Create note'),
<VnSelect onDataSaved: () => {
v-model="row[col.model]" entryObservationsRef.reload();
:options="col.options" },
:option-value="col.optionValue" formInitialData: { entryFk: entityId },
:option-label="col.optionLabel" }"
:autofocus="col.tabIndex == 1" :table="{
input-debounce="0" 'row-key': 'id',
hide-selected selection: 'multiple',
:required="true" }"
auto-load
/> />
</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

@ -1,5 +1,5 @@
<script setup> <script setup>
import VnCardBeta from 'components/common/VnCardBeta.vue'; import VnCard from 'components/common/VnCard.vue';
import InvoiceInDescriptor from './InvoiceInDescriptor.vue'; import InvoiceInDescriptor from './InvoiceInDescriptor.vue';
import { onBeforeRouteUpdate } from 'vue-router'; import { onBeforeRouteUpdate } from 'vue-router';
import { setRectificative } from '../composables/setRectificative'; import { setRectificative } from '../composables/setRectificative';
@ -9,7 +9,7 @@ onBeforeRouteUpdate(async (to) => await setRectificative(to));
</script> </script>
<template> <template>
<VnCardBeta <VnCard
data-key="InvoiceIn" data-key="InvoiceIn"
url="InvoiceIns" url="InvoiceIns"
:descriptor="InvoiceInDescriptor" :descriptor="InvoiceInDescriptor"

View File

@ -1,10 +1,10 @@
<script setup> <script setup>
import InvoiceOutDescriptor from './InvoiceOutDescriptor.vue'; import InvoiceOutDescriptor from './InvoiceOutDescriptor.vue';
import VnCardBeta from 'components/common/VnCardBeta.vue'; import VnCard from 'components/common/VnCard.vue';
import filter from './InvoiceOutFilter.js'; import filter from './InvoiceOutFilter.js';
</script> </script>
<template> <template>
<VnCardBeta <VnCard
data-key="InvoiceOut" data-key="InvoiceOut"
url="InvoiceOuts" url="InvoiceOuts"
:filter="filter" :filter="filter"

View File

@ -75,6 +75,7 @@ function ticketFilter(invoice) {
icon="vn:client" icon="vn:client"
color="primary" color="primary"
:to="{ name: 'CustomerCard', params: { id: entity.client.id } }" :to="{ name: 'CustomerCard', params: { id: entity.client.id } }"
data-cy="invoiceOutDescriptorCustomerCard"
> >
<QTooltip>{{ t('invoiceOut.card.customerCard') }}</QTooltip> <QTooltip>{{ t('invoiceOut.card.customerCard') }}</QTooltip>
</QBtn> </QBtn>
@ -86,6 +87,7 @@ function ticketFilter(invoice) {
name: 'TicketList', name: 'TicketList',
query: { table: ticketFilter(entity) }, query: { table: ticketFilter(entity) },
}" }"
data-cy="invoiceOutDescriptorTicketList"
> >
<QTooltip>{{ t('invoiceOut.card.ticketList') }}</QTooltip> <QTooltip>{{ t('invoiceOut.card.ticketList') }}</QTooltip>
</QBtn> </QBtn>

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();
@ -94,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',
@ -193,7 +208,7 @@ watchEffect(selectedRows);
prefix="invoiceOut" prefix="invoiceOut"
:array-data-props="{ :array-data-props="{
url: 'InvoiceOuts/filter', url: 'InvoiceOuts/filter',
order: ['id DESC'], order: 'id DESC',
}" }"
> >
<template #advanced-menu> <template #advanced-menu>
@ -237,8 +252,14 @@ 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"> <div class="row q-col-gutter-xs col-span-2">
<div class="col-12"> <div class="col-12">
<div class="q-col-gutter-xs"> <div class="q-col-gutter-xs">
<VnRow fixed> <VnRow fixed>
@ -404,7 +425,6 @@ watchEffect(selectedRows);
:label=" :label="
t('invoiceOutList.tableVisibleColumns.taxArea') t('invoiceOutList.tableVisibleColumns.taxArea')
" "
:options="taxAreasOptions"
option-label="code" option-label="code"
option-value="code" option-value="code"
/> />

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

@ -20,7 +20,7 @@ const props = defineProps({
<VnFilterPanel <VnFilterPanel
:data-key="props.dataKey" :data-key="props.dataKey"
:search-button="true" :search-button="true"
:un-removable-params="['from', 'to']" :unremovable-params="['from', 'to']"
:hidden-tags="['from', 'to']" :hidden-tags="['from', 'to']"
> >
<template #tags="{ tag, formatFn }"> <template #tags="{ tag, formatFn }">
@ -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

@ -13,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
@ -27,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

@ -13,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
@ -27,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

@ -1,9 +1,9 @@
<script setup> <script setup>
import VnCardBeta from 'components/common/VnCardBeta.vue'; import VnCard from 'components/common/VnCard.vue';
import ItemDescriptor from './ItemDescriptor.vue'; import ItemDescriptor from './ItemDescriptor.vue';
</script> </script>
<template> <template>
<VnCardBeta <VnCard
data-key="Item" data-key="Item"
:url="`Items/${$route.params.id}/getCard`" :url="`Items/${$route.params.id}/getCard`"
:descriptor="ItemDescriptor" :descriptor="ItemDescriptor"

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

@ -1,11 +1,11 @@
<script setup> <script setup>
import VnCardBeta from 'components/common/VnCardBeta.vue'; import VnCard from 'components/common/VnCard.vue';
import ItemTypeDescriptor from 'src/pages/Item/ItemType/Card/ItemTypeDescriptor.vue'; import ItemTypeDescriptor from 'src/pages/Item/ItemType/Card/ItemTypeDescriptor.vue';
import filter from './ItemTypeFilter.js'; import filter from './ItemTypeFilter.js';
</script> </script>
<template> <template>
<VnCardBeta <VnCard
data-key="ItemType" data-key="ItemType"
url="ItemTypes" url="ItemTypes"
:filter="filter" :filter="filter"

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',
label: t('customer.summary.team'),
component: 'select', component: 'select',
attrs: { attrs: {
url: 'Workers/search?departmentCodes=["VT"]', url: 'Departments',
fields: ['id', 'name', 'nickname', 'code'],
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

@ -1,11 +1,11 @@
<script setup> <script setup>
import VnCardBeta from 'components/common/VnCardBeta.vue'; import VnCard from 'components/common/VnCard.vue';
import OrderDescriptor from 'pages/Order/Card/OrderDescriptor.vue'; import OrderDescriptor from 'pages/Order/Card/OrderDescriptor.vue';
import filter from './OrderFilter.js'; import filter from './OrderFilter.js';
</script> </script>
<template> <template>
<VnCardBeta <VnCard
data-key="Order" data-key="Order"
url="Orders" url="Orders"
:filter="filter" :filter="filter"

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