Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix-front into 6317-createVnCurrency
This commit is contained in:
commit
151d08f0bb
|
@ -1,5 +1,6 @@
|
||||||
FROM node:stretch-slim
|
FROM node:stretch-slim
|
||||||
RUN npm install -g @quasar/cli
|
RUN corepack enable pnpm
|
||||||
|
RUN pnpm install -g @quasar/cli
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY dist/spa ./
|
COPY dist/spa ./
|
||||||
CMD ["quasar", "serve", "./", "--history", "--hostname", "0.0.0.0"]
|
CMD ["quasar", "serve", "./", "--history", "--hostname", "0.0.0.0"]
|
|
@ -1,99 +1,119 @@
|
||||||
#!/usr/bin/env groovy
|
#!/usr/bin/env groovy
|
||||||
|
|
||||||
|
def PROTECTED_BRANCH
|
||||||
|
|
||||||
|
def BRANCH_ENV = [
|
||||||
|
test: 'test',
|
||||||
|
master: 'production'
|
||||||
|
]
|
||||||
|
|
||||||
|
node {
|
||||||
|
stage('Setup') {
|
||||||
|
env.FRONT_REPLICAS = 1
|
||||||
|
env.NODE_ENV = BRANCH_ENV[env.BRANCH_NAME] ?: 'dev'
|
||||||
|
|
||||||
|
PROTECTED_BRANCH = [
|
||||||
|
'dev',
|
||||||
|
'test',
|
||||||
|
'master'
|
||||||
|
].contains(env.BRANCH_NAME)
|
||||||
|
|
||||||
|
// https://www.jenkins.io/doc/book/pipeline/jenkinsfile/#using-environment-variables
|
||||||
|
echo "NODE_NAME: ${env.NODE_NAME}"
|
||||||
|
echo "WORKSPACE: ${env.WORKSPACE}"
|
||||||
|
|
||||||
|
configFileProvider([
|
||||||
|
configFile(fileId: 'salix-front.properties',
|
||||||
|
variable: 'PROPS_FILE')
|
||||||
|
]) {
|
||||||
|
def props = readProperties file: PROPS_FILE
|
||||||
|
props.each {key, value -> env."${key}" = value }
|
||||||
|
props.each {key, value -> echo "${key}: ${value}" }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PROTECTED_BRANCH) {
|
||||||
|
configFileProvider([
|
||||||
|
configFile(fileId: "salix-front.branch.${env.BRANCH_NAME}",
|
||||||
|
variable: 'BRANCH_PROPS_FILE')
|
||||||
|
]) {
|
||||||
|
def props = readProperties file: BRANCH_PROPS_FILE
|
||||||
|
props.each {key, value -> env."${key}" = value }
|
||||||
|
props.each {key, value -> echo "${key}: ${value}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pipeline {
|
pipeline {
|
||||||
agent any
|
agent any
|
||||||
options {
|
options {
|
||||||
disableConcurrentBuilds()
|
disableConcurrentBuilds()
|
||||||
}
|
}
|
||||||
|
tools {
|
||||||
|
nodejs 'node-v18'
|
||||||
|
}
|
||||||
environment {
|
environment {
|
||||||
PROJECT_NAME = 'lilium'
|
PROJECT_NAME = 'lilium'
|
||||||
STACK_NAME = "${env.PROJECT_NAME}-${env.BRANCH_NAME}"
|
STACK_NAME = "${env.PROJECT_NAME}-${env.BRANCH_NAME}"
|
||||||
}
|
}
|
||||||
stages {
|
stages {
|
||||||
stage('Checkout') {
|
|
||||||
steps {
|
|
||||||
script {
|
|
||||||
switch (env.BRANCH_NAME) {
|
|
||||||
case 'master':
|
|
||||||
env.NODE_ENV = 'production'
|
|
||||||
env.FRONT_REPLICAS = 2
|
|
||||||
break
|
|
||||||
case 'test':
|
|
||||||
env.NODE_ENV = 'test'
|
|
||||||
env.FRONT_REPLICAS = 1
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setEnv()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage('Install') {
|
stage('Install') {
|
||||||
environment {
|
environment {
|
||||||
NODE_ENV = ""
|
NODE_ENV = ""
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
nodejs('node-v18') {
|
sh 'pnpm install --prefer-offline'
|
||||||
sh 'npm install --no-audit --prefer-offline'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Test') {
|
stage('Test') {
|
||||||
when { not { anyOf {
|
when {
|
||||||
branch 'test'
|
expression { !PROTECTED_BRANCH }
|
||||||
branch 'master'
|
}
|
||||||
}}}
|
|
||||||
environment {
|
environment {
|
||||||
NODE_ENV = ""
|
NODE_ENV = ""
|
||||||
}
|
}
|
||||||
parallel {
|
|
||||||
stage('Frontend') {
|
|
||||||
steps {
|
steps {
|
||||||
nodejs('node-v18') {
|
sh 'pnpm run test:unit:ci'
|
||||||
sh 'npm run test:unit:ci'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
junit(
|
||||||
|
testResults: 'junitresults.xml',
|
||||||
|
allowEmptyResults: true
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Build') {
|
stage('Build') {
|
||||||
when { anyOf {
|
when {
|
||||||
branch 'test'
|
expression { PROTECTED_BRANCH }
|
||||||
branch 'master'
|
}
|
||||||
}}
|
|
||||||
environment {
|
environment {
|
||||||
CREDENTIALS = credentials('docker-registry')
|
CREDENTIALS = credentials('docker-registry')
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
nodejs('node-v18') {
|
|
||||||
sh 'quasar build'
|
sh 'quasar build'
|
||||||
|
script {
|
||||||
|
def packageJson = readJSON file: 'package.json'
|
||||||
|
env.VERSION = packageJson.version
|
||||||
}
|
}
|
||||||
dockerBuild()
|
dockerBuild()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Deploy') {
|
stage('Deploy') {
|
||||||
when { anyOf {
|
when {
|
||||||
branch 'test'
|
expression { PROTECTED_BRANCH }
|
||||||
branch 'master'
|
}
|
||||||
}}
|
|
||||||
environment {
|
environment {
|
||||||
DOCKER_HOST = "${env.SWARM_HOST}"
|
DOCKER_HOST = "${env.SWARM_HOST}"
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
|
script {
|
||||||
|
def packageJson = readJSON file: 'package.json'
|
||||||
|
env.VERSION = packageJson.version
|
||||||
|
}
|
||||||
sh "docker stack deploy --with-registry-auth --compose-file docker-compose.yml ${env.STACK_NAME}"
|
sh "docker stack deploy --with-registry-auth --compose-file docker-compose.yml ${env.STACK_NAME}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
post {
|
|
||||||
always {
|
|
||||||
script {
|
|
||||||
if (!['master', 'test'].contains(env.BRANCH_NAME)) {
|
|
||||||
try {
|
|
||||||
junit 'junitresults.xml'
|
|
||||||
junit 'junit.xml'
|
|
||||||
} catch (e) {
|
|
||||||
echo e.toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,13 @@ Lilium frontend
|
||||||
## Install the dependencies
|
## Install the dependencies
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install
|
bun install
|
||||||
```
|
```
|
||||||
|
|
||||||
### Install quasar cli
|
### Install quasar cli
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo npm install -g @quasar/cli
|
sudo bun install -g @quasar/cli
|
||||||
```
|
```
|
||||||
|
|
||||||
### Start the app in development mode (hot-code reloading, error reporting, etc.)
|
### Start the app in development mode (hot-code reloading, error reporting, etc.)
|
||||||
|
@ -23,7 +23,7 @@ quasar dev
|
||||||
### Run unit tests
|
### Run unit tests
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run test:unit
|
bun run test:unit
|
||||||
```
|
```
|
||||||
|
|
||||||
### Run e2e tests
|
### Run e2e tests
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
20
package.json
20
package.json
|
@ -1,10 +1,11 @@
|
||||||
{
|
{
|
||||||
"name": "salix-front",
|
"name": "salix-front",
|
||||||
"version": "24.10.0",
|
"version": "24.12.0",
|
||||||
"description": "Salix frontend",
|
"description": "Salix frontend",
|
||||||
"productName": "Salix",
|
"productName": "Salix",
|
||||||
"author": "Verdnatura",
|
"author": "Verdnatura",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"packageManager": "pnpm@8.15.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint --ext .js,.vue ./",
|
"lint": "eslint --ext .js,.vue ./",
|
||||||
"format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore",
|
"format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore",
|
||||||
|
@ -16,12 +17,12 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@quasar/cli": "^2.3.0",
|
"@quasar/cli": "^2.3.0",
|
||||||
"@quasar/extras": "^1.16.4",
|
"@quasar/extras": "^1.16.9",
|
||||||
"axios": "^1.4.0",
|
"axios": "^1.4.0",
|
||||||
"chromium": "^3.0.3",
|
"chromium": "^3.0.3",
|
||||||
"croppie": "^2.6.5",
|
"croppie": "^2.6.5",
|
||||||
"pinia": "^2.1.3",
|
"pinia": "^2.1.3",
|
||||||
"quasar": "^2.12.0",
|
"quasar": "^2.14.5",
|
||||||
"validator": "^13.9.0",
|
"validator": "^13.9.0",
|
||||||
"vue": "^3.3.4",
|
"vue": "^3.3.4",
|
||||||
"vue-i18n": "^9.2.2",
|
"vue-i18n": "^9.2.2",
|
||||||
|
@ -30,9 +31,9 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@intlify/unplugin-vue-i18n": "^0.8.1",
|
"@intlify/unplugin-vue-i18n": "^0.8.1",
|
||||||
"@pinia/testing": "^0.1.2",
|
"@pinia/testing": "^0.1.2",
|
||||||
"@quasar/app-vite": "^1.4.3",
|
"@quasar/app-vite": "^1.7.3",
|
||||||
"@quasar/quasar-app-extension-testing-unit-vitest": "^0.3.0",
|
"@quasar/quasar-app-extension-testing-unit-vitest": "^0.4.0",
|
||||||
"@vue/test-utils": "^2.3.2",
|
"@vue/test-utils": "^2.4.4",
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
"cypress": "^12.13.0",
|
"cypress": "^12.13.0",
|
||||||
"eslint": "^8.41.0",
|
"eslint": "^8.41.0",
|
||||||
|
@ -46,11 +47,12 @@
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20 || ^18 || ^16",
|
"node": "^20 || ^18 || ^16",
|
||||||
"npm": ">= 8.1.2",
|
"npm": ">= 8.1.2",
|
||||||
"yarn": ">= 1.21.1"
|
"yarn": ">= 1.21.1",
|
||||||
|
"bun": ">= 1.0.25"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"@vitejs/plugin-vue": "^4.0.0",
|
"@vitejs/plugin-vue": "^5.0.4",
|
||||||
"vite": "^4.3.5",
|
"vite": "^5.1.4",
|
||||||
"vitest": "^0.31.1"
|
"vitest": "^0.31.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -176,8 +176,8 @@ async function remove(data) {
|
||||||
.dialog({
|
.dialog({
|
||||||
component: VnConfirm,
|
component: VnConfirm,
|
||||||
componentProps: {
|
componentProps: {
|
||||||
title: t('confirmDeletion'),
|
title: t('globals.confirmDeletion'),
|
||||||
message: t('confirmDeletionMessage'),
|
message: t('globals.confirmDeletionMessage'),
|
||||||
newData,
|
newData,
|
||||||
ids,
|
ids,
|
||||||
},
|
},
|
||||||
|
@ -317,16 +317,3 @@ watch(formUrl, async () => {
|
||||||
color="primary"
|
color="primary"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<i18n>
|
|
||||||
{
|
|
||||||
"en": {
|
|
||||||
"confirmDeletion": "Confirm deletion",
|
|
||||||
"confirmDeletionMessage": "Are you sure you want to delete this?"
|
|
||||||
},
|
|
||||||
"es": {
|
|
||||||
"confirmDeletion": "Confirmar eliminación",
|
|
||||||
"confirmDeletionMessage": "Seguro que quieres eliminar?"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</i18n>
|
|
||||||
|
|
|
@ -272,7 +272,7 @@ const makeRequest = async () => {
|
||||||
class="cursor-pointer q-mr-sm"
|
class="cursor-pointer q-mr-sm"
|
||||||
@click="openInputFile()"
|
@click="openInputFile()"
|
||||||
>
|
>
|
||||||
<!-- <QTooltip>{{ t('Select a file') }}</QTooltip> -->
|
<!-- <QTooltip>{{ t('globals.selectFile') }}</QTooltip> -->
|
||||||
</QIcon>
|
</QIcon>
|
||||||
<QIcon name="info" class="cursor-pointer">
|
<QIcon name="info" class="cursor-pointer">
|
||||||
<QTooltip>{{
|
<QTooltip>{{
|
||||||
|
|
|
@ -59,11 +59,4 @@ async function fetch(fetchFilter = {}) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const render = () => {
|
|
||||||
return h('div', []);
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
|
||||||
<render />
|
|
||||||
</template>
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { onMounted, onUnmounted, computed, ref, watch, nextTick } from 'vue';
|
import { onMounted, onUnmounted, computed, ref, watch, nextTick } from 'vue';
|
||||||
|
import { onBeforeRouteLeave } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import { useState } from 'src/composables/useState';
|
import { useState } from 'src/composables/useState';
|
||||||
|
@ -8,6 +9,7 @@ import { useStateStore } from 'stores/useStateStore';
|
||||||
import { useValidator } from 'src/composables/useValidator';
|
import { useValidator } from 'src/composables/useValidator';
|
||||||
import useNotify from 'src/composables/useNotify.js';
|
import useNotify from 'src/composables/useNotify.js';
|
||||||
import SkeletonForm from 'components/ui/SkeletonForm.vue';
|
import SkeletonForm from 'components/ui/SkeletonForm.vue';
|
||||||
|
import VnConfirm from './ui/VnConfirm.vue';
|
||||||
|
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const state = useState();
|
const state = useState();
|
||||||
|
@ -59,6 +61,10 @@ const $props = defineProps({
|
||||||
type: Function,
|
type: Function,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
saveFn: {
|
||||||
|
type: Function,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['onFetch', 'onDataSaved']);
|
const emit = defineEmits(['onFetch', 'onDataSaved']);
|
||||||
|
@ -75,9 +81,8 @@ onMounted(async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Podemos enviarle al form la estructura de data inicial sin necesidad de fetchearla
|
// Podemos enviarle al form la estructura de data inicial sin necesidad de fetchearla
|
||||||
if ($props.formInitialData && !$props.autoLoad) {
|
state.set($props.model, $props.formInitialData ?? {});
|
||||||
state.set($props.model, $props.formInitialData);
|
if ($props.autoLoad && !$props.formInitialData) {
|
||||||
} else {
|
|
||||||
await fetch();
|
await fetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,6 +95,19 @@ onMounted(async () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onBeforeRouteLeave((to, from, next) => {
|
||||||
|
if (!hasChanges.value) next();
|
||||||
|
|
||||||
|
quasar.dialog({
|
||||||
|
component: VnConfirm,
|
||||||
|
componentProps: {
|
||||||
|
title: t('Unsaved changes will be lost'),
|
||||||
|
message: t('Are you sure exit without saving?'),
|
||||||
|
promise: () => next(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
state.unset($props.model);
|
state.unset($props.model);
|
||||||
});
|
});
|
||||||
|
@ -138,17 +156,20 @@ async function save() {
|
||||||
try {
|
try {
|
||||||
const body = $props.mapper ? $props.mapper(formData.value) : formData.value;
|
const body = $props.mapper ? $props.mapper(formData.value) : formData.value;
|
||||||
let response;
|
let response;
|
||||||
if ($props.urlCreate) {
|
if ($props.saveFn) response = await $props.saveFn(body);
|
||||||
response = await axios.post($props.urlCreate, body);
|
else
|
||||||
notify('globals.dataCreated', 'positive');
|
response = await axios[$props.urlCreate ? 'post' : 'patch'](
|
||||||
} else {
|
$props.urlCreate || $props.urlUpdate || $props.url,
|
||||||
response = await axios.patch($props.urlUpdate || $props.url, body);
|
body
|
||||||
}
|
);
|
||||||
|
if ($props.urlCreate) notify('globals.dataCreated', 'positive');
|
||||||
|
|
||||||
emit('onDataSaved', formData.value, response?.data);
|
emit('onDataSaved', formData.value, response?.data);
|
||||||
originalData.value = JSON.parse(JSON.stringify(formData.value));
|
originalData.value = JSON.parse(JSON.stringify(formData.value));
|
||||||
hasChanges.value = false;
|
hasChanges.value = false;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
notify('errors.create', 'negative');
|
console.error(err);
|
||||||
|
notify('errors.writeRequest', 'negative');
|
||||||
}
|
}
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
|
@ -249,3 +270,8 @@ watch(formUrl, async () => {
|
||||||
padding: 32px;
|
padding: 32px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Unsaved changes will be lost: Los cambios que no haya guardado se perderán
|
||||||
|
Are you sure exit without saving?: ¿Seguro que quiere salir sin guardar?
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -0,0 +1,201 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import FormModelPopup from 'components/FormModelPopup.vue';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const emit = defineEmits(['onDataSaved']);
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
model: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
defaultDmsCode: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
formInitialData: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const warehouses = ref();
|
||||||
|
const companies = ref();
|
||||||
|
const dmsTypes = ref();
|
||||||
|
const allowedContentTypes = ref();
|
||||||
|
const inputFileRef = ref();
|
||||||
|
const dms = ref({});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
defaultData();
|
||||||
|
if (!$props.formInitialData)
|
||||||
|
dms.value.description = t($props.model + 'Description', dms.value);
|
||||||
|
});
|
||||||
|
function onFileChange(files) {
|
||||||
|
dms.value.hasFileAttached = !!files;
|
||||||
|
dms.value.file = files?.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapperDms(data) {
|
||||||
|
const formData = new FormData();
|
||||||
|
const { files } = data;
|
||||||
|
if (files) formData.append(files?.name, files);
|
||||||
|
delete data.files;
|
||||||
|
|
||||||
|
const dms = {
|
||||||
|
hasFile: !!data.hasFile,
|
||||||
|
hasFileAttached: data.hasFileAttached,
|
||||||
|
reference: data.reference,
|
||||||
|
warehouseId: data.warehouseFk,
|
||||||
|
companyId: data.companyFk,
|
||||||
|
dmsTypeId: data.dmsTypeFk,
|
||||||
|
description: data.description,
|
||||||
|
};
|
||||||
|
return [formData, { params: dms }];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUrl() {
|
||||||
|
if ($props.formInitialData) return 'dms/' + $props.formInitialData.id + '/updateFile';
|
||||||
|
return `${$props.model}/${route.params.id}/uploadFile`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function save() {
|
||||||
|
const body = mapperDms(dms.value);
|
||||||
|
await axios.post(getUrl(), body[0], body[1]);
|
||||||
|
emit('onDataSaved', body[1].params);
|
||||||
|
}
|
||||||
|
|
||||||
|
function defaultData() {
|
||||||
|
if ($props.formInitialData) return (dms.value = $props.formInitialData);
|
||||||
|
return addDefaultData({
|
||||||
|
reference: route.params.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDmsTypes(data) {
|
||||||
|
dmsTypes.value = data;
|
||||||
|
if (!$props.formInitialData && $props.defaultDmsCode) {
|
||||||
|
const { id } = data.find((dmsType) => dmsType.code == $props.defaultDmsCode);
|
||||||
|
addDefaultData({ dmsTypeFk: id });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addDefaultData(data) {
|
||||||
|
Object.assign(dms.value, data);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<FetchData url="Warehouses" @on-fetch="(data) => (warehouses = data)" auto-load />
|
||||||
|
<FetchData url="Companies" @on-fetch="(data) => (companies = data)" auto-load />
|
||||||
|
<FetchData url="DmsTypes" @on-fetch="setDmsTypes" auto-load />
|
||||||
|
<FetchData
|
||||||
|
url="DmsContainers/allowedContentTypes"
|
||||||
|
@on-fetch="(data) => (allowedContentTypes = data.join(','))"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="UserConfigs/getUserConfig"
|
||||||
|
@on-fetch="addDefaultData"
|
||||||
|
:auto-load="!$props.formInitialData"
|
||||||
|
/>
|
||||||
|
<FormModelPopup
|
||||||
|
:title="formInitialData ? t('globals.edit') : t('globals.create')"
|
||||||
|
model="dms"
|
||||||
|
:form-initial-data="formInitialData"
|
||||||
|
:save-fn="save"
|
||||||
|
>
|
||||||
|
<template #form-inputs>
|
||||||
|
<div class="q-gutter-y-ms">
|
||||||
|
<VnRow>
|
||||||
|
<VnInput :label="t('globals.reference')" v-model="dms.reference" />
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('globals.company')"
|
||||||
|
v-model="dms.companyFk"
|
||||||
|
:options="companies"
|
||||||
|
option-value="id"
|
||||||
|
option-label="code"
|
||||||
|
input-debounce="0"
|
||||||
|
/>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow>
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('globals.warehouse')"
|
||||||
|
v-model="dms.warehouseFk"
|
||||||
|
:options="warehouses"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
input-debounce="0"
|
||||||
|
/>
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('globals.type')"
|
||||||
|
v-model="dms.dmsTypeFk"
|
||||||
|
:options="dmsTypes"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
input-debounce="0"
|
||||||
|
/>
|
||||||
|
</VnRow>
|
||||||
|
<QInput
|
||||||
|
:label="t('globals.description')"
|
||||||
|
v-model="dms.description"
|
||||||
|
type="textarea"
|
||||||
|
/>
|
||||||
|
<QFile
|
||||||
|
ref="inputFileRef"
|
||||||
|
:label="t('entry.buys.file')"
|
||||||
|
v-model="dms.files"
|
||||||
|
:multiple="false"
|
||||||
|
:accept="allowedContentTypes"
|
||||||
|
@update:model-value="onFileChange(dms.files)"
|
||||||
|
class="required"
|
||||||
|
:display-value="dms.file"
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<QIcon
|
||||||
|
name="vn:attach"
|
||||||
|
class="cursor-pointer"
|
||||||
|
@click="inputFileRef.pickFiles()"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('globals.selectFile') }}</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
<QIcon name="info" class="cursor-pointer">
|
||||||
|
<QTooltip>{{
|
||||||
|
t('contentTypesInfo', { allowedContentTypes })
|
||||||
|
}}</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</template>
|
||||||
|
</QFile>
|
||||||
|
<QCheckbox
|
||||||
|
v-model="dms.hasFile"
|
||||||
|
:label="t('Generate identifier for original file')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</FormModelPopup>
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
.q-gutter-y-ms {
|
||||||
|
display: grid;
|
||||||
|
row-gap: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<i18n>
|
||||||
|
en:
|
||||||
|
contentTypesInfo: Allowed file types {allowedContentTypes}
|
||||||
|
EntryDmsDescription: Reference {reference}
|
||||||
|
es:
|
||||||
|
Generate identifier for original file: Generar identificador para archivo original
|
||||||
|
contentTypesInfo: Tipos de archivo permitidos {allowedContentTypes}
|
||||||
|
EntryDmsDescription: Referencia {reference}
|
||||||
|
|
||||||
|
</i18n>
|
|
@ -0,0 +1,316 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useQuasar, QCheckbox, QBtn, QInput } from 'quasar';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnDms from 'src/components/common/VnDms.vue';
|
||||||
|
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||||
|
import { downloadFile } from 'src/composables/downloadFile';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const quasar = useQuasar();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const rows = ref();
|
||||||
|
const dmsRef = ref();
|
||||||
|
const formDialog = ref({});
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
model: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
updateModel: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
defaultDmsCode: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
filter: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const dmsFilter = {
|
||||||
|
include: {
|
||||||
|
relation: 'dms',
|
||||||
|
scope: {
|
||||||
|
fields: [
|
||||||
|
'dmsTypeFk',
|
||||||
|
'reference',
|
||||||
|
'hardCopyNumber',
|
||||||
|
'workerFk',
|
||||||
|
'description',
|
||||||
|
'hasFile',
|
||||||
|
'file',
|
||||||
|
'created',
|
||||||
|
'companyFk',
|
||||||
|
'warehouseFk',
|
||||||
|
],
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
relation: 'dmsType',
|
||||||
|
scope: {
|
||||||
|
fields: ['name'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relation: 'worker',
|
||||||
|
scope: {
|
||||||
|
fields: ['id'],
|
||||||
|
include: {
|
||||||
|
relation: 'user',
|
||||||
|
scope: {
|
||||||
|
fields: ['name'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
order: ['dmsFk DESC'],
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = computed(() => [
|
||||||
|
{
|
||||||
|
align: 'left',
|
||||||
|
field: 'id',
|
||||||
|
label: t('globals.id'),
|
||||||
|
name: 'id',
|
||||||
|
component: 'span',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'left',
|
||||||
|
field: 'type',
|
||||||
|
label: t('globals.type'),
|
||||||
|
name: 'type',
|
||||||
|
component: QInput,
|
||||||
|
props: (prop) => ({
|
||||||
|
readonly: true,
|
||||||
|
borderless: true,
|
||||||
|
'model-value': prop.row.dmsType.name,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'left',
|
||||||
|
field: 'order',
|
||||||
|
label: t('globals.order'),
|
||||||
|
name: 'order',
|
||||||
|
component: 'span',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'left',
|
||||||
|
field: 'reference',
|
||||||
|
label: t('globals.reference'),
|
||||||
|
name: 'reference',
|
||||||
|
component: 'span',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'left',
|
||||||
|
field: 'description',
|
||||||
|
label: t('globals.description'),
|
||||||
|
name: 'description',
|
||||||
|
component: 'span',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'left',
|
||||||
|
field: 'hasFile',
|
||||||
|
label: t('globals.original'),
|
||||||
|
name: 'hasFile',
|
||||||
|
component: QCheckbox,
|
||||||
|
props: (prop) => ({
|
||||||
|
disable: true,
|
||||||
|
'model-value': Boolean(prop.value),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'left',
|
||||||
|
field: 'file',
|
||||||
|
label: t('globals.file'),
|
||||||
|
name: 'file',
|
||||||
|
component: 'span',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'options',
|
||||||
|
name: 'options',
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
component: QBtn,
|
||||||
|
props: () => ({
|
||||||
|
icon: 'cloud_download',
|
||||||
|
flat: true,
|
||||||
|
color: 'primary',
|
||||||
|
}),
|
||||||
|
click: (prop) => downloadFile(prop.row.id),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: QBtn,
|
||||||
|
props: () => ({
|
||||||
|
icon: 'edit',
|
||||||
|
flat: true,
|
||||||
|
color: 'primary',
|
||||||
|
}),
|
||||||
|
click: (prop) => showFormDialog(prop.row),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: QBtn,
|
||||||
|
props: () => ({
|
||||||
|
icon: 'delete',
|
||||||
|
flat: true,
|
||||||
|
color: 'primary',
|
||||||
|
}),
|
||||||
|
click: (prop) => deleteDms(prop.row.id),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
function setData(data) {
|
||||||
|
const newData = data.map((value) => value.dms);
|
||||||
|
rows.value = newData;
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteDms(dmsFk) {
|
||||||
|
quasar
|
||||||
|
.dialog({
|
||||||
|
component: VnConfirm,
|
||||||
|
componentProps: {
|
||||||
|
title: t('globals.confirmDeletion'),
|
||||||
|
message: t('globals.confirmDeletionMessage'),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.onOk(async () => {
|
||||||
|
await axios.post(`${$props.model}/${dmsFk}/removeFile`);
|
||||||
|
const index = rows.value.findIndex((row) => row.id == dmsFk);
|
||||||
|
rows.value.splice(index, 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function showFormDialog(dms) {
|
||||||
|
if (dms) dms = parseDms(dms);
|
||||||
|
formDialog.value = {
|
||||||
|
show: true,
|
||||||
|
dms,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseDms(data) {
|
||||||
|
for (let prop in data) {
|
||||||
|
if (prop.endsWith('Fk')) data[prop.replace('Fk', 'Id')] = data[prop];
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
ref="dmsRef"
|
||||||
|
:url="$props.model"
|
||||||
|
:filter="dmsFilter"
|
||||||
|
:where="{ [$props.filter]: route.params.id }"
|
||||||
|
@on-fetch="setData"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<QTable
|
||||||
|
:columns="columns"
|
||||||
|
:pagination="{ rowsPerPage: 0 }"
|
||||||
|
:rows="rows"
|
||||||
|
class="full-width q-mt-md"
|
||||||
|
hide-bottom
|
||||||
|
row-key="clientFk"
|
||||||
|
:grid="$q.screen.lt.sm"
|
||||||
|
>
|
||||||
|
<template #body-cell="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
<QTr :props="props">
|
||||||
|
<component
|
||||||
|
v-if="props.col.component"
|
||||||
|
:is="props.col.component"
|
||||||
|
v-bind="props.col.props && props.col.props(props)"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
v-if="props.col.component == 'span'"
|
||||||
|
style="white-space: wrap"
|
||||||
|
>{{ props.value }}</span
|
||||||
|
>
|
||||||
|
</component>
|
||||||
|
</QTr>
|
||||||
|
|
||||||
|
<div class="flex justify-center" v-if="props.col.name == 'options'">
|
||||||
|
<div v-for="button of props.col.components" :key="button.id">
|
||||||
|
<component
|
||||||
|
:is="button.component"
|
||||||
|
v-bind="button.props(props)"
|
||||||
|
@click="button.click(props)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #item="props">
|
||||||
|
<div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition">
|
||||||
|
<QCard
|
||||||
|
bordered
|
||||||
|
flat
|
||||||
|
@keyup.ctrl.enter.stop="claimDevelopmentForm?.saveChanges()"
|
||||||
|
>
|
||||||
|
<QSeparator />
|
||||||
|
<QList dense>
|
||||||
|
<QItem v-for="col in props.cols" :key="col.name">
|
||||||
|
<div v-if="col.name != 'options'" class="row">
|
||||||
|
<span class="labelColor">{{ col.label }}:</span>
|
||||||
|
<span>{{ col.value }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="col.name == 'options'" class="row">
|
||||||
|
<div
|
||||||
|
v-for="button of col.components"
|
||||||
|
:key="button.id"
|
||||||
|
class="row"
|
||||||
|
>
|
||||||
|
<component
|
||||||
|
:is="button.component"
|
||||||
|
v-bind="button.props(col)"
|
||||||
|
@click="button.click(col)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</QItem>
|
||||||
|
</QList>
|
||||||
|
</QCard>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</QTable>
|
||||||
|
<QDialog v-model="formDialog.show">
|
||||||
|
<VnDms
|
||||||
|
:model="updateModel ?? model"
|
||||||
|
:default-dms-code="defaultDmsCode"
|
||||||
|
:form-initial-data="formDialog.dms"
|
||||||
|
@on-data-saved="dmsRef.fetch()"
|
||||||
|
:description="$props.description"
|
||||||
|
/>
|
||||||
|
</QDialog>
|
||||||
|
<QPageSticky position="bottom-right" :offset="[25, 25]">
|
||||||
|
<QBtn fab color="primary" icon="add" @click="showFormDialog()" />
|
||||||
|
</QPageSticky>
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
.q-gutter-y-ms {
|
||||||
|
display: grid;
|
||||||
|
row-gap: 20px;
|
||||||
|
}
|
||||||
|
.labelColor {
|
||||||
|
color: var(--vn-label);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<i18n>
|
||||||
|
en:
|
||||||
|
contentTypesInfo: Allowed file types {allowedContentTypes}
|
||||||
|
es:
|
||||||
|
contentTypesInfo: Tipos de archivo permitidos {allowedContentTypes}
|
||||||
|
Generate identifier for original file: Generar identificador para archivo original
|
||||||
|
</i18n>
|
|
@ -94,16 +94,6 @@ async function send() {
|
||||||
<QSpace />
|
<QSpace />
|
||||||
<QBtn icon="close" :disable="isLoading" flat round dense v-close-popup />
|
<QBtn icon="close" :disable="isLoading" flat round dense v-close-popup />
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
<QCardSection v-if="props.locale">
|
|
||||||
<QBanner class="bg-amber text-white" rounded dense>
|
|
||||||
<template #avatar>
|
|
||||||
<QIcon name="warning" />
|
|
||||||
</template>
|
|
||||||
<span
|
|
||||||
v-html="t('CustomerDefaultLanguage', { locale: t(props.locale) })"
|
|
||||||
></span>
|
|
||||||
</QBanner>
|
|
||||||
</QCardSection>
|
|
||||||
<QCardSection class="q-pb-xs">
|
<QCardSection class="q-pb-xs">
|
||||||
<QSelect
|
<QSelect
|
||||||
:label="t('Language')"
|
:label="t('Language')"
|
||||||
|
@ -184,7 +174,6 @@ async function send() {
|
||||||
|
|
||||||
<i18n>
|
<i18n>
|
||||||
en:
|
en:
|
||||||
CustomerDefaultLanguage: This customer uses <strong>{locale}</strong> as their default language
|
|
||||||
templates:
|
templates:
|
||||||
pendingPayment: 'Your order is pending of payment.
|
pendingPayment: 'Your order is pending of payment.
|
||||||
Please, enter the website and make the payment with a credit card. Thank you.'
|
Please, enter the website and make the payment with a credit card. Thank you.'
|
||||||
|
@ -197,7 +186,6 @@ en:
|
||||||
pt: Portuguese
|
pt: Portuguese
|
||||||
es:
|
es:
|
||||||
Send SMS: Enviar SMS
|
Send SMS: Enviar SMS
|
||||||
CustomerDefaultLanguage: Este cliente utiliza <strong>{locale}</strong> como idioma por defecto
|
|
||||||
Language: Idioma
|
Language: Idioma
|
||||||
Phone: Móvil
|
Phone: Móvil
|
||||||
Subject: Asunto
|
Subject: Asunto
|
||||||
|
|
|
@ -188,16 +188,18 @@ const emit = defineEmits(['onFetch']);
|
||||||
.label {
|
.label {
|
||||||
color: var(--vn-label);
|
color: var(--vn-label);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
width: 47%;
|
::after {
|
||||||
|
content: ':';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.value {
|
.value {
|
||||||
color: var(--vn-text);
|
color: var(--vn-text);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
width: 47%;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
text-align: left;
|
||||||
}
|
}
|
||||||
.info {
|
.info {
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
|
|
|
@ -13,12 +13,49 @@ defineProps({
|
||||||
<template>
|
<template>
|
||||||
<div class="fetchedTags">
|
<div class="fetchedTags">
|
||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
<div class="inline-tag" :class="{ empty: !$props.item.value5 }">{{ $props.item.value5 }}</div>
|
<div
|
||||||
<div class="inline-tag" :class="{ empty: !$props.item.value6 }">{{ $props.item.value6 }}</div>
|
class="inline-tag"
|
||||||
<div class="inline-tag" :class="{ empty: !$props.item.value7 }">{{ $props.item.value7 }}</div>
|
:class="{ empty: !$props.item.value5 }"
|
||||||
<div class="inline-tag" :class="{ empty: !$props.item.value8 }">{{ $props.item.value8 }}</div>
|
:title="$props.item.tag5 + ': ' + $props.item.value5"
|
||||||
<div class="inline-tag" :class="{ empty: !$props.item.value9 }">{{ $props.item.value9 }}</div>
|
>
|
||||||
<div class="inline-tag" :class="{ empty: !$props.item.value10 }">{{ $props.item.value10 }}</div>
|
{{ $props.item.value5 }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="inline-tag"
|
||||||
|
:class="{ empty: !$props.item.tag6 }"
|
||||||
|
:title="$props.item.tag6 + ': ' + $props.item.value6"
|
||||||
|
>
|
||||||
|
{{ $props.item.value6 }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="inline-tag"
|
||||||
|
:class="{ empty: !$props.item.value7 }"
|
||||||
|
:title="$props.item.tag7 + ': ' + $props.item.value7"
|
||||||
|
>
|
||||||
|
{{ $props.item.value7 }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="inline-tag"
|
||||||
|
:class="{ empty: !$props.item.value8 }"
|
||||||
|
:title="$props.item.tag8 + ': ' + $props.item.value8"
|
||||||
|
>
|
||||||
|
{{ $props.item.value8 }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="inline-tag"
|
||||||
|
:class="{ empty: !$props.item.value9 }"
|
||||||
|
:title="$props.item.tag9 + ': ' + $props.item.value9"
|
||||||
|
>
|
||||||
|
{{ $props.item.value9 }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="inline-tag"
|
||||||
|
:class="{ empty: !$props.item.value10 }"
|
||||||
|
:title="$props.item.tag10 + ': ' + $props.item.value10"
|
||||||
|
>
|
||||||
|
{{ $props.item.value10 }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref, computed } from 'vue';
|
import { onMounted, ref, computed, watch } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useArrayData } from 'composables/useArrayData';
|
import { useArrayData } from 'composables/useArrayData';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
import toDate from 'filters/toDate';
|
import toDate from 'filters/toDate';
|
||||||
|
|
||||||
import VnFilterPanelChip from 'components/ui/VnFilterPanelChip.vue';
|
import VnFilterPanelChip from 'components/ui/VnFilterPanelChip.vue';
|
||||||
|
@ -52,6 +53,7 @@ const emit = defineEmits(['refresh', 'clear', 'search', 'init', 'remove']);
|
||||||
const arrayData = useArrayData(props.dataKey, {
|
const arrayData = useArrayData(props.dataKey, {
|
||||||
exprBuilder: props.exprBuilder,
|
exprBuilder: props.exprBuilder,
|
||||||
});
|
});
|
||||||
|
const route = useRoute();
|
||||||
const store = arrayData.store;
|
const store = arrayData.store;
|
||||||
const userParams = ref({});
|
const userParams = ref({});
|
||||||
|
|
||||||
|
@ -63,6 +65,18 @@ onMounted(() => {
|
||||||
emit('init', { params: userParams.value });
|
emit('init', { params: userParams.value });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => route.query.params,
|
||||||
|
(val) => {
|
||||||
|
if (!val) {
|
||||||
|
userParams.value = {};
|
||||||
|
} else {
|
||||||
|
const parsedParams = JSON.parse(val);
|
||||||
|
userParams.value = { ...parsedParams };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
async function search() {
|
async function search() {
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { dashIfEmpty } from 'src/filters';
|
import { dashIfEmpty } from 'src/filters';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useClipboard } from 'src/composables/useClipboard';
|
import { useClipboard } from 'src/composables/useClipboard';
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
label: { type: String, default: null },
|
label: { type: String, default: null },
|
||||||
value: {
|
value: {
|
||||||
|
@ -13,8 +14,9 @@ const $props = defineProps({
|
||||||
dash: { type: Boolean, default: true },
|
dash: { type: Boolean, default: true },
|
||||||
copy: { type: Boolean, default: false },
|
copy: { type: Boolean, default: false },
|
||||||
});
|
});
|
||||||
const isBooleanValue = computed(() => typeof $props.value === 'boolean');
|
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const isBooleanValue = computed(() => typeof $props.value === 'boolean');
|
||||||
const { copyText } = useClipboard();
|
const { copyText } = useClipboard();
|
||||||
|
|
||||||
function copyValueText() {
|
function copyValueText() {
|
||||||
|
@ -54,22 +56,29 @@ function copyValueText() {
|
||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
<div class="info" v-if="$props.info">
|
<div class="info" v-if="$props.info">
|
||||||
<QIcon name="info">
|
<QIcon name="info" class="cursor-pointer" size="xs" color="grey">
|
||||||
<QTooltip class="bg-dark text-white shadow-4" :offset="[10, 10]">
|
<QTooltip class="bg-dark text-white shadow-4" :offset="[10, 10]">
|
||||||
{{ $props.info }}
|
{{ $props.info }}
|
||||||
</QTooltip>
|
</QTooltip>
|
||||||
</QIcon>
|
</QIcon>
|
||||||
</div>
|
</div>
|
||||||
<div class="copy" v-if="$props.copy && $props.value" @click="copyValueText()">
|
<div class="copy" v-if="$props.copy && $props.value" @click="copyValueText()">
|
||||||
<QIcon name="Content_Copy" color="primary" />
|
<QIcon name="Content_Copy" color="primary">
|
||||||
|
<QTooltip>{{ t('globals.copyClipboard') }}</QTooltip>
|
||||||
|
</QIcon>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.copy {
|
.vn-label-value:hover .copy {
|
||||||
&:hover {
|
visibility: visible;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
.copy {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
.info {
|
||||||
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="row">
|
<div id="row" class="q-gutter-md q-mb-md">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style lang="scss" scopped>
|
<style lang="scss" scopped>
|
||||||
|
#row {
|
||||||
|
display: flex;
|
||||||
|
> * {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@media screen and (max-width: 800px) {
|
@media screen and (max-width: 800px) {
|
||||||
#row {
|
#row {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
@ -1,20 +1,21 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRouter } from 'vue-router';
|
import { useState } from 'src/composables/useState';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
|
import DepartmentDescriptorProxy from 'src/pages/Department/Card/DepartmentDescriptorProxy.vue';
|
||||||
import CreateDepartmentChild from '../CreateDepartmentChild.vue';
|
import CreateDepartmentChild from '../CreateDepartmentChild.vue';
|
||||||
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import useNotify from 'src/composables/useNotify.js';
|
import useNotify from 'src/composables/useNotify.js';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const router = useRouter();
|
|
||||||
const { notify } = useNotify();
|
const { notify } = useNotify();
|
||||||
|
const state = useState();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
const treeRef = ref(null);
|
const treeRef = ref();
|
||||||
const showCreateNodeFormVal = ref(false);
|
const showCreateNodeFormVal = ref(false);
|
||||||
const creationNodeSelectedId = ref(null);
|
const creationNodeSelectedId = ref(null);
|
||||||
const expanded = ref([]);
|
const expanded = ref([]);
|
||||||
|
@ -24,30 +25,35 @@ const nodes = ref([{ id: null, name: t('Departments'), sons: true, children: [{}
|
||||||
const fetchedChildrensSet = ref(new Set());
|
const fetchedChildrensSet = ref(new Set());
|
||||||
|
|
||||||
const onNodeExpanded = (nodeKeysArray) => {
|
const onNodeExpanded = (nodeKeysArray) => {
|
||||||
// Verificar si el nodo ya fue expandido
|
|
||||||
if (!fetchedChildrensSet.value.has(nodeKeysArray.at(-1))) {
|
if (!fetchedChildrensSet.value.has(nodeKeysArray.at(-1))) {
|
||||||
fetchedChildrensSet.value.add(nodeKeysArray.at(-1));
|
fetchedChildrensSet.value.add(nodeKeysArray.at(-1));
|
||||||
fetchNodeLeaves(nodeKeysArray.at(-1)); // Llamar a la función para obtener los nodos hijos
|
fetchNodeLeaves(nodeKeysArray.at(-1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.set('Tree', nodeKeysArray);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchNodeLeaves = async (nodeKey) => {
|
const fetchNodeLeaves = async (nodeKey) => {
|
||||||
try {
|
try {
|
||||||
const node = treeRef.value.getNodeByKey(nodeKey);
|
const node = treeRef.value?.getNodeByKey(nodeKey);
|
||||||
|
|
||||||
if (!node || node.sons === 0) return;
|
if (!node || node.sons === 0) return;
|
||||||
|
|
||||||
const params = { parentId: node.id };
|
const params = { parentId: node.id };
|
||||||
const response = await axios.get('/departments/getLeaves', { params });
|
const response = await axios.get('/departments/getLeaves', { params });
|
||||||
|
|
||||||
// Si hay datos en la respuesta y tiene hijos, agregarlos al nodo actual
|
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
node.children = response.data;
|
node.children = response.data.map((n) => {
|
||||||
node.children.forEach((node) => {
|
const hasChildrens = n.sons > 0;
|
||||||
if (node.sons) node.children = [{}];
|
|
||||||
|
n.children = hasChildrens ? [{}] : null;
|
||||||
|
n.clickable = true;
|
||||||
|
return n;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.set('Tree', node);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error fetching department leaves');
|
console.error('Error fetching department leaves', err);
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -84,10 +90,49 @@ const onNodeCreated = async () => {
|
||||||
await fetchNodeLeaves(creationNodeSelectedId.value);
|
await fetchNodeLeaves(creationNodeSelectedId.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const redirectToDepartmentSummary = (id) => {
|
onMounted(async (n) => {
|
||||||
if (!id) return;
|
const tree = [...state.get('Tree'), 1];
|
||||||
router.push({ name: 'DepartmentSummary', params: { id } });
|
const lastStateTree = state.get('TreeState');
|
||||||
};
|
if (tree) {
|
||||||
|
for (let n of tree) {
|
||||||
|
await fetchNodeLeaves(n);
|
||||||
|
}
|
||||||
|
expanded.value = tree;
|
||||||
|
|
||||||
|
if (lastStateTree) {
|
||||||
|
tree.push(lastStateTree);
|
||||||
|
await fetchNodeLeaves(lastStateTree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
if (lastStateTree) {
|
||||||
|
document.getElementById(lastStateTree).scrollIntoView();
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleEvent(type, event, node) {
|
||||||
|
const isParent = node.sons > 0;
|
||||||
|
const lastId = isParent ? node.id : node.parentFk;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'path':
|
||||||
|
state.set('TreeState', lastId);
|
||||||
|
node.id && router.push({ path: `/department/department/${node.id}/summary` });
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'tab':
|
||||||
|
state.set('TreeState', lastId);
|
||||||
|
node.id &&
|
||||||
|
window.open(`#/department/department/${node.id}/summary`, '_blank');
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
node.id &&
|
||||||
|
router.push({ path: `#/department/department/${node.id}/summary` });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -99,15 +144,27 @@ const redirectToDepartmentSummary = (id) => {
|
||||||
label-key="name"
|
label-key="name"
|
||||||
v-model:expanded="expanded"
|
v-model:expanded="expanded"
|
||||||
@update:expanded="onNodeExpanded($event)"
|
@update:expanded="onNodeExpanded($event)"
|
||||||
|
:default-expand-all="true"
|
||||||
>
|
>
|
||||||
<template #default-header="{ node }">
|
<template #default-header="{ node }">
|
||||||
<div
|
<div
|
||||||
class="row justify-between full-width q-pr-md cursor-pointer"
|
:id="node.id"
|
||||||
@click.stop="redirectToDepartmentSummary(node.id)"
|
class="qtr row justify-between full-width q-pr-md cursor-pointer"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<span
|
||||||
|
@click="handleEvent('row', $event, node)"
|
||||||
|
class="cursor-pointer"
|
||||||
>
|
>
|
||||||
<span class="text-uppercase">
|
|
||||||
{{ node.name }}
|
{{ node.name }}
|
||||||
|
<DepartmentDescriptorProxy :id="node.id" />
|
||||||
</span>
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
@click.stop.exact="handleEvent('path', $event, node)"
|
||||||
|
@click.ctrl.stop="handleEvent('tab', $event, node)"
|
||||||
|
style="flex-grow: 1; width: 10px"
|
||||||
|
></div>
|
||||||
<div class="row justify-between" style="max-width: max-content">
|
<div class="row justify-between" style="max-width: max-content">
|
||||||
<QIcon
|
<QIcon
|
||||||
v-if="node.id"
|
v-if="node.id"
|
||||||
|
@ -149,6 +206,11 @@ const redirectToDepartmentSummary = (id) => {
|
||||||
</QCard>
|
</QCard>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
span {
|
||||||
|
color: $primary;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<i18n>
|
<i18n>
|
||||||
es:
|
es:
|
||||||
Departments: Departamentos
|
Departments: Departamentos
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
// Tip: Use the "Theme Builder" on Quasar's documentation website.
|
// Tip: Use the "Theme Builder" on Quasar's documentation website.
|
||||||
|
|
||||||
$primary: #ec8916;
|
$primary: #ec8916;
|
||||||
|
$primary-light: lighten($primary, 35%);
|
||||||
$secondary: #26a69a;
|
$secondary: #26a69a;
|
||||||
$accent: #9c27b0;
|
$accent: #9c27b0;
|
||||||
$white: #fff;
|
$white: #fff;
|
||||||
|
|
|
@ -24,6 +24,7 @@ export default {
|
||||||
dataCreated: 'Data created',
|
dataCreated: 'Data created',
|
||||||
add: 'Add',
|
add: 'Add',
|
||||||
create: 'Create',
|
create: 'Create',
|
||||||
|
edit: 'Edit',
|
||||||
save: 'Save',
|
save: 'Save',
|
||||||
remove: 'Remove',
|
remove: 'Remove',
|
||||||
reset: 'Reset',
|
reset: 'Reset',
|
||||||
|
@ -64,12 +65,23 @@ export default {
|
||||||
markAll: 'Mark all',
|
markAll: 'Mark all',
|
||||||
requiredField: 'Required field',
|
requiredField: 'Required field',
|
||||||
class: 'clase',
|
class: 'clase',
|
||||||
type: 'type',
|
type: 'Type',
|
||||||
reason: 'reason',
|
reason: 'reason',
|
||||||
noResults: 'No results',
|
noResults: 'No results',
|
||||||
system: 'System',
|
system: 'System',
|
||||||
|
warehouse: 'Warehouse',
|
||||||
|
company: 'Company',
|
||||||
fieldRequired: 'Field required',
|
fieldRequired: 'Field required',
|
||||||
allowedFilesText: 'Allowed file types: { allowedContentTypes }',
|
allowedFilesText: 'Allowed file types: { allowedContentTypes }',
|
||||||
|
confirmDeletion: 'Confirm deletion',
|
||||||
|
confirmDeletionMessage: 'Are you sure you want to delete this?',
|
||||||
|
description: 'Description',
|
||||||
|
id: 'Id',
|
||||||
|
order: 'Order',
|
||||||
|
original: 'Original',
|
||||||
|
file: 'File',
|
||||||
|
selectFile: 'Select a file',
|
||||||
|
copyClipboard: 'Copy on clipboard',
|
||||||
},
|
},
|
||||||
errors: {
|
errors: {
|
||||||
statusUnauthorized: 'Access denied',
|
statusUnauthorized: 'Access denied',
|
||||||
|
@ -77,7 +89,7 @@ export default {
|
||||||
statusBadGateway: 'It seems that the server has fall down',
|
statusBadGateway: 'It seems that the server has fall down',
|
||||||
statusGatewayTimeout: 'Could not contact the server',
|
statusGatewayTimeout: 'Could not contact the server',
|
||||||
userConfig: 'Error fetching user config',
|
userConfig: 'Error fetching user config',
|
||||||
create: 'Error during creation',
|
writeRequest: 'The requested operation could not be completed',
|
||||||
},
|
},
|
||||||
login: {
|
login: {
|
||||||
title: 'Login',
|
title: 'Login',
|
||||||
|
@ -166,7 +178,7 @@ export default {
|
||||||
fiscalAddress: 'Fiscal address',
|
fiscalAddress: 'Fiscal address',
|
||||||
fiscalData: 'Fiscal data',
|
fiscalData: 'Fiscal data',
|
||||||
billingData: 'Billing data',
|
billingData: 'Billing data',
|
||||||
consignee: 'Consignee',
|
consignee: 'Default consignee',
|
||||||
businessData: 'Business data',
|
businessData: 'Business data',
|
||||||
financialData: 'Financial data',
|
financialData: 'Financial data',
|
||||||
customerId: 'Customer ID',
|
customerId: 'Customer ID',
|
||||||
|
@ -219,6 +231,8 @@ export default {
|
||||||
recoverySince: 'Recovery since',
|
recoverySince: 'Recovery since',
|
||||||
businessType: 'Business Type',
|
businessType: 'Business Type',
|
||||||
city: 'City',
|
city: 'City',
|
||||||
|
rating: 'Rating',
|
||||||
|
recommendCredit: 'Recommended credit',
|
||||||
},
|
},
|
||||||
basicData: {
|
basicData: {
|
||||||
socialName: 'Fiscal name',
|
socialName: 'Fiscal name',
|
||||||
|
@ -273,6 +287,7 @@ export default {
|
||||||
basicData: 'Basic data',
|
basicData: 'Basic data',
|
||||||
buys: 'Buys',
|
buys: 'Buys',
|
||||||
notes: 'Notes',
|
notes: 'Notes',
|
||||||
|
dms: 'File management',
|
||||||
log: 'Log',
|
log: 'Log',
|
||||||
create: 'Create',
|
create: 'Create',
|
||||||
latestBuys: 'Latest buys',
|
latestBuys: 'Latest buys',
|
||||||
|
@ -344,7 +359,6 @@ export default {
|
||||||
reference: 'Reference',
|
reference: 'Reference',
|
||||||
observations: 'Observations',
|
observations: 'Observations',
|
||||||
item: 'Item',
|
item: 'Item',
|
||||||
description: 'Description',
|
|
||||||
size: 'Size',
|
size: 'Size',
|
||||||
packing: 'Packing',
|
packing: 'Packing',
|
||||||
grouping: 'Grouping',
|
grouping: 'Grouping',
|
||||||
|
@ -359,7 +373,6 @@ export default {
|
||||||
},
|
},
|
||||||
notes: {
|
notes: {
|
||||||
observationType: 'Observation type',
|
observationType: 'Observation type',
|
||||||
description: 'Description',
|
|
||||||
},
|
},
|
||||||
descriptor: {
|
descriptor: {
|
||||||
agency: 'Agency',
|
agency: 'Agency',
|
||||||
|
@ -372,7 +385,6 @@ export default {
|
||||||
packing: 'Packing',
|
packing: 'Packing',
|
||||||
grouping: 'Grouping',
|
grouping: 'Grouping',
|
||||||
quantity: 'Quantity',
|
quantity: 'Quantity',
|
||||||
description: 'Description',
|
|
||||||
size: 'Size',
|
size: 'Size',
|
||||||
tags: 'Tags',
|
tags: 'Tags',
|
||||||
type: 'Type',
|
type: 'Type',
|
||||||
|
@ -458,7 +470,6 @@ export default {
|
||||||
visible: 'Visible',
|
visible: 'Visible',
|
||||||
available: 'Available',
|
available: 'Available',
|
||||||
quantity: 'Quantity',
|
quantity: 'Quantity',
|
||||||
description: 'Description',
|
|
||||||
price: 'Price',
|
price: 'Price',
|
||||||
discount: 'Discount',
|
discount: 'Discount',
|
||||||
packing: 'Packing',
|
packing: 'Packing',
|
||||||
|
@ -531,7 +542,6 @@ export default {
|
||||||
landed: 'Landed',
|
landed: 'Landed',
|
||||||
quantity: 'Quantity',
|
quantity: 'Quantity',
|
||||||
claimed: 'Claimed',
|
claimed: 'Claimed',
|
||||||
description: 'Description',
|
|
||||||
price: 'Price',
|
price: 'Price',
|
||||||
discount: 'Discount',
|
discount: 'Discount',
|
||||||
total: 'Total',
|
total: 'Total',
|
||||||
|
@ -793,7 +803,6 @@ export default {
|
||||||
orderTicketList: 'Order Ticket List',
|
orderTicketList: 'Order Ticket List',
|
||||||
details: 'Details',
|
details: 'Details',
|
||||||
item: 'Item',
|
item: 'Item',
|
||||||
description: 'Description',
|
|
||||||
quantity: 'Quantity',
|
quantity: 'Quantity',
|
||||||
price: 'Price',
|
price: 'Price',
|
||||||
amount: 'Amount',
|
amount: 'Amount',
|
||||||
|
@ -1138,7 +1147,6 @@ export default {
|
||||||
warehouse: 'Warehouse',
|
warehouse: 'Warehouse',
|
||||||
travelFileDescription: 'Travel id { travelId }',
|
travelFileDescription: 'Travel id { travelId }',
|
||||||
file: 'File',
|
file: 'File',
|
||||||
description: 'Description',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
item: {
|
item: {
|
||||||
|
@ -1172,7 +1180,6 @@ export default {
|
||||||
clone: 'Clone',
|
clone: 'Clone',
|
||||||
openCard: 'View',
|
openCard: 'View',
|
||||||
openSummary: 'Summary',
|
openSummary: 'Summary',
|
||||||
viewDescription: 'Description',
|
|
||||||
},
|
},
|
||||||
cardDescriptor: {
|
cardDescriptor: {
|
||||||
mainList: 'Main list',
|
mainList: 'Main list',
|
||||||
|
|
|
@ -24,6 +24,7 @@ export default {
|
||||||
dataCreated: 'Datos creados',
|
dataCreated: 'Datos creados',
|
||||||
add: 'Añadir',
|
add: 'Añadir',
|
||||||
create: 'Crear',
|
create: 'Crear',
|
||||||
|
edit: 'Modificar',
|
||||||
save: 'Guardar',
|
save: 'Guardar',
|
||||||
remove: 'Eliminar',
|
remove: 'Eliminar',
|
||||||
reset: 'Restaurar',
|
reset: 'Restaurar',
|
||||||
|
@ -64,12 +65,23 @@ export default {
|
||||||
markAll: 'Marcar todo',
|
markAll: 'Marcar todo',
|
||||||
requiredField: 'Campo obligatorio',
|
requiredField: 'Campo obligatorio',
|
||||||
class: 'clase',
|
class: 'clase',
|
||||||
type: 'tipo',
|
type: 'Tipo',
|
||||||
reason: 'motivo',
|
reason: 'motivo',
|
||||||
noResults: 'Sin resultados',
|
noResults: 'Sin resultados',
|
||||||
system: 'Sistema',
|
system: 'Sistema',
|
||||||
|
warehouse: 'Almacén',
|
||||||
|
company: 'Empresa',
|
||||||
fieldRequired: 'Campo requerido',
|
fieldRequired: 'Campo requerido',
|
||||||
allowedFilesText: 'Tipos de archivo permitidos: { allowedContentTypes }',
|
allowedFilesText: 'Tipos de archivo permitidos: { allowedContentTypes }',
|
||||||
|
confirmDeletion: 'Confirmar eliminación',
|
||||||
|
confirmDeletionMessage: '¿Seguro que quieres eliminar?',
|
||||||
|
description: 'Descripción',
|
||||||
|
id: 'Id',
|
||||||
|
order: 'Orden',
|
||||||
|
original: 'Original',
|
||||||
|
file: 'Fichero',
|
||||||
|
selectFile: 'Seleccione un fichero',
|
||||||
|
copyClipboard: 'Copiar en portapapeles',
|
||||||
},
|
},
|
||||||
errors: {
|
errors: {
|
||||||
statusUnauthorized: 'Acceso denegado',
|
statusUnauthorized: 'Acceso denegado',
|
||||||
|
@ -77,7 +89,7 @@ export default {
|
||||||
statusBadGateway: 'Parece ser que el servidor ha caído',
|
statusBadGateway: 'Parece ser que el servidor ha caído',
|
||||||
statusGatewayTimeout: 'No se ha podido contactar con el servidor',
|
statusGatewayTimeout: 'No se ha podido contactar con el servidor',
|
||||||
userConfig: 'Error al obtener configuración de usuario',
|
userConfig: 'Error al obtener configuración de usuario',
|
||||||
create: 'Error al crear',
|
writeRequest: 'No se pudo completar la operación solicitada',
|
||||||
},
|
},
|
||||||
login: {
|
login: {
|
||||||
title: 'Inicio de sesión',
|
title: 'Inicio de sesión',
|
||||||
|
@ -165,7 +177,7 @@ export default {
|
||||||
fiscalAddress: 'Dirección fiscal',
|
fiscalAddress: 'Dirección fiscal',
|
||||||
fiscalData: 'Datos fiscales',
|
fiscalData: 'Datos fiscales',
|
||||||
billingData: 'Datos de facturación',
|
billingData: 'Datos de facturación',
|
||||||
consignee: 'Consignatario',
|
consignee: 'Consignatario pred.',
|
||||||
businessData: 'Datos comerciales',
|
businessData: 'Datos comerciales',
|
||||||
financialData: 'Datos financieros',
|
financialData: 'Datos financieros',
|
||||||
customerId: 'ID cliente',
|
customerId: 'ID cliente',
|
||||||
|
@ -218,6 +230,8 @@ export default {
|
||||||
recoverySince: 'Recobro desde',
|
recoverySince: 'Recobro desde',
|
||||||
businessType: 'Tipo de negocio',
|
businessType: 'Tipo de negocio',
|
||||||
city: 'Población',
|
city: 'Población',
|
||||||
|
rating: 'Clasificación',
|
||||||
|
recommendCredit: 'Crédito recomendado',
|
||||||
},
|
},
|
||||||
basicData: {
|
basicData: {
|
||||||
socialName: 'Nombre fiscal',
|
socialName: 'Nombre fiscal',
|
||||||
|
@ -272,6 +286,7 @@ export default {
|
||||||
basicData: 'Datos básicos',
|
basicData: 'Datos básicos',
|
||||||
buys: 'Compras',
|
buys: 'Compras',
|
||||||
notes: 'Notas',
|
notes: 'Notas',
|
||||||
|
dms: 'Gestión documental',
|
||||||
log: 'Historial',
|
log: 'Historial',
|
||||||
create: 'Crear',
|
create: 'Crear',
|
||||||
latestBuys: 'Últimas compras',
|
latestBuys: 'Últimas compras',
|
||||||
|
@ -343,7 +358,6 @@ export default {
|
||||||
reference: 'Referencia',
|
reference: 'Referencia',
|
||||||
observations: 'Observaciónes',
|
observations: 'Observaciónes',
|
||||||
item: 'Artículo',
|
item: 'Artículo',
|
||||||
description: 'Descripción',
|
|
||||||
size: 'Medida',
|
size: 'Medida',
|
||||||
packing: 'Packing',
|
packing: 'Packing',
|
||||||
grouping: 'Grouping',
|
grouping: 'Grouping',
|
||||||
|
@ -358,7 +372,6 @@ export default {
|
||||||
},
|
},
|
||||||
notes: {
|
notes: {
|
||||||
observationType: 'Tipo de observación',
|
observationType: 'Tipo de observación',
|
||||||
description: 'Descripción',
|
|
||||||
},
|
},
|
||||||
descriptor: {
|
descriptor: {
|
||||||
agency: 'Agencia',
|
agency: 'Agencia',
|
||||||
|
@ -371,7 +384,6 @@ export default {
|
||||||
packing: 'Packing',
|
packing: 'Packing',
|
||||||
grouping: 'Grouping',
|
grouping: 'Grouping',
|
||||||
quantity: 'Cantidad',
|
quantity: 'Cantidad',
|
||||||
description: 'Descripción',
|
|
||||||
size: 'Medida',
|
size: 'Medida',
|
||||||
tags: 'Etiquetas',
|
tags: 'Etiquetas',
|
||||||
type: 'Tipo',
|
type: 'Tipo',
|
||||||
|
@ -457,7 +469,6 @@ export default {
|
||||||
visible: 'Visible',
|
visible: 'Visible',
|
||||||
available: 'Disponible',
|
available: 'Disponible',
|
||||||
quantity: 'Cantidad',
|
quantity: 'Cantidad',
|
||||||
description: 'Descripción',
|
|
||||||
price: 'Precio',
|
price: 'Precio',
|
||||||
discount: 'Descuento',
|
discount: 'Descuento',
|
||||||
packing: 'Encajado',
|
packing: 'Encajado',
|
||||||
|
@ -530,7 +541,6 @@ export default {
|
||||||
landed: 'Entregado',
|
landed: 'Entregado',
|
||||||
quantity: 'Cantidad',
|
quantity: 'Cantidad',
|
||||||
claimed: 'Reclamado',
|
claimed: 'Reclamado',
|
||||||
description: 'Descripción',
|
|
||||||
price: 'Precio',
|
price: 'Precio',
|
||||||
discount: 'Descuento',
|
discount: 'Descuento',
|
||||||
total: 'Total',
|
total: 'Total',
|
||||||
|
@ -701,7 +711,6 @@ export default {
|
||||||
orderTicketList: 'Tickets del pedido',
|
orderTicketList: 'Tickets del pedido',
|
||||||
details: 'Detalles',
|
details: 'Detalles',
|
||||||
item: 'Item',
|
item: 'Item',
|
||||||
description: 'Descripción',
|
|
||||||
quantity: 'Cantidad',
|
quantity: 'Cantidad',
|
||||||
price: 'Precio',
|
price: 'Precio',
|
||||||
amount: 'Monto',
|
amount: 'Monto',
|
||||||
|
@ -1138,7 +1147,6 @@ export default {
|
||||||
warehouse: 'Almacén',
|
warehouse: 'Almacén',
|
||||||
travelFileDescription: 'Id envío { travelId }',
|
travelFileDescription: 'Id envío { travelId }',
|
||||||
file: 'Fichero',
|
file: 'Fichero',
|
||||||
description: 'Descripción',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
item: {
|
item: {
|
||||||
|
@ -1172,7 +1180,6 @@ export default {
|
||||||
clone: 'Clonar',
|
clone: 'Clonar',
|
||||||
openCard: 'Ficha',
|
openCard: 'Ficha',
|
||||||
openSummary: 'Detalles',
|
openSummary: 'Detalles',
|
||||||
viewDescription: 'Descripción',
|
|
||||||
},
|
},
|
||||||
cardDescriptor: {
|
cardDescriptor: {
|
||||||
mainList: 'Listado principal',
|
mainList: 'Listado principal',
|
||||||
|
|
|
@ -35,7 +35,6 @@ const claimDmsFilter = ref({
|
||||||
relation: 'dms',
|
relation: 'dms',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
where: { claimFk: entityId.value },
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
@ -141,6 +140,11 @@ const claimDms = ref([]);
|
||||||
const multimediaDialog = ref();
|
const multimediaDialog = ref();
|
||||||
const multimediaSlide = ref();
|
const multimediaSlide = ref();
|
||||||
|
|
||||||
|
async function getClaimDms() {
|
||||||
|
claimDmsFilter.value.where = { claimFk: entityId.value };
|
||||||
|
await claimDmsRef.value.fetch();
|
||||||
|
}
|
||||||
|
|
||||||
function setClaimDms(data) {
|
function setClaimDms(data) {
|
||||||
claimDms.value = [];
|
claimDms.value = [];
|
||||||
data.forEach((media) => {
|
data.forEach((media) => {
|
||||||
|
@ -163,10 +167,13 @@ function openDialog(dmsId) {
|
||||||
url="ClaimDms"
|
url="ClaimDms"
|
||||||
:filter="claimDmsFilter"
|
:filter="claimDmsFilter"
|
||||||
@on-fetch="(data) => setClaimDms(data)"
|
@on-fetch="(data) => setClaimDms(data)"
|
||||||
limit="20"
|
|
||||||
ref="claimDmsRef"
|
ref="claimDmsRef"
|
||||||
/>
|
/>
|
||||||
<CardSummary ref="summary" :url="`Claims/${entityId}/getSummary`">
|
<CardSummary
|
||||||
|
ref="summary"
|
||||||
|
:url="`Claims/${entityId}/getSummary`"
|
||||||
|
@on-fetch="getClaimDms"
|
||||||
|
>
|
||||||
<template #header="{ entity: { claim } }">
|
<template #header="{ entity: { claim } }">
|
||||||
{{ claim.id }} - {{ claim.client.name }}
|
{{ claim.id }} - {{ claim.client.name }}
|
||||||
</template>
|
</template>
|
||||||
|
@ -252,7 +259,8 @@ function openDialog(dmsId) {
|
||||||
>
|
>
|
||||||
<ItemDescriptorProxy
|
<ItemDescriptorProxy
|
||||||
v-if="col.name == 'description'"
|
v-if="col.name == 'description'"
|
||||||
:id="2"
|
:id="props.row.id"
|
||||||
|
:sale-fk="props.row.saleFk"
|
||||||
></ItemDescriptorProxy>
|
></ItemDescriptorProxy>
|
||||||
</QTh>
|
</QTh>
|
||||||
</QTr>
|
</QTr>
|
||||||
|
@ -274,7 +282,6 @@ function openDialog(dmsId) {
|
||||||
</template>
|
</template>
|
||||||
</QTable>
|
</QTable>
|
||||||
</QCard>
|
</QCard>
|
||||||
|
|
||||||
<QCard class="vn-max" v-if="claimDms.length > 0">
|
<QCard class="vn-max" v-if="claimDms.length > 0">
|
||||||
<a class="header" :href="`#/claim/${entityId}/photos`">
|
<a class="header" :href="`#/claim/${entityId}/photos`">
|
||||||
{{ t('claim.summary.photos') }}
|
{{ t('claim.summary.photos') }}
|
||||||
|
|
|
@ -116,7 +116,13 @@ function navigate(event, id) {
|
||||||
</template>
|
</template>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<QBtn
|
<QBtn
|
||||||
:label="t('components.smartCard.viewDescription')"
|
:label="t('components.smartCard.openCard')"
|
||||||
|
@click.stop="navigate(row.id)"
|
||||||
|
class="bg-vn-dark"
|
||||||
|
outline
|
||||||
|
/>
|
||||||
|
<QBtn
|
||||||
|
:label="t('globals.description')"
|
||||||
@click.stop
|
@click.stop
|
||||||
class="bg-vn-dark"
|
class="bg-vn-dark"
|
||||||
outline
|
outline
|
||||||
|
|
|
@ -122,7 +122,7 @@ const creditWarning = computed(() => {
|
||||||
</QCard>
|
</QCard>
|
||||||
<QCard class="vn-one">
|
<QCard class="vn-one">
|
||||||
<a class="header link" :href="clientUrl + `fiscal-data`" link>
|
<a class="header link" :href="clientUrl + `fiscal-data`" link>
|
||||||
{{ t('customer.summary.fiscalAddress') }}
|
{{ t('customer.summary.fiscalData') }}
|
||||||
<QIcon name="open_in_new" color="primary" />
|
<QIcon name="open_in_new" color="primary" />
|
||||||
</a>
|
</a>
|
||||||
<VnLv
|
<VnLv
|
||||||
|
@ -235,7 +235,8 @@ const creditWarning = computed(() => {
|
||||||
link
|
link
|
||||||
>
|
>
|
||||||
{{ t('customer.summary.financialData') }}
|
{{ t('customer.summary.financialData') }}
|
||||||
<QIcon name="vn:grafana" color="primary" />
|
<QIcon name="open_in_new" color="primary" />
|
||||||
|
<!-- Pendiente de añadir el icono <QIcon name="vn:grafana" color="primary" /> -->
|
||||||
</a>
|
</a>
|
||||||
<VnLv
|
<VnLv
|
||||||
:label="t('customer.summary.risk')"
|
:label="t('customer.summary.risk')"
|
||||||
|
@ -276,7 +277,30 @@ const creditWarning = computed(() => {
|
||||||
:label="t('customer.summary.recoverySince')"
|
:label="t('customer.summary.recoverySince')"
|
||||||
:value="toDate(entity.recovery.started)"
|
:value="toDate(entity.recovery.started)"
|
||||||
/>
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('customer.summary.rating')"
|
||||||
|
:value="entity.rating"
|
||||||
|
:info="t('valueInfo', { min: 1, max: 20 })"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<VnLv
|
||||||
|
:label="t('customer.summary.recommendCredit')"
|
||||||
|
:value="entity.recommendedCredit"
|
||||||
|
/>
|
||||||
</QCard>
|
</QCard>
|
||||||
</template>
|
</template>
|
||||||
</CardSummary>
|
</CardSummary>
|
||||||
</template>
|
</template>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@media (min-width: $breakpoint-md) {
|
||||||
|
.summary .vn-one {
|
||||||
|
min-width: 300px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<i18n>
|
||||||
|
en:
|
||||||
|
valueInfo: Value from {min} to {max}. The higher the better value
|
||||||
|
es:
|
||||||
|
valueInfo: Valor de {min} a {max}. Cuanto más alto, mejor valor
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -13,7 +13,7 @@ import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
||||||
|
|
||||||
import { useArrayData } from 'composables/useArrayData';
|
import { useArrayData } from 'composables/useArrayData';
|
||||||
import { useStateStore } from 'stores/useStateStore';
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import { dashIfEmpty, toDate } from 'src/filters';
|
import { toDate } from 'src/filters';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
@ -477,17 +477,11 @@ const stopEventPropagation = (event, col) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
};
|
};
|
||||||
|
|
||||||
const navigateToTravelId = (id) => {
|
const navigateToTravelId = (id) => router.push({ path: `/customer/${id}` });
|
||||||
router.push({ path: `/customer/${id}` });
|
|
||||||
};
|
|
||||||
|
|
||||||
const selectCustomerId = (id) => {
|
const selectCustomerId = (id) => (selectedCustomerId.value = id);
|
||||||
selectedCustomerId.value = id;
|
|
||||||
};
|
|
||||||
|
|
||||||
const selectSalesPersonId = (id) => {
|
const selectSalesPersonId = (id) => (selectedSalesPersonId.value = id);
|
||||||
selectedSalesPersonId.value = id;
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -521,37 +515,50 @@ const selectSalesPersonId = (id) => {
|
||||||
class="full-width q-mt-md"
|
class="full-width q-mt-md"
|
||||||
row-key="id"
|
row-key="id"
|
||||||
:visible-columns="visibleColumns"
|
:visible-columns="visibleColumns"
|
||||||
|
@row-click="(evt, row, id) => navigateToTravelId(row.id)"
|
||||||
>
|
>
|
||||||
<template #body="props">
|
<template #body-cell="{ col, value }">
|
||||||
<QTr
|
<QTd @click="stopEventPropagation($event, col)">
|
||||||
:props="props"
|
{{ value }}
|
||||||
@click="navigateToTravelId(props.row.id)"
|
</QTd>
|
||||||
class="cursor-pointer"
|
</template>
|
||||||
>
|
<template #body-cell-id="props">
|
||||||
<QTd
|
<QTd @click="stopEventPropagation($event, props.col)">
|
||||||
v-for="col in props.cols"
|
|
||||||
:key="col.name"
|
|
||||||
:props="props"
|
|
||||||
@click="stopEventPropagation($event, col)"
|
|
||||||
>
|
|
||||||
<component
|
<component
|
||||||
:is="tableColumnComponents[col.name].component"
|
:is="tableColumnComponents[props.col.name].component"
|
||||||
class="col-content"
|
class="col-content"
|
||||||
v-bind="tableColumnComponents[col.name].props(props)"
|
v-bind="tableColumnComponents[props.col.name].props(props)"
|
||||||
@click="tableColumnComponents[col.name].event(props)"
|
@click="tableColumnComponents[props.col.name].event(props)"
|
||||||
>
|
>
|
||||||
{{ dashIfEmpty(col.value) }}
|
<CustomerDescriptorProxy :id="props.row.id" />
|
||||||
<WorkerDescriptorProxy
|
{{ props.row.id }}
|
||||||
v-if="props.row.salesPersonFk"
|
|
||||||
:id="selectedSalesPersonId"
|
|
||||||
/>
|
|
||||||
<CustomerDescriptorProxy
|
|
||||||
v-if="props.row.id"
|
|
||||||
:id="selectedCustomerId"
|
|
||||||
/>
|
|
||||||
</component>
|
</component>
|
||||||
</QTd>
|
</QTd>
|
||||||
</QTr>
|
</template>
|
||||||
|
<template #body-cell-salesPersonFk="props">
|
||||||
|
<QTd @click="stopEventPropagation($event, props.col)">
|
||||||
|
<component
|
||||||
|
v-if="props.row.salesPerson"
|
||||||
|
class="col-content"
|
||||||
|
:is="tableColumnComponents[props.col.name].component"
|
||||||
|
v-bind="tableColumnComponents[props.col.name].props(props)"
|
||||||
|
@click="tableColumnComponents[props.col.name].event(props)"
|
||||||
|
>
|
||||||
|
<WorkerDescriptorProxy :id="props.row.salesPersonFk" />
|
||||||
|
{{ props.row.salesPerson }}
|
||||||
|
</component>
|
||||||
|
<span class="col-content" v-else>-</span>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-actions="props">
|
||||||
|
<QTd @click="stopEventPropagation($event, props.col)">
|
||||||
|
<component
|
||||||
|
:is="tableColumnComponents[props.col.name].component"
|
||||||
|
class="col-content"
|
||||||
|
v-bind="tableColumnComponents[props.col.name].props(props)"
|
||||||
|
@click="tableColumnComponents[props.col.name].event(props)"
|
||||||
|
/>
|
||||||
|
</QTd>
|
||||||
</template>
|
</template>
|
||||||
</QTable>
|
</QTable>
|
||||||
</QPage>
|
</QPage>
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import VnLocation from 'src/components/common/VnLocation.vue';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import FormModel from 'components/FormModel.vue';
|
import FormModel from 'components/FormModel.vue';
|
||||||
import VnRow from 'components/ui/VnRow.vue';
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
@ -20,13 +20,9 @@ const router = useRouter();
|
||||||
|
|
||||||
const formInitialData = reactive({ isDefaultAddress: false });
|
const formInitialData = reactive({ isDefaultAddress: false });
|
||||||
|
|
||||||
const townsFetchDataRef = ref(null);
|
|
||||||
const postcodeFetchDataRef = ref(null);
|
|
||||||
const urlCreate = ref('');
|
const urlCreate = ref('');
|
||||||
|
|
||||||
const postcodesOptions = ref([]);
|
const postcodesOptions = ref([]);
|
||||||
const citiesLocationOptions = ref([]);
|
|
||||||
const provincesLocationOptions = ref([]);
|
|
||||||
const agencyModes = ref([]);
|
const agencyModes = ref([]);
|
||||||
const incoterms = ref([]);
|
const incoterms = ref([]);
|
||||||
const customsAgents = ref([]);
|
const customsAgents = ref([]);
|
||||||
|
@ -36,14 +32,6 @@ onBeforeMount(() => {
|
||||||
getCustomsAgents();
|
getCustomsAgents();
|
||||||
});
|
});
|
||||||
|
|
||||||
const onPostcodeCreated = async ({ code, provinceFk, townFk }, formData) => {
|
|
||||||
await postcodeFetchDataRef.value.fetch();
|
|
||||||
await townsFetchDataRef.value.fetch();
|
|
||||||
formData.postalCode = code;
|
|
||||||
formData.provinceFk = provinceFk;
|
|
||||||
formData.city = citiesLocationOptions.value.find((town) => town.id === townFk).name;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getCustomsAgents = async () => {
|
const getCustomsAgents = async () => {
|
||||||
const { data } = await axios.get('CustomsAgents');
|
const { data } = await axios.get('CustomsAgents');
|
||||||
customsAgents.value = data;
|
customsAgents.value = data;
|
||||||
|
@ -61,26 +49,16 @@ const toCustomerConsignees = () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
function handleLocation(data, location) {
|
||||||
|
const { town, code, provinceFk, countryFk } = location ?? {};
|
||||||
|
data.postalCode = code;
|
||||||
|
data.city = town;
|
||||||
|
data.provinceFk = provinceFk;
|
||||||
|
data.countryFk = countryFk;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<FetchData
|
|
||||||
@on-fetch="(data) => (postcodesOptions = data)"
|
|
||||||
auto-load
|
|
||||||
ref="postcodeFetchDataRef"
|
|
||||||
url="Postcodes/location"
|
|
||||||
/>
|
|
||||||
<FetchData
|
|
||||||
@on-fetch="(data) => (citiesLocationOptions = data)"
|
|
||||||
auto-load
|
|
||||||
ref="townsFetchDataRef"
|
|
||||||
url="Towns/location"
|
|
||||||
/>
|
|
||||||
<FetchData
|
|
||||||
@on-fetch="(data) => (provincesLocationOptions = data)"
|
|
||||||
auto-load
|
|
||||||
url="Provinces/location"
|
|
||||||
/>
|
|
||||||
<fetch-data
|
<fetch-data
|
||||||
@on-fetch="(data) => (agencyModes = data)"
|
@on-fetch="(data) => (agencyModes = data)"
|
||||||
auto-load
|
auto-load
|
||||||
|
@ -113,83 +91,17 @@ const toCustomerConsignees = () => {
|
||||||
|
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectDialog
|
<VnLocation
|
||||||
:label="t('Postcode')"
|
|
||||||
:options="postcodesOptions"
|
|
||||||
:roles-allowed-to-create="['deliveryAssistant']"
|
|
||||||
:rules="validate('Worker.postcode')"
|
:rules="validate('Worker.postcode')"
|
||||||
hide-selected
|
:roles-allowed-to-create="['deliveryAssistant']"
|
||||||
option-label="code"
|
:options="postcodesOptions"
|
||||||
option-value="code"
|
v-model="data.location"
|
||||||
v-model="data.postalCode"
|
@update:model-value="(location) => handleLocation(data, location)"
|
||||||
>
|
></VnLocation>
|
||||||
<template #form>
|
|
||||||
<CustomerCreateNewPostcode
|
|
||||||
@on-data-saved="onPostcodeCreated($event, data)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #option="scope">
|
|
||||||
<QItem v-bind="scope.itemProps">
|
|
||||||
<QItemSection v-if="scope.opt">
|
|
||||||
<QItemLabel>{{ scope.opt.code }}</QItemLabel>
|
|
||||||
<QItemLabel caption>
|
|
||||||
{{ scope.opt.code }} -
|
|
||||||
{{ scope.opt.town.name }}
|
|
||||||
({{ scope.opt.town.province.name }},
|
|
||||||
{{ scope.opt.town.province.country.country }})
|
|
||||||
</QItemLabel>
|
|
||||||
</QItemSection>
|
|
||||||
</QItem>
|
|
||||||
</template>
|
|
||||||
</VnSelectDialog>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<!-- ciudades -->
|
|
||||||
<VnSelectFilter
|
|
||||||
:label="t('City')"
|
|
||||||
:options="citiesLocationOptions"
|
|
||||||
hide-selected
|
|
||||||
option-label="name"
|
|
||||||
option-value="name"
|
|
||||||
v-model="data.city"
|
|
||||||
>
|
|
||||||
<template #option="scope">
|
|
||||||
<QItem v-bind="scope.itemProps">
|
|
||||||
<QItemSection>
|
|
||||||
<QItemLabel>{{ scope.opt.name }}</QItemLabel>
|
|
||||||
<QItemLabel caption>
|
|
||||||
{{
|
|
||||||
`${scope.opt.name}, ${scope.opt.province.name} (${scope.opt.province.country.country})`
|
|
||||||
}}
|
|
||||||
</QItemLabel>
|
|
||||||
</QItemSection>
|
|
||||||
</QItem>
|
|
||||||
</template>
|
|
||||||
</VnSelectFilter>
|
|
||||||
</div>
|
</div>
|
||||||
</VnRow>
|
</VnRow>
|
||||||
|
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
<div class="col">
|
|
||||||
<VnSelectFilter
|
|
||||||
:label="t('Province')"
|
|
||||||
:options="provincesLocationOptions"
|
|
||||||
hide-selected
|
|
||||||
option-label="name"
|
|
||||||
option-value="id"
|
|
||||||
v-model="data.provinceFk"
|
|
||||||
>
|
|
||||||
<template #option="scope">
|
|
||||||
<QItem v-bind="scope.itemProps">
|
|
||||||
<QItemSection>
|
|
||||||
<QItemLabel>{{
|
|
||||||
`${scope.opt.name} (${scope.opt.country.country})`
|
|
||||||
}}</QItemLabel>
|
|
||||||
</QItemSection>
|
|
||||||
</QItem>
|
|
||||||
</template>
|
|
||||||
</VnSelectFilter>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectFilter
|
<VnSelectFilter
|
||||||
:label="t('Agency')"
|
:label="t('Agency')"
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import VnLocation from 'src/components/common/VnLocation.vue';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import FormModel from 'components/FormModel.vue';
|
import FormModel from 'components/FormModel.vue';
|
||||||
import VnRow from 'components/ui/VnRow.vue';
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
@ -17,13 +17,8 @@ import CustomsNewCustomsAgent from 'src/pages/Customer/components/CustomerNewCus
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
const townsFetchDataRef = ref(null);
|
|
||||||
const postcodeFetchDataRef = ref(null);
|
|
||||||
const urlUpdate = ref('');
|
const urlUpdate = ref('');
|
||||||
|
|
||||||
const postcodesOptions = ref([]);
|
const postcodesOptions = ref([]);
|
||||||
const citiesLocationOptions = ref([]);
|
|
||||||
const provincesLocationOptions = ref([]);
|
|
||||||
const agencyModes = ref([]);
|
const agencyModes = ref([]);
|
||||||
const incoterms = ref([]);
|
const incoterms = ref([]);
|
||||||
const customsAgents = ref([]);
|
const customsAgents = ref([]);
|
||||||
|
@ -34,14 +29,6 @@ onBeforeMount(() => {
|
||||||
urlUpdate.value = `Clients/${route.params.id}/updateAddress/${route.params.consigneeId}`;
|
urlUpdate.value = `Clients/${route.params.id}/updateAddress/${route.params.consigneeId}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
const onPostcodeCreated = async ({ code, provinceFk, townFk }, formData) => {
|
|
||||||
await postcodeFetchDataRef.value.fetch();
|
|
||||||
await townsFetchDataRef.value.fetch();
|
|
||||||
formData.postalCode = code;
|
|
||||||
formData.provinceFk = provinceFk;
|
|
||||||
formData.city = citiesLocationOptions.value.find((town) => town.id === townFk).name;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getData = async (observations) => {
|
const getData = async (observations) => {
|
||||||
observationTypes.value = observations;
|
observationTypes.value = observations;
|
||||||
|
|
||||||
|
@ -97,26 +84,16 @@ const onDataSaved = () => {
|
||||||
};
|
};
|
||||||
axios.post('AddressObservations/crud', payload);
|
axios.post('AddressObservations/crud', payload);
|
||||||
};
|
};
|
||||||
|
function handleLocation(data, location) {
|
||||||
|
const { town, code, provinceFk, countryFk } = location ?? {};
|
||||||
|
data.postalCode = code;
|
||||||
|
data.city = town;
|
||||||
|
data.provinceFk = provinceFk;
|
||||||
|
data.countryFk = countryFk;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<FetchData
|
|
||||||
ref="postcodeFetchDataRef"
|
|
||||||
@on-fetch="(data) => (postcodesOptions = data)"
|
|
||||||
auto-load
|
|
||||||
url="Postcodes/location"
|
|
||||||
/>
|
|
||||||
<FetchData
|
|
||||||
ref="townsFetchDataRef"
|
|
||||||
@on-fetch="(data) => (citiesLocationOptions = data)"
|
|
||||||
auto-load
|
|
||||||
url="Towns/location"
|
|
||||||
/>
|
|
||||||
<FetchData
|
|
||||||
@on-fetch="(data) => (provincesLocationOptions = data)"
|
|
||||||
auto-load
|
|
||||||
url="Provinces/location"
|
|
||||||
/>
|
|
||||||
<fetch-data
|
<fetch-data
|
||||||
@on-fetch="(data) => (agencyModes = data)"
|
@on-fetch="(data) => (agencyModes = data)"
|
||||||
auto-load
|
auto-load
|
||||||
|
@ -168,83 +145,17 @@ const onDataSaved = () => {
|
||||||
|
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectDialog
|
<VnLocation
|
||||||
:label="t('Postcode')"
|
|
||||||
:options="postcodesOptions"
|
|
||||||
:roles-allowed-to-create="['deliveryAssistant']"
|
|
||||||
:rules="validate('Worker.postcode')"
|
:rules="validate('Worker.postcode')"
|
||||||
hide-selected
|
:roles-allowed-to-create="['deliveryAssistant']"
|
||||||
option-label="code"
|
:options="postcodesOptions"
|
||||||
option-value="code"
|
v-model="data.location"
|
||||||
v-model="data.postalCode"
|
@update:model-value="(location) => handleLocation(data, location)"
|
||||||
>
|
></VnLocation>
|
||||||
<template #form>
|
|
||||||
<CustomerCreateNewPostcode
|
|
||||||
@on-data-saved="onPostcodeCreated($event, data)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #option="scope">
|
|
||||||
<QItem v-bind="scope.itemProps">
|
|
||||||
<QItemSection v-if="scope.opt">
|
|
||||||
<QItemLabel>{{ scope.opt.code }}</QItemLabel>
|
|
||||||
<QItemLabel caption>
|
|
||||||
{{ scope.opt.code }} -
|
|
||||||
{{ scope.opt.town.name }}
|
|
||||||
({{ scope.opt.town.province.name }},
|
|
||||||
{{ scope.opt.town.province.country.country }})
|
|
||||||
</QItemLabel>
|
|
||||||
</QItemSection>
|
|
||||||
</QItem>
|
|
||||||
</template>
|
|
||||||
</VnSelectDialog>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<!-- ciudades -->
|
|
||||||
<VnSelectFilter
|
|
||||||
:label="t('City')"
|
|
||||||
:options="citiesLocationOptions"
|
|
||||||
hide-selected
|
|
||||||
option-label="name"
|
|
||||||
option-value="name"
|
|
||||||
v-model="data.city"
|
|
||||||
>
|
|
||||||
<template #option="scope">
|
|
||||||
<QItem v-bind="scope.itemProps">
|
|
||||||
<QItemSection>
|
|
||||||
<QItemLabel>{{ scope.opt.name }}</QItemLabel>
|
|
||||||
<QItemLabel caption>
|
|
||||||
{{
|
|
||||||
`${scope.opt.name}, ${scope.opt.province.name} (${scope.opt.province.country.country})`
|
|
||||||
}}
|
|
||||||
</QItemLabel>
|
|
||||||
</QItemSection>
|
|
||||||
</QItem>
|
|
||||||
</template>
|
|
||||||
</VnSelectFilter>
|
|
||||||
</div>
|
</div>
|
||||||
</VnRow>
|
</VnRow>
|
||||||
|
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
<div class="col">
|
|
||||||
<VnSelectFilter
|
|
||||||
:label="t('Province')"
|
|
||||||
:options="provincesLocationOptions"
|
|
||||||
hide-selected
|
|
||||||
option-label="name"
|
|
||||||
option-value="id"
|
|
||||||
v-model="data.provinceFk"
|
|
||||||
>
|
|
||||||
<template #option="scope">
|
|
||||||
<QItem v-bind="scope.itemProps">
|
|
||||||
<QItemSection>
|
|
||||||
<QItemLabel>{{
|
|
||||||
`${scope.opt.name} (${scope.opt.country.country})`
|
|
||||||
}}</QItemLabel>
|
|
||||||
</QItemSection>
|
|
||||||
</QItem>
|
|
||||||
</template>
|
|
||||||
</VnSelectFilter>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectFilter
|
<VnSelectFilter
|
||||||
:label="t('Agency')"
|
:label="t('Agency')"
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
<script setup>
|
||||||
|
import DepartmentDescriptor from './DepartmentDescriptor.vue';
|
||||||
|
import DepartmentSummaryDialog from './DepartmentSummaryDialog.vue';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<QPopupProxy>
|
||||||
|
<DepartmentDescriptor
|
||||||
|
v-if="$props.id"
|
||||||
|
:id="$props.id"
|
||||||
|
:summary="DepartmentSummaryDialog"
|
||||||
|
/>
|
||||||
|
</QPopupProxy>
|
||||||
|
</template>
|
|
@ -0,0 +1,29 @@
|
||||||
|
<script setup>
|
||||||
|
import { useDialogPluginComponent } from 'quasar';
|
||||||
|
import DepartmentSummary from './DepartmentSummary.vue';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
defineEmits([...useDialogPluginComponent.emits]);
|
||||||
|
|
||||||
|
const { dialogRef, onDialogHide } = useDialogPluginComponent();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<QDialog ref="dialogRef" @hide="onDialogHide">
|
||||||
|
<DepartmentSummary v-if="$props.id" :id="$props.id" />
|
||||||
|
</QDialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.q-dialog .summary .header {
|
||||||
|
position: sticky;
|
||||||
|
z-index: $z-max;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -44,7 +44,7 @@ const columns = computed(() => [
|
||||||
align: 'left',
|
align: 'left',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('entry.buys.description'),
|
label: t('globals.description'),
|
||||||
name: 'description',
|
name: 'description',
|
||||||
field: 'description',
|
field: 'description',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
@ -214,7 +214,7 @@ const redirectToBuysView = () => {
|
||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
@click="inputFileRef.pickFiles()"
|
@click="inputFileRef.pickFiles()"
|
||||||
>
|
>
|
||||||
<QTooltip>{{ t('Select a file') }}</QTooltip>
|
<QTooltip>{{ t('globals.selectFile') }}</QTooltip>
|
||||||
</QIcon>
|
</QIcon>
|
||||||
</template>
|
</template>
|
||||||
</QFile>
|
</QFile>
|
||||||
|
@ -292,6 +292,6 @@ const redirectToBuysView = () => {
|
||||||
|
|
||||||
<i18n>
|
<i18n>
|
||||||
es:
|
es:
|
||||||
Select a file: Selecciona un fichero
|
globals.selectFile: Selecciona un fichero
|
||||||
Some of the imported buys does not have an item: Algunas de las compras importadas no tienen un artículo
|
Some of the imported buys does not have an item: Algunas de las compras importadas no tienen un artículo
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
<script setup>
|
||||||
|
import VnDmsList from 'src/components/common/VnDmsList.vue';
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<VnDmsList
|
||||||
|
model="EntryDms"
|
||||||
|
update-model="EntryDms"
|
||||||
|
default-dms-code="entry"
|
||||||
|
filter="entryFk"
|
||||||
|
/>
|
||||||
|
</template>
|
|
@ -50,7 +50,6 @@ onMounted(() => {
|
||||||
:key="index"
|
:key="index"
|
||||||
class="row q-gutter-md q-mb-md"
|
class="row q-gutter-md q-mb-md"
|
||||||
>
|
>
|
||||||
<div class="col-3">
|
|
||||||
<VnSelectFilter
|
<VnSelectFilter
|
||||||
:label="t('entry.notes.observationType')"
|
:label="t('entry.notes.observationType')"
|
||||||
v-model="row.observationTypeFk"
|
v-model="row.observationTypeFk"
|
||||||
|
@ -60,15 +59,14 @@ onMounted(() => {
|
||||||
option-value="id"
|
option-value="id"
|
||||||
hide-selected
|
hide-selected
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<VnInput
|
<VnInput
|
||||||
:label="t('entry.notes.description')"
|
:label="t('globals.description')"
|
||||||
v-model="row.description"
|
v-model="row.description"
|
||||||
:rules="validate('EntryObservation.description')"
|
:rules="validate('EntryObservation.description')"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div class="col-1 row justify-center items-center">
|
<div class="row justify-center items-center">
|
||||||
<QIcon
|
<QIcon
|
||||||
name="delete"
|
name="delete"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
@ -82,7 +80,6 @@ onMounted(() => {
|
||||||
</QIcon>
|
</QIcon>
|
||||||
</div>
|
</div>
|
||||||
</VnRow>
|
</VnRow>
|
||||||
<VnRow>
|
|
||||||
<QIcon
|
<QIcon
|
||||||
name="add"
|
name="add"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
@ -94,7 +91,6 @@ onMounted(() => {
|
||||||
{{ t('Add note') }}
|
{{ t('Add note') }}
|
||||||
</QTooltip>
|
</QTooltip>
|
||||||
</QIcon>
|
</QIcon>
|
||||||
</VnRow>
|
|
||||||
</QCard>
|
</QCard>
|
||||||
</template>
|
</template>
|
||||||
</CrudModel>
|
</CrudModel>
|
||||||
|
|
|
@ -59,7 +59,7 @@ const columns = computed(() => [
|
||||||
align: 'left',
|
align: 'left',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('entry.latestBuys.description'),
|
label: t('globals.description'),
|
||||||
field: 'description',
|
field: 'description',
|
||||||
name: 'description',
|
name: 'description',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
@ -214,7 +214,7 @@ const editTableCellFormFieldsOptions = [
|
||||||
{ field: 'grouping', label: t('entry.latestBuys.grouping') },
|
{ field: 'grouping', label: t('entry.latestBuys.grouping') },
|
||||||
{ field: 'packageValue', label: t('entry.latestBuys.packageValue') },
|
{ field: 'packageValue', label: t('entry.latestBuys.packageValue') },
|
||||||
{ field: 'weight', label: t('entry.latestBuys.weight') },
|
{ field: 'weight', label: t('entry.latestBuys.weight') },
|
||||||
{ field: 'description', label: t('entry.latestBuys.description') },
|
{ field: 'description', label: t('globals.description') },
|
||||||
{ field: 'size', label: t('entry.latestBuys.size') },
|
{ field: 'size', label: t('entry.latestBuys.size') },
|
||||||
{ field: 'weightByPiece', label: t('entry.latestBuys.weightByPiece') },
|
{ field: 'weightByPiece', label: t('entry.latestBuys.weightByPiece') },
|
||||||
{ field: 'packingOut', label: t('entry.latestBuys.packingOut') },
|
{ field: 'packingOut', label: t('entry.latestBuys.packingOut') },
|
||||||
|
|
|
@ -174,7 +174,12 @@ async function upsert() {
|
||||||
@on-fetch="(data) => (userConfig = data)"
|
@on-fetch="(data) => (userConfig = data)"
|
||||||
auto-load
|
auto-load
|
||||||
/>
|
/>
|
||||||
<FormModel v-if="invoiceIn" :url="`InvoiceIns/${route.params.id}`" model="invoiceIn">
|
<FormModel
|
||||||
|
v-if="invoiceIn"
|
||||||
|
:url="`InvoiceIns/${route.params.id}`"
|
||||||
|
model="invoiceIn"
|
||||||
|
:auto-load="true"
|
||||||
|
>
|
||||||
<template #form="{ data }">
|
<template #form="{ data }">
|
||||||
<div class="row q-gutter-md q-mb-md">
|
<div class="row q-gutter-md q-mb-md">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
|
@ -509,7 +514,7 @@ async function upsert() {
|
||||||
@click="inputFileRef.pickFiles()"
|
@click="inputFileRef.pickFiles()"
|
||||||
>
|
>
|
||||||
<QTooltip>
|
<QTooltip>
|
||||||
{{ t('Select a file') }}
|
{{ t('globals.selectFile') }}
|
||||||
</QTooltip>
|
</QTooltip>
|
||||||
</QBtn>
|
</QBtn>
|
||||||
<QBtn icon="info" flat round padding="xs">
|
<QBtn icon="info" flat round padding="xs">
|
||||||
|
@ -618,7 +623,7 @@ async function upsert() {
|
||||||
@click="inputFileRef.pickFiles()"
|
@click="inputFileRef.pickFiles()"
|
||||||
>
|
>
|
||||||
<QTooltip>
|
<QTooltip>
|
||||||
{{ t('Select a file') }}
|
{{ t('globals.selectFile') }}
|
||||||
</QTooltip>
|
</QTooltip>
|
||||||
</QBtn>
|
</QBtn>
|
||||||
<QBtn icon="info" flat round padding="xs">
|
<QBtn icon="info" flat round padding="xs">
|
||||||
|
@ -687,7 +692,6 @@ async function upsert() {
|
||||||
Generate identifier for original file: Generar identificador para archivo original
|
Generate identifier for original file: Generar identificador para archivo original
|
||||||
File: Fichero
|
File: Fichero
|
||||||
Create document: Crear documento
|
Create document: Crear documento
|
||||||
Select a file: Seleccione un fichero
|
|
||||||
Allowed content types: Tipos de archivo permitidos
|
Allowed content types: Tipos de archivo permitidos
|
||||||
The company can't be empty: La empresa no puede estar vacía
|
The company can't be empty: La empresa no puede estar vacía
|
||||||
The warehouse can't be empty: El almacén no puede estar vacío
|
The warehouse can't be empty: El almacén no puede estar vacío
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref, onMounted, onUnmounted } from 'vue';
|
import { computed, ref, onMounted } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
|
@ -14,6 +14,7 @@ import EditPictureForm from 'components/EditPictureForm.vue';
|
||||||
import { useState } from 'src/composables/useState';
|
import { useState } from 'src/composables/useState';
|
||||||
import useCardDescription from 'src/composables/useCardDescription';
|
import useCardDescription from 'src/composables/useCardDescription';
|
||||||
import { useSession } from 'src/composables/useSession';
|
import { useSession } from 'src/composables/useSession';
|
||||||
|
import { getUrl } from 'src/composables/getUrl';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
|
@ -30,6 +31,10 @@ const $props = defineProps({
|
||||||
type: String,
|
type: String,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
saleFk: {
|
||||||
|
type: Number,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
|
@ -51,6 +56,7 @@ const available = ref(null);
|
||||||
const visible = ref(null);
|
const visible = ref(null);
|
||||||
const _warehouseFk = ref(null);
|
const _warehouseFk = ref(null);
|
||||||
const warehouseText = ref(null);
|
const warehouseText = ref(null);
|
||||||
|
const salixUrl = ref();
|
||||||
const warehouseFk = computed({
|
const warehouseFk = computed({
|
||||||
get() {
|
get() {
|
||||||
return _warehouseFk.value;
|
return _warehouseFk.value;
|
||||||
|
@ -69,6 +75,7 @@ const showEditPhotoForm = ref(false);
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await getItemAvatar();
|
await getItemAvatar();
|
||||||
warehouseFk.value = user.value.warehouseFk;
|
warehouseFk.value = user.value.warehouseFk;
|
||||||
|
salixUrl.value = await getUrl('');
|
||||||
});
|
});
|
||||||
|
|
||||||
const getItemAvatar = async () => {
|
const getItemAvatar = async () => {
|
||||||
|
@ -276,7 +283,16 @@ const openCloneDialog = async () => {
|
||||||
<template #actions="{}">
|
<template #actions="{}">
|
||||||
<QCardActions class="row justify-center">
|
<QCardActions class="row justify-center">
|
||||||
<QBtn
|
<QBtn
|
||||||
:to="{ name: 'ItemDiary' }"
|
:href="
|
||||||
|
salixUrl +
|
||||||
|
'item/' +
|
||||||
|
entityId +
|
||||||
|
'/diary?' +
|
||||||
|
'warehouseFk=' +
|
||||||
|
warehouseFk +
|
||||||
|
'&lineFk=' +
|
||||||
|
$props.saleFk
|
||||||
|
"
|
||||||
size="md"
|
size="md"
|
||||||
icon="vn:transaction"
|
icon="vn:transaction"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|
|
@ -11,6 +11,10 @@ const $props = defineProps({
|
||||||
type: String,
|
type: String,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
saleFk: {
|
||||||
|
type: Number,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -21,6 +25,7 @@ const $props = defineProps({
|
||||||
:id="$props.id"
|
:id="$props.id"
|
||||||
:summary="ItemSummary"
|
:summary="ItemSummary"
|
||||||
:dated="dated"
|
:dated="dated"
|
||||||
|
:sale-fk="saleFk"
|
||||||
/>
|
/>
|
||||||
</QPopupProxy>
|
</QPopupProxy>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -31,7 +31,7 @@ const detailsColumns = ref([
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'description',
|
name: 'description',
|
||||||
label: t('order.summary.description'),
|
label: t('globals.description'),
|
||||||
field: (row) => row?.item?.name,
|
field: (row) => row?.item?.name,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -167,7 +167,7 @@ const detailsColumns = ref([
|
||||||
<template #header="props">
|
<template #header="props">
|
||||||
<QTr :props="props">
|
<QTr :props="props">
|
||||||
<QTh auto-width>{{ t('order.summary.item') }}</QTh>
|
<QTh auto-width>{{ t('order.summary.item') }}</QTh>
|
||||||
<QTh>{{ t('order.summary.description') }}</QTh>
|
<QTh>{{ t('globals.description') }}</QTh>
|
||||||
<QTh auto-width>{{ t('order.summary.quantity') }}</QTh>
|
<QTh auto-width>{{ t('order.summary.quantity') }}</QTh>
|
||||||
<QTh auto-width>{{ t('order.summary.price') }}</QTh>
|
<QTh auto-width>{{ t('order.summary.price') }}</QTh>
|
||||||
<QTh auto-width>{{ t('order.summary.amount') }}</QTh>
|
<QTh auto-width>{{ t('order.summary.amount') }}</QTh>
|
||||||
|
|
|
@ -199,7 +199,7 @@ const openBuscaman = async (route, ticket) => {
|
||||||
</QCard>
|
</QCard>
|
||||||
<QCard class="vn-one">
|
<QCard class="vn-one">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
{{ t('route.summary.description') }}
|
{{ t('globals.description') }}
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
{{ dashIfEmpty(entity?.route?.description) }}
|
{{ dashIfEmpty(entity?.route?.description) }}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { useStateStore } from 'stores/useStateStore';
|
||||||
import CardSummary from 'components/ui/CardSummary.vue';
|
import CardSummary from 'components/ui/CardSummary.vue';
|
||||||
import VnLv from 'components/ui/VnLv.vue';
|
import VnLv from 'components/ui/VnLv.vue';
|
||||||
import ShelvingFilter from 'pages/Shelving/Card/ShelvingFilter.vue';
|
import ShelvingFilter from 'pages/Shelving/Card/ShelvingFilter.vue';
|
||||||
import VnUserLink from "components/ui/VnUserLink.vue";
|
import VnUserLink from 'components/ui/VnUserLink.vue';
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
id: {
|
id: {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import VnInput from 'src/components/common/VnInput.vue';
|
||||||
import VnSelectDialog from 'src/components/common/VnSelectDialog.vue';
|
import VnSelectDialog from 'src/components/common/VnSelectDialog.vue';
|
||||||
import VnRow from 'components/ui/VnRow.vue';
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
import CustomerCreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
|
import CustomerCreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
|
||||||
|
import VnLocation from 'src/components/common/VnLocation.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
@ -55,27 +56,16 @@ onMounted(() => {
|
||||||
updateAddressForm(addressData);
|
updateAddressForm(addressData);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
function handleLocation(data, location) {
|
||||||
|
const { town, code, provinceFk, countryFk } = location ?? {};
|
||||||
|
data.postalCode = code;
|
||||||
|
data.city = town;
|
||||||
|
data.provinceFk = provinceFk;
|
||||||
|
data.countryFk = countryFk;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<FetchData
|
|
||||||
ref="postcodeFetchDataRef"
|
|
||||||
url="Postcodes/location"
|
|
||||||
@on-fetch="(data) => (postcodesOptions = data)"
|
|
||||||
auto-load
|
|
||||||
/>
|
|
||||||
<FetchData
|
|
||||||
ref="provincesFetchDataRef"
|
|
||||||
@on-fetch="(data) => (provincesOptions = data)"
|
|
||||||
auto-load
|
|
||||||
url="Provinces"
|
|
||||||
/>
|
|
||||||
<FetchData
|
|
||||||
ref="townsFetchDataRef"
|
|
||||||
@on-fetch="(data) => (townsLocationOptions = data)"
|
|
||||||
auto-load
|
|
||||||
url="Towns/location"
|
|
||||||
/>
|
|
||||||
<QPage>
|
<QPage>
|
||||||
<FormModel
|
<FormModel
|
||||||
model="supplierAddresses"
|
model="supplierAddresses"
|
||||||
|
@ -104,59 +94,15 @@ onMounted(() => {
|
||||||
</VnRow>
|
</VnRow>
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<VnSelectDialog
|
<VnLocation
|
||||||
v-model="data.postalCode"
|
:rules="validate('Worker.postcode')"
|
||||||
:label="t('supplier.addresses.postcode')"
|
|
||||||
:rules="validate('supplierAddress.postcode')"
|
|
||||||
:roles-allowed-to-create="['deliveryAssistant']"
|
:roles-allowed-to-create="['deliveryAssistant']"
|
||||||
:options="postcodesOptions"
|
:options="postcodesOptions"
|
||||||
option-label="code"
|
v-model="data.location"
|
||||||
option-value="code"
|
@update:model-value="
|
||||||
hide-selected
|
(location) => handleLocation(data, location)
|
||||||
>
|
"
|
||||||
<template #form>
|
></VnLocation>
|
||||||
<CustomerCreateNewPostcode
|
|
||||||
@on-data-saved="onPostcodeCreated($event)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #option="scope">
|
|
||||||
<QItem v-bind="scope.itemProps">
|
|
||||||
<QItemSection v-if="scope.opt">
|
|
||||||
<QItemLabel>{{ scope.opt.code }}</QItemLabel>
|
|
||||||
<QItemLabel caption
|
|
||||||
>{{ scope.opt.code }} -
|
|
||||||
{{ scope.opt.town.name }} ({{
|
|
||||||
scope.opt.town.province.name
|
|
||||||
}},
|
|
||||||
{{
|
|
||||||
scope.opt.town.province.country.country
|
|
||||||
}})</QItemLabel
|
|
||||||
>
|
|
||||||
</QItemSection>
|
|
||||||
</QItem>
|
|
||||||
</template>
|
|
||||||
</VnSelectDialog>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<VnSelectFilter
|
|
||||||
:label="t('supplier.addresses.city')"
|
|
||||||
:options="townsLocationOptions"
|
|
||||||
v-model="data.city"
|
|
||||||
hide-selected
|
|
||||||
option-label="name"
|
|
||||||
option-value="id"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<VnSelectFilter
|
|
||||||
:label="t('supplier.addresses.province')"
|
|
||||||
:options="provincesOptions"
|
|
||||||
hide-selected
|
|
||||||
map-options
|
|
||||||
option-label="name"
|
|
||||||
option-value="id"
|
|
||||||
v-model="data.provinceFk"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</VnRow>
|
</VnRow>
|
||||||
<VnRow class="row q-gutter-md q-mb-md">
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
|
|
@ -21,7 +21,7 @@ const postcodesOptions = ref([]);
|
||||||
|
|
||||||
function handleLocation(data, location) {
|
function handleLocation(data, location) {
|
||||||
const { town, code, provinceFk, countryFk } = location ?? {};
|
const { town, code, provinceFk, countryFk } = location ?? {};
|
||||||
data.postcode = code;
|
data.postCode = code;
|
||||||
data.city = town;
|
data.city = town;
|
||||||
data.provinceFk = provinceFk;
|
data.provinceFk = provinceFk;
|
||||||
data.countryFk = countryFk;
|
data.countryFk = countryFk;
|
||||||
|
|
|
@ -270,7 +270,7 @@ async function changeState(value) {
|
||||||
<QTh auto-width>{{ t('ticket.summary.visible') }}</QTh>
|
<QTh auto-width>{{ t('ticket.summary.visible') }}</QTh>
|
||||||
<QTh auto-width>{{ t('ticket.summary.available') }}</QTh>
|
<QTh auto-width>{{ t('ticket.summary.available') }}</QTh>
|
||||||
<QTh auto-width>{{ t('ticket.summary.quantity') }}</QTh>
|
<QTh auto-width>{{ t('ticket.summary.quantity') }}</QTh>
|
||||||
<QTh auto-width>{{ t('ticket.summary.description') }}</QTh>
|
<QTh auto-width>{{ t('globals.description') }}</QTh>
|
||||||
<QTh auto-width>{{ t('ticket.summary.price') }}</QTh>
|
<QTh auto-width>{{ t('ticket.summary.price') }}</QTh>
|
||||||
<QTh auto-width>{{ t('ticket.summary.discount') }}</QTh>
|
<QTh auto-width>{{ t('ticket.summary.discount') }}</QTh>
|
||||||
<QTh auto-width>{{ t('globals.amount') }}</QTh>
|
<QTh auto-width>{{ t('globals.amount') }}</QTh>
|
||||||
|
@ -425,7 +425,7 @@ async function changeState(value) {
|
||||||
<template #header="props">
|
<template #header="props">
|
||||||
<QTr :props="props">
|
<QTr :props="props">
|
||||||
<QTh auto-width>{{ t('ticket.summary.quantity') }}</QTh>
|
<QTh auto-width>{{ t('ticket.summary.quantity') }}</QTh>
|
||||||
<QTh auto-width>{{ t('ticket.summary.description') }}</QTh>
|
<QTh auto-width>{{ t('globals.description') }}</QTh>
|
||||||
<QTh auto-width>{{ t('ticket.summary.price') }}</QTh>
|
<QTh auto-width>{{ t('ticket.summary.price') }}</QTh>
|
||||||
<QTh auto-width>{{ t('ticket.summary.taxClass') }}</QTh>
|
<QTh auto-width>{{ t('ticket.summary.taxClass') }}</QTh>
|
||||||
<QTh auto-width>{{ t('globals.amount') }}</QTh>
|
<QTh auto-width>{{ t('globals.amount') }}</QTh>
|
||||||
|
|
|
@ -300,7 +300,7 @@ const onThermographCreated = async (data) => {
|
||||||
<VnRow v-if="viewAction === 'edit'" class="row q-gutter-md q-mb-md">
|
<VnRow v-if="viewAction === 'edit'" class="row q-gutter-md q-mb-md">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<QInput
|
<QInput
|
||||||
:label="t('travel.thermographs.description')"
|
:label="t('globals.description')"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
v-model="thermographForm.description"
|
v-model="thermographForm.description"
|
||||||
fill-input
|
fill-input
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref, computed } from 'vue';
|
import { onMounted, ref, computed, watch } from 'vue';
|
||||||
import { QBtn, QField, QPopupEdit } from 'quasar';
|
import { QBtn } from 'quasar';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
@ -46,7 +46,12 @@ const arrayData = useArrayData('ExtraCommunity', {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const rows = computed(() => arrayData.store.data || []);
|
const rows = ref([]);
|
||||||
|
const originalRowDataCopy = ref([]);
|
||||||
|
const draggedRowIndex = ref(null);
|
||||||
|
const targetRowIndex = ref(null);
|
||||||
|
const entryRowIndex = ref(null);
|
||||||
|
const draggedEntry = ref(null);
|
||||||
|
|
||||||
const tableColumnComponents = {
|
const tableColumnComponents = {
|
||||||
id: {
|
id: {
|
||||||
|
@ -55,7 +60,12 @@ const tableColumnComponents = {
|
||||||
},
|
},
|
||||||
cargoSupplierNickname: {
|
cargoSupplierNickname: {
|
||||||
component: QBtn,
|
component: QBtn,
|
||||||
attrs: { flat: true, color: 'primary', dense: true },
|
attrs: {
|
||||||
|
flat: true,
|
||||||
|
color: 'primary',
|
||||||
|
dense: true,
|
||||||
|
class: 'supplier-name-button',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
agencyModeName: {
|
agencyModeName: {
|
||||||
component: 'span',
|
component: 'span',
|
||||||
|
@ -66,16 +76,24 @@ const tableColumnComponents = {
|
||||||
attrs: {},
|
attrs: {},
|
||||||
},
|
},
|
||||||
ref: {
|
ref: {
|
||||||
component: QField,
|
component: VnInput,
|
||||||
attrs: { readonly: true, dense: true, class: 'cursor-pointer' },
|
attrs: { dense: true },
|
||||||
|
event: (val, field, rowIndex) => ({
|
||||||
|
'keyup.enter': () => saveFieldValue(val, field, rowIndex),
|
||||||
|
blur: () => saveFieldValue(val, field, rowIndex),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
stickers: {
|
stickers: {
|
||||||
component: 'span',
|
component: 'span',
|
||||||
attrs: {},
|
attrs: {},
|
||||||
},
|
},
|
||||||
kg: {
|
kg: {
|
||||||
component: QField,
|
component: VnInput,
|
||||||
attrs: { readonly: true, dense: true, class: 'cursor-pointer' },
|
attrs: { dense: true, type: 'number', min: 0, class: 'input-number' },
|
||||||
|
event: (val, field, rowIndex) => ({
|
||||||
|
'keyup.enter': () => saveFieldValue(val, field, rowIndex),
|
||||||
|
blur: () => saveFieldValue(val, field, rowIndex),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
loadedKg: {
|
loadedKg: {
|
||||||
component: 'span',
|
component: 'span',
|
||||||
|
@ -199,7 +217,7 @@ const columns = computed(() => [
|
||||||
align: 'left',
|
align: 'left',
|
||||||
showValue: true,
|
showValue: true,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
format: (value) => toDate(value.substring(0, 10)),
|
format: (value) => toDate(value),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('globals.wareHouseIn'),
|
label: t('globals.wareHouseIn'),
|
||||||
|
@ -216,7 +234,7 @@ const columns = computed(() => [
|
||||||
align: 'left',
|
align: 'left',
|
||||||
showValue: true,
|
showValue: true,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
format: (value) => toDate(value.substring(0, 10)),
|
format: (value) => toDate(value),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -224,6 +242,22 @@ async function getData() {
|
||||||
await arrayData.fetch({ append: false });
|
await arrayData.fetch({ append: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onStoreDataChange = () => {
|
||||||
|
const newData = JSON.parse(JSON.stringify(arrayData.store.data)) || [];
|
||||||
|
rows.value = newData;
|
||||||
|
// el objetivo de esto es guardar una copia de los valores iniciales de todas las rows para corroborar si la data cambio antes de guardar los cambios
|
||||||
|
originalRowDataCopy.value = JSON.parse(JSON.stringify(newData));
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
arrayData.store,
|
||||||
|
() => {
|
||||||
|
if (!arrayData.store.data) return;
|
||||||
|
onStoreDataChange();
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
const openReportPdf = () => {
|
const openReportPdf = () => {
|
||||||
const params = {
|
const params = {
|
||||||
...arrayData.store.userParams,
|
...arrayData.store.userParams,
|
||||||
|
@ -235,9 +269,14 @@ const openReportPdf = () => {
|
||||||
|
|
||||||
const saveFieldValue = async (val, field, index) => {
|
const saveFieldValue = async (val, field, index) => {
|
||||||
try {
|
try {
|
||||||
|
// Evitar la solicitud de guardado si el valor no ha cambiado
|
||||||
|
if (originalRowDataCopy.value[index][field] == val) return;
|
||||||
|
|
||||||
const id = rows.value[index].id;
|
const id = rows.value[index].id;
|
||||||
const params = { [field]: val };
|
const params = { [field]: val };
|
||||||
await axios.patch(`Travels/${id}`, params);
|
await axios.patch(`Travels/${id}`, params);
|
||||||
|
// Actualizar la copia de los datos originales con el nuevo valor
|
||||||
|
originalRowDataCopy.value[index][field] = val;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error updating travel');
|
console.error('Error updating travel');
|
||||||
}
|
}
|
||||||
|
@ -248,6 +287,7 @@ const navigateToTravelId = (id) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const stopEventPropagation = (event, col) => {
|
const stopEventPropagation = (event, col) => {
|
||||||
|
// Detener la propagación del evento de los siguientes elementos para evitar el click sobre la row que dispararía la función navigateToTravelId
|
||||||
if (!['ref', 'id', 'cargoSupplierNickname', 'kg'].includes(col.name)) return;
|
if (!['ref', 'id', 'cargoSupplierNickname', 'kg'].includes(col.name)) return;
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
@ -263,6 +303,121 @@ onMounted(async () => {
|
||||||
landedTo.value.setHours(23, 59, 59, 59);
|
landedTo.value.setHours(23, 59, 59, 59);
|
||||||
await getData();
|
await getData();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Handler del evento @dragstart (inicio del drag) y guarda información inicial
|
||||||
|
const handleDragStart = (event, rowIndex, entryIndex) => {
|
||||||
|
draggedRowIndex.value = rowIndex;
|
||||||
|
entryRowIndex.value = entryIndex;
|
||||||
|
event.dataTransfer.effectAllowed = 'move';
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handler del evento @dragenter (cuando haces drag sobre une elemento y lo arrastras sobre un posible target de drop) y actualiza el targetIndex
|
||||||
|
const handleDragEnter = (_, targetIndex) => {
|
||||||
|
targetRowIndex.value = targetIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveRowDrop = async (targetRowIndex) => {
|
||||||
|
const entryId = draggedEntry.value.id;
|
||||||
|
const travelId = rows.value[targetRowIndex].id;
|
||||||
|
await axios.patch(`Entries/${entryId}`, { travelFk: travelId });
|
||||||
|
};
|
||||||
|
|
||||||
|
const moveRow = async (draggedRowIndex, targetRowIndex, entryIndex) => {
|
||||||
|
try {
|
||||||
|
if (draggedRowIndex === targetRowIndex) return;
|
||||||
|
// Remover entry de la row original
|
||||||
|
draggedEntry.value = rows.value[draggedRowIndex].entries.splice(entryIndex, 1)[0];
|
||||||
|
//Si la row de destino por alguna razón no tiene la propiedad entry la creamos
|
||||||
|
if (!rows.value[targetRowIndex].entries) rows.value[targetRowIndex].entries = [];
|
||||||
|
// Añadir entry a la row de destino
|
||||||
|
rows.value[targetRowIndex].entries.push(draggedEntry.value);
|
||||||
|
|
||||||
|
await saveRowDrop(targetRowIndex);
|
||||||
|
} catch (err) {
|
||||||
|
cleanDragAndDropData();
|
||||||
|
console.error('Error moving row', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handler de cuando haces un drop tanto dentro como fuera de la tabla para limpiar acciones y data
|
||||||
|
const handleDragEnd = () => {
|
||||||
|
stopScroll();
|
||||||
|
cleanDragAndDropData();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handler del evento @drop (cuando soltas el elemento draggeado sobre un target)
|
||||||
|
const handleDrop = () => {
|
||||||
|
if (
|
||||||
|
!draggedRowIndex.value &&
|
||||||
|
draggedRowIndex.value !== 0 &&
|
||||||
|
!targetRowIndex.value &&
|
||||||
|
draggedRowIndex.value !== 0
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
moveRow(draggedRowIndex.value, targetRowIndex.value, entryRowIndex.value);
|
||||||
|
handleDragEnd();
|
||||||
|
};
|
||||||
|
|
||||||
|
const cleanDragAndDropData = () => {
|
||||||
|
draggedRowIndex.value = null;
|
||||||
|
targetRowIndex.value = null;
|
||||||
|
entryRowIndex.value = null;
|
||||||
|
draggedEntry.value = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const scrollInterval = ref(null);
|
||||||
|
|
||||||
|
const startScroll = (direction) => {
|
||||||
|
// Iniciar el scroll en la dirección especificada
|
||||||
|
if (!scrollInterval.value) {
|
||||||
|
scrollInterval.value = requestAnimationFrame(() => scroll(direction));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const stopScroll = () => {
|
||||||
|
if (scrollInterval.value) {
|
||||||
|
cancelAnimationFrame(scrollInterval.value);
|
||||||
|
scrollInterval.value = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const scroll = (direction) => {
|
||||||
|
// Controlar el desplazamiento en la dirección especificada
|
||||||
|
const yOffset = direction === 'up' ? -2 : 2;
|
||||||
|
window.scrollBy(0, yOffset);
|
||||||
|
|
||||||
|
const windowHeight = window.innerHeight;
|
||||||
|
const documentHeight = document.body.offsetHeight;
|
||||||
|
|
||||||
|
// Verificar si se alcanzaron los límites de la ventana para detener el desplazamiento
|
||||||
|
if (
|
||||||
|
(direction === 'up' && window.scrollY > 0) ||
|
||||||
|
(direction === 'down' && windowHeight + window.scrollY < documentHeight)
|
||||||
|
) {
|
||||||
|
scrollInterval.value = requestAnimationFrame(() => scroll(direction));
|
||||||
|
} else {
|
||||||
|
stopScroll();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handler del scroll mientras se hace el drag de una row
|
||||||
|
const handleDragScroll = (event) => {
|
||||||
|
// Obtener la posición y dimensiones del cursor
|
||||||
|
const y = event.clientY;
|
||||||
|
const windowHeight = window.innerHeight;
|
||||||
|
|
||||||
|
// Verificar si el cursor está cerca del borde superior o inferior de la ventana
|
||||||
|
const nearTop = y < 150;
|
||||||
|
const nearBottom = y > windowHeight - 100;
|
||||||
|
|
||||||
|
if (nearTop) {
|
||||||
|
startScroll('up');
|
||||||
|
} else if (nearBottom) {
|
||||||
|
startScroll('down');
|
||||||
|
} else {
|
||||||
|
stopScroll();
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -302,59 +457,60 @@ onMounted(async () => {
|
||||||
row-key="clientId"
|
row-key="clientId"
|
||||||
:pagination="{ rowsPerPage: 0 }"
|
:pagination="{ rowsPerPage: 0 }"
|
||||||
class="full-width"
|
class="full-width"
|
||||||
|
table-style="user-select: none;"
|
||||||
|
@drag="handleDragScroll($event)"
|
||||||
|
@dragend="handleDragEnd($event)"
|
||||||
|
:separator="!targetRowIndex && targetRowIndex !== 0 ? 'horizontal' : 'none'"
|
||||||
>
|
>
|
||||||
<template #body="props">
|
<template #body="props">
|
||||||
<QTr
|
<QTr
|
||||||
:props="props"
|
:props="props"
|
||||||
@click="navigateToTravelId(props.row.id)"
|
|
||||||
class="cursor-pointer bg-vn-primary-row"
|
class="cursor-pointer bg-vn-primary-row"
|
||||||
|
@click="navigateToTravelId(props.row.id)"
|
||||||
|
@dragenter="handleDragEnter($event, props.rowIndex)"
|
||||||
|
@dragover.prevent
|
||||||
|
@drop="handleDrop()"
|
||||||
|
:class="{
|
||||||
|
'dashed-border --top --left --right':
|
||||||
|
targetRowIndex === props.rowIndex,
|
||||||
|
'--bottom':
|
||||||
|
targetRowIndex === props.rowIndex &&
|
||||||
|
(!props.row.entries || props.row.entries.length === 0),
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<QTd
|
<QTd
|
||||||
v-for="col in props.cols"
|
v-for="col in props.cols"
|
||||||
:key="col.name"
|
:key="col.name"
|
||||||
:props="props"
|
:props="props"
|
||||||
@click="stopEventPropagation($event, col)"
|
@click="stopEventPropagation($event, col)"
|
||||||
|
auto-width
|
||||||
>
|
>
|
||||||
<component
|
<component
|
||||||
:is="tableColumnComponents[col.name].component"
|
:is="tableColumnComponents[col.name].component"
|
||||||
v-bind="tableColumnComponents[col.name].attrs"
|
v-bind="tableColumnComponents[col.name].attrs"
|
||||||
>
|
v-model="rows[props.rowIndex][col.field]"
|
||||||
<!-- Editable 'ref' and 'kg' QField slot -->
|
v-on="
|
||||||
<template
|
tableColumnComponents[col.name].event
|
||||||
v-if="col.name === 'ref' || col.name === 'kg'"
|
? tableColumnComponents[col.name].event(
|
||||||
#control
|
rows[props.rowIndex][col.field],
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="self-center full-width no-outline"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
{{ col.value }}
|
|
||||||
</div>
|
|
||||||
<QPopupEdit
|
|
||||||
:key="col.name"
|
|
||||||
v-model="col.value"
|
|
||||||
label-set="Save"
|
|
||||||
label-cancel="Close"
|
|
||||||
>
|
|
||||||
<VnInput
|
|
||||||
v-model="rows[props.pageIndex][col.field]"
|
|
||||||
dense
|
|
||||||
autofocus
|
|
||||||
@keyup.enter="
|
|
||||||
saveFieldValue(
|
|
||||||
rows[props.pageIndex][col.field],
|
|
||||||
col.field,
|
col.field,
|
||||||
props.rowIndex
|
props.rowIndex
|
||||||
)
|
)
|
||||||
|
: {}
|
||||||
"
|
"
|
||||||
/>
|
>
|
||||||
</QPopupEdit>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-if="col.showValue">
|
<template v-if="col.showValue">
|
||||||
{{ col.value }}
|
<span
|
||||||
|
:class="[
|
||||||
|
'text-left',
|
||||||
|
{
|
||||||
|
'supplier-name':
|
||||||
|
col.name === 'cargoSupplierNickname',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>{{ col.value }}</span
|
||||||
|
>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- Main Row Descriptors -->
|
<!-- Main Row Descriptors -->
|
||||||
<TravelDescriptorProxy
|
<TravelDescriptorProxy
|
||||||
v-if="col.name === 'id'"
|
v-if="col.name === 'id'"
|
||||||
|
@ -367,13 +523,27 @@ onMounted(async () => {
|
||||||
</component>
|
</component>
|
||||||
</QTd>
|
</QTd>
|
||||||
</QTr>
|
</QTr>
|
||||||
|
|
||||||
<QTr
|
<QTr
|
||||||
v-for="entry in props.row.entries"
|
v-for="(entry, index) in props.row.entries"
|
||||||
:key="entry.id"
|
:key="index"
|
||||||
:props="props"
|
:props="props"
|
||||||
class="bg-vn-secondary-row"
|
class="bg-vn-secondary-row cursor-pointer"
|
||||||
|
@dragstart="handleDragStart($event, props.rowIndex, index)"
|
||||||
|
@dragenter="handleDragEnter($event, props.rowIndex)"
|
||||||
|
@dragover.prevent
|
||||||
|
@drop="handleDrop()"
|
||||||
|
:draggable="true"
|
||||||
|
:class="{
|
||||||
|
'dragged-row':
|
||||||
|
entryRowIndex === index && props.rowIndex === draggedRowIndex,
|
||||||
|
'dashed-border --left --right': targetRowIndex === props.rowIndex,
|
||||||
|
'--bottom':
|
||||||
|
targetRowIndex === props.rowIndex &&
|
||||||
|
index === props.row.entries.length - 1,
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<QTd class="row justify-center">
|
<QTd>
|
||||||
<QBtn flat color="primary">{{ entry.id }} </QBtn>
|
<QBtn flat color="primary">{{ entry.id }} </QBtn>
|
||||||
<EntryDescriptorProxy :id="entry.id" />
|
<EntryDescriptorProxy :id="entry.id" />
|
||||||
</QTd>
|
</QTd>
|
||||||
|
@ -381,33 +551,75 @@ onMounted(async () => {
|
||||||
<QBtn flat color="primary" dense>{{ entry.supplierName }}</QBtn>
|
<QBtn flat color="primary" dense>{{ entry.supplierName }}</QBtn>
|
||||||
<SupplierDescriptorProxy :id="entry.supplierFk" />
|
<SupplierDescriptorProxy :id="entry.supplierFk" />
|
||||||
</QTd>
|
</QTd>
|
||||||
|
<QTd />
|
||||||
|
<QTd>
|
||||||
|
<span>{{ toCurrency(entry.invoiceAmount) }}</span>
|
||||||
|
</QTd>
|
||||||
|
<QTd>
|
||||||
|
<span>{{ entry.reference }}</span>
|
||||||
|
</QTd>
|
||||||
|
<QTd>
|
||||||
|
<span>{{ entry.stickers }}</span>
|
||||||
|
</QTd>
|
||||||
<QTd></QTd>
|
<QTd></QTd>
|
||||||
<QTd
|
<QTd>
|
||||||
><span>{{ toCurrency(entry.invoiceAmount) }}</span></QTd
|
<span>{{ entry.loadedkg }}</span>
|
||||||
>
|
</QTd>
|
||||||
<QTd
|
<QTd>
|
||||||
><span>{{ entry.reference }}</span></QTd
|
<span>{{ entry.volumeKg }}</span>
|
||||||
>
|
</QTd>
|
||||||
<QTd
|
<QTd />
|
||||||
><span>{{ entry.stickers }}</span></QTd
|
<QTd />
|
||||||
>
|
<QTd />
|
||||||
<QTd></QTd>
|
<QTd />
|
||||||
<QTd
|
|
||||||
><span>{{ entry.loadedkg }}</span></QTd
|
|
||||||
>
|
|
||||||
<QTd
|
|
||||||
><span>{{ entry.volumeKg }}</span></QTd
|
|
||||||
>
|
|
||||||
<QTd></QTd>
|
|
||||||
<QTd></QTd>
|
|
||||||
<QTd></QTd>
|
|
||||||
<QTd></QTd>
|
|
||||||
</QTr>
|
</QTr>
|
||||||
</template>
|
</template>
|
||||||
</QTable>
|
</QTable>
|
||||||
</QPage>
|
</QPage>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
:deep(.q-table) {
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashed-border {
|
||||||
|
&.--left {
|
||||||
|
border-left: 1px dashed #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.--right {
|
||||||
|
border-right: 1px dashed #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.--top {
|
||||||
|
border-top: 1px dashed #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.--bottom {
|
||||||
|
border-bottom: 1px dashed #ccc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dragged-row {
|
||||||
|
background-color: $primary-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
.supplier-name {
|
||||||
|
display: flex;
|
||||||
|
max-width: 150px;
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-md-max) {
|
||||||
|
max-width: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.supplier-name-button {
|
||||||
|
white-space: normal;
|
||||||
|
width: max-content;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<i18n>
|
<i18n>
|
||||||
en:
|
en:
|
||||||
searchExtraCommunity: Search for extra community shipping
|
searchExtraCommunity: Search for extra community shipping
|
||||||
|
|
|
@ -65,7 +65,7 @@ const decrement = (paramsObj, key) => {
|
||||||
<span>{{ formatFn(tag.value) }}</span>
|
<span>{{ formatFn(tag.value) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #body="{ params }">
|
<template #body="{ params, searchFn }">
|
||||||
<QItem>
|
<QItem>
|
||||||
<QItemSection>
|
<QItemSection>
|
||||||
<VnInput label="id" v-model="params.id" is-outlined />
|
<VnInput label="id" v-model="params.id" is-outlined />
|
||||||
|
@ -116,6 +116,7 @@ const decrement = (paramsObj, key) => {
|
||||||
<VnSelectFilter
|
<VnSelectFilter
|
||||||
:label="t('params.agencyModeFk')"
|
:label="t('params.agencyModeFk')"
|
||||||
v-model="params.agencyModeFk"
|
v-model="params.agencyModeFk"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
:options="agenciesOptions"
|
:options="agenciesOptions"
|
||||||
option-value="agencyFk"
|
option-value="agencyFk"
|
||||||
option-label="name"
|
option-label="name"
|
||||||
|
@ -129,8 +130,9 @@ const decrement = (paramsObj, key) => {
|
||||||
<QItem>
|
<QItem>
|
||||||
<QItemSection>
|
<QItemSection>
|
||||||
<VnInputDate
|
<VnInputDate
|
||||||
v-model="params.shippedFrom"
|
|
||||||
:label="t('params.shippedFrom')"
|
:label="t('params.shippedFrom')"
|
||||||
|
v-model="params.shippedFrom"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
is-outlined
|
is-outlined
|
||||||
/>
|
/>
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
|
@ -138,8 +140,9 @@ const decrement = (paramsObj, key) => {
|
||||||
<QItem>
|
<QItem>
|
||||||
<QItemSection>
|
<QItemSection>
|
||||||
<VnInputDate
|
<VnInputDate
|
||||||
v-model="params.landedTo"
|
|
||||||
:label="t('params.landedTo')"
|
:label="t('params.landedTo')"
|
||||||
|
v-model="params.landedTo"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
is-outlined
|
is-outlined
|
||||||
/>
|
/>
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
|
@ -149,6 +152,7 @@ const decrement = (paramsObj, key) => {
|
||||||
<VnSelectFilter
|
<VnSelectFilter
|
||||||
:label="t('params.warehouseOutFk')"
|
:label="t('params.warehouseOutFk')"
|
||||||
v-model="params.warehouseOutFk"
|
v-model="params.warehouseOutFk"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
:options="warehousesOptions"
|
:options="warehousesOptions"
|
||||||
option-value="id"
|
option-value="id"
|
||||||
option-label="name"
|
option-label="name"
|
||||||
|
@ -164,6 +168,7 @@ const decrement = (paramsObj, key) => {
|
||||||
<VnSelectFilter
|
<VnSelectFilter
|
||||||
:label="t('params.warehouseInFk')"
|
:label="t('params.warehouseInFk')"
|
||||||
v-model="params.warehouseInFk"
|
v-model="params.warehouseInFk"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
:options="warehousesOptions"
|
:options="warehousesOptions"
|
||||||
option-value="id"
|
option-value="id"
|
||||||
option-label="name"
|
option-label="name"
|
||||||
|
@ -179,6 +184,7 @@ const decrement = (paramsObj, key) => {
|
||||||
<VnSelectFilter
|
<VnSelectFilter
|
||||||
:label="t('supplier.pageTitles.supplier')"
|
:label="t('supplier.pageTitles.supplier')"
|
||||||
v-model="params.cargoSupplierFk"
|
v-model="params.cargoSupplierFk"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
:options="suppliersOptions"
|
:options="suppliersOptions"
|
||||||
option-value="id"
|
option-value="id"
|
||||||
option-label="name"
|
option-label="name"
|
||||||
|
@ -194,6 +200,7 @@ const decrement = (paramsObj, key) => {
|
||||||
<VnSelectFilter
|
<VnSelectFilter
|
||||||
:label="t('params.continent')"
|
:label="t('params.continent')"
|
||||||
v-model="params.continent"
|
v-model="params.continent"
|
||||||
|
@update:model-value="searchFn()"
|
||||||
:options="continentsOptions"
|
:options="continentsOptions"
|
||||||
option-value="code"
|
option-value="code"
|
||||||
option-label="name"
|
option-label="name"
|
||||||
|
@ -208,19 +215,6 @@ const decrement = (paramsObj, key) => {
|
||||||
</VnFilterPanel>
|
</VnFilterPanel>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.input-number >>> input[type='number'] {
|
|
||||||
-moz-appearance: textfield;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-number >>> input::-webkit-outer-spin-button,
|
|
||||||
.input-number >>> input::-webkit-inner-spin-button {
|
|
||||||
appearance: none;
|
|
||||||
-webkit-appearance: none;
|
|
||||||
-moz-appearance: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<i18n>
|
<i18n>
|
||||||
en:
|
en:
|
||||||
params:
|
params:
|
||||||
|
|
|
@ -11,7 +11,7 @@ export default {
|
||||||
redirect: { name: 'EntryMain' },
|
redirect: { name: 'EntryMain' },
|
||||||
menus: {
|
menus: {
|
||||||
main: ['EntryList', 'EntryLatestBuys'],
|
main: ['EntryList', 'EntryLatestBuys'],
|
||||||
card: ['EntryBasicData', 'EntryBuys', 'EntryNotes', 'EntryLog'],
|
card: ['EntryBasicData', 'EntryBuys', 'EntryNotes', 'EntryDms', 'EntryLog'],
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
@ -95,6 +95,15 @@ export default {
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Entry/Card/EntryNotes.vue'),
|
component: () => import('src/pages/Entry/Card/EntryNotes.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'dms',
|
||||||
|
name: 'EntryDms',
|
||||||
|
meta: {
|
||||||
|
title: 'dms',
|
||||||
|
icon: 'smb_share',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Entry/Card/EntryDms.vue'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'log',
|
path: 'log',
|
||||||
name: 'EntryLog',
|
name: 'EntryLog',
|
||||||
|
|
|
@ -13,7 +13,7 @@ describe('ClaimDevelopment', () => {
|
||||||
it('should reset line', () => {
|
it('should reset line', () => {
|
||||||
cy.selectOption(firstLineReason, 'Novato');
|
cy.selectOption(firstLineReason, 'Novato');
|
||||||
cy.resetCard();
|
cy.resetCard();
|
||||||
cy.getValue(firstLineReason).should('have.value', 'Prisas');
|
cy.getValue(firstLineReason).should('equal', 'Prisas');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should edit line', () => {
|
it('should edit line', () => {
|
||||||
|
@ -23,7 +23,7 @@ describe('ClaimDevelopment', () => {
|
||||||
cy.login('developer');
|
cy.login('developer');
|
||||||
cy.visit(`/#/claim/${claimId}/development`);
|
cy.visit(`/#/claim/${claimId}/development`);
|
||||||
|
|
||||||
cy.getValue(firstLineReason).should('have.value', 'Novato');
|
cy.getValue(firstLineReason).should('equal', 'Novato');
|
||||||
|
|
||||||
//Restart data
|
//Restart data
|
||||||
cy.selectOption(firstLineReason, 'Prisas');
|
cy.selectOption(firstLineReason, 'Prisas');
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
describe('WagonTypeCreate', () => {
|
||||||
|
const entryId = 1;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.viewport(1920, 1080);
|
||||||
|
cy.login('developer');
|
||||||
|
cy.visit(`/#/entry/${entryId}/dms`);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create edit and remove new dms', () => {
|
||||||
|
cy.addRow();
|
||||||
|
cy.get('.icon-attach').click()
|
||||||
|
cy.get('.q-file').selectFile('test/cypress/fixtures/image.jpg', {
|
||||||
|
force: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.get("tbody > tr").then((value) => {
|
||||||
|
//Create and check if exist new row
|
||||||
|
let newFileTd = Cypress.$(value).length;
|
||||||
|
cy.get('.q-btn--standard > .q-btn__content > .block').click();
|
||||||
|
expect(value).to.have.length(newFileTd++);
|
||||||
|
const newRowSelector = `tbody > :nth-child(${newFileTd})`
|
||||||
|
cy.waitForElement(newRowSelector);
|
||||||
|
|
||||||
|
//Edit new dms
|
||||||
|
const u = undefined;
|
||||||
|
cy.validateRow(newRowSelector, [u,u,u,u,'ENTRADA ID 1'])
|
||||||
|
cy.get(`tbody :nth-child(${newFileTd}) > .text-right > .flex > :nth-child(2) > .q-btn > .q-btn__content > .q-icon`).click();
|
||||||
|
})
|
||||||
|
// cy.log('newFileTd', newFileTd)
|
||||||
|
|
||||||
|
// //Create and check if exist new row
|
||||||
|
// cy.log('newFileTd:', newFileTd);
|
||||||
|
// cy.get(`tbody :nth-child(${newFileTd}) > .text-right > .flex > :nth-child(2) > .q-btn > .q-btn__content > .q-icon`).click()
|
||||||
|
|
||||||
|
// cy.get(`tbody :nth-child(${newFileTd}) > :nth-child(5) > .q-tr > :nth-child(1) > span`).then((value) => {
|
||||||
|
// cy.log(value)
|
||||||
|
// });
|
||||||
|
});
|
||||||
|
});
|
|
@ -18,7 +18,7 @@ describe('InvoiceInIntrastat', () => {
|
||||||
cy.visit(`/#/invoice-in/1/intrastat`);
|
cy.visit(`/#/invoice-in/1/intrastat`);
|
||||||
|
|
||||||
cy.getValue(firstLineCode).should(
|
cy.getValue(firstLineCode).should(
|
||||||
'have.value',
|
'equal',
|
||||||
'Plantas vivas: Esqueje/injerto, Vid'
|
'Plantas vivas: Esqueje/injerto, Vid'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -21,7 +21,7 @@ describe('InvoiceInVat', () => {
|
||||||
cy.saveCard();
|
cy.saveCard();
|
||||||
cy.visit(`/#/invoice-in/1/vat`);
|
cy.visit(`/#/invoice-in/1/vat`);
|
||||||
|
|
||||||
cy.getValue(firstLineVat).should('have.value', 'H.P. IVA 21% CEE');
|
cy.getValue(firstLineVat).should('equal', 'H.P. IVA 21% CEE');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add a new row', () => {
|
it('should add a new row', () => {
|
||||||
|
|
|
@ -42,7 +42,7 @@ Cypress.Commands.add('login', (user) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
Cypress.Commands.add('waitForElement', (element) => {
|
Cypress.Commands.add('waitForElement', (element) => {
|
||||||
cy.get(element, { timeout: 2000 }).should('be.visible');
|
cy.get(element, { timeout: 5000 }).should('be.visible');
|
||||||
});
|
});
|
||||||
|
|
||||||
Cypress.Commands.add('getValue', (selector) => {
|
Cypress.Commands.add('getValue', (selector) => {
|
||||||
|
@ -55,7 +55,13 @@ Cypress.Commands.add('getValue', (selector) => {
|
||||||
return cy.get(
|
return cy.get(
|
||||||
selector +
|
selector +
|
||||||
'> .q-field > .q-field__inner > .q-field__control > .q-field__control-container > .q-field__native > input'
|
'> .q-field > .q-field__inner > .q-field__control > .q-field__control-container > .q-field__native > input'
|
||||||
);
|
).invoke('val')
|
||||||
|
}
|
||||||
|
// Si es un QSelect
|
||||||
|
if ($el.find('span').length) {
|
||||||
|
return cy.get(
|
||||||
|
selector + ' span'
|
||||||
|
).then(($span) => { return $span[0].innerText })
|
||||||
}
|
}
|
||||||
// Puedes añadir un log o lanzar un error si el elemento no es reconocido
|
// Puedes añadir un log o lanzar un error si el elemento no es reconocido
|
||||||
cy.log('Elemento no soportado');
|
cy.log('Elemento no soportado');
|
||||||
|
@ -126,12 +132,13 @@ Cypress.Commands.add('validateRow', (rowSelector, expectedValues) => {
|
||||||
cy.get(rowSelector).within(() => {
|
cy.get(rowSelector).within(() => {
|
||||||
for (const [index, value] of expectedValues.entries()) {
|
for (const [index, value] of expectedValues.entries()) {
|
||||||
cy.log('CHECKING ', index, value);
|
cy.log('CHECKING ', index, value);
|
||||||
|
if(value === undefined) continue
|
||||||
if (typeof value == 'boolean') {
|
if (typeof value == 'boolean') {
|
||||||
const prefix = value ? '' : 'not.';
|
const prefix = value ? '' : 'not.';
|
||||||
cy.getValue(`:nth-child(${index + 1})`).should(`${prefix}be.checked`);
|
cy.getValue(`:nth-child(${index + 1})`).should(`${prefix}be.checked`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
cy.getValue(`:nth-child(${index + 1})`).should('have.value', value);
|
cy.getValue(`:nth-child(${index + 1})`).should('equal', value)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue