#8442 - createVehicleDms #1614

Merged
jtubau merged 47 commits from 8422-createVehicleDms into dev 2025-04-16 14:20:24 +00:00
211 changed files with 2323 additions and 2135 deletions
Showing only changes of commit 6c7ea26a41 - Show all commits

View File

@ -1,3 +1,187 @@
# Version 25.10 - 2025-03-11
### Added 🆕
- chore: refs #6695 empty commit by:alexm
- chore: refs #6695 get docker compose version by:alexm
- chore: refs #6695 try use docker compose by:alexm
- feat: add --browser chromium by:Javier Segarra
- feat: docker pull back image by:alexm
- feat(jenkinsE2E): refs #6695 new image by:alexm
- feat(jenkinsE2E): refs #6695 try fix db by:alexm
- feat(jenkinsE2E): refs #6695 try new sintax by:alexm
- feat(Jenkinsfile): refs #8714 add CHANGE_TARGET environment variable logging (origin/8714-devToTest, 8714-devToTest) by:alexm
- feat: refs #6695 add additional test directories for Cypress integration tests in Jenkinsfile by:alexm
- feat: refs #6695 add cypress-cache volume to docker-compose.e2e.yml by:alexm
- feat: refs #6695 add Dockerfile for Cypress setup and update Jenkinsfile for installation steps by:alexm
- feat: refs #6695 add setup and e2e testing by:alexm
- feat: refs #6695 better stages for e2e by:alexm
- feat: refs #6695 better stages for e2e rollback by:alexm
- feat: refs #6695 install Cypress during Jenkins pipeline setup by:alexm
- feat: refs #6695 jenkins run e2e by:alexm
- feat: refs #6695 jenkins run e2e front deteach by:alexm
- feat: refs #6695 jenkins run e2e rebuild by:alexm
- feat: refs #6695 jenkins run e2e remove ports by:alexm
- feat: refs #6695 jenkins run e2e try down and rm by:alexm
- feat: refs #6695 jenkins run e2e try fix db by:alexm
- feat: refs #6695 jenkins run e2e whitout rebuild by:alexm
- feat: refs #6695 pull salix-back image and use by:alexm
- feat: refs #6695 run e2e in docker by:alexm
- feat: refs #6695 run front by:alexm
- feat: refs #6695 run front quasar build by:alexm
- feat: refs #6695 run parallel e2e in local by:alexm
- feat: refs #6695 update cypress cache path command in Jenkinsfile by:alexm
- feat: refs #6695 update cypress-cache volume path in docker-compose.e2e.yml by:alexm
- feat: refs #6695 update cypress command in Jenkinsfile and docker-compose.e2e.yml by:alexm
- feat: refs #6695 update Docker configurations and Cypress settings for improved local development (origin/6695-docker_push_2, 6695-docker_push_2) by:alexm
- feat: refs #6695 when failure, clean by:alexm
- feat: refs #7937 add import claim button to ClaimAction component by:jgallego
- feat: refs #7937 add shelving selection to claim actions with data fetching by:jgallego
- feat: refs #8348 Added grouping by:guillermo
- feat: refs #8402 added lost filters from Salix by:Jon
- feat: refs #8484 add addressId to createForm in CustomerDescriptor by:jorgep
- feat: refs #8484 overwrite Cypress visit command to ensure main element exists by:jorgep
- feat: refs #8555 added new filter field and translations by:Jon
- feat: refs #8593 added summary button & modified e2e tests by:provira
- feat: refs #8593 changed parking to VnTable and modified e2e tests by:provira
- feat: refs #8599 added new test and translations by:Jon
- feat: refs #8599 modified tests to be more complete and added new ones by:Jon
- feat: refs #8697 enable data-cy attribute for VnTable, update test cases to remove skips and adjust selectors by:pablone
- feat: rename test:unit by test:front by:Javier Segarra
- feat: try run salix back by:alexm
- fix: style w-80 by:Javier Segarra
- Merge pull request 'fix: style' (!1425) from warmfix_vntable_card_style into test by:Javier Segarra
### Changed 📦
- ci: refs #6695 Docker & Jenkinsfile fixes/refactor by:Juan Ferrer Toribio
- ci: refs #6695 refactor Cypress setup in Jenkinsfile and replace local docker-compose with new configuration by:alexm
- perf: refs #6695 only necessary by:alexm
- refactor: adjust translation to standardize it by:Jon
- refactor: refs #6695 comment out vnComponent tests in Jenkinsfile by:alexm
- refactor: refs #6695 improve group size calculation for parallel test execution in Jenkinsfile by:alexm
- refactor: refs #6695 improve parallel test execution logic in Jenkinsfile by:alexm
- refactor: refs #6695 simplify Docker cleanup commands in Jenkinsfile by:alexm
- refactor: refs #6695 update Docker setup for Cypress and remove obsolete files by:alexm
- refactor: refs #6695 update E2E test execution to support parallel groups and improve by:alexm
- refactor: refs #6695 update Jenkinsfile and Dockerfile to use 'developer' by:alexm
- refactor: refs #6695 update Jenkinsfile to run E2E tests in parallel and simplify docker-compose command by:alexm
- refactor: refs #6897 clean up Cypress configuration and improve entry list filtering (origin/6897-fixEntryE2e) by:pablone
- refactor: refs #7414 update VnLog component to change display order value changes on update action by:jtubau
- refactor: refs #7937 align columns to the right and add shelvingCode to ClaimSummaryAction by:jgallego
- refactor: refs #8484 add data-cy attribute for claim photo image and update test to use it by:jorgep
- refactor: refs #8484 clean up test files by removing commented issue references and updating test cases by:jorgep
- refactor: refs #8484 enhance login command with session management and clean up unused commands by:jtubau
- refactor: refs #8484 improve search input behavior and enhance visit command with DOM content load by:jtubau
- refactor: refs #8484 improve selectOption command with retry logic for visibility checks by:jtubau
- refactor: refs #8484 remove comment in wagonCreate.spec.js by:jtubau
- refactor: refs #8484 remove redundant visit command overwrite by:jorgep
- refactor: refs #8484 remove unnecessary domContentLoad calls from client tests by:jorgep
- refactor: refs #8484 remove unnecessary intercepts and waits in ticket and zone tests by:jorgep
- refactor: refs #8484 simplify image dialog test by using aliases for elements by:jorgep
- refactor: refs #8484 streamline assertions in ClaimNotes test by:jorgep
- refactor: refs #8484 streamline login command and remove commented code by:jorgep
- refactor: refs #8484 update specPattern to include all spec files and remove data-cy attribute by:jorgep
- refactor: refs #8594 update vehicle summary tests to use expected variable for consistency by:jtubau
- refactor: refs #8599 corrected it name by:Jon
- refactor: refs #8599 invoice out list e2e by:Jon
- refactor: refs #8599 requested changes by:Jon
- refactor: refs #8606 modified table height and deleted void file by:Jon
- refactor: refs #8606 modified table width and order by:Jon
- refactor: refs #8606 modified upcoming deliveries view by:Jon
- refactor: refs #8606 translations by:Jon
- refactor: refs #8618 simplify selectors and improve test readability in routeExtendedList.spec.js by:jtubau
- refactor: refs #8620 update RouteAutonomous to notify on data save and change invoice reference display by:jtubau
- refactor: remove default browser setting from Cypress configuration by:alexm
- refactor: remove unused variables by:alexm
- refactor: skip claimNotes by:alexm
- refactor: update labels and conditions in Claim components by:jgallego
- refactor: use constant for account input selector in VnAccountNumber tests by:alexm
### Fixed 🛠️
- build: refs #6695 cypress-setup fix volume by:alexm
- build: refs #6695 cypress-setup fix volume (origin/6695-docker_push, 6695-docker_push) by:alexm
- ci: refs #6695 cypress reporter fix by:Juan Ferrer Toribio
- ci: refs #6695 Docker & Jenkinsfile fixes/refactor by:Juan Ferrer Toribio
- ci: refs #6695 JUnit report fixes by:Juan Ferrer Toribio
- ci: refs #6695 vitest junit file fix by:Juan Ferrer Toribio
- feat(jenkinsE2E): refs #6695 try fix db by:alexm
- feat: refs #6695 jenkins run e2e try fix db by:alexm
- fix: add data-cy attribute to card button for improved testing by:jtubau
- fix: added lost code by:Jon
- fix: add --init flag to Cypress Docker container for improved stability by:alexm
- fix: add mapper before Save by:Javier Segarra
- fix: cy.domContentLoad(); not exist by:alexm
- fix: elements position by:Javier Segarra
- fix: fixed select not filtering when typing by:Jon
- fix: fixed wagonTypeCreate test (origin/wagonTypeTestFix) by:PAU ROVIRA ROSALENY
- fix: fix sctions by:carlossa
- fix(Jenkinsfile): enhance Docker registry credentials handling with dynamic URL (origin/warmFix_use_withDockerRegistry, warmFix_use_withDockerRegistry) by:alexm
- fix(Jenkinsfile): update Docker registry credentials handling in E2E stage by:alexm
- fix: junit report by:alexm
- fix: merge revert by:alexm
- fix: merge test to dev by:alexm
- fix: prevent 'cypress run' error to show junit by:alexm
- fix: refs #6695 add --volumes flag to docker-compose down command by:alexm
- fix: refs #6695 checkErrors(folderName) by:alexm
- fix: refs #6695 clientBasicData by:alexm
- fix: refs #6695 dockerFile by:alexm
- fix: refs #6695 e2e.sh by:alexm
- fix: refs #6695 e2e stockBought by:alexm
- fix: refs #6695 fix e2e's by:alexm
- fix: refs #6695 storage by:alexm
- fix: refs #6695 try by:alexm
- fix: refs #6695 try parallel by:alexm
- fix: refs #6695 update Cypress cache handling and increase wait timeout for elements by:alexm
- fix: refs #6695 update Cypress configuration and Docker setup for improved testing by:alexm
- fix: refs #6695 update E2E stages to run tests in parallel for specific folders by:alexm
- fix: refs #6695 update remove Cypress installation by:alexm
- fix: refs #6695 zoneWarehouse est by:alexm
- fix: refs #6943 e2e clientList, formModel by:carlossa
- fix: refs #6943 formModel workerDepartment by:carlossa
- fix: refs #7323 e2e (origin/7323-fixe2e) by:carlossa
- fix: refs #7323 notification manager by:carlossa
- fix: refs #7414 updated default value rendering for non-update scenarios by:jtubau
- fix: refs #7414 update VnLog.vue to correctly display log actions and values by:jtubau
- fix: refs #7937 update claimId in ClaimAction test to reflect correct value (origin/7937-claimAgile) by:jgallego
- fix: refs #8484 ensure document is fully loaded before visiting pages in tests by:jorgep
- fix: refs #8484 fixed some tests to enable previously skipped cases and enhance functionality by:jtubau
- fix: refs #8484 remove unused addressId from createForm in CustomerDescriptor.vue by:jtubau
- fix: refs #8484 rollback by:jorgep
- fix: refs #8484 update Boss field type to 'selectWorker' and add selectWorkerOption command by:jtubau
- fix: refs #8484 update Boss type from 'selectWorker' to 'select' by:jorgep
- fix: refs #8484 update parking list URL to correct shelving path in integration test by:jtubau
- fix: refs #8484 update selector for buyLabel button in myEntry test by:jtubau
- fix: refs #8484 update selector for removing wagon type in wagonCreate.spec.js by:jtubau
- fix: refs #8484 update wagon type deletion selector and clean up unused code in commands.js by:jtubau
- fix: refs #8593 fixed parking e2e tests by:provira
- fix: refs #8606 fixed list e2e test by:Jon
- fix: refs #8620 add module name to InvoiceInSummary by:jtubau
- fix: refs #8623 fixed different errors by:Jon
- fix: remove info by:carlossa
- fix: remove old end-to-end test files before building Docker image by:alexm
- fix: revert cypress.config by:alexm
- fix: style w-80 by:Javier Segarra
- fix: unnecessary function by:alexm
- fix: update docker-compose command to remove volumes on teardown by:alexm
- fix: update Jenkinsfile to remove specific end-to-end test files by:alexm
- fix: update Jenkinsfile to use environment variable for Docker registry credentials by:alexm
- fix: warmFix vnInput dataCy by:alexm
- Merge pull request 'fix: style' (!1425) from warmfix_vntable_card_style into test by:Javier Segarra
- revert: browser chromium package.json by:Javier Segarra
- Revert "revert 1015acefb7e400be2d8b5958dba69b4d98276b34" by:alexm
- test: refs #6695 e2e fix allowedHosts by:alexm
- test: refs #6695 e2e fix back image by:alexm
- test: refs #6695 e2e fix base urls by:alexm
- test: refs #6695 e2e fix command by:alexm
- test: refs #6695 e2e fix connection db by:alexm
- test: refs #6695 e2e fix network by:alexm
- test: refs #6695 e2e fix sequential by:alexm
- test: refs #6695 fix e2e by:alexm
- test: refs #6695 fix e2e command by:alexm
- test: refs #6695 fix selectOption command by:alexm
# Version 25.08 - 2025-03-04
### Added 🆕

2
Jenkinsfile vendored
View File

@ -120,6 +120,8 @@ pipeline {
def image = docker.build('lilium-dev', '-f docs/Dockerfile.dev docs')
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"
image.inside("--network ${env.COMPOSE_PROJECT}_default -e CI -e TZ --init") {

View File

@ -32,6 +32,18 @@ pnpm run test:front
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
```bash

View File

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

View File

@ -13,7 +13,7 @@
"format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore",
"test:e2e": "cypress open",
"test:e2e:ci": "npm run resetDatabase && cd ../salix-front && cypress run",
"test:e2e:parallel": "bash ./test/cypress/cypressParallel.sh",
"test:e2e:parallel": "bash ./test/cypress/run.sh",
"test:e2e:summary": "bash ./test/cypress/summary.sh",
"test": "echo \"See package.json => scripts for available tests.\" && exit 0",
"test:front": "vitest",
@ -56,6 +56,7 @@
"eslint-plugin-cypress": "^4.1.0",
"eslint-plugin-vue": "^9.32.0",
"husky": "^8.0.0",
"junit-merge": "^2.0.0",
"mocha": "^11.1.0",
"postcss": "^8.4.23",
"prettier": "^3.4.2",

View File

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

View File

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

View File

@ -196,7 +196,7 @@ const selectTravel = ({ id }) => {
>
<template #body-cell-id="{ row }">
<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" />
</QTd>
</template>

View File

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

View File

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

View File

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

View File

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

View File

@ -920,12 +920,24 @@ const rowCtrlClickFunction = computed(() => {
:row-index="index"
>
<VnColumn
:column="col"
:column="{
...col,
disable:
col?.component ===
'checkbox'
? true
: false,
}"
:row="row"
:is-editable="false"
v-model="row[col.name]"
component-prop="columnField"
:show-label="true"
:show-label="
col?.component ===
'checkbox'
? false
: true
"
/>
</slot>
</span>
@ -1136,9 +1148,13 @@ es:
.grid-create {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, max-content));
grid-template-columns: 1fr 1fr;
max-width: 100%;
grid-gap: 20px;
margin: 0 auto;
.col-span-2 {
grid-column: span 2;
}
}
.flex-one {

View File

@ -15,10 +15,7 @@ vi.mock('src/router/modules', () => ({
meta: {
title: 'customers',
icon: 'vn:client',
},
menus: {
main: ['CustomerList', 'CustomerCreate'],
card: ['CustomerBasicData'],
menu: ['CustomerList', 'CustomerCreate'],
},
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',
moduleName: 'Customer',
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', () => {
vm.props.source = 'main';
vm.getRoutes();
@ -351,8 +331,9 @@ describe('addChildren', () => {
it('should handle routes with no meta menu', () => {
const route = {
meta: {},
menus: {},
meta: {
menu: [],
},
};
const parent = [];

View File

@ -1,50 +1,56 @@
<script setup>
import { onBeforeMount, computed } from 'vue';
import { useRoute, useRouter, onBeforeRouteUpdate } from 'vue-router';
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';
import VnSearchbar from 'components/ui/VnSearchbar.vue';
import LeftMenu from 'components/LeftMenu.vue';
import RightMenu from 'components/common/RightMenu.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 },
idInWhere: { type: Boolean, default: false },
searchDataKey: { type: String, default: undefined },
searchbarProps: { type: Object, default: undefined },
redirectOnError: { type: Boolean, default: false },
});
const stateStore = useStateStore();
const route = useRoute();
const router = useRouter();
const searchRightDataKey = computed(() => {
if (!props.searchDataKey) return route.name;
return props.searchDataKey;
});
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 } = router.currentRoute.value;
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);
});
@ -56,34 +62,13 @@ async function fetch(id, append = false) {
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>
<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 />
<div :class="[useCardSize(), $attrs.class]">
<RouterView :key="$route.path" />
</div>
</QPage>
</QPageContainer>
<VnSubToolbar />
<div :class="[useCardSize(), $attrs.class]">
<RouterView :key="$route.path" />
</div>
</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

@ -5,7 +5,7 @@ import SkeletonDescriptor from 'components/ui/SkeletonDescriptor.vue';
import { useArrayData } from 'composables/useArrayData';
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
import { useState } from 'src/composables/useState';
import { useRoute } from 'vue-router';
import { useRoute, useRouter } from 'vue-router';
import { useClipboard } from 'src/composables/useClipboard';
import VnMoreOptions from './VnMoreOptions.vue';
@ -42,6 +42,7 @@ const $props = defineProps({
const state = useState();
const route = useRoute();
const router = useRouter();
const { t } = useI18n();
const { copyText } = useClipboard();
const { viewSummary } = useSummaryDialog();
@ -50,6 +51,9 @@ let store;
let entity;
const isLoading = ref(false);
const isSameDataKey = computed(() => $props.dataKey === route.meta.moduleName);
const DESCRIPTOR_PROXY = 'DescriptorProxy';
const moduleName = ref();
const isSameModuleName = route.matched[1].meta.moduleName !== moduleName.value;
defineExpose({ getData });
onBeforeMount(async () => {
@ -76,15 +80,18 @@ onBeforeMount(async () => {
);
});
const routeName = computed(() => {
const DESCRIPTOR_PROXY = 'DescriptorProxy';
function getName() {
let name = $props.dataKey;
if ($props.dataKey.includes(DESCRIPTOR_PROXY)) {
name = name.split(DESCRIPTOR_PROXY)[0];
}
return `${name}Summary`;
return name;
}
const routeName = computed(() => {
let routeName = getName();
return `${routeName}Summary`;
});
async function getData() {
store.url = $props.url;
store.filter = $props.filter ?? {};
@ -120,20 +127,35 @@ function copyIdText(id) {
const emit = defineEmits(['onFetch']);
const iconModule = computed(() => route.matched[1].meta.icon);
const toModule = computed(() =>
route.matched[1].path.split('/').length > 2
? route.matched[1].redirect
: route.matched[1].children[0].redirect,
);
const iconModule = computed(() => {
moduleName.value = getName();
if (isSameModuleName) {
return router.options.routes[1].children.find((r) => r.name === moduleName.value)
?.meta?.icon;
} else {
return route.matched[1].meta.icon;
}
});
const toModule = computed(() => {
moduleName.value = getName();
if (isSameModuleName) {
return router.options.routes[1].children.find((r) => r.name === moduleName.value)
?.children[0]?.redirect;
} else {
return route.matched[1].path.split('/').length > 2
? route.matched[1].redirect
: route.matched[1].children[0].redirect;
}
});
</script>
<template>
<div class="descriptor">
<template v-if="entity && !isLoading">
<div class="header bg-primary q-pa-sm justify-between">
<slot name="header-extra-action"
><QBtn
<slot name="header-extra-action">
<QBtn
round
flat
dense
@ -141,13 +163,13 @@ const toModule = computed(() =>
:icon="iconModule"
color="white"
class="link"
:to="$attrs['to-module'] ?? toModule"
:to="toModule"
>
<QTooltip>
{{ t('globals.goToModuleIndex') }}
</QTooltip>
</QBtn></slot
>
</QBtn>
</slot>
<QBtn
@click.stop="viewSummary(entity.id, $props.summary, $props.width)"
round
@ -230,7 +252,6 @@ const toModule = computed(() =>
</div>
<slot name="after" />
</template>
<!-- Skeleton -->
<SkeletonDescriptor v-if="!entity || isLoading" />
</div>
<QInnerLoading

View File

@ -54,7 +54,7 @@ const $props = defineProps({
default: 'table',
},
redirect: {
type: Boolean,
type: [String, Boolean],
default: true,
},
arrayData: {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -99,7 +99,6 @@ globals:
file: File
selectFile: Select a file
copyClipboard: Copy on clipboard
salesPerson: SalesPerson
send: Send
code: Code
since: Since
@ -158,7 +157,9 @@ globals:
changeState: Change state
raid: 'Raid {daysInForward} days'
isVies: Vies
department: Department
noData: No data available
vehicle: Vehicle
pageTitles:
jtubau marked this conversation as resolved Outdated

Seguro que es la traducción más correcta? Lo digo porque en español estás diciendo "introduzca"

Seguro que es la traducción más correcta? Lo digo porque en español estás diciendo "introduzca"
logIn: Login
addressEdit: Update address
@ -346,7 +347,6 @@ globals:
params:
description: Description
clientFk: Client id
salesPersonFk: Sales person
warehouseFk: Warehouse
provinceFk: Province
stateFk: State
@ -531,6 +531,7 @@ ticket:
customerCard: Customer card
ticketList: Ticket List
newOrder: New Order
ticketClaimed: Claimed ticket
boxing:
expedition: Expedition
created: Created
@ -603,7 +604,6 @@ worker:
balance: Balance
medical: Medical
list:
department: Department
schedule: Schedule
newWorker: New worker
summary:
@ -862,7 +862,6 @@ components:
mine: For me
hasMinPrice: Minimum price
# LatestBuysFilter
salesPersonFk: Buyer
supplierFk: Supplier
from: From
to: To

View File

@ -103,7 +103,6 @@ globals:
file: Fichero
selectFile: Seleccione un fichero
copyClipboard: Copiar en portapapeles
salesPerson: Comercial
send: Enviar
code: Código
since: Desde
@ -163,6 +162,8 @@ globals:
raid: 'Redada {daysInForward} días'
isVies: Vies
noData: Datos no disponibles
department: Departamento
vehicle: Vehículo
pageTitles:
logIn: Inicio de sesión
addressEdit: Modificar consignatario
@ -349,7 +350,6 @@ globals:
params:
description: Descripción
clientFk: Id cliente
salesPersonFk: Comercial
warehouseFk: Almacén
provinceFk: Provincia
stateFk: Estado
@ -531,13 +531,13 @@ ticket:
state: Estado
shipped: Enviado
landed: Entregado
salesPerson: Comercial
total: Total
card:
customerId: ID cliente
customerCard: Ficha del cliente
ticketList: Listado de tickets
newOrder: Nuevo pedido
ticketClaimed: Ticket reclamado
boxing:
expedition: Expedición
created: Creado
@ -622,8 +622,6 @@ invoiceOut:
errors:
downloadCsvFailed: Error al descargar CSV
order:
field:
salesPersonFk: Comercial
form:
clientFk: Cliente
addressFk: Dirección
@ -691,7 +689,6 @@ worker:
formation: Formación
medical: Mutua
list:
department: Departamento
schedule: Horario
newWorker: Nuevo trabajador
summary:
@ -949,7 +946,6 @@ components:
hasMinPrice: Precio mínimo
wareHouseFk: Almacén
# LatestBuysFilter
salesPersonFk: Comprador
supplierFk: Proveedor
visible: Visible
active: Activo

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -328,7 +328,7 @@ async function post(query, params) {
<QTd>
<VnSelect
v-model="row.shelvingFk"
:options="shelvings"
url="Shelvings"
option-label="code"
option-value="id"
style="width: 100px"

View File

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

View File

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

View File

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

View File

@ -117,7 +117,7 @@ const selected = ref([]);
const mana = ref(0);
async function fetchMana() {
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;
}

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

View File

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

View File

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

View File

@ -4,6 +4,7 @@ import { useI18n } from 'vue-i18n';
import { toDate } from 'filters/index';
import ClaimFilter from './ClaimFilter.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 ClaimSummary from './Card/ClaimSummary.vue';
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
@ -48,6 +49,20 @@ const columns = computed(() => [
},
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',
label: t('claim.attendedBy'),
@ -152,6 +167,12 @@ const STATE_COLOR = {
<CustomerDescriptorProxy :id="row.clientFk" />
</span>
</template>
<template #column-departmentFk="{ row }">
<span class="link" @click.stop>
{{ row.departmentName || '-' }}
<DepartmentDescriptorProxy :id="row?.departmentFk" />
</span>
</template>
<template #column-attendedBy="{ row }">
<span @click.stop>
<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 VnInput from 'src/components/common/VnInput.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import VnSelectWorker from 'src/components/common/VnSelectWorker.vue';
import { getDifferences, getUpdatedValues } from 'src/filters';
const route = useRoute();
@ -37,7 +36,7 @@ const exprBuilder = (param, value) => {
function onBeforeSave(formData, originalData) {
return getUpdatedValues(
Object.keys(getDifferences(formData, originalData)),
formData
formData,
);
}
</script>
@ -119,16 +118,11 @@ function onBeforeSave(formData, originalData) {
/>
</VnRow>
<VnRow>
<VnSelectWorker
:label="t('customer.summary.salesPerson')"
v-model="data.salesPersonFk"
:params="{
departmentCodes: ['VT', 'shopping'],
}"
:has-avatar="true"
:rules="validate('client.salesPersonFk')"
:expr-builder="exprBuilder"
emit-value
<VnSelect
:label="t('globals.department')"
v-model="data.departmentFk"
url="Departments"
:fields="['id', 'name']"
/>
<VnSelect
v-model="data.contactChannelFk"
@ -160,7 +154,7 @@ function onBeforeSave(formData, originalData) {
<QIcon name="info" class="cursor-pointer">
<QTooltip>{{
t(
'In case of a company succession, specify the grantor company'
'In case of a company succession, specify the grantor company',
)
}}</QTooltip>
</QIcon>

View File

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

View File

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

View File

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

View File

@ -16,9 +16,7 @@ const filter = {
{ relation: 'mandateType', scope: { fields: ['id', 'code'] } },
{ relation: 'company', scope: { fields: ['id', 'code'] } },
],
where: { clientFk: route.params.id },
order: ['created DESC'],
limit: 20,
};
const columns = computed(() => [
@ -32,7 +30,7 @@ const columns = computed(() => [
{
align: 'left',
cardVisible: true,
format: ({ company }) => company.code,
format: ({ company }) => company?.code,
label: t('globals.company'),
name: 'company',
},
@ -65,7 +63,8 @@ const columns = computed(() => [
<VnTable
data-key="Mandates"
url="Mandates"
:filter="filter"
:user-filter="filter"
:filter="{ where: { clientFk: route.params.id } }"
auto-load
:columns="columns"
class="full-width q-mt-md"

View File

@ -2,7 +2,6 @@
import { computed, ref } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import VnUserLink from 'src/components/ui/VnUserLink.vue';
import { toCurrency, toPercentage, toDate, dashOrCurrency } from 'src/filters';
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 VnRow from 'src/components/ui/VnRow.vue';
import CustomerDescriptorMenu from './CustomerDescriptorMenu.vue';
import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue';
const route = useRoute();
const { t } = useI18n();
const grafanaUrl = 'https://grafana.verdnatura.es';
@ -106,16 +107,12 @@ const sumRisk = ({ clientRisks }) => {
{{ t('globals.params.email') }}
<VnLinkMail email="entity.email"></VnLinkMail> </template
></VnLv>
<VnLv
:label="t('customer.summary.salesPerson')"
:value="entity?.salesPersonUser?.name"
>
<VnLv :label="t('globals.department')">
<template #value>
<VnUserLink
:name="entity.salesPersonUser?.name"
:worker-id="entity.salesPersonFk"
/> </template
></VnLv>
<span class="link" v-text="entity.department?.name" />
<DepartmentDescriptorProxy :id="entity?.department?.id" />
</template>
</VnLv>
<VnLv
:label="t('customer.summary.contactChannel')"
: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 VnSelect from 'components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnSelectWorker from 'src/components/common/VnSelectWorker.vue';
const { t } = useI18n();
defineProps({
@ -65,22 +64,15 @@ const exprBuilder = (param, value) => {
</QItem>
<QItem class="q-mb-sm">
<QItemSection>
<VnSelectWorker
: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
<VnSelect
outlined
dense
rounded
:input-debounce="0"
:label="t('globals.params.departmentFk')"
v-model="params.departmentFk"
option-value="id"
option-label="name"
url="Departments"
/>
</QItemSection>
</QItem>
@ -164,7 +156,6 @@ en:
params:
search: Contains
fi: FI
salesPersonFk: Salesperson
provinceFk: Province
isActive: Is active
city: City
@ -191,7 +182,6 @@ es:
sageTaxTypeFk: Tipo de impuesto Sage
sageTransactionTypeFk: Tipo de impuesto Sage
payMethodFk: Forma de pago
salesPersonFk: Comercial
provinceFk: Provincia
city: Ciudad
phone: Teléfono
@ -201,7 +191,6 @@ es:
name: Nombre
postcode: CP
FI: NIF
Salesperson: Comercial
Province: Provincia
City: Ciudad
Phone: Teléfono

View File

@ -10,7 +10,6 @@ import CustomerFilter from './CustomerFilter.vue';
import VnTable from 'components/VnTable/VnTable.vue';
import VnLocation from 'src/components/common/VnLocation.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';
const { t } = useI18n();
@ -73,30 +72,17 @@ const columns = computed(() => [
},
{
align: 'left',
name: 'salesPersonFk',
label: t('customer.extendedList.tableVisibleColumns.salesPersonFk'),
name: 'departmentFk',
label: t('customer.summary.team'),
component: 'select',
attrs: {
url: 'Workers/activeWithInheritedRole',
fields: ['id', 'name', 'firstName'],
where: { role: 'salesPerson' },
optionFilter: 'firstName',
url: 'Departments',
},
columnFilter: {
component: 'select',
attrs: {
url: 'Workers/activeWithInheritedRole',
fields: ['id', 'name', 'firstName'],
where: { role: 'salesPerson' },
optionLabel: 'firstName',
optionValue: 'id',
},
},
create: false,
create: true,
columnField: {
component: null,
},
format: (row, dashIfEmpty) => dashIfEmpty(row.salesPerson),
format: (row, dashIfEmpty) => dashIfEmpty(row.departmentName),
},
{
align: 'left',
@ -155,6 +141,9 @@ const columns = computed(() => [
inWhere: true,
},
columnClass: 'expand',
attrs: {
uppercase: true,
},
},
{
align: 'left',
@ -446,36 +435,6 @@ function handleLocation(data, location) {
redirect="customer"
>
<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
:acls="[{ model: 'Province', props: '*', accessType: 'WRITE' }]"
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',
name: 'departmentFk',
@ -153,6 +131,11 @@ const columns = computed(() => [
label: t('Has recovery'),
name: 'hasRecovery',
},
{
align: 'left',
name: 'isWorker',
label: t('customer.params.isWorker'),
},
]);
const viewAddObservation = (rowsSelected) => {
@ -167,7 +150,6 @@ const viewAddObservation = (rowsSelected) => {
function exprBuilder(param, value) {
switch (param) {
case 'salesPersonFk':
case 'creditInsurance':
case 'countryFk':
return { [`c.${param}`]: value };
@ -176,7 +158,7 @@ function exprBuilder(param, value) {
case 'workerFk':
return { [`co.${param}`]: value };
case 'departmentFk':
return { [`wd.${param}`]: value };
return { [`c.${param}`]: value };
case 'amount':
case 'clientFk':
return { [`d.${param}`]: value };
@ -241,12 +223,6 @@ function exprBuilder(param, value) {
<template #column-observation="{ row }">
<VnInput type="textarea" v-model="row.observation" readonly dense rows="2" />
</template>
<template #column-salesPersonFk="{ row }">
<span class="link" @click.stop>
{{ row.salesPersonName }}
<WorkerDescriptorProxy :id="row.salesPersonFk" />
</span>
</template>
<template #column-departmentFk="{ row }">
<span class="link" @click.stop>
{{ row.departmentName }}
@ -265,8 +241,6 @@ function exprBuilder(param, value) {
es:
Add observation: Añadir observación
Client: Cliente
Is worker: Es trabajador
Salesperson: Comercial
Department: Departamento
Country: País
P. Method: F. Pago
@ -281,5 +255,5 @@ es:
Credit I.: Crédito A.
Credit insurance: Crédito asegurado
From: Desde
Has recovery: Tiene recobro
Has recovery: Recobro
</i18n>

View File

@ -15,19 +15,12 @@ const props = defineProps({
},
});
const salespersons = ref();
const countries = ref();
const authors = ref();
const departments = ref();
</script>
<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) => (authors = data)"
@ -62,29 +55,6 @@ const departments = ref();
@update:model-value="searchFn()"
/>
</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">
<QItemSection v-if="departments">
<VnSelect
@ -219,7 +189,6 @@ const departments = ref();
en:
params:
clientFk: Client
salesPersonFk: Salesperson
countryFk: Country
paymentMethod: P. Method
balance: Balance D.
@ -230,7 +199,6 @@ en:
es:
params:
clientFk: Cliente
salesPersonFk: Comercial
countryFk: País
paymentMethod: F. Pago
balance: Saldo V.
@ -239,7 +207,6 @@ es:
credit: Crédito A.
defaulterSinced: Desde
Client: Cliente
Salesperson: Comercial
Departments: Departamentos
Country: País
P. Method: F. Pago

View File

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

View File

@ -5,7 +5,7 @@ import { useRoute } from 'vue-router';
import axios from 'axios';
import { getClientRisk } from '../composables/getClientRisk';
import { useDialogPluginComponent } from 'quasar';
import FormModelPopup from 'components/FormModelPopup.vue';
import { usePrintService } from 'composables/usePrintService';
import useNotify from 'src/composables/useNotify.js';
import FetchData from 'components/FetchData.vue';
@ -74,26 +74,24 @@ onBeforeMount(() => {
urlCreate.value = `Clients/${route.params.id}/createReceipt`;
});
function setPaymentType(accounting) {
function setPaymentType(data, accounting) {
data.bankFk = accounting.id;
if (!accounting) return;
accountingType.value = accounting.accountingType;
initialData.description = [];
initialData.payed = Date.vnNew();
data.description = [];
data.payed = Date.vnNew();
isCash.value = accountingType.value.code == 'cash';
viewReceipt.value = isCash.value;
if (accountingType.value.daysInFuture)
initialData.payed.setDate(
initialData.payed.getDate() + accountingType.value.daysInFuture,
);
data.payed.setDate(data.payed.getDate() + accountingType.value.daysInFuture);
maxAmount.value = accountingType.value && accountingType.value.maxAmount;
if (accountingType.value.code == 'compensation')
return (initialData.description = '');
if (accountingType.value.code == 'compensation') return (data.description = '');
let descriptions = [];
if (accountingType.value.receiptDescription)
descriptions.push(accountingType.value.receiptDescription);
if (initialData.description) descriptions.push(initialData.description);
initialData.description = descriptions.join(', ');
if (data.description) descriptions.push(data.description);
data.description = descriptions.join(', ');
}
const calculateFromAmount = (event) => {
@ -113,7 +111,6 @@ function onBeforeSave(data) {
if (isCash.value && shouldSendEmail.value && !data.email)
return notify(t('There is no assigned email for this client'), 'negative');
data.bankFk = data.bankFk?.id;
return data;
}
@ -181,42 +178,19 @@ async function getAmountPaid() {
auto-load
url="Clients/findOne"
/>
<FormModel
<FormModelPopup
ref="formModelRef"
:form-initial-data="initialData"
:observe-form-changes="false"
:url-create="urlCreate"
:mapper="onBeforeSave"
@on-data-saved="onDataSaved"
prevent-submit
:prevent-submit="true"
>
<template #form="{ data, validate }">
<span ref="closeButton" class="row justify-end close-icon" v-close-popup>
<QIcon name="close" size="sm" />
</span>
<template #form-inputs="{ data, validate }">
<h5 class="q-mt-none">{{ t('New payment') }}</h5>
<VnRow>
<VnInputDate
:label="t('Date')"
:required="true"
v-model="data.payed"
/>
<VnSelect
:label="t('Company')"
:options="companyOptions"
:required="true"
:rules="validate('entry.companyFk')"
hide-selected
option-label="code"
option-value="id"
v-model="data.companyFk"
@update:model-value="getAmountPaid()"
/>
</VnRow>
<VnRow>
<VnSelect
autofocus
:label="t('Bank')"
v-model="data.bankFk"
url="Accountings"
@ -225,9 +199,10 @@ async function getAmountPaid() {
sort-by="id"
:limit="0"
@update:model-value="
(value, options) => setPaymentType(value, options)
(value, options) => setPaymentType(data, value, options)
"
:emit-value="false"
data-cy="paymentBank"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
@ -245,8 +220,28 @@ async function getAmountPaid() {
@update:model-value="calculateFromAmount($event)"
clearable
v-model.number="data.amountPaid"
data-cy="paymentAmount"
/>
</VnRow>
<VnRow>
<VnInputDate
:label="t('Date')"
v-model="data.payed"
:required="true"
/>
<VnSelect
:label="t('Company')"
:options="companyOptions"
:required="true"
:rules="validate('entry.companyFk')"
hide-selected
option-label="code"
option-value="id"
v-model="data.companyFk"
@update:model-value="getAmountPaid()"
/>
</VnRow>
<div v-if="data.bankFk?.accountingType?.code == 'compensation'">
<div class="text-h6">
{{ t('Compensation') }}
@ -287,27 +282,8 @@ async function getAmountPaid() {
<QCheckbox v-model="shouldSendEmail" :label="t('Send email')" />
</VnRow>
</div>
<div class="q-mt-lg row justify-end">
<QBtn
:disabled="formModelRef.isLoading"
:label="t('globals.cancel')"
:loading="formModelRef.isLoading"
class="q-ml-sm"
color="primary"
flat
type="reset"
v-close-popup
/>
<QBtn
:disabled="formModelRef.isLoading"
:label="t('globals.save')"
:loading="formModelRef.isLoading"
color="primary"
@click="formModelRef.save()"
/>
</div>
</template>
</FormModel>
</FormModelPopup>
</QDialog>
</template>

View File

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

View File

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

View File

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

View File

@ -146,9 +146,8 @@ async function deleteEntry() {
<template>
<CardDescriptor
ref="entryDescriptorRef"
:url="`Entries/${entityId}`"
:userFilter="entryFilter"
:filter="entryFilter"
title="supplier.nickname"
data-key="Entry"
width="lg-width"

View File

@ -20,14 +20,21 @@ const tagValues = ref([]);
</script>
<template>
<FetchData
url="TicketRequests/getItemTypeWorker"
auto-load
:filter="{ fields: ['id', 'nickname'], order: 'nickname ASC' }"
@on-fetch="(data) => (itemTypeWorkersOptions = data)"
/>
<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"
:label="t('components.itemsFilterPanel.buyerFk')"
v-model="params.buyerFk"
:options="itemTypeWorkersOptions"
option-value="id"
option-label="nickname"
:fields="['id', 'nickname']"
sort-by="nickname ASC"

View File

@ -283,7 +283,11 @@ onBeforeMount(async () => {
</script>
<template>
<VnSection :data-key="dataKey" prefix="entry">
<VnSection
:data-key="dataKey"
prefix="entry"
:array-data-props="{ url: 'Entries/filter' }"
>
<template #advanced-menu>
<EntryFilter :data-key="dataKey" />
</template>

View File

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

View File

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

View File

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

View File

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

View File

@ -8,7 +8,7 @@ import { useSummaryDialog } from 'src/composables/useSummaryDialog';
import { usePrintService } from 'src/composables/usePrintService';
import VnTable from 'src/components/VnTable/VnTable.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 axios from 'axios';
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 VnInput from 'src/components/common/VnInput.vue';
import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vue';
import DepartmentDescriptorProxy from '../Worker/Department/Card/DepartmentDescriptorProxy.vue';
import VnSection from 'src/components/common/VnSection.vue';
const { t } = useI18n();
@ -54,6 +55,14 @@ const columns = computed(() => [
name: 'id',
},
},
{
align: 'left',
name: 'issued',
label: t('invoiceOut.summary.issued'),
component: 'date',
format: (row) => toDate(row.issued),
columnField: { component: null },
},
{
align: 'left',
name: 'ref',
@ -86,6 +95,20 @@ const columns = computed(() => [
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',
name: 'companyFk',
@ -185,7 +208,7 @@ watchEffect(selectedRows);
prefix="invoiceOut"
:array-data-props="{
url: 'InvoiceOuts/filter',
order: ['id DESC'],
order: 'id DESC',
}"
>
<template #advanced-menu>
@ -229,8 +252,14 @@ watchEffect(selectedRows);
<CustomerDescriptorProxy :id="row.clientFk" />
</span>
</template>
<template #column-departmentFk="{ row }">
<span class="link" @click.stop>
{{ dashIfEmpty(row.departmentName) }}
<DepartmentDescriptorProxy :id="row?.departmentFk" />
</span>
</template>
<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="q-col-gutter-xs">
<VnRow fixed>
@ -396,7 +425,6 @@ watchEffect(selectedRows);
:label="
t('invoiceOutList.tableVisibleColumns.taxArea')
"
:options="taxAreasOptions"
option-label="code"
option-value="code"
/>

View File

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

View File

@ -20,7 +20,7 @@ const props = defineProps({
<VnFilterPanel
:data-key="props.dataKey"
:search-button="true"
:un-removable-params="['from', 'to']"
:unremovable-params="['from', 'to']"
:hidden-tags="['from', 'to']"
>
<template #tags="{ tag, formatFn }">
@ -129,12 +129,15 @@ const props = defineProps({
</QItem>
<QItem>
<QItemSection>
<VnSelectWorker
:label="t('invoiceOut.negativeBases.comercial')"
v-model="params.workerName"
option-value="name"
is-outlined
@update:model-value="searchFn()"
<VnSelect
outlined
dense
rounded
:label="t('globals.params.departmentFk')"
v-model="params.departmentFk"
option-value="id"
option-label="name"
url="Departments"
/>
</QItemSection>
</QItem>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,11 +1,11 @@
<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 filter from './ItemTypeFilter.js';
</script>
<template>
<VnCardBeta
<VnCard
data-key="ItemType"
url="ItemTypes"
:filter="filter"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,11 +1,11 @@
<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 filter from './OrderFilter.js';
</script>
<template>
<VnCardBeta
<VnCard
data-key="Order"
url="Orders"
:filter="filter"

View File

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

View File

@ -10,14 +10,14 @@ export default {
relation: 'client',
scope: {
fields: [
'salesPersonFk',
'departmentFk',
'name',
'isActive',
'isFreezed',
'isTaxDataChecked',
],
include: {
relation: 'salesPersonUser',
relation: 'department',
scope: { fields: ['id', 'name'] },
},
},

View File

@ -6,7 +6,6 @@ import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnSelect from 'components/common/VnSelect.vue';
import VnInputDate from 'components/common/VnInputDate.vue';
import VnInput from 'components/common/VnInput.vue';
import VnSelectWorker from 'src/components/common/VnSelectWorker.vue';
const { t } = useI18n();
const props = defineProps({
@ -62,15 +61,15 @@ const sourceList = ref([]);
outlined
rounded
/>
<VnSelectWorker
:label="t('globals.salesPerson')"
v-model="params.workerFk"
:params="{
departmentCodes: ['VT'],
}"
dense
<VnSelect
outlined
dense
rounded
:label="t('globals.params.departmentFk')"
v-model="params.departmentFk"
option-value="id"
option-label="name"
url="Departments"
/>
<VnInputDate
v-model="params.from"
@ -125,7 +124,6 @@ en:
search: Includes
clientFk: Client
agencyModeFk: Agency
salesPersonFk: Sales Person
from: From
to: To
orderFk: Order
@ -136,7 +134,6 @@ en:
showEmpty: Show Empty
customerId: Customer ID
agency: Agency
salesPerson: Sales Person
fromLanded: From Landed
toLanded: To Landed
orderId: Order ID
@ -149,7 +146,6 @@ es:
search: Búsqueda
clientFk: Cliente
agencyModeFk: Agencia
salesPersonFk: Comercial
from: Desde
to: Hasta
orderFk: Cesta
@ -160,7 +156,6 @@ es:
showEmpty: Mostrar vacías
customerId: ID Cliente
agency: Agencia
salesPerson: Comercial
fromLanded: Desde F. entrega
toLanded: Hasta F. entrega
orderId: ID Cesta

View File

@ -10,12 +10,12 @@ import axios from 'axios';
import OrderSummary from 'pages/Order/Card/OrderSummary.vue';
import OrderFilter from './Card/OrderFilter.vue';
import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vue';
import WorkerDescriptorProxy from '../Worker/Card/WorkerDescriptorProxy.vue';
import VnTable from 'src/components/VnTable/VnTable.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import VnSection from 'src/components/common/VnSection.vue';
import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue';
import { getAddresses } from '../Customer/composables/getAddresses';
const { t } = useI18n();
@ -59,22 +59,17 @@ const columns = computed(() => [
},
{
align: 'left',
name: 'salesPersonFk',
label: t('module.salesPerson'),
columnFilter: {
component: 'select',
inWhere: true,
attrs: {
url: 'Workers/activeWithInheritedRole',
fields: ['id', 'name'],
where: { role: 'salesPerson' },
useLike: false,
optionValue: 'id',
optionLabel: 'name',
optionFilter: 'firstName',
},
name: 'departmentFk',
label: t('customer.summary.team'),
component: 'select',
attrs: {
url: 'Departments',
},
format: (row) => row?.name,
create: true,
columnField: {
component: null,
},
format: (row, dashIfEmpty) => dashIfEmpty(row.departmentName),
},
{
align: 'center',
@ -156,9 +151,7 @@ const columns = computed(() => [
onMounted(async () => {
if (!route.query) return;
if (route.query?.createForm) {
const query = JSON.parse(route.query?.createForm);
formInitialData.value = query;
await onClientSelected({ ...formInitialData.value, clientFk: query?.clientFk });
await onClientSelected(JSON.parse(route.query?.createForm));
} else if (route.query?.table) {
const query = JSON.parse(route.query?.table);
const clientFk = query?.clientFk;
@ -177,7 +170,6 @@ watch(
tableRef.value.create.formInitialData = formInitialData.value;
}
},
{ immediate: true },
);
async function onClientSelected({ clientFk }, formData = {}) {
@ -191,13 +183,17 @@ async function onClientSelected({ clientFk }, formData = {}) {
addressOptions.value = data;
formData.defaultAddressFk = data[0].client.defaultAddressFk;
formData.addressId = formData.defaultAddressFk;
formInitialData.value = { addressId: formData.addressId, clientFk };
formInitialData.value = { ...formData, clientFk };
await fetchAgencies(formData);
}
async function fetchAgencies({ landed, addressId }) {
if (!landed || !addressId) return (agencyList.value = []);
async function fetchAgencies(formData) {
const { landed, addressId } = formData;
if (!landed || !addressId) {
formData.defaultAddressFk = formInitialData.value.defaultAddressFk;
return (agencyList.value = []);
}
const { data } = await axios.get('Agencies/landsThatDay', {
params: {
@ -220,6 +216,11 @@ const getDateColor = (date) => {
if (difference == 0) return 'bg-warning';
if (difference < 0) return 'bg-success';
};
const isDefaultAddress = (opt, data) => {
const addressId = data.defaultAddressFk ?? data.addressId;
return addressId === opt.id && opt.isActive;
};
</script>
<template>
@ -258,10 +259,10 @@ const getDateColor = (date) => {
<CustomerDescriptorProxy :id="row?.clientFk" />
</span>
</template>
<template #column-salesPersonFk="{ row }">
<template #column-departmentFk="{ row }">
<span class="link" @click.stop>
{{ row?.name }}
<WorkerDescriptorProxy :id="row?.salesPersonFk" />
{{ row?.departmentName }}
<DepartmentDescriptorProxy :id="row?.departmentFk" />
</span>
</template>
<template #column-landed="{ row }">
@ -310,10 +311,7 @@ const getDateColor = (date) => {
>
<QItemSection style="min-width: min-content" avatar>
<QIcon
v-if="
scope.opt.isActive &&
data.defaultAddressFk === scope.opt.id
"
v-if="isDefaultAddress(scope.opt, data)"
size="sm"
color="grey"
name="star"

View File

@ -8,7 +8,6 @@ module:
hour: Hour
agency: Agency
total: Total
salesPerson: Sales Person
address: Address
cerateOrder: Create order
lines:
@ -22,8 +21,6 @@ lines:
params:
tagGroups: Tags
order:
field:
salesPersonFk: Sales Person
form:
clientFk: Client
addressFk: Address

View File

@ -8,7 +8,6 @@ module:
hour: Hora
agency: Agencia
total: Total
salesPerson: Comercial
address: Dirección
cerateOrder: Crear cesta
lines:
@ -22,8 +21,6 @@ lines:
params:
tagGroups: Tags
order:
field:
salesPersonFk: Comercial
form:
clientFk: Cliente
addressFk: Dirección

View File

@ -2,10 +2,13 @@
import { computed } from 'vue';
import { useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
import VnTable from 'components/VnTable/VnTable.vue';
import VnSection from 'src/components/common/VnSection.vue';
import AgencySummary from 'pages/Route/Agency/Card/AgencySummary.vue';
const { t } = useI18n();
const { viewSummary } = useSummaryDialog();
const router = useRouter();
const dataKey = 'AgencyList';
function navigate(id) {
@ -40,16 +43,22 @@ const columns = computed(() => [
},
{
align: 'left',
label: t('isOwn'),
label: t('agency.isOwn'),
name: 'isOwn',
component: 'checkbox',
columnFilter: {
inWhere: true,
},
cardVisible: true,
},
{
align: 'left',
label: t('isAnyVolumeAllowed'),
label: t('agency.isAnyVolumeAllowed'),
name: 'isAnyVolumeAllowed',
component: 'checkbox',
columnFilter: {
inWhere: true,
},
cardVisible: true,
},
{
@ -58,9 +67,10 @@ const columns = computed(() => [
name: 'tableActions',
actions: [
{
title: t('Client ticket list'),
title: t('globals.pageTitles.summary'),
icon: 'preview',
action: (row) => navigate(row.id),
action: (row) => viewSummary(row?.id, AgencySummary),
isPrimary: true,
},
],
},
@ -82,7 +92,7 @@ const columns = computed(() => [
<VnTable
:data-key
:columns="columns"
is-editable="false"
:is-editable="false"
:right-search="false"
:use-model="true"
redirect="route/agency"
@ -103,11 +113,3 @@ const columns = computed(() => [
justify-content: center;
}
</style>
<i18n>
es:
isOwn: Tiene propietario
isAnyVolumeAllowed: Permite cualquier volumen
en:
isOwn: Has owner
isAnyVolumeAllowed: Allows any volume
</i18n>

View File

@ -21,7 +21,7 @@ const warehouses = ref([]);
@on-fetch="(data) => (warehouses = data)"
auto-load
/>
<FormModel :update-url="`Agencies/${routeId}`" model="Agency" auto-load>
<FormModel :url-update="`Agencies/${routeId}`" model="Agency" auto-load>
<template #form="{ data }">
<VnRow>
<VnInput v-model="data.name" :label="t('globals.name')" />

View File

@ -1,7 +1,7 @@
<script setup>
import AgencyDescriptor from 'pages/Route/Agency/Card/AgencyDescriptor.vue';
import VnCardBeta from 'src/components/common/VnCardBeta.vue';
import VnCard from 'src/components/common/VnCard.vue';
</script>
<template>
<VnCardBeta data-key="Agency" url="Agencies" :descriptor="AgencyDescriptor" />
<VnCard data-key="Agency" url="Agencies" :descriptor="AgencyDescriptor" />
</template>

View File

@ -0,0 +1,20 @@
<script setup>
import AgencyDescriptor from 'pages/Route/Agency/Card/AgencyDescriptor.vue';
import AgencySummary from './AgencySummary.vue';
const $props = defineProps({
id: {
type: Number,
required: true,
},
summary: {
type: Object,
default: null,
},
});
</script>
<template>
<QPopupProxy>
<AgencyDescriptor v-if="$props.id" :id="$props.id" :summary="AgencySummary" />
</QPopupProxy>
</template>

View File

@ -6,29 +6,31 @@ import { useI18n } from 'vue-i18n';
import CardSummary from 'components/ui/CardSummary.vue';
import VnLv from 'components/ui/VnLv.vue';
import VnTitle from 'src/components/common/VnTitle.vue';
import VnCheckbox from 'components/common/VnCheckbox.vue';
const route = useRoute();
const $props = defineProps({ id: { type: Number, default: 0 } });
const { t } = useI18n();
const entityId = computed(() => $props.id || useRoute().params.id);
const entityId = computed(() => $props.id || route.params.id);
</script>
<template>
<div class="q-pa-md">
<CardSummary :url="`Agencies/${entityId}`" data-key="Agency">
<CardSummary :url="`Agencies/${entityId}`" data-key="Agency" module-name="Agency">
<template #header="{ entity: agency }">{{ agency.name }}</template>
<template #body="{ entity: agency }">
<QCard class="vn-one">
<VnTitle
:url="`#/agency/${entityId}/basic-data`"
:url="`#/${route.meta.moduleName.toLowerCase()}/agency/${entityId}/basic-data`"
:text="t('globals.pageTitles.basicData')"
/>
<VnLv :label="t('globals.name')" :value="agency.name" />
<QCheckbox
<VnCheckbox
:label="t('agency.isOwn')"
v-model="agency.isOwn"
:disable="true"
/>
<QCheckbox
<VnCheckbox
:label="t('agency.isAnyVolumeAllowed')"
v-model="agency.isAnyVolumeAllowed"
:disable="true"

View File

@ -80,6 +80,7 @@ async function deleteWorCenter(id) {
color="primary"
round
flat
data-cy="removeWorkCenterBtn"
/>
</QItemSection>
</QItem>

View File

@ -1,11 +1,12 @@
agency:
search: Search agency
searchInfo: You can search by name
searchInfo: You can search by name and by id
isOwn: Own
isAnyVolumeAllowed: Any volume allowed
removeItem: Agency removed successfully
notification:
removeItemError: Error removing agency
removeItem: WorkCenter removed successfully
removeItemError: Error removing work center
removeItem: Work center removed successfully
pageTitles:
agency: Agency
searchBar:

View File

@ -1,15 +1,14 @@
agency:
search: Buscar agencia
searchInfo: Puedes buscar por nombre
searchInfo: Puedes buscar por nombre y por id
isOwn: Propio
isAnyVolumeAllowed: Cualquier volumen
removeItem: Agencia eliminada correctamente
notification:
removeItemError: Error al eliminar la agencia
removeItemError: Error al eliminar la el centro de trabajo
removeItem: Centro de trabajo eliminado correctamente
pageTitles:
agency: Agencia
searchBar:
info: Puedes buscar por nombre o id
label: Buscar agencia...

View File

@ -1,10 +1,10 @@
<script setup>
import RouteDescriptor from 'pages/Route/Card/RouteDescriptor.vue';
import VnCardBeta from 'src/components/common/VnCardBeta.vue';
import VnCard from 'src/components/common/VnCard.vue';
import filter from './RouteFilter.js';
</script>
<template>
<VnCardBeta
<VnCard
data-key="Route"
url="Routes"
:filter="filter"

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