Merge branch 'dev' into minor_requested_changes_invoiceOutList
gitea/salix-front/pipeline/pr-dev This commit looks good
Details
gitea/salix-front/pipeline/pr-dev This commit looks good
Details
This commit is contained in:
commit
a330efd13f
|
@ -0,0 +1 @@
|
|||
node_modules
|
3095
CHANGELOG.md
3095
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,7 @@
|
|||
#!/usr/bin/env groovy
|
||||
|
||||
def PROTECTED_BRANCH
|
||||
def IS_LATEST
|
||||
|
||||
def BRANCH_ENV = [
|
||||
test: 'test',
|
||||
|
@ -10,17 +11,19 @@ def BRANCH_ENV = [
|
|||
|
||||
node {
|
||||
stage('Setup') {
|
||||
env.FRONT_REPLICAS = 1
|
||||
env.NODE_ENV = BRANCH_ENV[env.BRANCH_NAME] ?: 'dev'
|
||||
|
||||
PROTECTED_BRANCH = [
|
||||
'dev',
|
||||
'test',
|
||||
'master',
|
||||
'main',
|
||||
'beta'
|
||||
].contains(env.BRANCH_NAME)
|
||||
]
|
||||
|
||||
// https://www.jenkins.io/doc/book/pipeline/jenkinsfile/#using-environment-variables
|
||||
IS_PROTECTED_BRANCH = PROTECTED_BRANCH.contains(env.BRANCH_NAME)
|
||||
IS_LATEST = ['master', 'main'].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}"
|
||||
|
||||
|
@ -33,7 +36,7 @@ node {
|
|||
props.each {key, value -> echo "${key}: ${value}" }
|
||||
}
|
||||
|
||||
if (PROTECTED_BRANCH) {
|
||||
if (IS_PROTECTED_BRANCH) {
|
||||
configFileProvider([
|
||||
configFile(fileId: "salix-front.branch.${env.BRANCH_NAME}",
|
||||
variable: 'BRANCH_PROPS_FILE')
|
||||
|
@ -58,6 +61,19 @@ pipeline {
|
|||
PROJECT_NAME = 'lilium'
|
||||
}
|
||||
stages {
|
||||
stage('Version') {
|
||||
when {
|
||||
expression { IS_PROTECTED_BRANCH }
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
def packageJson = readJSON file: 'package.json'
|
||||
def version = "${packageJson.version}-build${env.BUILD_ID}"
|
||||
writeFile(file: 'VERSION.txt', text: version)
|
||||
echo "VERSION: ${version}"
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Install') {
|
||||
environment {
|
||||
NODE_ENV = ""
|
||||
|
@ -68,48 +84,85 @@ pipeline {
|
|||
}
|
||||
stage('Test') {
|
||||
when {
|
||||
expression { !PROTECTED_BRANCH }
|
||||
expression { !IS_PROTECTED_BRANCH }
|
||||
}
|
||||
environment {
|
||||
NODE_ENV = ""
|
||||
NODE_ENV = ''
|
||||
CI = 'true'
|
||||
TZ = 'Europe/Madrid'
|
||||
}
|
||||
steps {
|
||||
sh 'pnpm run test:unit:ci'
|
||||
}
|
||||
post {
|
||||
always {
|
||||
junit(
|
||||
testResults: 'junitresults.xml',
|
||||
allowEmptyResults: true
|
||||
)
|
||||
parallel {
|
||||
stage('Unit') {
|
||||
steps {
|
||||
sh 'pnpm run test:unit:ci'
|
||||
}
|
||||
post {
|
||||
always {
|
||||
junit(
|
||||
testResults: 'junit/vitest.xml',
|
||||
allowEmptyResults: true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('E2E') {
|
||||
environment {
|
||||
CREDENTIALS = credentials('docker-registry')
|
||||
COMPOSE_PROJECT = "${PROJECT_NAME}-${env.BUILD_ID}"
|
||||
COMPOSE_PARAMS = "-p ${env.COMPOSE_PROJECT} -f test/cypress/docker-compose.yml --project-directory ."
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
sh 'rm junit/e2e-*.xml || true'
|
||||
env.COMPOSE_TAG = PROTECTED_BRANCH.contains(env.CHANGE_TARGET) ? env.CHANGE_TARGET : 'dev'
|
||||
def image = docker.build('lilium-dev', '-f docs/Dockerfile.dev docs')
|
||||
sh "docker-compose ${env.COMPOSE_PARAMS} up -d"
|
||||
image.inside("--network ${env.COMPOSE_PROJECT}_default -e CI -e TZ") {
|
||||
sh 'cypress run --browser chromium || true'
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
sh "docker-compose ${env.COMPOSE_PARAMS} down -v"
|
||||
junit(
|
||||
testResults: 'junit/e2e-*.xml',
|
||||
allowEmptyResults: true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Build') {
|
||||
when {
|
||||
expression { PROTECTED_BRANCH }
|
||||
expression { IS_PROTECTED_BRANCH }
|
||||
}
|
||||
environment {
|
||||
CREDENTIALS = credentials('docker-registry')
|
||||
VERSION = readFile 'VERSION.txt'
|
||||
}
|
||||
steps {
|
||||
sh 'quasar build'
|
||||
script {
|
||||
def packageJson = readJSON file: 'package.json'
|
||||
env.VERSION = "${packageJson.version}-build${env.BUILD_ID}"
|
||||
sh 'quasar build'
|
||||
|
||||
def baseImage = "salix-frontend:${env.VERSION}"
|
||||
def image = docker.build(baseImage, ".")
|
||||
docker.withRegistry("https://${env.REGISTRY}", 'docker-registry') {
|
||||
image.push()
|
||||
image.push(env.BRANCH_NAME)
|
||||
if (IS_LATEST) image.push('latest')
|
||||
}
|
||||
}
|
||||
dockerBuild()
|
||||
}
|
||||
}
|
||||
stage('Deploy') {
|
||||
when {
|
||||
expression { PROTECTED_BRANCH }
|
||||
expression { IS_PROTECTED_BRANCH }
|
||||
}
|
||||
environment {
|
||||
VERSION = readFile 'VERSION.txt'
|
||||
}
|
||||
steps {
|
||||
script {
|
||||
def packageJson = readJSON file: 'package.json'
|
||||
env.VERSION = "${packageJson.version}-build${env.BUILD_ID}"
|
||||
}
|
||||
withKubeConfig([
|
||||
serverUrl: "$KUBERNETES_API",
|
||||
credentialsId: 'kubernetes',
|
||||
|
|
|
@ -1,12 +1,37 @@
|
|||
import { defineConfig } from 'cypress';
|
||||
// https://docs.cypress.io/app/tooling/reporters
|
||||
// https://docs.cypress.io/app/references/configuration
|
||||
// https://www.npmjs.com/package/cypress-mochawesome-reporter
|
||||
|
||||
let urlHost, reporter, reporterOptions;
|
||||
|
||||
if (process.env.CI) {
|
||||
urlHost = 'front';
|
||||
reporter = 'junit';
|
||||
reporterOptions = {
|
||||
mochaFile: 'junit/e2e-[hash].xml',
|
||||
toConsole: false,
|
||||
};
|
||||
} else {
|
||||
urlHost = 'localhost';
|
||||
reporter = 'cypress-mochawesome-reporter';
|
||||
reporterOptions = {
|
||||
charts: true,
|
||||
reportPageTitle: 'Cypress Inline Reporter',
|
||||
reportFilename: '[status]_[datetime]-report',
|
||||
embeddedScreenshots: true,
|
||||
reportDir: 'test/cypress/reports',
|
||||
inlineAssets: true,
|
||||
};
|
||||
}
|
||||
|
||||
export default defineConfig({
|
||||
e2e: {
|
||||
baseUrl: 'http://localhost:9000/',
|
||||
experimentalStudio: true,
|
||||
baseUrl: `http://${urlHost}:9000`,
|
||||
experimentalStudio: false,
|
||||
defaultCommandTimeout: 10000,
|
||||
trashAssetsBeforeRuns: false,
|
||||
requestTimeout: 10000,
|
||||
responseTimeout: 30000,
|
||||
pageLoadTimeout: 60000,
|
||||
defaultBrowser: 'chromium',
|
||||
fixturesFolder: 'test/cypress/fixtures',
|
||||
screenshotsFolder: 'test/cypress/screenshots',
|
||||
supportFile: 'test/cypress/support/index.js',
|
||||
|
@ -14,29 +39,19 @@ export default defineConfig({
|
|||
downloadsFolder: 'test/cypress/downloads',
|
||||
video: false,
|
||||
specPattern: 'test/cypress/integration/**/*.spec.js',
|
||||
experimentalRunAllSpecs: false,
|
||||
watchForFileChanges: false,
|
||||
reporter: 'cypress-mochawesome-reporter',
|
||||
reporterOptions: {
|
||||
charts: true,
|
||||
reportPageTitle: 'Cypress Inline Reporter',
|
||||
reportFilename: '[status]_[datetime]-report',
|
||||
embeddedScreenshots: true,
|
||||
reportDir: 'test/cypress/reports',
|
||||
inlineAssets: true,
|
||||
},
|
||||
experimentalRunAllSpecs: true,
|
||||
watchForFileChanges: true,
|
||||
reporter,
|
||||
reporterOptions,
|
||||
component: {
|
||||
componentFolder: 'src',
|
||||
testFiles: '**/*.spec.js',
|
||||
supportFile: 'test/cypress/support/unit.js',
|
||||
},
|
||||
setupNodeEvents: async (on, config) => {
|
||||
const plugin = await import('cypress-mochawesome-reporter/plugin');
|
||||
plugin.default(on);
|
||||
|
||||
return config;
|
||||
},
|
||||
viewportWidth: 1280,
|
||||
viewportHeight: 720,
|
||||
},
|
||||
experimentalMemoryManagement: true,
|
||||
defaultCommandTimeout: 10000,
|
||||
numTestsKeptInMemory: 2,
|
||||
});
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
version: '3.7'
|
||||
services:
|
||||
main:
|
||||
image: registry.verdnatura.es/salix-frontend:${VERSION:?}
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./Dockerfile
|
|
@ -0,0 +1,45 @@
|
|||
FROM debian:12.9-slim
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
ca-certificates \
|
||||
curl \
|
||||
gnupg2 \
|
||||
&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
|
||||
&& apt-get install -y --no-install-recommends nodejs \
|
||||
&& npm install -g corepack@0.31.0 \
|
||||
&& corepack enable pnpm \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get -y --no-install-recommends install \
|
||||
apt-utils \
|
||||
chromium \
|
||||
libasound2 \
|
||||
libgbm-dev \
|
||||
libgtk-3-0 \
|
||||
libgtk2.0-0 \
|
||||
libnotify-dev \
|
||||
libnss3 \
|
||||
libxss1 \
|
||||
libxtst6 \
|
||||
xauth \
|
||||
xvfb \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN groupadd -r -g 1000 app \
|
||||
&& useradd -r -u 1000 -g app -m -d /home/app app
|
||||
USER app
|
||||
|
||||
ENV SHELL=bash
|
||||
ENV PNPM_HOME="/home/app/.local/share/pnpm"
|
||||
ENV PATH="$PNPM_HOME:$PATH"
|
||||
|
||||
RUN pnpm setup \
|
||||
&& pnpm install --global cypress@13.6.6 \
|
||||
&& cypress install
|
||||
|
||||
WORKDIR /app
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "salix-front",
|
||||
"version": "25.08.0",
|
||||
"version": "25.10.0",
|
||||
"description": "Salix frontend",
|
||||
"productName": "Salix",
|
||||
"author": "Verdnatura",
|
||||
|
@ -71,4 +71,4 @@
|
|||
"vite": "^6.0.11",
|
||||
"vitest": "^0.31.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
export default [
|
||||
{
|
||||
path: '/api',
|
||||
rule: { target: 'http://0.0.0.0:3000' },
|
||||
rule: { target: 'http://127.0.0.1:3000' },
|
||||
},
|
||||
];
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
import { configure } from 'quasar/wrappers';
|
||||
import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite';
|
||||
import path from 'path';
|
||||
const target = `http://${process.env.CI ? 'back' : 'localhost'}:3000`;
|
||||
|
||||
export default configure(function (/* ctx */) {
|
||||
return {
|
||||
|
@ -108,13 +109,17 @@ export default configure(function (/* ctx */) {
|
|||
},
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://0.0.0.0:3000',
|
||||
target: target,
|
||||
logLevel: 'debug',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
},
|
||||
},
|
||||
open: false,
|
||||
allowedHosts: [
|
||||
'front', // Agrega este nombre de host
|
||||
'localhost', // Opcional, para pruebas locales
|
||||
],
|
||||
},
|
||||
|
||||
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#framework
|
||||
|
|
|
@ -30,22 +30,5 @@ export default {
|
|||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
form.addEventListener('keyup', function (evt) {
|
||||
if (evt.key === 'Enter' && !that.$attrs['prevent-submit']) {
|
||||
const input = evt.target;
|
||||
if (input.type == 'textarea' && evt.shiftKey) {
|
||||
evt.preventDefault();
|
||||
let { selectionStart, selectionEnd } = input;
|
||||
input.value =
|
||||
input.value.substring(0, selectionStart) +
|
||||
'\n' +
|
||||
input.value.substring(selectionEnd);
|
||||
selectionStart = selectionEnd = selectionStart + 1;
|
||||
return;
|
||||
}
|
||||
evt.preventDefault();
|
||||
that.onSubmit();
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup>
|
||||
import axios from 'axios';
|
||||
import { onMounted, onUnmounted, computed, ref, watch, nextTick } from 'vue';
|
||||
import { onMounted, onUnmounted, computed, ref, watch, nextTick, useAttrs } from 'vue';
|
||||
import { onBeforeRouteLeave, useRouter, useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useQuasar } from 'quasar';
|
||||
|
@ -12,6 +12,7 @@ import SkeletonForm from 'components/ui/SkeletonForm.vue';
|
|||
import VnConfirm from './ui/VnConfirm.vue';
|
||||
import { tMobile } from 'src/composables/tMobile';
|
||||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
import { getDifferences, getUpdatedValues } from 'src/filters';
|
||||
|
||||
const { push } = useRouter();
|
||||
const quasar = useQuasar();
|
||||
|
@ -22,6 +23,7 @@ const { validate } = useValidator();
|
|||
const { notify } = useNotify();
|
||||
const route = useRoute();
|
||||
const myForm = ref(null);
|
||||
const attrs = useAttrs();
|
||||
const $props = defineProps({
|
||||
url: {
|
||||
type: String,
|
||||
|
@ -94,6 +96,10 @@ const $props = defineProps({
|
|||
type: [String, Boolean],
|
||||
default: '800px',
|
||||
},
|
||||
onDataSaved: {
|
||||
type: Function,
|
||||
default: () => {},
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(['onFetch', 'onDataSaved']);
|
||||
const modelValue = computed(
|
||||
|
@ -106,14 +112,14 @@ const isLoading = ref(false);
|
|||
const isResetting = ref(false);
|
||||
const hasChanges = ref(!$props.observeFormChanges);
|
||||
const originalData = computed(() => state.get(modelValue));
|
||||
const formData = ref({});
|
||||
const formData = ref();
|
||||
const defaultButtons = computed(() => ({
|
||||
save: {
|
||||
dataCy: 'saveDefaultBtn',
|
||||
color: 'primary',
|
||||
icon: 'save',
|
||||
label: 'globals.save',
|
||||
click: () => myForm.value.onSubmit(false),
|
||||
click: async () => await save(),
|
||||
type: 'submit',
|
||||
},
|
||||
reset: {
|
||||
|
@ -208,8 +214,7 @@ async function fetch() {
|
|||
}
|
||||
}
|
||||
|
||||
async function save(prevent = false) {
|
||||
if (prevent) return;
|
||||
async function save() {
|
||||
if ($props.observeFormChanges && !hasChanges.value)
|
||||
return notify('globals.noChanges', 'negative');
|
||||
|
||||
|
@ -247,6 +252,7 @@ async function saveAndGo() {
|
|||
}
|
||||
|
||||
function reset() {
|
||||
formData.value = JSON.parse(JSON.stringify(originalData.value));
|
||||
updateAndEmit('onFetch', { val: originalData.value });
|
||||
if ($props.observeFormChanges) {
|
||||
hasChanges.value = false;
|
||||
|
@ -283,6 +289,27 @@ function trimData(data) {
|
|||
}
|
||||
return data;
|
||||
}
|
||||
function onBeforeSave(formData, originalData) {
|
||||
return getUpdatedValues(
|
||||
Object.keys(getDifferences(formData, originalData)),
|
||||
formData,
|
||||
);
|
||||
}
|
||||
async function onKeyup(evt) {
|
||||
if (evt.key === 'Enter' && !('prevent-submit' in attrs)) {
|
||||
const input = evt.target;
|
||||
if (input.type == 'textarea' && evt.shiftKey) {
|
||||
let { selectionStart, selectionEnd } = input;
|
||||
input.value =
|
||||
input.value.substring(0, selectionStart) +
|
||||
'\n' +
|
||||
input.value.substring(selectionEnd);
|
||||
selectionStart = selectionEnd = selectionStart + 1;
|
||||
return;
|
||||
}
|
||||
await save();
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
save,
|
||||
|
@ -298,12 +325,13 @@ defineExpose({
|
|||
<QForm
|
||||
ref="myForm"
|
||||
v-if="formData"
|
||||
@submit="save(!!$event)"
|
||||
@submit.prevent
|
||||
@keyup.prevent="onKeyup"
|
||||
@reset="reset"
|
||||
class="q-pa-md"
|
||||
:style="maxWidth ? 'max-width: ' + maxWidth : ''"
|
||||
id="formModel"
|
||||
:prevent-submit="$attrs['prevent-submit']"
|
||||
:mapper="onBeforeSave"
|
||||
>
|
||||
<QCard>
|
||||
<slot
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { ref, computed, useAttrs, nextTick } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useState } from 'src/composables/useState';
|
||||
|
||||
import FormModel from 'components/FormModel.vue';
|
||||
|
||||
const emit = defineEmits(['onDataSaved', 'onDataCanceled']);
|
||||
|
||||
defineProps({
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
|
@ -22,17 +23,28 @@ defineProps({
|
|||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const attrs = useAttrs();
|
||||
const state = useState();
|
||||
const formModelRef = ref(null);
|
||||
const closeButton = ref(null);
|
||||
const isSaveAndContinue = ref(false);
|
||||
const onDataSaved = (formData, requestResponse) => {
|
||||
if (closeButton.value && isSaveAndContinue) closeButton.value.click();
|
||||
const isSaveAndContinue = ref(props.showSaveAndContinueBtn);
|
||||
const isLoading = computed(() => formModelRef.value?.isLoading);
|
||||
const reset = computed(() => formModelRef.value?.reset);
|
||||
|
||||
const onDataSaved = async (formData, requestResponse) => {
|
||||
if (!isSaveAndContinue.value) closeButton.value?.click();
|
||||
if (isSaveAndContinue.value) {
|
||||
await nextTick();
|
||||
state.set(attrs.model, attrs.formInitialData);
|
||||
}
|
||||
isSaveAndContinue.value = props.showSaveAndContinueBtn;
|
||||
emit('onDataSaved', formData, requestResponse);
|
||||
};
|
||||
|
||||
const isLoading = computed(() => formModelRef.value?.isLoading);
|
||||
const reset = computed(() => formModelRef.value?.reset);
|
||||
const onClick = async (saveAndContinue) => {
|
||||
isSaveAndContinue.value = saveAndContinue;
|
||||
await formModelRef.value.save();
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
isLoading,
|
||||
|
@ -69,19 +81,13 @@ defineExpose({
|
|||
data-cy="FormModelPopup_cancel"
|
||||
v-close-popup
|
||||
z-max
|
||||
@click="
|
||||
isSaveAndContinue = false;
|
||||
emit('onDataCanceled');
|
||||
"
|
||||
@click="emit('onDataCanceled')"
|
||||
/>
|
||||
<QBtn
|
||||
:flat="showSaveAndContinueBtn"
|
||||
:label="t('globals.save')"
|
||||
:title="t('globals.save')"
|
||||
@click="
|
||||
formModelRef.save();
|
||||
isSaveAndContinue = false;
|
||||
"
|
||||
@click="onClick(false)"
|
||||
color="primary"
|
||||
class="q-ml-sm"
|
||||
:disabled="isLoading"
|
||||
|
@ -99,10 +105,7 @@ defineExpose({
|
|||
:loading="isLoading"
|
||||
data-cy="FormModelPopup_isSaveAndContinue"
|
||||
z-max
|
||||
@click="
|
||||
isSaveAndContinue = true;
|
||||
formModelRef.save();
|
||||
"
|
||||
@click="onClick(true)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -85,7 +85,15 @@ const refresh = () => window.location.reload();
|
|||
</QTooltip>
|
||||
<PinnedModules ref="pinnedModulesRef" />
|
||||
</QBtn>
|
||||
<QBtn class="q-pa-none" rounded dense flat no-wrap id="user">
|
||||
<QBtn
|
||||
class="q-pa-none"
|
||||
rounded
|
||||
dense
|
||||
flat
|
||||
no-wrap
|
||||
id="user"
|
||||
data-cy="userPanel_btn"
|
||||
>
|
||||
<VnAvatar
|
||||
:worker-id="user.id"
|
||||
:title="user.name"
|
||||
|
|
|
@ -5,6 +5,18 @@ defineProps({ row: { type: Object, required: true } });
|
|||
</script>
|
||||
<template>
|
||||
<span class="q-gutter-x-xs">
|
||||
<router-link
|
||||
v-if="row.claim?.claimFk"
|
||||
:to="{ name: 'ClaimBasicData', params: { id: row.claim?.claimFk } }"
|
||||
class="link"
|
||||
>
|
||||
<QIcon name="vn:claims" size="xs">
|
||||
<QTooltip>
|
||||
{{ t('ticketSale.claim') }}:
|
||||
{{ row.claim?.claimFk }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
</router-link>
|
||||
<QIcon
|
||||
v-if="row?.risk"
|
||||
name="vn:risk"
|
||||
|
@ -56,7 +68,7 @@ defineProps({ row: { type: Object, required: true } });
|
|||
<QTooltip>{{ $t('salesTicketsTable.purchaseRequest') }}</QTooltip>
|
||||
</QIcon>
|
||||
<QIcon
|
||||
v-if="!row?.isTaxDataChecked === 0"
|
||||
v-if="row?.isTaxDataChecked !== 0"
|
||||
name="vn:no036"
|
||||
color="primary"
|
||||
size="xs"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup>
|
||||
import { markRaw, computed } from 'vue';
|
||||
import { QIcon, QCheckbox, QToggle } from 'quasar';
|
||||
import { QIcon, QToggle } from 'quasar';
|
||||
import { dashIfEmpty } from 'src/filters';
|
||||
|
||||
import VnSelect from 'components/common/VnSelect.vue';
|
||||
|
|
|
@ -91,7 +91,6 @@ const components = {
|
|||
event: updateEvent,
|
||||
attrs: {
|
||||
...defaultAttrs,
|
||||
style: 'min-width: 150px',
|
||||
},
|
||||
forceAttrs,
|
||||
},
|
||||
|
@ -152,7 +151,7 @@ const onTabPressed = async () => {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<div v-if="showFilter" class="full-width flex-center" style="overflow: hidden">
|
||||
<div v-if="showFilter" class="full-width" style="overflow: hidden">
|
||||
<VnColumn
|
||||
:column="$props.column"
|
||||
default="input"
|
||||
|
|
|
@ -23,6 +23,10 @@ const $props = defineProps({
|
|||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
align: {
|
||||
type: String,
|
||||
default: 'end',
|
||||
},
|
||||
});
|
||||
const hover = ref();
|
||||
const arrayData = useArrayData($props.dataKey, { searchUrl: $props.searchUrl });
|
||||
|
@ -46,16 +50,27 @@ async function orderBy(name, direction) {
|
|||
}
|
||||
|
||||
defineExpose({ orderBy });
|
||||
|
||||
function textAlignToFlex(textAlign) {
|
||||
return `justify-content: ${
|
||||
{
|
||||
'text-center': 'center',
|
||||
'text-left': 'start',
|
||||
'text-right': 'end',
|
||||
}[textAlign] || 'start'
|
||||
};`;
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div
|
||||
@mouseenter="hover = true"
|
||||
@mouseleave="hover = false"
|
||||
@click="orderBy(name, model?.direction)"
|
||||
class="row items-center no-wrap cursor-pointer title"
|
||||
class="items-center no-wrap cursor-pointer title"
|
||||
:style="textAlignToFlex(align)"
|
||||
>
|
||||
<span :title="label">{{ label }}</span>
|
||||
<sup v-if="name && model?.index">
|
||||
<div v-if="name && model?.index">
|
||||
<QChip
|
||||
:label="!vertical ? model?.index : ''"
|
||||
:icon="
|
||||
|
@ -92,20 +107,16 @@ defineExpose({ orderBy });
|
|||
/>
|
||||
</div>
|
||||
</QChip>
|
||||
</sup>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.title {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 30px;
|
||||
width: 100%;
|
||||
color: var(--vn-label-color);
|
||||
}
|
||||
sup {
|
||||
vertical-align: super; /* Valor predeterminado */
|
||||
/* También puedes usar otros valores como "baseline", "top", "text-top", etc. */
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -10,14 +10,15 @@ import {
|
|||
render,
|
||||
inject,
|
||||
useAttrs,
|
||||
nextTick,
|
||||
} from 'vue';
|
||||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { useQuasar, date } from 'quasar';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import { useFilterParams } from 'src/composables/useFilterParams';
|
||||
import { dashIfEmpty } from 'src/filters';
|
||||
import { dashIfEmpty, toDate } from 'src/filters';
|
||||
|
||||
import CrudModel from 'src/components/CrudModel.vue';
|
||||
import FormModelPopup from 'components/FormModelPopup.vue';
|
||||
|
@ -30,6 +31,7 @@ import VnLv from 'components/ui/VnLv.vue';
|
|||
import VnTableOrder from 'src/components/VnTable/VnOrder.vue';
|
||||
import VnTableFilter from './VnTableFilter.vue';
|
||||
import { getColAlign } from 'src/composables/getColAlign';
|
||||
import RightMenu from '../common/RightMenu.vue';
|
||||
|
||||
const arrayData = useArrayData(useAttrs()['data-key']);
|
||||
const $props = defineProps({
|
||||
|
@ -49,10 +51,6 @@ const $props = defineProps({
|
|||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
rightSearchIcon: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
rowClick: {
|
||||
type: [Function, Boolean],
|
||||
default: null,
|
||||
|
@ -136,6 +134,10 @@ const $props = defineProps({
|
|||
createComplement: {
|
||||
type: Object,
|
||||
},
|
||||
dataCy: {
|
||||
type: String,
|
||||
default: 'vn-table',
|
||||
},
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
|
@ -164,7 +166,6 @@ const app = inject('app');
|
|||
const editingRow = ref(null);
|
||||
const editingField = ref(null);
|
||||
const isTableMode = computed(() => mode.value == TABLE_MODE);
|
||||
const showRightIcon = computed(() => $props.rightSearch || $props.rightSearchIcon);
|
||||
const selectRegex = /select/;
|
||||
const emit = defineEmits(['onFetch', 'update:selected', 'saveChanges']);
|
||||
const tableModes = [
|
||||
|
@ -252,7 +253,9 @@ function splitColumns(columns) {
|
|||
col.columnFilter = { inWhere: true, ...col.columnFilter };
|
||||
splittedColumns.value.columns.push(col);
|
||||
}
|
||||
// Status column
|
||||
|
||||
splittedColumns.value.create = createOrderSort(splittedColumns.value.create);
|
||||
|
||||
if (splittedColumns.value.chips.length) {
|
||||
splittedColumns.value.columnChips = splittedColumns.value.chips.filter(
|
||||
(c) => !c.isId,
|
||||
|
@ -268,6 +271,24 @@ function splitColumns(columns) {
|
|||
}
|
||||
}
|
||||
|
||||
function createOrderSort(columns) {
|
||||
const orderedColumn = columns
|
||||
.map((column, index) =>
|
||||
column.createOrder !== undefined ? { ...column, originalIndex: index } : null,
|
||||
)
|
||||
.filter((item) => item !== null);
|
||||
|
||||
orderedColumn.sort((a, b) => a.createOrder - b.createOrder);
|
||||
|
||||
const filteredColumns = columns.filter((col) => col.createOrder === undefined);
|
||||
|
||||
orderedColumn.forEach((col) => {
|
||||
filteredColumns.splice(col.createOrder, 0, col);
|
||||
});
|
||||
|
||||
return filteredColumns;
|
||||
}
|
||||
|
||||
const rowClickFunction = computed(() => {
|
||||
if ($props.rowClick != undefined) return $props.rowClick;
|
||||
if ($props.redirect) return ({ id }) => redirectFn(id);
|
||||
|
@ -313,8 +334,14 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
|
|||
if (evt?.shiftKey && added) {
|
||||
const rowIndex = selectedRows[0].$index;
|
||||
const selectedIndexes = new Set(selected.value.map((row) => row.$index));
|
||||
for (const row of rows) {
|
||||
if (row.$index == rowIndex) break;
|
||||
const minIndex = selectedIndexes.size
|
||||
? Math.min(...selectedIndexes, rowIndex)
|
||||
: 0;
|
||||
const maxIndex = Math.max(...selectedIndexes, rowIndex);
|
||||
|
||||
for (let i = minIndex; i <= maxIndex; i++) {
|
||||
const row = rows[i];
|
||||
if (row.$index == rowIndex) continue;
|
||||
if (!selectedIndexes.has(row.$index)) {
|
||||
selected.value.push(row);
|
||||
selectedIndexes.add(row.$index);
|
||||
|
@ -337,15 +364,14 @@ function hasEditableFormat(column) {
|
|||
|
||||
const clickHandler = async (event) => {
|
||||
const clickedElement = event.target.closest('td');
|
||||
|
||||
const isDateElement = event.target.closest('.q-date');
|
||||
const isTimeElement = event.target.closest('.q-time');
|
||||
const isQselectDropDown = event.target.closest('.q-select__dropdown-icon');
|
||||
const isQSelectDropDown = event.target.closest('.q-select__dropdown-icon');
|
||||
|
||||
if (isDateElement || isTimeElement || isQselectDropDown) return;
|
||||
if (isDateElement || isTimeElement || isQSelectDropDown) return;
|
||||
|
||||
if (clickedElement === null) {
|
||||
destroyInput(editingRow.value, editingField.value);
|
||||
await destroyInput(editingRow.value, editingField.value);
|
||||
return;
|
||||
}
|
||||
const rowIndex = clickedElement.getAttribute('data-row-index');
|
||||
|
@ -355,7 +381,7 @@ const clickHandler = async (event) => {
|
|||
if (editingRow.value !== null && editingField.value !== null) {
|
||||
if (editingRow.value == rowIndex && editingField.value == colField) return;
|
||||
|
||||
destroyInput(editingRow.value, editingField.value);
|
||||
await destroyInput(editingRow.value, editingField.value);
|
||||
}
|
||||
|
||||
if (isEditableColumn(column)) {
|
||||
|
@ -365,7 +391,7 @@ const clickHandler = async (event) => {
|
|||
|
||||
async function handleTabKey(event, rowIndex, colField) {
|
||||
if (editingRow.value == rowIndex && editingField.value == colField)
|
||||
destroyInput(editingRow.value, editingField.value);
|
||||
await destroyInput(editingRow.value, editingField.value);
|
||||
|
||||
const direction = event.shiftKey ? -1 : 1;
|
||||
const { nextRowIndex, nextColumnName } = await handleTabNavigation(
|
||||
|
@ -413,19 +439,13 @@ async function renderInput(rowId, field, clickedElement) {
|
|||
eventHandlers: {
|
||||
'update:modelValue': async (value) => {
|
||||
if (isSelect && value) {
|
||||
row[column.name] = value[column.attrs?.optionValue ?? 'id'];
|
||||
row[column?.name + 'TextValue'] =
|
||||
value[column.attrs?.optionLabel ?? 'name'];
|
||||
await column?.cellEvent?.['update:modelValue']?.(
|
||||
value,
|
||||
oldValue,
|
||||
row,
|
||||
);
|
||||
await updateSelectValue(value, column, row, oldValue);
|
||||
} else row[column.name] = value;
|
||||
await column?.cellEvent?.['update:modelValue']?.(value, oldValue, row);
|
||||
},
|
||||
keyup: async (event) => {
|
||||
if (event.key === 'Enter') handleBlur(rowId, field, clickedElement);
|
||||
if (event.key === 'Enter')
|
||||
await destroyInput(rowId, field, clickedElement);
|
||||
},
|
||||
keydown: async (event) => {
|
||||
switch (event.key) {
|
||||
|
@ -434,7 +454,7 @@ async function renderInput(rowId, field, clickedElement) {
|
|||
event.stopPropagation();
|
||||
break;
|
||||
case 'Escape':
|
||||
destroyInput(rowId, field, clickedElement);
|
||||
await destroyInput(rowId, field, clickedElement);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -456,12 +476,24 @@ async function renderInput(rowId, field, clickedElement) {
|
|||
node.el?.querySelector('span > div > div').focus();
|
||||
}
|
||||
|
||||
function destroyInput(rowIndex, field, clickedElement) {
|
||||
async function updateSelectValue(value, column, row, oldValue) {
|
||||
row[column.name] = value[column.attrs?.optionValue ?? 'id'];
|
||||
|
||||
row[column?.name + 'VnTableTextValue'] = value[column.attrs?.optionLabel ?? 'name'];
|
||||
|
||||
if (column?.attrs?.find?.label)
|
||||
row[column?.attrs?.find?.label] = value[column.attrs?.optionLabel ?? 'name'];
|
||||
|
||||
await column?.cellEvent?.['update:modelValue']?.(value, oldValue, row);
|
||||
}
|
||||
|
||||
async function destroyInput(rowIndex, field, clickedElement) {
|
||||
if (!clickedElement)
|
||||
clickedElement = document.querySelector(
|
||||
`[data-row-index="${rowIndex}"][data-col-field="${field}"]`,
|
||||
);
|
||||
if (clickedElement) {
|
||||
await nextTick();
|
||||
render(null, clickedElement);
|
||||
Array.from(clickedElement.childNodes).forEach((child) => {
|
||||
child.style.visibility = 'visible';
|
||||
|
@ -473,10 +505,6 @@ function destroyInput(rowIndex, field, clickedElement) {
|
|||
editingField.value = null;
|
||||
}
|
||||
|
||||
function handleBlur(rowIndex, field, clickedElement) {
|
||||
destroyInput(rowIndex, field, clickedElement);
|
||||
}
|
||||
|
||||
async function handleTabNavigation(rowIndex, colName, direction) {
|
||||
const columns = $props.columns;
|
||||
const totalColumns = columns.length;
|
||||
|
@ -521,30 +549,83 @@ function getToggleIcon(value) {
|
|||
}
|
||||
|
||||
function formatColumnValue(col, row, dashIfEmpty) {
|
||||
if (col?.format) {
|
||||
if (selectRegex.test(col?.component) && row[col?.name + 'TextValue']) {
|
||||
return dashIfEmpty(row[col?.name + 'TextValue']);
|
||||
if (col?.format || row[col?.name + 'VnTableTextValue']) {
|
||||
if (selectRegex.test(col?.component) && row[col?.name + 'VnTableTextValue']) {
|
||||
return dashIfEmpty(row[col?.name + 'VnTableTextValue']);
|
||||
} else {
|
||||
return col.format(row, dashIfEmpty);
|
||||
}
|
||||
} else {
|
||||
return dashIfEmpty(row[col?.name]);
|
||||
}
|
||||
|
||||
if (col?.component === 'date') return dashIfEmpty(toDate(row[col?.name]));
|
||||
|
||||
if (col?.component === 'time')
|
||||
return row[col?.name] >= 5
|
||||
? dashIfEmpty(date.formatDate(new Date(row[col?.name]), 'HH:mm'))
|
||||
: row[col?.name];
|
||||
|
||||
if (selectRegex.test(col?.component) && $props.isEditable) {
|
||||
const { find, url } = col.attrs;
|
||||
const urlRelation = url?.charAt(0)?.toLocaleLowerCase() + url?.slice(1, -1);
|
||||
|
||||
if (col?.attrs.options) {
|
||||
const find = col?.attrs.options.find((option) => option.id === row[col.name]);
|
||||
if (!col.attrs?.optionLabel || !find) return dashIfEmpty(row[col?.name]);
|
||||
return dashIfEmpty(find[col.attrs?.optionLabel ?? 'name']);
|
||||
}
|
||||
|
||||
if (typeof row[urlRelation] == 'object') {
|
||||
if (typeof find == 'object')
|
||||
return dashIfEmpty(row[urlRelation][find?.label ?? 'name']);
|
||||
|
||||
return dashIfEmpty(row[urlRelation][col?.attrs.optionLabel ?? 'name']);
|
||||
}
|
||||
if (typeof row[urlRelation] == 'string') return dashIfEmpty(row[urlRelation]);
|
||||
}
|
||||
return dashIfEmpty(row[col?.name]);
|
||||
}
|
||||
const checkbox = ref(null);
|
||||
|
||||
function cardClick(_, row) {
|
||||
if ($props.redirect) router.push({ path: `/${$props.redirect}/${row.id}` });
|
||||
}
|
||||
|
||||
function removeTextValue(data, getChanges) {
|
||||
let changes = data.updates;
|
||||
if (!changes) return data;
|
||||
|
||||
for (const change of changes) {
|
||||
for (const key in change.data) {
|
||||
if (key.endsWith('VnTableTextValue')) {
|
||||
delete change.data[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data.updates = changes.filter((change) => Object.keys(change.data).length > 0);
|
||||
|
||||
if ($attrs?.beforeSaveFn) data = $attrs.beforeSaveFn(data, getChanges);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function handleRowClick(event, row) {
|
||||
if (event.ctrlKey) return rowCtrlClickFunction.value(event, row);
|
||||
if (rowClickFunction.value) rowClickFunction.value(row);
|
||||
}
|
||||
|
||||
const rowCtrlClickFunction = computed(() => {
|
||||
if ($props.rowCtrlClick != undefined) return $props.rowCtrlClick;
|
||||
if ($props.redirect)
|
||||
return (evt, { id }) => {
|
||||
stopEventPropagation(evt);
|
||||
window.open(`/#/${$props.redirect}/${id}`, '_blank');
|
||||
};
|
||||
return () => {};
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<QDrawer
|
||||
v-if="$props.rightSearch"
|
||||
v-model="stateStore.rightDrawer"
|
||||
side="right"
|
||||
:width="256"
|
||||
:overlay="$props.overlay"
|
||||
>
|
||||
<QScrollArea class="fit">
|
||||
<RightMenu v-if="$props.rightSearch" :overlay="overlay">
|
||||
<template #right-panel>
|
||||
<VnTableFilter
|
||||
:data-key="$attrs['data-key']"
|
||||
:columns="columns"
|
||||
|
@ -558,8 +639,8 @@ function cardClick(_, row) {
|
|||
<slot :name="slotName" v-bind="slotData ?? {}" :key="slotName" />
|
||||
</template>
|
||||
</VnTableFilter>
|
||||
</QScrollArea>
|
||||
</QDrawer>
|
||||
</template>
|
||||
</RightMenu>
|
||||
<CrudModel
|
||||
v-bind="$attrs"
|
||||
:class="$attrs['class'] ?? 'q-px-md'"
|
||||
|
@ -568,6 +649,7 @@ function cardClick(_, row) {
|
|||
@on-fetch="(...args) => emit('onFetch', ...args)"
|
||||
:search-url="searchUrl"
|
||||
:disable-infinite-scroll="isTableMode"
|
||||
:before-save-fn="removeTextValue"
|
||||
@save-changes="reload"
|
||||
:has-sub-toolbar="$props.hasSubToolbar ?? isEditable"
|
||||
:auto-load="hasParams || $attrs['auto-load']"
|
||||
|
@ -595,7 +677,7 @@ function cardClick(_, row) {
|
|||
:style="isTableMode && `max-height: ${tableHeight}`"
|
||||
:virtual-scroll="isTableMode"
|
||||
@virtual-scroll="handleScroll"
|
||||
@row-click="(_, row) => rowClickFunction && rowClickFunction(row)"
|
||||
@row-click="(event, row) => handleRowClick(event, row)"
|
||||
@update:selected="emit('update:selected', $event)"
|
||||
@selection="(details) => handleSelection(details, rows)"
|
||||
:hide-selected-banner="true"
|
||||
|
@ -612,20 +694,13 @@ function cardClick(_, row) {
|
|||
:skip="columnsVisibilitySkipped"
|
||||
/>
|
||||
<QBtnToggle
|
||||
v-if="!tableModes.some((mode) => mode.disable)"
|
||||
v-model="mode"
|
||||
toggle-color="primary"
|
||||
class="bg-vn-section-color"
|
||||
dense
|
||||
:options="tableModes.filter((mode) => !mode.disable)"
|
||||
/>
|
||||
|
||||
<QBtn
|
||||
v-if="showRightIcon"
|
||||
icon="filter_alt"
|
||||
class="bg-vn-section-color q-ml-sm"
|
||||
dense
|
||||
@click="stateStore.toggleRightDrawer()"
|
||||
/>
|
||||
</template>
|
||||
<template #header-cell="{ col }">
|
||||
<QTh
|
||||
|
@ -633,15 +708,14 @@ function cardClick(_, row) {
|
|||
v-bind:class="col.headerClass"
|
||||
class="body-cell"
|
||||
:style="col?.width ? `max-width: ${col?.width}` : ''"
|
||||
style="padding: inherit"
|
||||
>
|
||||
<div
|
||||
class="no-padding"
|
||||
:style="
|
||||
withFilters && $props.columnSearch ? 'height: 75px' : ''
|
||||
"
|
||||
:style="[
|
||||
withFilters && $props.columnSearch ? 'height: 75px' : '',
|
||||
]"
|
||||
>
|
||||
<div class="text-center" style="height: 30px">
|
||||
<div style="height: 30px">
|
||||
<QTooltip v-if="col.toolTip">{{ col.toolTip }}</QTooltip>
|
||||
<VnTableOrder
|
||||
v-model="orders[col.orderBy ?? col.name]"
|
||||
|
@ -649,6 +723,7 @@ function cardClick(_, row) {
|
|||
:label="col?.labelAbbreviation ?? col?.label"
|
||||
:data-key="$attrs['data-key']"
|
||||
:search-url="searchUrl"
|
||||
:align="getColAlign(col)"
|
||||
/>
|
||||
</div>
|
||||
<VnFilter
|
||||
|
@ -730,7 +805,11 @@ function cardClick(_, row) {
|
|||
<span
|
||||
v-else
|
||||
:class="hasEditableFormat(col)"
|
||||
:style="col?.style ? col.style(row) : null"
|
||||
:style="
|
||||
typeof col?.style == 'function'
|
||||
? col.style(row)
|
||||
: col?.style
|
||||
"
|
||||
style="bottom: 0"
|
||||
>
|
||||
{{ formatColumnValue(col, row, dashIfEmpty) }}
|
||||
|
@ -757,7 +836,7 @@ function cardClick(_, row) {
|
|||
flat
|
||||
dense
|
||||
:class="
|
||||
btn.isPrimary ? 'text-primary-light' : 'color-vn-text '
|
||||
btn.isPrimary ? 'text-primary-light' : 'color-vn-label'
|
||||
"
|
||||
:style="`visibility: ${
|
||||
((btn.show && btn.show(row)) ?? true)
|
||||
|
@ -814,7 +893,7 @@ function cardClick(_, row) {
|
|||
</QCardSection>
|
||||
<!-- Fields -->
|
||||
<QCardSection
|
||||
class="q-pl-sm q-pr-lg q-py-xs"
|
||||
class="q-pl-sm q-py-xs"
|
||||
:class="$props.cardClass"
|
||||
>
|
||||
<div
|
||||
|
@ -861,13 +940,14 @@ function cardClick(_, row) {
|
|||
:key="index"
|
||||
:title="btn.title"
|
||||
:icon="btn.icon"
|
||||
data-cy="cardBtn"
|
||||
class="q-pa-xs"
|
||||
flat
|
||||
:class="
|
||||
btn.isPrimary
|
||||
? 'text-primary-light'
|
||||
: 'color-vn-text '
|
||||
: 'color-vn-label'
|
||||
"
|
||||
flat
|
||||
@click="btn.action(row)"
|
||||
/>
|
||||
</QCardSection>
|
||||
|
@ -934,14 +1014,6 @@ function cardClick(_, row) {
|
|||
transition-show="scale"
|
||||
transition-hide="scale"
|
||||
:full-width="createComplement?.isFullWidth ?? false"
|
||||
@before-hide="
|
||||
() => {
|
||||
if (createRef.isSaveAndContinue) {
|
||||
showForm = true;
|
||||
createForm.formInitialData = { ...create.formInitialData };
|
||||
}
|
||||
}
|
||||
"
|
||||
data-cy="vn-table-create-dialog"
|
||||
>
|
||||
<FormModelPopup
|
||||
|
@ -952,7 +1024,10 @@ function cardClick(_, row) {
|
|||
>
|
||||
<template #form-inputs="{ data }">
|
||||
<div :style="createComplement?.containerStyle">
|
||||
<div>
|
||||
<div
|
||||
:style="createComplement?.previousStyle"
|
||||
v-if="!quasar.screen.xs"
|
||||
>
|
||||
<slot name="previous-create-dialog" :data="data" />
|
||||
</div>
|
||||
<div class="grid-create" :style="createComplement?.columnGridStyle">
|
||||
|
@ -965,7 +1040,10 @@ function cardClick(_, row) {
|
|||
:label="column.label"
|
||||
>
|
||||
<VnColumn
|
||||
:column="column"
|
||||
:column="{
|
||||
...column,
|
||||
...{ disable: column?.createDisable ?? false },
|
||||
}"
|
||||
:row="{}"
|
||||
default="input"
|
||||
v-model="data[column.name]"
|
||||
|
@ -1025,8 +1103,8 @@ es:
|
|||
}
|
||||
|
||||
.body-cell {
|
||||
padding-left: 2px !important;
|
||||
padding-right: 2px !important;
|
||||
padding-left: 4px !important;
|
||||
padding-right: 4px !important;
|
||||
position: relative;
|
||||
}
|
||||
.bg-chip-secondary {
|
||||
|
@ -1120,6 +1198,7 @@ es:
|
|||
.vn-label-value {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
color: var(--vn-text-color);
|
||||
.value {
|
||||
overflow: hidden;
|
||||
|
|
|
@ -27,30 +27,58 @@ describe('VnTable', () => {
|
|||
beforeEach(() => (vm.selected = []));
|
||||
|
||||
describe('handleSelection()', () => {
|
||||
const rows = [{ $index: 0 }, { $index: 1 }, { $index: 2 }];
|
||||
const selectedRows = [{ $index: 1 }];
|
||||
it('should add rows to selected when shift key is pressed and rows are added except last one', () => {
|
||||
const rows = [
|
||||
{ $index: 0 },
|
||||
{ $index: 1 },
|
||||
{ $index: 2 },
|
||||
{ $index: 3 },
|
||||
{ $index: 4 },
|
||||
];
|
||||
|
||||
it('should add rows to selected when shift key is pressed and rows are added in ascending order', () => {
|
||||
const selectedRows = [{ $index: 1 }];
|
||||
vm.handleSelection(
|
||||
{ evt: { shiftKey: true }, added: true, rows: selectedRows },
|
||||
rows
|
||||
rows,
|
||||
);
|
||||
expect(vm.selected).toEqual([{ $index: 0 }]);
|
||||
});
|
||||
|
||||
it('should add rows to selected when shift key is pressed and rows are added in descending order', () => {
|
||||
const selectedRows = [{ $index: 3 }];
|
||||
vm.handleSelection(
|
||||
{ evt: { shiftKey: true }, added: true, rows: selectedRows },
|
||||
rows,
|
||||
);
|
||||
expect(vm.selected).toEqual([{ $index: 0 }, { $index: 1 }, { $index: 2 }]);
|
||||
});
|
||||
|
||||
it('should not add rows to selected when shift key is not pressed', () => {
|
||||
const selectedRows = [{ $index: 1 }];
|
||||
vm.handleSelection(
|
||||
{ evt: { shiftKey: false }, added: true, rows: selectedRows },
|
||||
rows
|
||||
rows,
|
||||
);
|
||||
expect(vm.selected).toEqual([]);
|
||||
});
|
||||
|
||||
it('should not add rows to selected when rows are not added', () => {
|
||||
const selectedRows = [{ $index: 1 }];
|
||||
vm.handleSelection(
|
||||
{ evt: { shiftKey: true }, added: false, rows: selectedRows },
|
||||
rows
|
||||
rows,
|
||||
);
|
||||
expect(vm.selected).toEqual([]);
|
||||
});
|
||||
|
||||
it('should add all rows between the smallest and largest selected indexes', () => {
|
||||
vm.selected = [{ $index: 1 }, { $index: 3 }];
|
||||
const selectedRows = [{ $index: 4 }];
|
||||
vm.handleSelection(
|
||||
{ evt: { shiftKey: true }, added: true, rows: selectedRows },
|
||||
rows,
|
||||
);
|
||||
expect(vm.selected).toEqual([{ $index: 1 }, { $index: 3 }, { $index: 2 }]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -57,6 +57,7 @@ describe('FormModel', () => {
|
|||
vm.state.set(model, formInitialData);
|
||||
expect(vm.hasChanges).toBe(false);
|
||||
|
||||
await vm.$nextTick();
|
||||
vm.formData.mockKey = 'newVal';
|
||||
await vm.$nextTick();
|
||||
expect(vm.hasChanges).toBe(true);
|
||||
|
@ -94,8 +95,12 @@ describe('FormModel', () => {
|
|||
it('should call axios.patch with the right data', async () => {
|
||||
const spy = vi.spyOn(axios, 'patch').mockResolvedValue({ data: {} });
|
||||
const { vm } = mount({ propsData: { url, model } });
|
||||
vm.formData.mockKey = 'newVal';
|
||||
|
||||
vm.formData = {};
|
||||
await vm.$nextTick();
|
||||
vm.formData = { mockKey: 'newVal' };
|
||||
await vm.$nextTick();
|
||||
|
||||
await vm.save();
|
||||
expect(spy).toHaveBeenCalled();
|
||||
vm.formData.mockKey = 'mockVal';
|
||||
|
|
|
@ -11,6 +11,13 @@ const stateStore = useStateStore();
|
|||
const slots = useSlots();
|
||||
const hasContent = useHasContent('#right-panel');
|
||||
|
||||
defineProps({
|
||||
overlay: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if ((!slots['right-panel'] && !hasContent.value) || quasar.platform.is.mobile)
|
||||
stateStore.rightDrawer = false;
|
||||
|
@ -34,7 +41,12 @@ onMounted(() => {
|
|||
</QBtn>
|
||||
</div>
|
||||
</Teleport>
|
||||
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256">
|
||||
<QDrawer
|
||||
v-model="stateStore.rightDrawer"
|
||||
side="right"
|
||||
:width="256"
|
||||
:overlay="overlay"
|
||||
>
|
||||
<QScrollArea class="fit">
|
||||
<div id="right-panel"></div>
|
||||
<slot v-if="!hasContent" name="right-panel" />
|
||||
|
|
|
@ -38,20 +38,24 @@ onBeforeMount(async () => {
|
|||
}
|
||||
});
|
||||
|
||||
if (props.baseUrl) {
|
||||
onBeforeRouteUpdate(async (to, from) => {
|
||||
if (hasRouteParam(to.params)) {
|
||||
const { matched } = router.currentRoute.value;
|
||||
const { name } = matched.at(-3);
|
||||
if (name) {
|
||||
router.push({ name, params: to.params });
|
||||
}
|
||||
onBeforeRouteUpdate(async (to, from) => {
|
||||
if (hasRouteParam(to.params)) {
|
||||
const { matched } = router.currentRoute.value;
|
||||
const { name } = matched.at(-3);
|
||||
if (name) {
|
||||
router.push({ name, params: to.params });
|
||||
}
|
||||
if (to.params.id !== from.params.id) {
|
||||
arrayData.store.url = `${props.baseUrl}/${to.params.id}`;
|
||||
await arrayData.fetch({ append: false, updateRouter: false });
|
||||
}
|
||||
});
|
||||
}
|
||||
const id = to.params.id;
|
||||
if (id !== from.params.id) await fetch(id, true);
|
||||
});
|
||||
|
||||
async function fetch(id, append = false) {
|
||||
const regex = /\/(\d+)/;
|
||||
if (props.idInWhere) arrayData.store.filter.where = { id };
|
||||
else if (!regex.test(props.url)) arrayData.store.url = `${props.url}/${id}`;
|
||||
else arrayData.store.url = props.url.replace(regex, `/${id}`);
|
||||
await arrayData.fetch({ append, updateRouter: false });
|
||||
}
|
||||
function hasRouteParam(params, valueToCheck = ':addressId') {
|
||||
return Object.values(params).includes(valueToCheck);
|
||||
|
|
|
@ -27,7 +27,7 @@ const checkboxModel = computed({
|
|||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<QCheckbox v-bind="$attrs" v-on="$attrs" v-model="checkboxModel" />
|
||||
<QCheckbox v-bind="$attrs" v-model="checkboxModel" />
|
||||
<QIcon
|
||||
v-if="info"
|
||||
v-bind="$attrs"
|
||||
|
|
|
@ -48,7 +48,8 @@ function toValueAttrs(attrs) {
|
|||
<span
|
||||
v-for="toComponent of componentArray"
|
||||
:key="toComponent.name"
|
||||
class="column flex-center fit"
|
||||
class="column fit"
|
||||
:class="toComponent?.component == 'checkbox' ? 'flex-center' : ''"
|
||||
>
|
||||
<component
|
||||
v-if="toComponent?.component"
|
||||
|
|
|
@ -107,6 +107,7 @@ const manageDate = (date) => {
|
|||
@click="isPopupOpen = !isPopupOpen"
|
||||
@keydown="isPopupOpen = false"
|
||||
hide-bottom-space
|
||||
:data-cy="$attrs.dataCy ?? $attrs.label + '_inputDate'"
|
||||
>
|
||||
<template #append>
|
||||
<QIcon
|
||||
|
|
|
@ -641,15 +641,7 @@ watch(
|
|||
>
|
||||
{{ prop.nameI18n }}:
|
||||
</span>
|
||||
<VnJsonValue :value="prop.val.val" />
|
||||
<span
|
||||
v-if="prop.val.id"
|
||||
class="id-value"
|
||||
>
|
||||
#{{ prop.val.id }}
|
||||
</span>
|
||||
<span v-if="log.action == 'update'">
|
||||
←
|
||||
<VnJsonValue
|
||||
:value="prop.old.val"
|
||||
/>
|
||||
|
@ -659,6 +651,26 @@ watch(
|
|||
>
|
||||
#{{ prop.old.id }}
|
||||
</span>
|
||||
→
|
||||
<VnJsonValue
|
||||
:value="prop.val.val"
|
||||
/>
|
||||
<span
|
||||
v-if="prop.val.id"
|
||||
class="id-value"
|
||||
>
|
||||
#{{ prop.val.id }}
|
||||
</span>
|
||||
</span>
|
||||
<span v-else="prop.old.val">
|
||||
<VnJsonValue
|
||||
:value="prop.val.val"
|
||||
/>
|
||||
<span
|
||||
v-if="prop.old.id"
|
||||
class="id-value"
|
||||
>#{{ prop.old.id }}</span
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
|
|
|
@ -10,7 +10,12 @@ const emit = defineEmits(['update:modelValue', 'update:options', 'remove']);
|
|||
const $attrs = useAttrs();
|
||||
const { t } = useI18n();
|
||||
|
||||
const { isRequired, requiredFieldRule } = useRequired($attrs);
|
||||
const isRequired = computed(() => {
|
||||
return useRequired($attrs).isRequired;
|
||||
});
|
||||
const requiredFieldRule = computed(() => {
|
||||
return useRequired($attrs).requiredFieldRule;
|
||||
});
|
||||
|
||||
const $props = defineProps({
|
||||
modelValue: {
|
||||
|
@ -297,8 +302,6 @@ defineExpose({ opts: myOptions, vnSelectRef });
|
|||
|
||||
function handleKeyDown(event) {
|
||||
if (event.key === 'Tab' && !event.shiftKey) {
|
||||
event.preventDefault();
|
||||
|
||||
const inputValue = vnSelectRef.value?.inputValue;
|
||||
|
||||
if (inputValue) {
|
||||
|
|
|
@ -76,6 +76,15 @@ onBeforeMount(async () => {
|
|||
);
|
||||
});
|
||||
|
||||
const routeName = computed(() => {
|
||||
const DESCRIPTOR_PROXY = 'DescriptorProxy';
|
||||
|
||||
let name = $props.dataKey;
|
||||
if ($props.dataKey.includes(DESCRIPTOR_PROXY)) {
|
||||
name = name.split(DESCRIPTOR_PROXY)[0];
|
||||
}
|
||||
return `${name}Summary`;
|
||||
});
|
||||
async function getData() {
|
||||
store.url = $props.url;
|
||||
store.filter = $props.filter ?? {};
|
||||
|
@ -154,9 +163,7 @@ const toModule = computed(() =>
|
|||
{{ t('components.smartCard.openSummary') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
<RouterLink
|
||||
:to="{ name: `${dataKey}Summary`, params: { id: entity.id } }"
|
||||
>
|
||||
<RouterLink :to="{ name: routeName, params: { id: entity.id } }">
|
||||
<QBtn
|
||||
class="link"
|
||||
color="white"
|
||||
|
@ -193,22 +200,22 @@ const toModule = computed(() =>
|
|||
</div>
|
||||
</QItemLabel>
|
||||
<QItem>
|
||||
<QItemLabel class="subtitle" caption>
|
||||
<QItemLabel class="subtitle">
|
||||
#{{ getValueFromPath(subtitle) ?? entity.id }}
|
||||
<QBtn
|
||||
round
|
||||
flat
|
||||
dense
|
||||
size="sm"
|
||||
icon="content_copy"
|
||||
color="primary"
|
||||
@click.stop="copyIdText(entity.id)"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('globals.copyId') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
</QItemLabel>
|
||||
<QBtn
|
||||
round
|
||||
flat
|
||||
dense
|
||||
size="sm"
|
||||
icon="content_copy"
|
||||
color="primary"
|
||||
@click.stop="copyIdText(entity.id)"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('globals.copyId') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
</QItem>
|
||||
</QList>
|
||||
<div class="list-box q-mt-xs">
|
||||
|
@ -218,7 +225,7 @@ const toModule = computed(() =>
|
|||
<div class="icons">
|
||||
<slot name="icons" :entity="entity" />
|
||||
</div>
|
||||
<div class="actions justify-center">
|
||||
<div class="actions justify-center" data-cy="descriptor_actions">
|
||||
<slot name="actions" :entity="entity" />
|
||||
</div>
|
||||
<slot name="after" />
|
||||
|
|
|
@ -204,8 +204,9 @@ async function search() {
|
|||
}
|
||||
|
||||
:deep(.q-field--focused) {
|
||||
.q-icon {
|
||||
color: black;
|
||||
.q-icon,
|
||||
.q-placeholder {
|
||||
color: var(--vn-black-text-color);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -53,3 +53,8 @@ const manaCode = ref(props.manaCode);
|
|||
/>
|
||||
</div>
|
||||
</template>
|
||||
<i18n>
|
||||
es:
|
||||
Promotion mana: Maná promoción
|
||||
Claim mana: Maná reclamación
|
||||
</i18n>
|
||||
|
|
|
@ -29,7 +29,6 @@ export async function checkEntryLock(entryFk, userFk) {
|
|||
.dialog({
|
||||
component: VnConfirm,
|
||||
componentProps: {
|
||||
'data-cy': 'entry-lock-confirm',
|
||||
title: t('entry.lock.title'),
|
||||
message: t('entry.lock.message', {
|
||||
userName: data?.user?.nickname,
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
export function getColAlign(col) {
|
||||
let align;
|
||||
switch (col.component) {
|
||||
case 'time':
|
||||
case 'date':
|
||||
case 'select':
|
||||
align = 'left';
|
||||
break;
|
||||
case 'number':
|
||||
align = 'right';
|
||||
break;
|
||||
case 'date':
|
||||
case 'checkbox':
|
||||
align = 'center';
|
||||
break;
|
||||
|
|
|
@ -11,7 +11,7 @@ export async function useCau(res, message) {
|
|||
const { config, headers, request, status, statusText, data } = res || {};
|
||||
const { params, url, method, signal, headers: confHeaders } = config || {};
|
||||
const { message: resMessage, code, name } = data?.error || {};
|
||||
delete confHeaders.Authorization;
|
||||
delete confHeaders?.Authorization;
|
||||
|
||||
const additionalData = {
|
||||
path: location.hash,
|
||||
|
|
|
@ -335,3 +335,7 @@ input::-webkit-inner-spin-button {
|
|||
border: 1px solid;
|
||||
box-shadow: 0 4px 6px #00000000;
|
||||
}
|
||||
|
||||
.containerShrinked {
|
||||
width: 80%;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ import { useI18n } from 'vue-i18n';
|
|||
export default function (value, options = {}) {
|
||||
if (!value) return;
|
||||
|
||||
if (!isValidDate(value)) return null;
|
||||
|
||||
if (!options.dateStyle && !options.timeStyle) {
|
||||
options.day = '2-digit';
|
||||
options.month = '2-digit';
|
||||
|
@ -10,7 +12,12 @@ export default function (value, options = {}) {
|
|||
}
|
||||
|
||||
const { locale } = useI18n();
|
||||
const date = new Date(value);
|
||||
const newDate = new Date(value);
|
||||
|
||||
return new Intl.DateTimeFormat(locale.value, options).format(date);
|
||||
return new Intl.DateTimeFormat(locale.value, options).format(newDate);
|
||||
}
|
||||
// handle 0000-00-00
|
||||
function isValidDate(date) {
|
||||
const parsedDate = new Date(date);
|
||||
return parsedDate instanceof Date && !isNaN(parsedDate.getTime());
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@ globals:
|
|||
rowRemoved: Row removed
|
||||
pleaseWait: Please wait...
|
||||
noPinnedModules: You don't have any pinned modules
|
||||
enterToConfirm: Press Enter to confirm
|
||||
summary:
|
||||
basicData: Basic data
|
||||
daysOnward: Days onward
|
||||
|
@ -152,6 +153,7 @@ globals:
|
|||
maxTemperature: Max
|
||||
minTemperature: Min
|
||||
changePass: Change password
|
||||
setPass: Set password
|
||||
deleteConfirmTitle: Delete selected elements
|
||||
changeState: Change state
|
||||
raid: 'Raid {daysInForward} days'
|
||||
|
@ -692,8 +694,10 @@ worker:
|
|||
machine: Machine
|
||||
business:
|
||||
tableVisibleColumns:
|
||||
id: ID
|
||||
started: Start Date
|
||||
ended: End Date
|
||||
hourlyLabor: Time sheet
|
||||
company: Company
|
||||
reasonEnd: Reason for Termination
|
||||
department: Department
|
||||
|
@ -701,6 +705,7 @@ worker:
|
|||
calendarType: Work Calendar
|
||||
workCenter: Work Center
|
||||
payrollCategories: Contract Category
|
||||
workerBusinessAgreementName: Agreement
|
||||
occupationCode: Contribution Code
|
||||
rate: Rate
|
||||
businessType: Contract Type
|
||||
|
@ -830,6 +835,8 @@ travel:
|
|||
CloneTravelAndEntries: Clone travel and his entries
|
||||
deleteTravel: Delete travel
|
||||
AddEntry: Add entry
|
||||
availabled: Availabled
|
||||
availabledHour: Availabled hour
|
||||
thermographs: Thermographs
|
||||
hb: HB
|
||||
basicData:
|
||||
|
|
|
@ -51,6 +51,7 @@ globals:
|
|||
pleaseWait: Por favor espera...
|
||||
noPinnedModules: No has fijado ningún módulo
|
||||
split: Split
|
||||
enterToConfirm: Pulsa Enter para confirmar
|
||||
summary:
|
||||
basicData: Datos básicos
|
||||
daysOnward: Días adelante
|
||||
|
@ -156,6 +157,7 @@ globals:
|
|||
maxTemperature: Máx
|
||||
minTemperature: Mín
|
||||
changePass: Cambiar contraseña
|
||||
setPass: Establecer contraseña
|
||||
deleteConfirmTitle: Eliminar los elementos seleccionados
|
||||
changeState: Cambiar estado
|
||||
raid: 'Redada {daysInForward} días'
|
||||
|
@ -768,8 +770,10 @@ worker:
|
|||
concept: Concepto
|
||||
business:
|
||||
tableVisibleColumns:
|
||||
id: Id
|
||||
started: Fecha inicio
|
||||
ended: Fecha fin
|
||||
hourlyLabor: Ficha
|
||||
company: Empresa
|
||||
reasonEnd: Motivo finalización
|
||||
department: Departamento
|
||||
|
@ -780,12 +784,13 @@ worker:
|
|||
occupationCode: Cotización
|
||||
rate: Tarifa
|
||||
businessType: Contrato
|
||||
workerBusinessAgreementName: Convenio
|
||||
amount: Salario
|
||||
basicSalary: Salario transportistas
|
||||
notes: Notas
|
||||
operator:
|
||||
numberOfWagons: Número de vagones
|
||||
train: tren
|
||||
train: Tren
|
||||
itemPackingType: Tipo de embalaje
|
||||
warehouse: Almacén
|
||||
sector: Sector
|
||||
|
@ -917,6 +922,8 @@ travel:
|
|||
deleteTravel: Eliminar envío
|
||||
AddEntry: Añadir entrada
|
||||
thermographs: Termógrafos
|
||||
availabled: F. Disponible
|
||||
availabledHour: Hora Disponible
|
||||
hb: HB
|
||||
basicData:
|
||||
daysInForward: Desplazamiento automatico (redada)
|
||||
|
|
|
@ -25,12 +25,13 @@ const $props = defineProps({
|
|||
const { t } = useI18n();
|
||||
const { hasAccount } = toRefs($props);
|
||||
const { openConfirmationModal } = useVnConfirm();
|
||||
const arrayData = useArrayData('Account');
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const state = useState();
|
||||
const user = state.getUser();
|
||||
const { notify } = useQuasar();
|
||||
const account = computed(() => useArrayData('Account').store.data[0]);
|
||||
const account = computed(() => arrayData.store.data);
|
||||
account.value.hasAccount = hasAccount.value;
|
||||
const entityId = computed(() => +route.params.id);
|
||||
const hasitManagementAccess = ref();
|
||||
|
@ -39,7 +40,7 @@ const isHimself = computed(() => user.value.id === account.value.id);
|
|||
const url = computed(() =>
|
||||
isHimself.value
|
||||
? 'Accounts/change-password'
|
||||
: `Accounts/${entityId.value}/setPassword`
|
||||
: `Accounts/${entityId.value}/setPassword`,
|
||||
);
|
||||
|
||||
async function updateStatusAccount(active) {
|
||||
|
@ -153,6 +154,7 @@ onMounted(() => {
|
|||
t('account.card.actions.disableAccount.title'),
|
||||
t('account.card.actions.disableAccount.subtitle'),
|
||||
() => deleteAccount(),
|
||||
() => deleteAccount(),
|
||||
)
|
||||
"
|
||||
>
|
||||
|
@ -172,6 +174,7 @@ onMounted(() => {
|
|||
t('account.card.actions.enableAccount.title'),
|
||||
t('account.card.actions.enableAccount.subtitle'),
|
||||
() => updateStatusAccount(true),
|
||||
() => updateStatusAccount(true),
|
||||
)
|
||||
"
|
||||
>
|
||||
|
@ -186,6 +189,7 @@ onMounted(() => {
|
|||
t('account.card.actions.disableAccount.title'),
|
||||
t('account.card.actions.disableAccount.subtitle'),
|
||||
() => updateStatusAccount(false),
|
||||
() => updateStatusAccount(false),
|
||||
)
|
||||
"
|
||||
>
|
||||
|
@ -201,6 +205,7 @@ onMounted(() => {
|
|||
t('account.card.actions.activateUser.title'),
|
||||
t('account.card.actions.activateUser.title'),
|
||||
() => updateStatusUser(true),
|
||||
() => updateStatusUser(true),
|
||||
)
|
||||
"
|
||||
>
|
||||
|
@ -215,6 +220,7 @@ onMounted(() => {
|
|||
t('account.card.actions.deactivateUser.title'),
|
||||
t('account.card.actions.deactivateUser.title'),
|
||||
() => updateStatusUser(false),
|
||||
() => updateStatusUser(false),
|
||||
)
|
||||
"
|
||||
>
|
||||
|
|
|
@ -27,6 +27,7 @@ const claimActionsForm = ref();
|
|||
const rows = ref([]);
|
||||
const selectedRows = ref([]);
|
||||
const destinationTypes = ref([]);
|
||||
const shelvings = ref([]);
|
||||
const totalClaimed = ref(null);
|
||||
const DEFAULT_MAX_RESPONSABILITY = 5;
|
||||
const DEFAULT_MIN_RESPONSABILITY = 1;
|
||||
|
@ -56,6 +57,12 @@ const columns = computed(() => [
|
|||
field: (row) => row.claimDestinationFk,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'shelving',
|
||||
label: t('shelvings.shelving'),
|
||||
field: (row) => row.shelvingFk,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'Landed',
|
||||
label: t('Landed'),
|
||||
|
@ -125,6 +132,10 @@ async function updateDestination(claimDestinationFk, row, options = {}) {
|
|||
options.reload && claimActionsForm.value.reload();
|
||||
}
|
||||
}
|
||||
async function updateShelving(shelvingFk, row) {
|
||||
await axios.patch(`ClaimEnds/${row.id}`, { shelvingFk });
|
||||
claimActionsForm.value.reload();
|
||||
}
|
||||
|
||||
async function regularizeClaim() {
|
||||
await post(`Claims/${claimId}/regularizeClaim`);
|
||||
|
@ -200,6 +211,7 @@ async function post(query, params) {
|
|||
auto-load
|
||||
@on-fetch="(data) => (destinationTypes = data)"
|
||||
/>
|
||||
<FetchData url="Shelvings" auto-load @on-fetch="(data) => (shelvings = data)" />
|
||||
<RightMenu v-if="claim">
|
||||
<template #right-panel>
|
||||
<QCard class="totalClaim q-my-md q-pa-sm no-box-shadow">
|
||||
|
@ -312,6 +324,20 @@ async function post(query, params) {
|
|||
/>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-shelving="{ row }">
|
||||
<QTd>
|
||||
<VnSelect
|
||||
v-model="row.shelvingFk"
|
||||
:options="shelvings"
|
||||
option-label="code"
|
||||
option-value="id"
|
||||
style="width: 100px"
|
||||
hide-selected
|
||||
@update:model-value="(value) => updateShelving(value, row)"
|
||||
/>
|
||||
</QTd>
|
||||
</template>
|
||||
|
||||
<template #body-cell-price="{ value }">
|
||||
<QTd align="center">
|
||||
{{ toCurrency(value) }}
|
||||
|
@ -354,7 +380,7 @@ async function post(query, params) {
|
|||
(value) =>
|
||||
updateDestination(
|
||||
value,
|
||||
props.row
|
||||
props.row,
|
||||
)
|
||||
"
|
||||
/>
|
||||
|
@ -371,6 +397,17 @@ async function post(query, params) {
|
|||
</QTable>
|
||||
</template>
|
||||
<template #moreBeforeActions>
|
||||
<QBtn
|
||||
color="primary"
|
||||
text-color="white"
|
||||
:unelevated="true"
|
||||
:label="tMobile('Import claim')"
|
||||
:title="t('Import claim')"
|
||||
icon="Download"
|
||||
@click="importToNewRefundTicket"
|
||||
:disable="claim.claimStateFk == resolvedStateId"
|
||||
:loading="loading"
|
||||
/>
|
||||
<QBtn
|
||||
color="primary"
|
||||
text-color="white"
|
||||
|
@ -394,17 +431,6 @@ async function post(query, params) {
|
|||
@click="dialogDestination = !dialogDestination"
|
||||
:loading="loading"
|
||||
/>
|
||||
<QBtn
|
||||
color="primary"
|
||||
text-color="white"
|
||||
:unelevated="true"
|
||||
:label="tMobile('Import claim')"
|
||||
:title="t('Import claim')"
|
||||
icon="Upload"
|
||||
@click="importToNewRefundTicket"
|
||||
:disable="claim.claimStateFk == resolvedStateId"
|
||||
:loading="loading"
|
||||
/>
|
||||
</template>
|
||||
</CrudModel>
|
||||
<QDialog v-model="dialogDestination">
|
||||
|
|
|
@ -40,7 +40,7 @@ const workersOptions = ref([]);
|
|||
</VnRow>
|
||||
<VnRow>
|
||||
<VnSelect
|
||||
:label="t('claim.assignedTo')"
|
||||
:label="t('claim.attendedBy')"
|
||||
v-model="data.workerFk"
|
||||
:options="workersOptions"
|
||||
option-value="id"
|
||||
|
|
|
@ -233,20 +233,27 @@ function claimUrl(section) {
|
|||
<ClaimDescriptorMenu :claim="entity.claim" />
|
||||
</template>
|
||||
<template #body="{ entity: { claim, salesClaimed, developments } }">
|
||||
<QCard class="vn-one" v-if="$route.name != 'ClaimSummary'">
|
||||
<QCard class="vn-one">
|
||||
<VnTitle
|
||||
:url="claimUrl('basic-data')"
|
||||
:text="t('globals.pageTitles.basicData')"
|
||||
/>
|
||||
<VnLv :label="t('claim.created')" :value="toDate(claim.created)" />
|
||||
<VnLv :label="t('claim.state')">
|
||||
<VnLv
|
||||
v-if="$route.name != 'ClaimSummary'"
|
||||
:label="t('claim.created')"
|
||||
:value="toDate(claim.created)"
|
||||
/>
|
||||
<VnLv v-if="$route.name != 'ClaimSummary'" :label="t('claim.state')">
|
||||
<template #value>
|
||||
<QChip :color="stateColor(claim.claimState.code)" dense>
|
||||
{{ claim.claimState.description }}
|
||||
</QChip>
|
||||
</template>
|
||||
</VnLv>
|
||||
<VnLv :label="t('globals.salesPerson')">
|
||||
<VnLv
|
||||
v-if="$route.name != 'ClaimSummary'"
|
||||
:label="t('globals.salesPerson')"
|
||||
>
|
||||
<template #value>
|
||||
<VnUserLink
|
||||
:name="claim.client?.salesPersonUser?.name"
|
||||
|
@ -254,7 +261,7 @@ function claimUrl(section) {
|
|||
/>
|
||||
</template>
|
||||
</VnLv>
|
||||
<VnLv :label="t('claim.attendedBy')">
|
||||
<VnLv v-if="$route.name != 'ClaimSummary'" :label="t('claim.attendedBy')">
|
||||
<template #value>
|
||||
<VnUserLink
|
||||
:name="claim.worker?.user?.nickname"
|
||||
|
@ -262,7 +269,7 @@ function claimUrl(section) {
|
|||
/>
|
||||
</template>
|
||||
</VnLv>
|
||||
<VnLv :label="t('claim.customer')">
|
||||
<VnLv v-if="$route.name != 'ClaimSummary'" :label="t('claim.customer')">
|
||||
<template #value>
|
||||
<span class="link cursor-pointer">
|
||||
{{ claim.client?.name }}
|
||||
|
@ -274,6 +281,11 @@ function claimUrl(section) {
|
|||
:label="t('claim.pickup')"
|
||||
:value="`${dashIfEmpty(claim.pickup)}`"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('globals.packages')"
|
||||
:value="`${dashIfEmpty(claim.packages)}`"
|
||||
:translation="(value) => t(`claim.packages`)"
|
||||
/>
|
||||
</QCard>
|
||||
<QCard class="vn-two">
|
||||
<VnTitle :url="claimUrl('notes')" :text="t('claim.notes')" />
|
||||
|
|
|
@ -19,30 +19,36 @@ const columns = [
|
|||
name: 'itemFk',
|
||||
label: t('Id item'),
|
||||
columnFilter: false,
|
||||
align: 'left',
|
||||
align: 'right',
|
||||
},
|
||||
{
|
||||
name: 'ticketFk',
|
||||
label: t('Ticket'),
|
||||
columnFilter: false,
|
||||
align: 'left',
|
||||
align: 'right',
|
||||
},
|
||||
{
|
||||
name: 'claimDestinationFk',
|
||||
label: t('Destination'),
|
||||
columnFilter: false,
|
||||
align: 'left',
|
||||
align: 'right',
|
||||
},
|
||||
{
|
||||
name: 'shelvingCode',
|
||||
label: t('Shelving'),
|
||||
columnFilter: false,
|
||||
align: 'right',
|
||||
},
|
||||
{
|
||||
name: 'landed',
|
||||
label: t('Landed'),
|
||||
format: (row) => toDate(row.landed),
|
||||
align: 'left',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'quantity',
|
||||
label: t('Quantity'),
|
||||
align: 'left',
|
||||
align: 'right',
|
||||
},
|
||||
{
|
||||
name: 'concept',
|
||||
|
@ -52,18 +58,18 @@ const columns = [
|
|||
{
|
||||
name: 'price',
|
||||
label: t('Price'),
|
||||
align: 'left',
|
||||
align: 'right',
|
||||
},
|
||||
{
|
||||
name: 'discount',
|
||||
label: t('Discount'),
|
||||
format: ({ discount }) => toPercentage(discount / 100),
|
||||
align: 'left',
|
||||
align: 'right',
|
||||
},
|
||||
{
|
||||
name: 'total',
|
||||
label: t('Total'),
|
||||
align: 'left',
|
||||
align: 'right',
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
|
|
@ -106,7 +106,6 @@ const props = defineProps({
|
|||
:label="t('claim.zone')"
|
||||
v-model="params.zoneFk"
|
||||
url="Zones"
|
||||
:use-like="false"
|
||||
outlined
|
||||
rounded
|
||||
dense
|
||||
|
|
|
@ -13,7 +13,6 @@ claim:
|
|||
province: Province
|
||||
zone: Zone
|
||||
customerId: client ID
|
||||
assignedTo: Assigned
|
||||
created: Created
|
||||
details: Details
|
||||
item: Item
|
||||
|
|
|
@ -13,7 +13,6 @@ claim:
|
|||
province: Provincia
|
||||
zone: Zona
|
||||
customerId: ID de cliente
|
||||
assignedTo: Asignado a
|
||||
created: Creado
|
||||
details: Detalles
|
||||
item: Artículo
|
||||
|
|
|
@ -232,7 +232,6 @@ const updateDateParams = (value, params) => {
|
|||
:include="'category'"
|
||||
:sortBy="'name ASC'"
|
||||
dense
|
||||
@update:model-value="(data) => updateDateParams(data, params)"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
|
@ -254,7 +253,6 @@ const updateDateParams = (value, params) => {
|
|||
:fields="['id', 'name']"
|
||||
:sortBy="'name ASC'"
|
||||
dense
|
||||
@update:model-value="(data) => updateDateParams(data, params)"
|
||||
/>
|
||||
<VnSelect
|
||||
v-model="params.campaign"
|
||||
|
@ -303,12 +301,14 @@ en:
|
|||
valentinesDay: Valentine's Day
|
||||
mothersDay: Mother's Day
|
||||
allSaints: All Saints' Day
|
||||
frenchMothersDay: Mother's Day in France
|
||||
es:
|
||||
Enter a new search: Introduce una nueva búsqueda
|
||||
Group by items: Agrupar por artículos
|
||||
valentinesDay: Día de San Valentín
|
||||
mothersDay: Día de la Madre
|
||||
allSaints: Día de Todos los Santos
|
||||
frenchMothersDay: (Francia) Día de la Madre
|
||||
Campaign consumption: Consumo campaña
|
||||
Campaign: Campaña
|
||||
From: Desde
|
||||
|
|
|
@ -143,6 +143,7 @@ const exprBuilder = (param, value) => {
|
|||
outlined
|
||||
rounded
|
||||
auto-load
|
||||
sortBy="name ASC"
|
||||
/></QItemSection>
|
||||
</QItem>
|
||||
<QItem class="q-mb-sm">
|
||||
|
|
|
@ -78,10 +78,20 @@ const columns = computed(() => [
|
|||
component: 'select',
|
||||
attrs: {
|
||||
url: 'Workers/activeWithInheritedRole',
|
||||
fields: ['id', 'name'],
|
||||
fields: ['id', 'name', 'firstName'],
|
||||
where: { role: 'salesPerson' },
|
||||
optionFilter: 'firstName',
|
||||
},
|
||||
columnFilter: {
|
||||
component: 'select',
|
||||
attrs: {
|
||||
url: 'Workers/activeWithInheritedRole',
|
||||
fields: ['id', 'name', 'firstName'],
|
||||
where: { role: 'salesPerson' },
|
||||
optionLabel: 'firstName',
|
||||
optionValue: 'id',
|
||||
},
|
||||
},
|
||||
create: false,
|
||||
columnField: {
|
||||
component: null,
|
||||
|
|
|
@ -233,7 +233,7 @@ function handleLocation(data, location) {
|
|||
postcode: data.postalCode,
|
||||
city: data.city,
|
||||
province: data.province,
|
||||
country: data.province.country,
|
||||
country: data.province?.country,
|
||||
}"
|
||||
@update:model-value="(location) => handleLocation(data, location)"
|
||||
></VnLocation>
|
||||
|
|
|
@ -77,7 +77,6 @@ onBeforeMount(() => {
|
|||
function setPaymentType(accounting) {
|
||||
if (!accounting) return;
|
||||
accountingType.value = accounting.accountingType;
|
||||
|
||||
initialData.description = [];
|
||||
initialData.payed = Date.vnNew();
|
||||
isCash.value = accountingType.value.code == 'cash';
|
||||
|
@ -87,14 +86,14 @@ function setPaymentType(accounting) {
|
|||
initialData.payed.getDate() + accountingType.value.daysInFuture,
|
||||
);
|
||||
maxAmount.value = accountingType.value && accountingType.value.maxAmount;
|
||||
|
||||
if (accountingType.value.code == 'compensation')
|
||||
return (initialData.description = '');
|
||||
if (accountingType.value.receiptDescription)
|
||||
initialData.description.push(accountingType.value.receiptDescription);
|
||||
if (initialData.description) initialData.description.push(initialData.description);
|
||||
|
||||
initialData.description = initialData.description.join(', ');
|
||||
let descriptions = [];
|
||||
if (accountingType.value.receiptDescription)
|
||||
descriptions.push(accountingType.value.receiptDescription);
|
||||
if (initialData.description) descriptions.push(initialData.description);
|
||||
initialData.description = descriptions.join(', ');
|
||||
}
|
||||
|
||||
const calculateFromAmount = (event) => {
|
||||
|
@ -114,7 +113,7 @@ function onBeforeSave(data) {
|
|||
if (isCash.value && shouldSendEmail.value && !data.email)
|
||||
return notify(t('There is no assigned email for this client'), 'negative');
|
||||
|
||||
data.bankFk = data.bankFk.id;
|
||||
data.bankFk = data.bankFk?.id;
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -189,7 +188,7 @@ async function getAmountPaid() {
|
|||
:url-create="urlCreate"
|
||||
:mapper="onBeforeSave"
|
||||
@on-data-saved="onDataSaved"
|
||||
:prevent-submit="true"
|
||||
prevent-submit
|
||||
>
|
||||
<template #form="{ data, validate }">
|
||||
<span ref="closeButton" class="row justify-end close-icon" v-close-popup>
|
||||
|
|
|
@ -18,6 +18,7 @@ import VnInput from 'src/components/common/VnInput.vue';
|
|||
import VnInputDate from 'src/components/common/VnInputDate.vue';
|
||||
import CustomerSamplesPreview from 'src/pages/Customer/components/CustomerSamplesPreview.vue';
|
||||
import FormPopup from 'src/components/FormPopup.vue';
|
||||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
|
||||
const { dialogRef, onDialogOK } = useDialogPluginComponent();
|
||||
|
||||
|
@ -39,7 +40,7 @@ const optionsSamplesVisible = ref([]);
|
|||
const sampleType = ref({ hasPreview: false });
|
||||
const initialData = reactive({});
|
||||
const entityId = computed(() => route.params.id);
|
||||
const customer = computed(() => state.get('Customer'));
|
||||
const customer = computed(() => useArrayData('Customer').store?.data);
|
||||
const filterEmailUsers = { where: { userFk: user.value.id } };
|
||||
const filterClientsAddresses = {
|
||||
include: [
|
||||
|
@ -65,9 +66,9 @@ const filterSamplesVisible = {
|
|||
defineEmits(['confirm', ...useDialogPluginComponent.emits]);
|
||||
|
||||
onBeforeMount(async () => {
|
||||
initialData.clientFk = customer.value.id;
|
||||
initialData.recipient = customer.value.email;
|
||||
initialData.recipientId = customer.value.id;
|
||||
initialData.clientFk = customer.value?.id;
|
||||
initialData.recipient = customer.value?.email;
|
||||
initialData.recipientId = customer.value?.id;
|
||||
});
|
||||
|
||||
const setEmailUser = (data) => {
|
||||
|
|
|
@ -16,7 +16,6 @@ import ItemDescriptor from 'src/pages/Item/Card/ItemDescriptor.vue';
|
|||
import axios from 'axios';
|
||||
import VnSelectEnum from 'src/components/common/VnSelectEnum.vue';
|
||||
import { checkEntryLock } from 'src/composables/checkEntryLock';
|
||||
import SkeletonDescriptor from 'src/components/ui/SkeletonDescriptor.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
|
@ -55,6 +54,7 @@ const columns = [
|
|||
toggleIndeterminate: false,
|
||||
},
|
||||
create: true,
|
||||
createOrder: 12,
|
||||
width: '25px',
|
||||
},
|
||||
{
|
||||
|
@ -62,9 +62,10 @@ const columns = [
|
|||
name: 'workerFk',
|
||||
component: 'select',
|
||||
attrs: {
|
||||
url: 'Workers/search',
|
||||
url: 'TicketRequests/getItemTypeWorker',
|
||||
fields: ['id', 'nickname'],
|
||||
optionLabel: 'nickname',
|
||||
sortBy: 'nickname ASC',
|
||||
optionValue: 'id',
|
||||
},
|
||||
visible: false,
|
||||
|
@ -88,22 +89,13 @@ const columns = [
|
|||
isEditable: false,
|
||||
columnFilter: false,
|
||||
},
|
||||
{
|
||||
name: 'entryFk',
|
||||
isId: true,
|
||||
visible: false,
|
||||
isEditable: false,
|
||||
disable: true,
|
||||
create: true,
|
||||
columnFilter: false,
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
label: 'Id',
|
||||
name: 'itemFk',
|
||||
component: 'number',
|
||||
isEditable: false,
|
||||
width: '40px',
|
||||
width: '35px',
|
||||
},
|
||||
{
|
||||
labelAbbreviation: '',
|
||||
|
@ -111,7 +103,7 @@ const columns = [
|
|||
name: 'hex',
|
||||
columnSearch: false,
|
||||
isEditable: false,
|
||||
width: '5px',
|
||||
width: '9px',
|
||||
component: 'select',
|
||||
attrs: {
|
||||
url: 'Inks',
|
||||
|
@ -138,6 +130,7 @@ const columns = [
|
|||
name: 'itemFk',
|
||||
visible: false,
|
||||
create: true,
|
||||
createOrder: 0,
|
||||
columnFilter: false,
|
||||
},
|
||||
{
|
||||
|
@ -161,6 +154,8 @@ const columns = [
|
|||
name: 'stickers',
|
||||
component: 'input',
|
||||
create: true,
|
||||
|
||||
createOrder: 1,
|
||||
attrs: {
|
||||
positive: false,
|
||||
},
|
||||
|
@ -181,6 +176,7 @@ const columns = [
|
|||
url: 'packagings',
|
||||
fields: ['id'],
|
||||
optionLabel: 'id',
|
||||
optionValue: 'id',
|
||||
},
|
||||
create: true,
|
||||
width: '40px',
|
||||
|
@ -192,7 +188,7 @@ const columns = [
|
|||
component: 'number',
|
||||
create: true,
|
||||
width: '35px',
|
||||
format: (row, dashIfEmpty) => parseFloat(row['weight']).toFixed(1),
|
||||
format: (row) => parseFloat(row['weight']).toFixed(1),
|
||||
},
|
||||
{
|
||||
labelAbbreviation: 'P',
|
||||
|
@ -271,6 +267,7 @@ const columns = [
|
|||
},
|
||||
width: '45px',
|
||||
create: true,
|
||||
createOrder: 3,
|
||||
style: getQuantityStyle,
|
||||
},
|
||||
{
|
||||
|
@ -280,6 +277,7 @@ const columns = [
|
|||
toolTip: t('Buying value'),
|
||||
name: 'buyingValue',
|
||||
create: true,
|
||||
createOrder: 2,
|
||||
component: 'number',
|
||||
attrs: {
|
||||
positive: false,
|
||||
|
@ -312,6 +310,7 @@ const columns = [
|
|||
toolTip: t('Package'),
|
||||
name: 'price2',
|
||||
component: 'number',
|
||||
createDisable: true,
|
||||
width: '35px',
|
||||
create: true,
|
||||
format: (row) => parseFloat(row['price2']).toFixed(2),
|
||||
|
@ -321,6 +320,7 @@ const columns = [
|
|||
label: t('Box'),
|
||||
name: 'price3',
|
||||
component: 'number',
|
||||
createDisable: true,
|
||||
cellEvent: {
|
||||
'update:modelValue': async (value, oldValue, row) => {
|
||||
row['price2'] = row['price2'] * (value / oldValue);
|
||||
|
@ -330,6 +330,25 @@ const columns = [
|
|||
create: true,
|
||||
format: (row) => parseFloat(row['price3']).toFixed(2),
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
labelAbbreviation: 'CM',
|
||||
label: t('Check min price'),
|
||||
toolTip: t('Check min price'),
|
||||
name: 'hasMinPrice',
|
||||
attrs: {
|
||||
toggleIndeterminate: false,
|
||||
},
|
||||
component: 'checkbox',
|
||||
cellEvent: {
|
||||
'update:modelValue': async (value, oldValue, row) => {
|
||||
await axios.patch(`Items/${row['itemFk']}`, {
|
||||
hasMinPrice: value,
|
||||
});
|
||||
},
|
||||
},
|
||||
width: '25px',
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
labelAbbreviation: 'Min.',
|
||||
|
@ -350,25 +369,6 @@ const columns = [
|
|||
},
|
||||
format: (row) => parseFloat(row['minPrice']).toFixed(2),
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
labelAbbreviation: 'CM',
|
||||
label: t('Check min price'),
|
||||
toolTip: t('Check min price'),
|
||||
name: 'hasMinPrice',
|
||||
attrs: {
|
||||
toggleIndeterminate: false,
|
||||
},
|
||||
component: 'checkbox',
|
||||
cellEvent: {
|
||||
'update:modelValue': async (value, oldValue, row) => {
|
||||
await axios.patch(`Items/${row['itemFk']}`, {
|
||||
hasMinPrice: value,
|
||||
});
|
||||
},
|
||||
},
|
||||
width: '25px',
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
labelAbbreviation: t('P.Sen'),
|
||||
|
@ -378,6 +378,9 @@ const columns = [
|
|||
component: 'number',
|
||||
isEditable: false,
|
||||
width: '40px',
|
||||
style: () => {
|
||||
return { color: 'var(--vn-label-color)' };
|
||||
},
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
|
@ -417,6 +420,9 @@ const columns = [
|
|||
component: 'input',
|
||||
isEditable: false,
|
||||
width: '35px',
|
||||
style: () => {
|
||||
return { color: 'var(--vn-label-color)' };
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -502,13 +508,14 @@ async function setBuyUltimate(itemFk, data) {
|
|||
},
|
||||
});
|
||||
const buyUltimateData = buyUltimate.data[0];
|
||||
if (!buyUltimateData) return;
|
||||
|
||||
const allowedKeys = columns
|
||||
.filter((col) => col.create === true)
|
||||
.map((col) => col.name);
|
||||
|
||||
allowedKeys.forEach((key) => {
|
||||
if (buyUltimateData.hasOwnProperty(key) && key !== 'entryFk') {
|
||||
if (buyUltimateData?.hasOwnProperty(key) && key !== 'entryFk') {
|
||||
if (!['stickers', 'quantity'].includes(key)) data[key] = buyUltimateData[key];
|
||||
}
|
||||
});
|
||||
|
@ -601,6 +608,7 @@ onMounted(() => {
|
|||
ref="entryBuysRef"
|
||||
data-key="EntryBuys"
|
||||
:url="`Entries/${entityId}/getBuyList`"
|
||||
search-url="EntryBuys"
|
||||
save-url="Buys/crud"
|
||||
:disable-option="{ card: true }"
|
||||
v-model:selected="selectedRows"
|
||||
|
@ -630,22 +638,25 @@ onMounted(() => {
|
|||
isFullWidth: true,
|
||||
containerStyle: {
|
||||
display: 'flex',
|
||||
'flex-wrap': 'wrap',
|
||||
gap: '16px',
|
||||
position: 'relative',
|
||||
height: '450px',
|
||||
},
|
||||
columnGridStyle: {
|
||||
'max-width': '50%',
|
||||
flex: 1,
|
||||
'margin-right': '30px',
|
||||
flex: 1,
|
||||
},
|
||||
previousStyle: {
|
||||
'max-width': '30%',
|
||||
height: '500px',
|
||||
},
|
||||
displayPrevious: true,
|
||||
}"
|
||||
:is-editable="editableMode"
|
||||
:without-header="!editableMode"
|
||||
:with-filters="editableMode"
|
||||
:right-search="false"
|
||||
:right-search-icon="false"
|
||||
:right-search="editableMode"
|
||||
:right-search-icon="true"
|
||||
:row-click="false"
|
||||
:columns="columns"
|
||||
:beforeSaveFn="beforeSave"
|
||||
|
@ -654,6 +665,7 @@ onMounted(() => {
|
|||
auto-load
|
||||
footer
|
||||
data-cy="entry-buys"
|
||||
overlay
|
||||
>
|
||||
<template #column-hex="{ row }">
|
||||
<VnColor :colors="row?.hexJson" style="height: 100%; min-width: 2000px" />
|
||||
|
|
|
@ -65,7 +65,7 @@ const entriesTableColumns = computed(() => [
|
|||
]);
|
||||
|
||||
function downloadCSV(rows) {
|
||||
const headers = ['id', 'itemFk', 'name', 'stickers', 'packing', 'comment'];
|
||||
const headers = ['id', 'itemFk', 'name', 'stickers', 'packing', 'grouping', 'comment'];
|
||||
|
||||
const csvRows = rows.map((row) => {
|
||||
const buy = row;
|
||||
|
@ -77,6 +77,7 @@ function downloadCSV(rows) {
|
|||
item.name || '',
|
||||
buy.stickers,
|
||||
buy.packing,
|
||||
buy.grouping,
|
||||
item.comment || '',
|
||||
].join(',');
|
||||
});
|
||||
|
|
|
@ -248,7 +248,7 @@ const entryFilterPanel = ref();
|
|||
<i18n>
|
||||
en:
|
||||
params:
|
||||
isExcludedFromAvailable: Inventory
|
||||
isExcludedFromAvailable: Is excluded
|
||||
isOrdered: Ordered
|
||||
isReceived: Received
|
||||
isConfirmed: Confirmed
|
||||
|
|
|
@ -11,6 +11,8 @@ import VnTable from 'components/VnTable/VnTable.vue';
|
|||
import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue';
|
||||
import VnSelectTravelExtended from 'src/components/common/VnSelectTravelExtended.vue';
|
||||
import { toDate } from 'src/filters';
|
||||
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
||||
import EntrySummary from './Card/EntrySummary.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const tableRef = ref();
|
||||
|
@ -18,6 +20,7 @@ const defaultEntry = ref({});
|
|||
const state = useState();
|
||||
const user = state.getUser();
|
||||
const dataKey = 'EntryList';
|
||||
const { viewSummary } = useSummaryDialog();
|
||||
|
||||
const entryQueryFilter = {
|
||||
include: [
|
||||
|
@ -199,7 +202,6 @@ const columns = computed(() => [
|
|||
optionValue: 'code',
|
||||
optionLabel: 'description',
|
||||
},
|
||||
cardVisible: true,
|
||||
width: '65px',
|
||||
format: (row, dashIfEmpty) => dashIfEmpty(row.entryTypeDescription),
|
||||
},
|
||||
|
@ -223,6 +225,19 @@ const columns = computed(() => [
|
|||
visible: false,
|
||||
create: true,
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
label: '',
|
||||
name: 'tableActions',
|
||||
actions: [
|
||||
{
|
||||
title: t('components.smartCard.viewSummary'),
|
||||
icon: 'preview',
|
||||
isPrimary: true,
|
||||
action: (row) => viewSummary(row.id, EntrySummary, 'xlg-width'),
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
function getBadgeAttrs(row) {
|
||||
const date = row.landed;
|
||||
|
@ -268,16 +283,7 @@ onBeforeMount(async () => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<VnSection
|
||||
:data-key="dataKey"
|
||||
prefix="entry"
|
||||
url="Entries/filter"
|
||||
:array-data-props="{
|
||||
url: 'Entries/filter',
|
||||
order: 'landed DESC',
|
||||
userFilter: EntryFilter,
|
||||
}"
|
||||
>
|
||||
<VnSection :data-key="dataKey" prefix="entry">
|
||||
<template #advanced-menu>
|
||||
<EntryFilter :data-key="dataKey" />
|
||||
</template>
|
||||
|
@ -286,6 +292,7 @@ onBeforeMount(async () => {
|
|||
v-if="defaultEntry.defaultSupplierFk"
|
||||
ref="tableRef"
|
||||
:data-key="dataKey"
|
||||
search-url="EntryList"
|
||||
url="Entries/filter"
|
||||
:filter="entryQueryFilter"
|
||||
order="landed DESC"
|
||||
|
|
|
@ -19,6 +19,7 @@ const { t } = useI18n();
|
|||
const quasar = useQuasar();
|
||||
const state = useState();
|
||||
const user = state.getUser();
|
||||
const footer = ref({ bought: 0, reserve: 0 });
|
||||
const columns = computed(() => [
|
||||
{
|
||||
align: 'left',
|
||||
|
@ -38,16 +39,14 @@ const columns = computed(() => [
|
|||
cardVisible: true,
|
||||
create: true,
|
||||
attrs: {
|
||||
url: 'Workers/activeWithInheritedRole',
|
||||
fields: ['id', 'name', 'nickname'],
|
||||
where: { role: 'buyer' },
|
||||
optionFilter: 'firstName',
|
||||
url: 'TicketRequests/getItemTypeWorker',
|
||||
fields: ['id', 'nickname'],
|
||||
optionLabel: 'nickname',
|
||||
sortBy: 'nickname ASC',
|
||||
optionValue: 'id',
|
||||
useLike: false,
|
||||
},
|
||||
columnFilter: false,
|
||||
width: '70px',
|
||||
width: '50px',
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
|
@ -57,7 +56,8 @@ const columns = computed(() => [
|
|||
create: true,
|
||||
component: 'number',
|
||||
summation: true,
|
||||
width: '60px',
|
||||
width: '50px',
|
||||
format: ({ reserve }, dashIfEmpty) => dashIfEmpty(round(reserve)),
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
|
@ -65,6 +65,7 @@ const columns = computed(() => [
|
|||
name: 'bought',
|
||||
summation: true,
|
||||
cardVisible: true,
|
||||
style: ({ reserve, bought }) => boughtStyle(bought, reserve),
|
||||
columnFilter: false,
|
||||
},
|
||||
{
|
||||
|
@ -95,7 +96,6 @@ const columns = computed(() => [
|
|||
},
|
||||
},
|
||||
],
|
||||
'data-cy': 'table-actions',
|
||||
},
|
||||
]);
|
||||
|
||||
|
@ -137,20 +137,20 @@ function openDialog() {
|
|||
}
|
||||
|
||||
function setFooter(data) {
|
||||
const footer = {
|
||||
bought: 0,
|
||||
reserve: 0,
|
||||
};
|
||||
footer.value = { bought: 0, reserve: 0 };
|
||||
data.forEach((row) => {
|
||||
footer.bought += row?.bought;
|
||||
footer.reserve += row?.reserve;
|
||||
footer.value.bought += row?.bought;
|
||||
footer.value.reserve += row?.reserve;
|
||||
});
|
||||
tableRef.value.footer = footer;
|
||||
}
|
||||
|
||||
function round(value) {
|
||||
return Math.round(value * 100) / 100;
|
||||
}
|
||||
|
||||
function boughtStyle(bought, reserve) {
|
||||
return reserve < bought ? { color: 'var(--q-negative)' } : '';
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<VnSubToolbar>
|
||||
|
@ -253,24 +253,14 @@ function round(value) {
|
|||
<WorkerDescriptorProxy :id="row?.workerFk" />
|
||||
</span>
|
||||
</template>
|
||||
<template #column-bought="{ row }">
|
||||
<span :class="{ 'text-negative': row.reserve < row.bought }">
|
||||
{{ row?.bought }}
|
||||
</span>
|
||||
</template>
|
||||
<template #column-footer-reserve>
|
||||
<span>
|
||||
{{ round(tableRef.footer.reserve) }}
|
||||
{{ round(footer.reserve) }}
|
||||
</span>
|
||||
</template>
|
||||
<template #column-footer-bought>
|
||||
<span
|
||||
:class="{
|
||||
'text-negative':
|
||||
tableRef.footer.reserve < tableRef.footer.bought,
|
||||
}"
|
||||
>
|
||||
{{ round(tableRef.footer.bought) }}
|
||||
<span :style="boughtStyle(footer?.bought, footer?.reserve)">
|
||||
{{ round(footer.bought) }}
|
||||
</span>
|
||||
</template>
|
||||
</VnTable>
|
||||
|
@ -286,7 +276,7 @@ function round(value) {
|
|||
justify-content: center;
|
||||
}
|
||||
.column {
|
||||
min-width: 30%;
|
||||
min-width: 35%;
|
||||
margin-top: 5%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
@ -14,7 +14,7 @@ const $props = defineProps({
|
|||
required: true,
|
||||
},
|
||||
dated: {
|
||||
type: Date,
|
||||
type: [Date, String],
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
@ -101,7 +101,8 @@ const columns = [
|
|||
</template>
|
||||
<style lang="css" scoped>
|
||||
.container {
|
||||
max-width: 50vw;
|
||||
max-width: 100%;
|
||||
width: 50%;
|
||||
overflow: auto;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
@ -109,9 +110,6 @@ const columns = [
|
|||
background-color: var(--vn-section-color);
|
||||
padding: 2%;
|
||||
}
|
||||
.container > div > div > .q-table__top.relative-position.row.items-center {
|
||||
background-color: red !important;
|
||||
}
|
||||
</style>
|
||||
<i18n>
|
||||
es:
|
||||
|
|
|
@ -185,6 +185,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
|
|||
data-key="InvoiceInSummary"
|
||||
:url="`InvoiceIns/${entityId}/summary`"
|
||||
@on-fetch="(data) => init(data)"
|
||||
module-name="InvoiceIn"
|
||||
>
|
||||
<template #header="{ entity }">
|
||||
<div>{{ entity.id }} - {{ entity.supplier?.name }}</div>
|
||||
|
|
|
@ -202,7 +202,7 @@ function setCursor(ref) {
|
|||
:option-label="col.optionLabel"
|
||||
:filter-options="['id', 'name']"
|
||||
:tooltip="t('Create a new expense')"
|
||||
@keydown.tab="
|
||||
@keydown.tab.prevent="
|
||||
autocompleteExpense(
|
||||
$event,
|
||||
row,
|
||||
|
|
|
@ -103,7 +103,7 @@ const refundInvoice = async (withWarehouse) => {
|
|||
t('refundInvoiceSuccessMessage', {
|
||||
refundTicket: data[0].id,
|
||||
}),
|
||||
'positive'
|
||||
'positive',
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -124,6 +124,13 @@ const showRefundInvoiceForm = () => {
|
|||
},
|
||||
});
|
||||
};
|
||||
|
||||
const showExportationLetter = () => {
|
||||
openReport(`InvoiceOuts/${$props.invoiceOutData.ref}/exportation-pdf`, {
|
||||
recipientId: $props.invoiceOutData.client.id,
|
||||
refFk: $props.invoiceOutData.ref,
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -156,10 +163,14 @@ const showRefundInvoiceForm = () => {
|
|||
<QMenu anchor="top end" self="top start">
|
||||
<QList>
|
||||
<QItem v-ripple clickable @click="showSendInvoiceDialog('pdf')">
|
||||
<QItemSection>{{ t('Send PDF') }}</QItemSection>
|
||||
<QItemSection data-cy="InvoiceOutDescriptorMenuSendPdfOption">
|
||||
{{ t('Send PDF') }}
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem v-ripple clickable @click="showSendInvoiceDialog('csv')">
|
||||
<QItemSection>{{ t('Send CSV') }}</QItemSection>
|
||||
<QItemSection data-cy="InvoiceOutDescriptorMenuSendCsvOption">
|
||||
{{ t('Send CSV') }}
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</QList>
|
||||
</QMenu>
|
||||
|
@ -172,7 +183,7 @@ const showRefundInvoiceForm = () => {
|
|||
t('Confirm deletion'),
|
||||
t('Are you sure you want to delete this invoice?'),
|
||||
deleteInvoice,
|
||||
redirectToInvoiceOutList
|
||||
redirectToInvoiceOutList,
|
||||
)
|
||||
"
|
||||
>
|
||||
|
@ -185,7 +196,7 @@ const showRefundInvoiceForm = () => {
|
|||
openConfirmationModal(
|
||||
'',
|
||||
t('Are you sure you want to book this invoice?'),
|
||||
bookInvoice
|
||||
bookInvoice,
|
||||
)
|
||||
"
|
||||
>
|
||||
|
@ -198,7 +209,7 @@ const showRefundInvoiceForm = () => {
|
|||
openConfirmationModal(
|
||||
t('Generate PDF invoice document'),
|
||||
t('Are you sure you want to generate/regenerate the PDF invoice?'),
|
||||
generateInvoicePdf
|
||||
generateInvoicePdf,
|
||||
)
|
||||
"
|
||||
>
|
||||
|
@ -226,6 +237,14 @@ const showRefundInvoiceForm = () => {
|
|||
{{ t('Create a single ticket with all the content of the current invoice') }}
|
||||
</QTooltip>
|
||||
</QItem>
|
||||
<QItem
|
||||
v-if="$props.invoiceOutData.serial === 'E'"
|
||||
v-ripple
|
||||
clickable
|
||||
@click="showExportationLetter()"
|
||||
>
|
||||
<QItemSection>{{ t('Show CITES letter') }}</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
|
@ -255,7 +274,7 @@ es:
|
|||
Create a single ticket with all the content of the current invoice: Crear un ticket único con todo el contenido de la factura actual
|
||||
refundInvoiceSuccessMessage: Se ha creado el siguiente ticket de abono {refundTicket}
|
||||
The email can't be empty: El email no puede estar vacío
|
||||
|
||||
Show CITES letter: Ver carta CITES
|
||||
en:
|
||||
refundInvoiceSuccessMessage: The following refund ticket have been created {refundTicket}
|
||||
</i18n>
|
||||
|
|
|
@ -22,7 +22,7 @@ const states = ref();
|
|||
<VnFilterPanel :data-key="props.dataKey" :search-button="true">
|
||||
<template #tags="{ tag, formatFn }">
|
||||
<div class="q-gutter-x-xs">
|
||||
<strong>{{ t(`params.${tag.label}`) }}: </strong>
|
||||
<strong>{{ t(`invoiceOut.params.${tag.label}`) }}: </strong>
|
||||
<span>{{ formatFn(tag.value) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -84,15 +84,6 @@ const states = ref();
|
|||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate
|
||||
v-model="params.issued"
|
||||
:label="t('Issued')"
|
||||
is-outlined
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate
|
||||
|
@ -110,37 +101,3 @@ const states = ref();
|
|||
</template>
|
||||
</VnFilterPanel>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
en:
|
||||
params:
|
||||
search: Contains
|
||||
clientFk: Customer
|
||||
fi: FI
|
||||
amount: Amount
|
||||
min: Min
|
||||
max: Max
|
||||
hasPdf: Has PDF
|
||||
issued: Issued
|
||||
created: Created
|
||||
dued: Dued
|
||||
es:
|
||||
params:
|
||||
search: Contiene
|
||||
clientFk: Cliente
|
||||
fi: CIF
|
||||
amount: Importe
|
||||
min: Min
|
||||
max: Max
|
||||
hasPdf: Tiene PDF
|
||||
issued: Emitida
|
||||
created: Creada
|
||||
dued: Vencida
|
||||
Customer ID: ID cliente
|
||||
FI: CIF
|
||||
Amount: Importe
|
||||
Has PDF: Tiene PDF
|
||||
Issued: Fecha emisión
|
||||
Created: Fecha creación
|
||||
Dued: Fecha vencimiento
|
||||
</i18n>
|
||||
|
|
|
@ -21,7 +21,6 @@ import VnSection from 'src/components/common/VnSection.vue';
|
|||
const { t } = useI18n();
|
||||
const { viewSummary } = useSummaryDialog();
|
||||
const tableRef = ref();
|
||||
const invoiceOutSerialsOptions = ref([]);
|
||||
const customerOptions = ref([]);
|
||||
const selectedRows = ref([]);
|
||||
const hasSelectedCards = computed(() => selectedRows.value.length > 0);
|
||||
|
@ -376,7 +375,6 @@ watchEffect(selectedRows);
|
|||
url="InvoiceOutSerials"
|
||||
v-model="data.serial"
|
||||
:label="t('invoiceOutModule.serial')"
|
||||
:options="invoiceOutSerialsOptions"
|
||||
option-label="description"
|
||||
option-value="code"
|
||||
option-filter
|
||||
|
|
|
@ -10,6 +10,8 @@ import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vu
|
|||
import TicketDescriptorProxy from '../Ticket/Card/TicketDescriptorProxy.vue';
|
||||
import WorkerDescriptorProxy from '../Worker/Card/WorkerDescriptorProxy.vue';
|
||||
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||
import InvoiceOutNegativeBasesFilter from './InvoiceOutNegativeBasesFilter.vue';
|
||||
import RightMenu from 'src/components/common/RightMenu.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const tableRef = ref();
|
||||
|
@ -97,16 +99,19 @@ const columns = computed(() => [
|
|||
align: 'left',
|
||||
name: 'isActive',
|
||||
label: t('invoiceOut.negativeBases.active'),
|
||||
component: 'checkbox',
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'hasToInvoice',
|
||||
label: t('invoiceOut.negativeBases.hasToInvoice'),
|
||||
component: 'checkbox',
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'hasVerifiedData',
|
||||
name: 'isTaxDataChecked',
|
||||
label: t('invoiceOut.negativeBases.verifiedData'),
|
||||
component: 'checkbox',
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
|
@ -142,7 +147,7 @@ const downloadCSV = async () => {
|
|||
await invoiceOutGlobalStore.getNegativeBasesCsv(
|
||||
userParams.from,
|
||||
userParams.to,
|
||||
filterParams
|
||||
filterParams,
|
||||
);
|
||||
};
|
||||
</script>
|
||||
|
@ -154,6 +159,11 @@ const downloadCSV = async () => {
|
|||
</QBtn>
|
||||
</template>
|
||||
</VnSubToolbar>
|
||||
<RightMenu>
|
||||
<template #right-panel>
|
||||
<InvoiceOutNegativeBasesFilter data-key="negativeFilter" />
|
||||
</template>
|
||||
</RightMenu>
|
||||
<VnTable
|
||||
ref="tableRef"
|
||||
data-key="negativeFilter"
|
||||
|
@ -174,6 +184,7 @@ const downloadCSV = async () => {
|
|||
auto-load
|
||||
:is-editable="false"
|
||||
:use-model="true"
|
||||
:right-search="false"
|
||||
>
|
||||
<template #column-clientId="{ row }">
|
||||
<span class="link" @click.stop>
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import VnSelectWorker from 'src/components/common/VnSelectWorker.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps({
|
||||
|
@ -24,11 +25,11 @@ const props = defineProps({
|
|||
>
|
||||
<template #tags="{ tag, formatFn }">
|
||||
<div class="q-gutter-x-xs">
|
||||
<strong>{{ t(`params.${tag.label}`) }}: </strong>
|
||||
<strong>{{ t(`invoiceOut.params.${tag.label}`) }}: </strong>
|
||||
<span>{{ formatFn(tag.value) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #body="{ params }">
|
||||
<template #body="{ params, searchFn }">
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate
|
||||
|
@ -49,38 +50,70 @@ const props = defineProps({
|
|||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
v-model="params.company"
|
||||
<VnSelect
|
||||
url="Companies"
|
||||
:label="t('globals.company')"
|
||||
is-outlined
|
||||
/>
|
||||
v-model="params.company"
|
||||
option-label="code"
|
||||
option-value="code"
|
||||
dense
|
||||
outlined
|
||||
rounded
|
||||
@update:model-value="searchFn()"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>
|
||||
{{ scope.opt?.code }}
|
||||
</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{ `#${scope.opt?.id}` }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
<VnSelect
|
||||
url="Countries"
|
||||
:label="t('globals.params.countryFk')"
|
||||
v-model="params.country"
|
||||
:label="t('globals.country')"
|
||||
is-outlined
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
v-model="params.clientId"
|
||||
:label="t('invoiceOut.negativeBases.clientId')"
|
||||
is-outlined
|
||||
/>
|
||||
option-label="name"
|
||||
option-value="name"
|
||||
outlined
|
||||
dense
|
||||
rounded
|
||||
@update:model-value="searchFn()"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>
|
||||
{{ scope.opt?.name }}
|
||||
</QItemLabel>
|
||||
<QItemLabel caption>
|
||||
{{ `#${scope.opt?.id}` }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
v-model="params.clientSocialName"
|
||||
<VnSelect
|
||||
url="Clients"
|
||||
:label="t('globals.client')"
|
||||
is-outlined
|
||||
v-model="params.clientId"
|
||||
outlined
|
||||
dense
|
||||
rounded
|
||||
@update:model-value="searchFn()"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
|
@ -90,15 +123,18 @@ const props = defineProps({
|
|||
v-model="params.amount"
|
||||
:label="t('globals.amount')"
|
||||
is-outlined
|
||||
:positive="false"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
v-model="params.comercialName"
|
||||
<VnSelectWorker
|
||||
:label="t('invoiceOut.negativeBases.comercial')"
|
||||
v-model="params.workerName"
|
||||
option-value="name"
|
||||
is-outlined
|
||||
@update:model-value="searchFn()"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
|
|
|
@ -2,9 +2,10 @@ invoiceOut:
|
|||
search: Search invoice
|
||||
searchInfo: You can search by invoice reference
|
||||
params:
|
||||
id: ID
|
||||
company: Company
|
||||
country: Country
|
||||
clientId: Client ID
|
||||
clientId: Client
|
||||
clientSocialName: Client
|
||||
taxableBase: Base
|
||||
ticketFk: Ticket
|
||||
|
@ -12,6 +13,19 @@ invoiceOut:
|
|||
hasToInvoice: Has to invoice
|
||||
hasVerifiedData: Verified data
|
||||
workerName: Worker
|
||||
isTaxDataChecked: Verified data
|
||||
amount: Amount
|
||||
clientFk: Client
|
||||
companyFk: Company
|
||||
created: Created
|
||||
dued: Dued
|
||||
customsAgentFk: Custom Agent
|
||||
ref: Reference
|
||||
fi: FI
|
||||
min: Min
|
||||
max: Max
|
||||
hasPdf: Has PDF
|
||||
search: Contains
|
||||
card:
|
||||
issued: Issued
|
||||
customerCard: Customer card
|
||||
|
@ -53,7 +67,7 @@ invoiceOut:
|
|||
active: Active
|
||||
hasToInvoice: Has to Invoice
|
||||
verifiedData: Verified Data
|
||||
comercial: Commercial
|
||||
comercial: Sales person
|
||||
errors:
|
||||
downloadCsvFailed: CSV download failed
|
||||
invoiceOutModule:
|
||||
|
|
|
@ -2,9 +2,10 @@ invoiceOut:
|
|||
search: Buscar factura emitida
|
||||
searchInfo: Puedes buscar por referencia de la factura
|
||||
params:
|
||||
id: ID
|
||||
company: Empresa
|
||||
country: País
|
||||
clientId: ID del cliente
|
||||
clientId: Cliente
|
||||
clientSocialName: Cliente
|
||||
taxableBase: Base
|
||||
ticketFk: Ticket
|
||||
|
@ -12,6 +13,19 @@ invoiceOut:
|
|||
hasToInvoice: Debe facturar
|
||||
hasVerifiedData: Datos verificados
|
||||
workerName: Comercial
|
||||
isTaxDataChecked: Datos comprobados
|
||||
amount: Importe
|
||||
clientFk: Cliente
|
||||
companyFk: Empresa
|
||||
created: Creada
|
||||
dued: Vencida
|
||||
customsAgentFk: Agente aduanas
|
||||
ref: Referencia
|
||||
fi: CIF
|
||||
min: Min
|
||||
max: Max
|
||||
hasPdf: Tiene PDF
|
||||
search: Contiene
|
||||
card:
|
||||
issued: Fecha emisión
|
||||
customerCard: Ficha del cliente
|
||||
|
|
|
@ -120,22 +120,9 @@ const updateStock = async () => {
|
|||
</template>
|
||||
</VnLv>
|
||||
<VnLv :label="t('globals.producer')" :value="dashIfEmpty(entity.subName)" />
|
||||
<VnLv
|
||||
v-if="entity.value5"
|
||||
:label="t('item.descriptor.color')"
|
||||
:value="entity.value5"
|
||||
>
|
||||
</VnLv>
|
||||
<VnLv
|
||||
v-if="entity.value6"
|
||||
:label="t('item.descriptor.category')"
|
||||
:value="entity.value6"
|
||||
/>
|
||||
<VnLv
|
||||
v-if="entity.value7"
|
||||
:label="t('item.list.stems')"
|
||||
:value="entity.value7"
|
||||
/>
|
||||
<VnLv v-if="entity?.value5" :label="entity?.tag5" :value="entity.value5" />
|
||||
<VnLv v-if="entity?.value6" :label="entity?.tag6" :value="entity.value6" />
|
||||
<VnLv v-if="entity?.value7" :label="entity?.tag7" :value="entity.value7" />
|
||||
</template>
|
||||
<template #icons="{ entity }">
|
||||
<QCardActions v-if="entity" class="q-gutter-x-md">
|
||||
|
|
|
@ -12,7 +12,7 @@ import FetchData from 'components/FetchData.vue';
|
|||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import VnInputDate from 'src/components/common/VnInputDate.vue';
|
||||
|
||||
import { toDateFormat } from 'src/filters/date.js';
|
||||
import { toDateTimeFormat } from 'src/filters/date.js';
|
||||
import { dashIfEmpty } from 'src/filters';
|
||||
import { date } from 'quasar';
|
||||
import { useState } from 'src/composables/useState';
|
||||
|
@ -27,7 +27,7 @@ const user = state.getUser();
|
|||
const today = Date.vnNew();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
const warehousesOptions = ref([]);
|
||||
const itemBalances = computed(() => arrayDataItemBalances.store.data);
|
||||
const itemBalances = computed(() => arrayDataItemBalances.store.data || []);
|
||||
const where = computed(() => arrayDataItemBalances.store.filter.where || {});
|
||||
const showWhatsBeforeInventory = ref(false);
|
||||
const inventoriedDate = ref(null);
|
||||
|
@ -143,7 +143,12 @@ onMounted(async () => {
|
|||
const fetchItemBalances = async () => await arrayDataItemBalances.fetch({});
|
||||
|
||||
const getBadgeAttrs = (_date) => {
|
||||
const isSameDate = date.isSameDate(today, _date);
|
||||
let today = Date.vnNew();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
let timeTicket = new Date(_date);
|
||||
timeTicket.setHours(0, 0, 0, 0);
|
||||
|
||||
const isSameDate = date.isSameDate(today, timeTicket);
|
||||
const attrs = {
|
||||
'text-color': isSameDate ? 'black' : 'white',
|
||||
color: isSameDate ? 'warning' : 'transparent',
|
||||
|
@ -244,7 +249,7 @@ async function updateWarehouse(warehouseFk) {
|
|||
dense
|
||||
style="font-size: 14px"
|
||||
>
|
||||
{{ toDateFormat(row.shipped) }}
|
||||
{{ toDateTimeFormat(row.shipped) }}
|
||||
</QBadge>
|
||||
</QTd>
|
||||
</template>
|
||||
|
@ -313,8 +318,8 @@ async function updateWarehouse(warehouseFk) {
|
|||
row.lineFk == row.lastPreparedLineFk
|
||||
? 'black'
|
||||
: row.balance < 0
|
||||
? 'negative'
|
||||
: ''
|
||||
? 'negative'
|
||||
: ''
|
||||
"
|
||||
dense
|
||||
style="font-size: 14px"
|
||||
|
|
|
@ -11,7 +11,6 @@ import { toCurrency } from 'filters/index';
|
|||
import { useArrayData } from 'composables/useArrayData';
|
||||
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
||||
import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const from = ref();
|
||||
|
@ -41,7 +40,7 @@ const itemLastEntries = ref([]);
|
|||
|
||||
const columns = computed(() => [
|
||||
{
|
||||
label: 'Nv',
|
||||
label: 'NV',
|
||||
name: 'ig',
|
||||
align: 'center',
|
||||
},
|
||||
|
@ -70,6 +69,7 @@ const columns = computed(() => [
|
|||
field: 'reference',
|
||||
align: 'center',
|
||||
format: (_, row) => toCurrency(row.price2) + ' / ' + toCurrency(row.price3),
|
||||
style: (row) => highlightedRow(row),
|
||||
},
|
||||
{
|
||||
label: t('lastEntries.printedStickers'),
|
||||
|
@ -84,6 +84,7 @@ const columns = computed(() => [
|
|||
field: 'stickers',
|
||||
align: 'center',
|
||||
format: (val) => dashIfEmpty(val),
|
||||
style: (row) => highlightedRow(row),
|
||||
},
|
||||
{
|
||||
label: 'Packing',
|
||||
|
@ -102,12 +103,14 @@ const columns = computed(() => [
|
|||
name: 'stems',
|
||||
field: 'stems',
|
||||
align: 'center',
|
||||
style: (row) => highlightedRow(row),
|
||||
},
|
||||
{
|
||||
label: t('lastEntries.quantity'),
|
||||
name: 'quantity',
|
||||
field: 'quantity',
|
||||
align: 'center',
|
||||
style: (row) => highlightedRow(row),
|
||||
},
|
||||
{
|
||||
label: t('lastEntries.cost'),
|
||||
|
@ -120,12 +123,14 @@ const columns = computed(() => [
|
|||
name: 'weight',
|
||||
field: 'weight',
|
||||
align: 'center',
|
||||
style: (row) => highlightedRow(row),
|
||||
},
|
||||
{
|
||||
label: t('lastEntries.cube'),
|
||||
name: 'cube',
|
||||
field: 'packagingFk',
|
||||
align: 'center',
|
||||
style: (row) => highlightedRow(row),
|
||||
},
|
||||
{
|
||||
label: t('lastEntries.supplier'),
|
||||
|
@ -208,6 +213,14 @@ onMounted(async () => {
|
|||
function getBadgeClass(groupingMode, expectedGrouping) {
|
||||
return groupingMode === expectedGrouping ? 'accent-badge' : 'simple-badge';
|
||||
}
|
||||
|
||||
function highlightedRow(row) {
|
||||
return row?.isInventorySupplier
|
||||
? {
|
||||
'background-color': 'var(--vn-section-hover-color)',
|
||||
}
|
||||
: '';
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<VnSubToolbar>
|
||||
|
@ -236,7 +249,7 @@ function getBadgeClass(groupingMode, expectedGrouping) {
|
|||
:no-data-label="t('globals.noResults')"
|
||||
>
|
||||
<template #body-cell-ig="{ row }">
|
||||
<QTd class="text-center">
|
||||
<QTd class="text-center" :style="highlightedRow(row)">
|
||||
<QIcon
|
||||
:name="row.isIgnored ? 'check_box' : 'check_box_outline_blank'"
|
||||
style="color: var(--vn-label-color)"
|
||||
|
@ -245,38 +258,38 @@ function getBadgeClass(groupingMode, expectedGrouping) {
|
|||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-warehouse="{ row }">
|
||||
<QTd>
|
||||
<QTd :style="highlightedRow(row)">
|
||||
<span>{{ row.warehouse }}</span>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-date="{ row }">
|
||||
<QTd class="text-center">
|
||||
<QTd class="text-center" :style="highlightedRow(row)">
|
||||
<VnDateBadge :date="row.landed" />
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-entry="{ row }">
|
||||
<QTd @click.stop>
|
||||
<QTd @click.stop :style="highlightedRow(row)">
|
||||
<div class="full-width flex justify-center">
|
||||
<EntryDescriptorProxy :id="row.entryFk" class="q-ma-none" dense />
|
||||
<span class="link">{{ row.entryFk }}</span>
|
||||
</div>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-pvp="{ value }">
|
||||
<QTd @click.stop class="text-center">
|
||||
<template #body-cell-pvp="{ row, value }">
|
||||
<QTd @click.stop class="text-center" :style="highlightedRow(row)">
|
||||
<span> {{ value }}</span>
|
||||
<QTooltip> {{ t('lastEntries.grouping') }}/Packing </QTooltip></QTd
|
||||
>
|
||||
<QTooltip> {{ t('lastEntries.grouping') }}/Packing </QTooltip>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-printedStickers="{ row }">
|
||||
<QTd @click.stop class="text-center">
|
||||
<QTd @click.stop class="text-center" :style="highlightedRow(row)">
|
||||
<span style="color: var(--vn-label-color)">
|
||||
{{ row.printedStickers }}</span
|
||||
>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-packing="{ row }">
|
||||
<QTd @click.stop>
|
||||
<QTd @click.stop :style="highlightedRow(row)">
|
||||
<QBadge
|
||||
class="center-content"
|
||||
:class="getBadgeClass(row.groupingMode, 'packing')"
|
||||
|
@ -288,7 +301,7 @@ function getBadgeClass(groupingMode, expectedGrouping) {
|
|||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-grouping="{ row }">
|
||||
<QTd @click.stop>
|
||||
<QTd @click.stop :style="highlightedRow(row)">
|
||||
<QBadge
|
||||
class="center-content"
|
||||
:class="getBadgeClass(row.groupingMode, 'grouping')"
|
||||
|
@ -300,7 +313,7 @@ function getBadgeClass(groupingMode, expectedGrouping) {
|
|||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-cost="{ row }">
|
||||
<QTd @click.stop class="text-center">
|
||||
<QTd @click.stop class="text-center" :style="highlightedRow(row)">
|
||||
<span>
|
||||
{{ toCurrency(row.cost, 'EUR', 3) }}
|
||||
<QTooltip>
|
||||
|
@ -319,7 +332,7 @@ function getBadgeClass(groupingMode, expectedGrouping) {
|
|||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-supplier="{ row }">
|
||||
<QTd @click.stop>
|
||||
<QTd @click.stop :style="highlightedRow(row)">
|
||||
<div class="full-width flex justify-left">
|
||||
<QBadge
|
||||
:class="
|
||||
|
@ -354,7 +367,6 @@ function getBadgeClass(groupingMode, expectedGrouping) {
|
|||
.th :first-child {
|
||||
.td {
|
||||
text-align: center;
|
||||
background-color: red;
|
||||
}
|
||||
}
|
||||
.accent-badge {
|
||||
|
|
|
@ -87,7 +87,7 @@ const insertTag = (rows) => {
|
|||
tagFk: undefined,
|
||||
}"
|
||||
:default-remove="false"
|
||||
:filter="{
|
||||
:user-filter="{
|
||||
fields: ['id', 'itemFk', 'tagFk', 'value', 'priority'],
|
||||
where: { itemFk: route.params.id },
|
||||
include: {
|
||||
|
@ -119,6 +119,7 @@ const insertTag = (rows) => {
|
|||
"
|
||||
:required="true"
|
||||
:rules="validate('itemTag.tagFk')"
|
||||
:data-cy="`tag${row?.tag?.name}`"
|
||||
/>
|
||||
<VnSelect
|
||||
v-if="row.tag?.isFree === false"
|
||||
|
@ -145,6 +146,7 @@ const insertTag = (rows) => {
|
|||
:label="t('itemTags.value')"
|
||||
:is-clearable="false"
|
||||
@keyup.enter.stop="(data) => itemTagsRef.onSubmit(data)"
|
||||
:data-cy="`tag${row?.tag?.name}Value`"
|
||||
/>
|
||||
<VnInput
|
||||
:label="t('itemBasicData.relevancy')"
|
||||
|
@ -162,6 +164,7 @@ const insertTag = (rows) => {
|
|||
name="delete"
|
||||
size="sm"
|
||||
dense
|
||||
:data-cy="`deleteTag${row?.tag?.name}`"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('itemTags.removeTag') }}
|
||||
|
@ -177,6 +180,7 @@ const insertTag = (rows) => {
|
|||
icon="add"
|
||||
v-shortcut="'+'"
|
||||
fab
|
||||
data-cy="createNewTag"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('itemTags.addTag') }}
|
||||
|
|
|
@ -8,6 +8,7 @@ import VnInput from 'src/components/common/VnInput.vue';
|
|||
import FetchData from 'components/FetchData.vue';
|
||||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
import VnSelectWorker from 'src/components/common/VnSelectWorker.vue';
|
||||
import VnInputDate from 'src/components/common/VnInputDate.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps({
|
||||
|
@ -52,7 +53,7 @@ onMounted(async () => {
|
|||
name: key,
|
||||
value,
|
||||
selectedField: { name: key, label: t(`params.${key}`) },
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
exprBuilder('state', arrayData.store?.userParams?.state);
|
||||
|
@ -157,6 +158,32 @@ onMounted(async () => {
|
|||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate
|
||||
v-model="params.from"
|
||||
:label="t('params.from')"
|
||||
is-outlined
|
||||
/>
|
||||
</QItemSection>
|
||||
<QItemSection>
|
||||
<VnInputDate
|
||||
v-model="params.to"
|
||||
:label="t('params.to')"
|
||||
is-outlined
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
:label="t('params.daysOnward')"
|
||||
v-model="params.daysOnward"
|
||||
lazy-rules
|
||||
is-outlined
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnSelect
|
||||
|
@ -175,11 +202,10 @@ onMounted(async () => {
|
|||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
:label="t('params.daysOnward')"
|
||||
v-model="params.daysOnward"
|
||||
lazy-rules
|
||||
is-outlined
|
||||
<QCheckbox
|
||||
:label="t('params.mine')"
|
||||
v-model="params.mine"
|
||||
:toggle-indeterminate="false"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
|
|
|
@ -6,6 +6,8 @@ import VnLv from 'components/ui/VnLv.vue';
|
|||
import { dashIfEmpty, toDate } from 'src/filters';
|
||||
import RouteDescriptorMenu from 'pages/Route/Card/RouteDescriptorMenu.vue';
|
||||
import filter from './RouteFilter.js';
|
||||
import useCardDescription from 'src/composables/useCardDescription';
|
||||
import axios from 'axios';
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
|
@ -16,7 +18,6 @@ const $props = defineProps({
|
|||
});
|
||||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
const zone = ref();
|
||||
const zoneId = ref();
|
||||
const entityId = computed(() => {
|
||||
|
@ -50,9 +51,9 @@ onMounted(async () => {
|
|||
width="lg-width"
|
||||
>
|
||||
<template #body="{ entity }">
|
||||
<VnLv :label="t('Date')" :value="toDate(entity?.dated)" />
|
||||
<VnLv :label="t('Agency')" :value="entity?.agencyMode?.name" />
|
||||
<VnLv :label="t('Zone')" :value="zone" />
|
||||
<VnLv :label="$t('Date')" :value="toDate(entity?.dated)" />
|
||||
<VnLv :label="$t('Agency')" :value="entity?.agencyMode?.name" />
|
||||
<VnLv :label="$t('Zone')" :value="zone" />
|
||||
<VnLv
|
||||
:label="$t('Volume')"
|
||||
:value="`${dashIfEmpty(entity?.m3)} / ${dashIfEmpty(
|
||||
|
|
|
@ -14,7 +14,6 @@ export default {
|
|||
'started',
|
||||
'finished',
|
||||
'cost',
|
||||
'zoneFk',
|
||||
'isOk',
|
||||
],
|
||||
include: [
|
||||
|
@ -23,7 +22,6 @@ export default {
|
|||
relation: 'vehicle',
|
||||
scope: { fields: ['id', 'm3'] },
|
||||
},
|
||||
{ relation: 'zone', scope: { fields: ['id', 'name'] } },
|
||||
{
|
||||
relation: 'worker',
|
||||
scope: {
|
||||
|
|
|
@ -28,52 +28,6 @@ const defaultInitialData = {
|
|||
isOk: false,
|
||||
};
|
||||
const maxDistance = ref();
|
||||
|
||||
const routeFilter = {
|
||||
fields: [
|
||||
'id',
|
||||
'workerFk',
|
||||
'agencyModeFk',
|
||||
'dated',
|
||||
'm3',
|
||||
'warehouseFk',
|
||||
'description',
|
||||
'vehicleFk',
|
||||
'kmStart',
|
||||
'kmEnd',
|
||||
'started',
|
||||
'finished',
|
||||
'cost',
|
||||
'isOk',
|
||||
],
|
||||
include: [
|
||||
{ relation: 'agencyMode', scope: { fields: ['id', 'name'] } },
|
||||
{
|
||||
relation: 'vehicle',
|
||||
scope: { fields: ['id', 'm3'] },
|
||||
},
|
||||
{
|
||||
relation: 'ticket',
|
||||
scope: {
|
||||
fields: ['id', 'name', 'zoneFk'],
|
||||
include: { relation: 'zone', scope: { fields: ['id', 'name'] } },
|
||||
},
|
||||
},
|
||||
{
|
||||
relation: 'worker',
|
||||
scope: {
|
||||
fields: ['id'],
|
||||
include: {
|
||||
relation: 'user',
|
||||
scope: {
|
||||
fields: ['id'],
|
||||
include: { relation: 'emailUser', scope: { fields: ['email'] } },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
const onSave = (data, response) => {
|
||||
if (isNew) {
|
||||
axios.post(`Routes/${response?.id}/updateWorkCenter`);
|
||||
|
|
|
@ -180,6 +180,7 @@ const onDmsSaved = async (dms, response) => {
|
|||
rows: dmsDialog.value.rowsToCreateInvoiceIn,
|
||||
dms: response.data,
|
||||
});
|
||||
notify(t('Data saved'), 'positive');
|
||||
}
|
||||
dmsDialog.value.show = false;
|
||||
dmsDialog.value.initialForm = null;
|
||||
|
@ -243,7 +244,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
|
|||
</template>
|
||||
<template #column-invoiceInFk="{ row }">
|
||||
<span class="link" @click.stop>
|
||||
{{ row.invoiceInFk }}
|
||||
{{ row.supplierRef }}
|
||||
<InvoiceInDescriptorProxy v-if="row.invoiceInFk" :id="row.invoiceInFk" />
|
||||
</span>
|
||||
</template>
|
||||
|
|
|
@ -3,7 +3,7 @@ import { computed, ref } from 'vue';
|
|||
import { useI18n } from 'vue-i18n';
|
||||
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { dashIfEmpty, toDate, toHour } from 'src/filters';
|
||||
import { toDate, toHour } from 'src/filters';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { usePrintService } from 'src/composables/usePrintService';
|
||||
|
||||
|
@ -280,7 +280,7 @@ const openTicketsDialog = (id) => {
|
|||
</QCardSection>
|
||||
<QCardSection class="q-pt-none">
|
||||
<VnInputDate
|
||||
:label="t('route.Stating date')"
|
||||
:label="t('route.Starting date')"
|
||||
v-model="startingDate"
|
||||
autofocus
|
||||
/>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { computed, ref, markRaw } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
||||
import { toHour } from 'src/filters';
|
||||
|
@ -8,6 +8,7 @@ import RouteFilter from 'pages/Route/Card/RouteFilter.vue';
|
|||
import VnTable from 'components/VnTable/VnTable.vue';
|
||||
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
|
||||
import VnSection from 'src/components/common/VnSection.vue';
|
||||
import VnSelectWorker from 'src/components/common/VnSelectWorker.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const { viewSummary } = useSummaryDialog();
|
||||
|
@ -38,17 +39,7 @@ const columns = computed(() => [
|
|||
align: 'left',
|
||||
name: 'workerFk',
|
||||
label: t('route.Worker'),
|
||||
component: 'select',
|
||||
attrs: {
|
||||
url: 'Workers/activeWithInheritedRole',
|
||||
fields: ['id', 'name'],
|
||||
useLike: false,
|
||||
optionFilter: 'firstName',
|
||||
find: {
|
||||
value: 'workerFk',
|
||||
label: 'workerUserName',
|
||||
},
|
||||
},
|
||||
component: markRaw(VnSelectWorker),
|
||||
create: true,
|
||||
cardVisible: true,
|
||||
format: (row, dashIfEmpty) => dashIfEmpty(row.travelRef),
|
||||
|
@ -59,6 +50,10 @@ const columns = computed(() => [
|
|||
name: 'agencyName',
|
||||
label: t('route.Agency'),
|
||||
cardVisible: true,
|
||||
},
|
||||
{
|
||||
label: t('route.Agency'),
|
||||
name: 'agencyModeFk',
|
||||
component: 'select',
|
||||
attrs: {
|
||||
url: 'agencyModes',
|
||||
|
@ -69,14 +64,19 @@ const columns = computed(() => [
|
|||
},
|
||||
},
|
||||
create: true,
|
||||
columnClass: 'expand',
|
||||
columnFilter: false,
|
||||
visible: false,
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'vehiclePlateNumber',
|
||||
label: t('route.Vehicle'),
|
||||
cardVisible: true,
|
||||
},
|
||||
{
|
||||
name: 'vehicleFk',
|
||||
label: t('route.Vehicle'),
|
||||
cardVisible: true,
|
||||
component: 'select',
|
||||
attrs: {
|
||||
url: 'vehicles',
|
||||
|
@ -90,6 +90,7 @@ const columns = computed(() => [
|
|||
},
|
||||
create: true,
|
||||
columnFilter: false,
|
||||
visible: false,
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
|
@ -156,6 +157,7 @@ const columns = computed(() => [
|
|||
<VnTable
|
||||
:data-key
|
||||
:columns="columns"
|
||||
ref="tableRef"
|
||||
:right-search="false"
|
||||
redirect="route"
|
||||
:create="{
|
||||
|
|
|
@ -45,8 +45,6 @@ const filter = {
|
|||
:label="t('parking.sector')"
|
||||
:value="entity.sector?.description"
|
||||
/>
|
||||
<VnLv :label="t('parking.row')" :value="entity.row" />
|
||||
<VnLv :label="t('parking.column')" :value="entity.column" />
|
||||
</QCard>
|
||||
</template>
|
||||
</CardSummary>
|
||||
|
|
|
@ -1,19 +1,15 @@
|
|||
<script setup>
|
||||
import { onMounted, onUnmounted } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { computed, onMounted, onUnmounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
||||
import VnPaginate from 'components/ui/VnPaginate.vue';
|
||||
import CardList from 'components/ui/CardList.vue';
|
||||
import VnLv from 'components/ui/VnLv.vue';
|
||||
import ParkingFilter from './ParkingFilter.vue';
|
||||
import ParkingSummary from './Card/ParkingSummary.vue';
|
||||
import exprBuilder from './ParkingExprBuilder.js';
|
||||
import VnTable from 'components/VnTable/VnTable.vue';
|
||||
import VnSection from 'src/components/common/VnSection.vue';
|
||||
import ParkingFilter from './ParkingFilter.vue';
|
||||
import exprBuilder from './ParkingExprBuilder.js';
|
||||
import ParkingSummary from './Card/ParkingSummary.vue';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
const { push } = useRouter();
|
||||
const { t } = useI18n();
|
||||
const { viewSummary } = useSummaryDialog();
|
||||
const dataKey = 'ParkingList';
|
||||
|
@ -24,7 +20,48 @@ onUnmounted(() => (stateStore.rightDrawer = false));
|
|||
const filter = {
|
||||
fields: ['id', 'sectorFk', 'code', 'pickingOrder'],
|
||||
};
|
||||
|
||||
const columns = computed(() => [
|
||||
{
|
||||
align: 'left',
|
||||
name: 'code',
|
||||
label: t('globals.code'),
|
||||
isId: true,
|
||||
isTitle: true,
|
||||
columnFilter: false,
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'sector',
|
||||
label: t('parking.sector'),
|
||||
format: (val) => val.sector.description ?? '',
|
||||
sortable: true,
|
||||
cardVisible: true,
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'pickingOrder',
|
||||
label: t('parking.pickingOrder'),
|
||||
sortable: true,
|
||||
cardVisible: true,
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
label: '',
|
||||
name: 'tableActions',
|
||||
actions: [
|
||||
{
|
||||
title: t('components.smartCard.viewSummary'),
|
||||
icon: 'preview',
|
||||
action: (row) => viewSummary(row.id, ParkingSummary),
|
||||
isPrimary: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VnSection
|
||||
:data-key="dataKey"
|
||||
|
@ -40,41 +77,24 @@ const filter = {
|
|||
<ParkingFilter data-key="ParkingList" />
|
||||
</template>
|
||||
<template #body>
|
||||
<QPage class="column items-center q-pa-md">
|
||||
<div class="vn-card-list">
|
||||
<VnPaginate :data-key="dataKey">
|
||||
<template #body="{ rows }">
|
||||
<CardList
|
||||
v-for="row of rows"
|
||||
:key="row.id"
|
||||
:id="row.id"
|
||||
:title="row.code"
|
||||
@click="
|
||||
push({ path: `/shelving/parking/${row.id}/summary` })
|
||||
"
|
||||
>
|
||||
<template #list-items>
|
||||
<VnLv
|
||||
label="Sector"
|
||||
:value="row.sector?.description"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('parking.pickingOrder')"
|
||||
:value="row.pickingOrder"
|
||||
/>
|
||||
</template>
|
||||
<template #actions>
|
||||
<QBtn
|
||||
:label="t('components.smartCard.openSummary')"
|
||||
@click.stop="viewSummary(row.id, ParkingSummary)"
|
||||
color="primary"
|
||||
/>
|
||||
</template>
|
||||
</CardList>
|
||||
</template>
|
||||
</VnPaginate>
|
||||
</div>
|
||||
</QPage>
|
||||
<VnTable
|
||||
:data-key="dataKey"
|
||||
:columns="columns"
|
||||
is-editable="false"
|
||||
:right-search="false"
|
||||
:use-model="true"
|
||||
:disable-option="{ table: true }"
|
||||
redirect="shelving/parking"
|
||||
default-mode="card"
|
||||
>
|
||||
<template #actions="{ row }">
|
||||
<QBtn
|
||||
:label="t('components.smartCard.openSummary')"
|
||||
@click.stop="viewSummary(row.id, ParkingSummary)"
|
||||
color="primary"
|
||||
/>
|
||||
</template>
|
||||
</VnTable>
|
||||
</template>
|
||||
</VnSection>
|
||||
</template>
|
||||
|
|
|
@ -6,7 +6,10 @@ import VnSection from 'src/components/common/VnSection.vue';
|
|||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import FetchData from 'src/components/FetchData.vue';
|
||||
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
|
||||
import SupplierSummary from './Card/SupplierSummary.vue';
|
||||
|
||||
const { viewSummary } = useSummaryDialog();
|
||||
const { t } = useI18n();
|
||||
const tableRef = ref();
|
||||
const dataKey = 'SupplierList';
|
||||
|
@ -103,6 +106,19 @@ const columns = computed(() => [
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
label: '',
|
||||
name: 'tableActions',
|
||||
actions: [
|
||||
{
|
||||
title: t('components.smartCard.viewSummary'),
|
||||
icon: 'preview',
|
||||
isPrimary: true,
|
||||
action: (row) => viewSummary(row.id, SupplierSummary, 'md-width'),
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
<template>
|
||||
|
|
|
@ -1,64 +1,78 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import axios from 'axios';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { toCurrency } from 'src/filters';
|
||||
import VnUsesMana from 'components/ui/VnUsesMana.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
mana: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
newPrice: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
usesMana: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
manaCode: {
|
||||
type: String,
|
||||
default: 'mana',
|
||||
sale: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
const route = useRoute();
|
||||
const mana = ref(null);
|
||||
const usesMana = ref(false);
|
||||
|
||||
const emit = defineEmits(['save', 'cancel']);
|
||||
|
||||
const { t } = useI18n();
|
||||
const QPopupProxyRef = ref(null);
|
||||
const manaCode = ref($props.manaCode);
|
||||
|
||||
const save = () => {
|
||||
emit('save');
|
||||
const save = (sale = $props.sale) => {
|
||||
emit('save', sale);
|
||||
QPopupProxyRef.value.hide();
|
||||
};
|
||||
|
||||
const getMana = async () => {
|
||||
const { data } = await axios.get(`Tickets/${route.params.id}/getSalesPersonMana`);
|
||||
mana.value = data;
|
||||
await getUsesMana();
|
||||
};
|
||||
|
||||
const getUsesMana = async () => {
|
||||
const { data } = await axios.get('Sales/usesMana');
|
||||
usesMana.value = data;
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
emit('cancel');
|
||||
QPopupProxyRef.value.hide();
|
||||
};
|
||||
const hasMana = computed(() => typeof mana.value === 'number');
|
||||
defineExpose({ save });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QPopupProxy ref="QPopupProxyRef">
|
||||
<QPopupProxy
|
||||
ref="QPopupProxyRef"
|
||||
@before-show="getMana"
|
||||
data-cy="ticketEditManaProxy"
|
||||
>
|
||||
<div class="container">
|
||||
<QSpinner v-if="!mana" color="primary" size="md" />
|
||||
<div v-else>
|
||||
<div class="header">Mana: {{ toCurrency(mana) }}</div>
|
||||
<div class="q-pa-md">
|
||||
<slot />
|
||||
<div v-if="newPrice" class="column items-center q-mt-lg">
|
||||
<span class="text-primary">{{ t('New price') }}</span>
|
||||
<span class="text-subtitle1">
|
||||
{{ toCurrency($props.newPrice) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="header">Mana: {{ toCurrency(mana) }}</div>
|
||||
<QSpinner v-if="!hasMana" color="primary" size="md" />
|
||||
<div class="q-pa-md" v-else>
|
||||
<slot :popup="QPopupProxyRef" />
|
||||
<div v-if="usesMana" class="column q-gutter-y-sm q-mt-sm">
|
||||
<VnUsesMana :mana-code="manaCode" />
|
||||
</div>
|
||||
<div v-if="newPrice" class="column items-center q-mt-lg">
|
||||
<span class="text-primary">{{ t('New price') }}</span>
|
||||
<span class="text-subtitle1">
|
||||
{{ toCurrency($props.newPrice) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="usesMana" class="column q-gutter-y-sm q-mt-sm">
|
||||
<VnUsesMana :mana-code="manaCode" />
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<QBtn
|
||||
color="primary"
|
||||
|
|
|
@ -22,7 +22,6 @@ import { useVnConfirm } from 'composables/useVnConfirm';
|
|||
import useNotify from 'src/composables/useNotify.js';
|
||||
import axios from 'axios';
|
||||
import VnTable from 'src/components/VnTable/VnTable.vue';
|
||||
import VnUsesMana from 'src/components/ui/VnUsesMana.vue';
|
||||
import VnConfirm from 'src/components/ui/VnConfirm.vue';
|
||||
import TicketProblems from 'src/components/TicketProblems.vue';
|
||||
import RightMenu from 'src/components/common/RightMenu.vue';
|
||||
|
@ -33,6 +32,7 @@ const { t } = useI18n();
|
|||
const { notify } = useNotify();
|
||||
const { openConfirmationModal } = useVnConfirm();
|
||||
const editPriceProxyRef = ref(null);
|
||||
const editManaProxyRef = ref(null);
|
||||
const stateBtnDropdownRef = ref(null);
|
||||
const quasar = useQuasar();
|
||||
const arrayData = useArrayData('Ticket');
|
||||
|
@ -45,7 +45,6 @@ const isTicketEditable = ref(false);
|
|||
const sales = ref([]);
|
||||
const editableStatesOptions = ref([]);
|
||||
const selectedSales = ref([]);
|
||||
const mana = ref(null);
|
||||
const manaCode = ref('mana');
|
||||
const ticketState = computed(() => store.data?.ticketState?.state?.code);
|
||||
const transfer = ref({
|
||||
|
@ -53,7 +52,6 @@ const transfer = ref({
|
|||
sales: [],
|
||||
});
|
||||
const tableRef = ref([]);
|
||||
const canProceed = ref();
|
||||
|
||||
watch(
|
||||
() => route.params.id,
|
||||
|
@ -133,7 +131,6 @@ const columns = computed(() => [
|
|||
align: 'left',
|
||||
label: t('globals.amount'),
|
||||
name: 'amount',
|
||||
format: (row) => parseInt(row.amount * row.quantity),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
|
@ -177,27 +174,36 @@ const getSaleTotal = (sale) => {
|
|||
return price - discount;
|
||||
};
|
||||
|
||||
const getRowUpdateInputEvents = (sale) => ({
|
||||
'keyup.enter': () => {
|
||||
changeQuantity(sale);
|
||||
},
|
||||
blur: () => {
|
||||
changeQuantity(sale);
|
||||
},
|
||||
});
|
||||
|
||||
const resetChanges = async () => {
|
||||
arrayData.fetch({ append: false });
|
||||
tableRef.value.reload();
|
||||
};
|
||||
const rowToUpdate = ref(null);
|
||||
const changeQuantity = async (sale) => {
|
||||
canProceed.value = await isSalePrepared(sale);
|
||||
if (!canProceed.value) return;
|
||||
if (
|
||||
!sale.itemFk ||
|
||||
sale.quantity == null ||
|
||||
edit.value?.oldQuantity === sale.quantity
|
||||
)
|
||||
if (!sale.itemFk || sale.quantity == null || sale?.originalQuantity === sale.quantity)
|
||||
return;
|
||||
if (!sale.id) return addSale(sale);
|
||||
|
||||
if (await isSalePrepared(sale)) {
|
||||
await confirmUpdate(() => updateQuantity(sale));
|
||||
} else await updateQuantity(sale);
|
||||
};
|
||||
|
||||
const updateQuantity = async (sale) => {
|
||||
try {
|
||||
if (!rowToUpdate.value) return;
|
||||
rowToUpdate.value = null;
|
||||
let { quantity, id } = sale;
|
||||
sale.isNew = false;
|
||||
await updateQuantity(sale);
|
||||
await axios.post(`Sales/${id}/updateQuantity`, { quantity });
|
||||
notify('globals.dataSaved', 'positive');
|
||||
resetChanges();
|
||||
} catch (e) {
|
||||
const { quantity } = tableRef.value.CrudModelRef.originalData.find(
|
||||
(s) => s.id === sale.id,
|
||||
|
@ -207,12 +213,6 @@ const changeQuantity = async (sale) => {
|
|||
}
|
||||
};
|
||||
|
||||
const updateQuantity = async ({ quantity, id }) => {
|
||||
const params = { quantity: quantity };
|
||||
await axios.post(`Sales/${id}/updateQuantity`, params);
|
||||
notify('globals.dataSaved', 'positive');
|
||||
};
|
||||
|
||||
const addSale = async (sale) => {
|
||||
const params = {
|
||||
barcode: sale.itemFk,
|
||||
|
@ -237,13 +237,17 @@ const addSale = async (sale) => {
|
|||
sale.isNew = false;
|
||||
arrayData.fetch({});
|
||||
};
|
||||
const changeConcept = async (sale) => {
|
||||
if (await isSalePrepared(sale)) {
|
||||
await confirmUpdate(() => updateConcept(sale));
|
||||
} else await updateConcept(sale);
|
||||
};
|
||||
|
||||
const updateConcept = async (sale) => {
|
||||
canProceed.value = await isSalePrepared(sale);
|
||||
if (!canProceed.value) return;
|
||||
const data = { newConcept: sale.concept };
|
||||
await axios.post(`Sales/${sale.id}/updateConcept`, data);
|
||||
notify('globals.dataSaved', 'positive');
|
||||
resetChanges();
|
||||
};
|
||||
|
||||
const DEFAULT_EDIT = {
|
||||
|
@ -254,18 +258,6 @@ const DEFAULT_EDIT = {
|
|||
oldQuantity: null,
|
||||
};
|
||||
const edit = ref({ ...DEFAULT_EDIT });
|
||||
const usesMana = ref(null);
|
||||
|
||||
const getUsesMana = async () => {
|
||||
const { data } = await axios.get('Sales/usesMana');
|
||||
usesMana.value = data;
|
||||
};
|
||||
|
||||
const getMana = async () => {
|
||||
const { data } = await axios.get(`Tickets/${route.params.id}/getSalesPersonMana`);
|
||||
mana.value = data;
|
||||
await getUsesMana();
|
||||
};
|
||||
|
||||
const selectedValidSales = computed(() => {
|
||||
if (!sales.value) return;
|
||||
|
@ -273,7 +265,6 @@ const selectedValidSales = computed(() => {
|
|||
});
|
||||
|
||||
const onOpenEditPricePopover = async (sale) => {
|
||||
await getMana();
|
||||
edit.value = {
|
||||
sale: JSON.parse(JSON.stringify(sale)),
|
||||
price: sale.price,
|
||||
|
@ -281,7 +272,6 @@ const onOpenEditPricePopover = async (sale) => {
|
|||
};
|
||||
|
||||
const onOpenEditDiscountPopover = async (sale) => {
|
||||
await getMana();
|
||||
if (isLocked.value) return;
|
||||
if (sale) {
|
||||
edit.value = {
|
||||
|
@ -295,33 +285,42 @@ const onOpenEditDiscountPopover = async (sale) => {
|
|||
};
|
||||
}
|
||||
};
|
||||
|
||||
const updatePrice = async (sale) => {
|
||||
canProceed.value = await isSalePrepared(sale);
|
||||
if (!canProceed.value) return;
|
||||
const changePrice = async (sale) => {
|
||||
const newPrice = edit.value.price;
|
||||
if (newPrice != null && newPrice != sale.price) {
|
||||
await axios.post(`Sales/${sale.id}/updatePrice`, { newPrice });
|
||||
sale.price = newPrice;
|
||||
edit.value = { ...DEFAULT_EDIT };
|
||||
notify('globals.dataSaved', 'positive');
|
||||
if (await isSalePrepared(sale)) {
|
||||
await confirmUpdate(() => updatePrice(sale, newPrice));
|
||||
} else updatePrice(sale, newPrice);
|
||||
}
|
||||
|
||||
await getMana();
|
||||
};
|
||||
const updatePrice = async (sale, newPrice) => {
|
||||
await axios.post(`Sales/${sale.id}/updatePrice`, { newPrice });
|
||||
sale.price = newPrice;
|
||||
edit.value = { ...DEFAULT_EDIT };
|
||||
notify('globals.dataSaved', 'positive');
|
||||
resetChanges();
|
||||
};
|
||||
|
||||
const changeDiscount = async (sale) => {
|
||||
canProceed.value = await isSalePrepared(sale);
|
||||
if (!canProceed.value) return;
|
||||
const newDiscount = edit.value.discount;
|
||||
if (newDiscount != null && newDiscount != sale.discount) updateDiscount([sale]);
|
||||
if (newDiscount != null && newDiscount != sale.discount) {
|
||||
if (await isSalePrepared(sale))
|
||||
await confirmUpdate(() => updateDiscount([sale], newDiscount));
|
||||
else await updateDiscount([sale], newDiscount);
|
||||
}
|
||||
};
|
||||
|
||||
const updateDiscounts = async (sales, newDiscount = null) => {
|
||||
const salesTracking = await fetchSalesTracking();
|
||||
|
||||
const someSaleIsPrepared = salesTracking.some((sale) =>
|
||||
matchSale(salesTracking, sale),
|
||||
);
|
||||
if (someSaleIsPrepared) await confirmUpdate(() => updateDiscount(sales, newDiscount));
|
||||
else updateDiscount(sales, newDiscount);
|
||||
};
|
||||
|
||||
const updateDiscount = async (sales, newDiscount = null) => {
|
||||
for (const sale of sales) {
|
||||
const canProceed = await isSalePrepared(sale);
|
||||
if (!canProceed) return;
|
||||
}
|
||||
const saleIds = sales.map((sale) => sale.id);
|
||||
const _newDiscount = newDiscount || edit.value.discount;
|
||||
const params = {
|
||||
|
@ -331,8 +330,7 @@ const updateDiscount = async (sales, newDiscount = null) => {
|
|||
};
|
||||
await axios.post(`Tickets/${route.params.id}/updateDiscount`, params);
|
||||
notify('globals.dataSaved', 'positive');
|
||||
for (let sale of sales) sale.discount = _newDiscount;
|
||||
edit.value = { ...DEFAULT_EDIT };
|
||||
resetChanges();
|
||||
};
|
||||
|
||||
const getNewPrice = computed(() => {
|
||||
|
@ -400,7 +398,7 @@ const removeSales = async () => {
|
|||
await axios.post('Sales/deleteSales', params);
|
||||
removeSelectedSales();
|
||||
notify('globals.dataSaved', 'positive');
|
||||
window.location.reload();
|
||||
resetChanges();
|
||||
};
|
||||
|
||||
const setTransferParams = async () => {
|
||||
|
@ -425,9 +423,13 @@ onMounted(async () => {
|
|||
const items = ref([]);
|
||||
const newRow = ref({});
|
||||
|
||||
const changeItem = async (sale) => {
|
||||
if (await isSalePrepared(sale)) {
|
||||
await confirmUpdate(() => updateItem(sale));
|
||||
} else await updateItem(sale);
|
||||
};
|
||||
|
||||
const updateItem = async (row) => {
|
||||
canProceed.value = await isSalePrepared(row);
|
||||
if (!canProceed.value) return;
|
||||
const selectedItem = items.value.find((item) => item.id === row.itemFk);
|
||||
if (selectedItem) {
|
||||
row.item = selectedItem;
|
||||
|
@ -471,7 +473,18 @@ const endNewRow = (row) => {
|
|||
}
|
||||
};
|
||||
|
||||
async function isSalePrepared(item) {
|
||||
async function confirmUpdate(cb) {
|
||||
await quasar
|
||||
.dialog({
|
||||
component: VnConfirm,
|
||||
componentProps: {
|
||||
title: t('Item prepared'),
|
||||
message: t('This item is already prepared. Do you want to continue?'),
|
||||
},
|
||||
})
|
||||
.onOk(cb);
|
||||
}
|
||||
async function fetchSalesTracking() {
|
||||
const filter = {
|
||||
params: {
|
||||
where: { ticketFk: route.params.id },
|
||||
|
@ -483,48 +496,37 @@ async function isSalePrepared(item) {
|
|||
filter: JSON.stringify(filter),
|
||||
},
|
||||
});
|
||||
|
||||
const matchingSale = data.find((sale) => sale.itemFk === item.itemFk);
|
||||
if (!matchingSale) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
matchingSale.hasSaleGroupDetail ||
|
||||
matchingSale.isControled ||
|
||||
matchingSale.isPrepared ||
|
||||
matchingSale.isPrevious ||
|
||||
matchingSale.isPreviousSelected
|
||||
) {
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
quasar
|
||||
.dialog({
|
||||
component: VnConfirm,
|
||||
componentProps: {
|
||||
title: t('Item prepared'),
|
||||
message: t(
|
||||
'This item is already prepared. Do you want to continue?',
|
||||
),
|
||||
data: item,
|
||||
},
|
||||
})
|
||||
.onOk(() => resolve(true))
|
||||
.onCancel(() => reject(new Error('cancelled')));
|
||||
});
|
||||
} catch (error) {
|
||||
tableRef.value.reload();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return data;
|
||||
}
|
||||
|
||||
async function isSalePrepared(sale) {
|
||||
const data = await fetchSalesTracking();
|
||||
return matchSale(data, sale);
|
||||
}
|
||||
function matchSale(data, sale) {
|
||||
const matchingSale = data.find(({ itemFk }) => itemFk === sale.itemFk);
|
||||
|
||||
if (!matchingSale) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isPrepared(matchingSale);
|
||||
}
|
||||
function isPrepared(sale) {
|
||||
const flagsToCheck = [
|
||||
'hasSaleGroupDetail',
|
||||
'isControled',
|
||||
'isPrepared',
|
||||
'isPrevious',
|
||||
'isPreviousSelected',
|
||||
];
|
||||
return flagsToCheck.some((flag) => sale[flag] === 1);
|
||||
}
|
||||
watch(
|
||||
() => newRow.value.itemFk,
|
||||
(newItemFk) => {
|
||||
if (newItemFk) {
|
||||
updateItem(newRow.value);
|
||||
changeItem(newRow.value);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -582,10 +584,8 @@ watch(
|
|||
:is-ticket-editable="isTicketEditable"
|
||||
:sales="selectedValidSales"
|
||||
:disable="!hasSelectedRows"
|
||||
:mana="mana"
|
||||
:ticket-config="ticketConfig"
|
||||
@get-mana="getMana()"
|
||||
@update-discounts="updateDiscount"
|
||||
@update-discounts="updateDiscounts"
|
||||
@refresh-table="resetChanges"
|
||||
/>
|
||||
<QBtn
|
||||
|
@ -716,7 +716,7 @@ watch(
|
|||
option-value="id"
|
||||
v-model="row.itemFk"
|
||||
:use-like="false"
|
||||
@update:model-value="updateItem(row)"
|
||||
@update:model-value="changeItem(row)"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
|
@ -742,17 +742,20 @@ watch(
|
|||
</div>
|
||||
<FetchedTags :item="row" :max-length="6" />
|
||||
<QPopupProxy v-if="row.id && isTicketEditable">
|
||||
<VnInput v-model="row.concept" @change="updateConcept(row)" />
|
||||
<VnInput
|
||||
v-model="row.concept"
|
||||
@keyup.enter.stop="changeConcept(row)"
|
||||
:hint="t('globals.enterToConfirm')"
|
||||
/>
|
||||
</QPopupProxy>
|
||||
</template>
|
||||
<template #column-quantity="{ row }">
|
||||
<VnInput
|
||||
data-cy="ticketSaleQuantityInput"
|
||||
v-if="row.isNew || isTicketEditable"
|
||||
type="number"
|
||||
v-model.number="row.quantity"
|
||||
@blur="changeQuantity(row)"
|
||||
@keyup.enter="changeQuantity(row)"
|
||||
@update:model-value="() => (rowToUpdate = row)"
|
||||
v-on="getRowUpdateInputEvents(row)"
|
||||
@focus="edit.oldQuantity = row.quantity"
|
||||
/>
|
||||
<span v-else>{{ row.quantity }}</span>
|
||||
|
@ -764,11 +767,12 @@ watch(
|
|||
</QBtn>
|
||||
<TicketEditManaProxy
|
||||
ref="editPriceProxyRef"
|
||||
:mana="mana"
|
||||
:sale="row"
|
||||
:new-price="getNewPrice"
|
||||
@save="updatePrice(row)"
|
||||
@save="changePrice"
|
||||
>
|
||||
<VnInput
|
||||
@keyup.enter.stop="() => editManaProxyRef.save(row)"
|
||||
v-model.number="edit.price"
|
||||
:label="t('basicData.price')"
|
||||
type="number"
|
||||
|
@ -782,27 +786,27 @@ watch(
|
|||
<QBtn flat class="link" dense @click="onOpenEditDiscountPopover(row)">
|
||||
{{ toPercentage(row.discount / 100) }}
|
||||
</QBtn>
|
||||
|
||||
<TicketEditManaProxy
|
||||
:mana="mana"
|
||||
ref="editManaProxyRef"
|
||||
:sale="row"
|
||||
:new-price="getNewPrice"
|
||||
:uses-mana="usesMana"
|
||||
:mana-code="manaCode"
|
||||
@save="changeDiscount(row)"
|
||||
@save="changeDiscount"
|
||||
>
|
||||
<VnInput
|
||||
autofocus
|
||||
@keyup.enter.stop="() => editManaProxyRef.save(row)"
|
||||
v-model.number="edit.discount"
|
||||
:label="t('ticketSale.discount')"
|
||||
type="number"
|
||||
/>
|
||||
<div v-if="usesMana" class="column q-gutter-y-sm q-mt-sm">
|
||||
<VnUsesMana :mana-code="manaCode" />
|
||||
</div>
|
||||
</TicketEditManaProxy>
|
||||
</template>
|
||||
<span v-else>{{ toPercentage(row.discount / 100) }}</span>
|
||||
</template>
|
||||
<template #column-amount="{ row }">
|
||||
{{ toCurrency(row.quantity * row.price) }}
|
||||
{{ toCurrency(getSaleTotal(row)) }}
|
||||
</template>
|
||||
</VnTable>
|
||||
|
||||
|
|
|
@ -34,10 +34,6 @@ const props = defineProps({
|
|||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
mana: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
ticketConfig: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
|
@ -50,6 +46,7 @@ const { dialog } = useQuasar();
|
|||
const { notify } = useNotify();
|
||||
const acl = useAcl();
|
||||
const btnDropdownRef = ref(null);
|
||||
const editManaProxyRef = ref(null);
|
||||
const { openConfirmationModal } = useVnConfirm();
|
||||
|
||||
const newDiscount = ref(null);
|
||||
|
@ -131,13 +128,13 @@ const createClaim = () => {
|
|||
openConfirmationModal(
|
||||
t('Claim out of time'),
|
||||
t('Do you want to continue?'),
|
||||
onCreateClaimAccepted
|
||||
onCreateClaimAccepted,
|
||||
);
|
||||
else
|
||||
openConfirmationModal(
|
||||
t('Do you want to create a claim?'),
|
||||
false,
|
||||
onCreateClaimAccepted
|
||||
onCreateClaimAccepted,
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -216,8 +213,14 @@ const createRefund = async (withWarehouse) => {
|
|||
<QItemSection>
|
||||
<QItemLabel>{{ t('Update discount') }}</QItemLabel>
|
||||
</QItemSection>
|
||||
<TicketEditManaProxy :mana="props.mana" @save="changeMultipleDiscount()">
|
||||
<TicketEditManaProxy
|
||||
ref="editManaProxyRef"
|
||||
:sale="row"
|
||||
@save="changeMultipleDiscount"
|
||||
>
|
||||
<VnInput
|
||||
autofocus
|
||||
@keyup.enter.stop="() => editManaProxyRef.save(row)"
|
||||
v-model.number="newDiscount"
|
||||
:label="t('ticketSale.discount')"
|
||||
type="number"
|
||||
|
|
|
@ -31,7 +31,7 @@ const oldQuantity = ref(null);
|
|||
|
||||
watch(
|
||||
() => route.params.id,
|
||||
async () => nextTick(async () => await saleTrackingFetchDataRef.value.fetch())
|
||||
async () => nextTick(async () => await saleTrackingFetchDataRef.value.fetch()),
|
||||
);
|
||||
|
||||
const columns = computed(() => [
|
||||
|
@ -212,7 +212,7 @@ const updateShelving = async (sale) => {
|
|||
|
||||
const { data: patchResponseData } = await axios.patch(
|
||||
`ItemShelvings/${sale.itemShelvingFk}`,
|
||||
params
|
||||
params,
|
||||
);
|
||||
const filter = {
|
||||
fields: ['parkingFk'],
|
||||
|
@ -385,7 +385,7 @@ const qCheckBoxController = (sale, action) => {
|
|||
</template>
|
||||
<template #body-cell-parking="{ row }">
|
||||
<QTd style="width: 10%">
|
||||
{{ dashIfEmpty(row.parkingFk) }}
|
||||
{{ dashIfEmpty(row.parkingCode) }}
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-actions="{ row }">
|
||||
|
|
|
@ -46,6 +46,15 @@ const descriptorData = useArrayData('Ticket');
|
|||
onMounted(async () => {
|
||||
ticketUrl.value = (await getUrl('ticket/')) + entityId.value + '/';
|
||||
});
|
||||
const formattedAddress = computed(() => {
|
||||
if (!ticket.value) return '';
|
||||
|
||||
const address = ticket.value.address;
|
||||
const postcode = address.postalCode;
|
||||
const province = address.province ? `(${address.province.name})` : '';
|
||||
|
||||
return `${address.street} - ${postcode} - ${address.city} ${province}`;
|
||||
});
|
||||
|
||||
function isEditable() {
|
||||
try {
|
||||
|
@ -238,7 +247,7 @@ onMounted(async () => {
|
|||
/>
|
||||
<VnLv
|
||||
:label="t('ticket.summary.consigneeStreet')"
|
||||
:value="entity.address?.street"
|
||||
:value="formattedAddress"
|
||||
/>
|
||||
</QCard>
|
||||
<QCard class="vn-one" v-if="entity.notes.length">
|
||||
|
|
|
@ -293,6 +293,7 @@ en:
|
|||
clientFk: Customer
|
||||
orderFk: Order
|
||||
from: From
|
||||
shipped: Shipped
|
||||
to: To
|
||||
salesPersonFk: Salesperson
|
||||
stateFk: State
|
||||
|
@ -320,6 +321,7 @@ es:
|
|||
clientFk: Cliente
|
||||
orderFk: Pedido
|
||||
from: Desde
|
||||
shipped: F. envío
|
||||
to: Hasta
|
||||
salesPersonFk: Comercial
|
||||
stateFk: Estado
|
||||
|
|
|
@ -16,6 +16,7 @@ import useNotify from 'src/composables/useNotify.js';
|
|||
import { useState } from 'src/composables/useState';
|
||||
import { toDateTimeFormat } from 'src/filters/date.js';
|
||||
import axios from 'axios';
|
||||
import TicketProblems from 'src/components/TicketProblems.vue';
|
||||
|
||||
const state = useState();
|
||||
const { t } = useI18n();
|
||||
|
@ -286,71 +287,7 @@ watch(
|
|||
</span>
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
<QIcon
|
||||
v-if="row.isTaxDataChecked === 0"
|
||||
color="primary"
|
||||
name="vn:no036"
|
||||
size="xs"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('futureTickets.noVerified') }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
<QIcon
|
||||
v-if="row.hasTicketRequest"
|
||||
color="primary"
|
||||
name="vn:buyrequest"
|
||||
size="xs"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('futureTickets.purchaseRequest') }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
<QIcon
|
||||
v-if="row.itemShortage"
|
||||
color="primary"
|
||||
name="vn:unavailable"
|
||||
size="xs"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('ticketSale.noVisible') }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
<QIcon
|
||||
v-if="row.isFreezed"
|
||||
color="primary"
|
||||
name="vn:frozen"
|
||||
size="xs"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('futureTickets.clientFrozen') }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
<QIcon v-if="row.risk" color="primary" name="vn:risk" size="xs">
|
||||
<QTooltip>
|
||||
{{ t('futureTickets.risk') }}: {{ row.risk }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
<QIcon
|
||||
v-if="row.hasComponentLack"
|
||||
color="primary"
|
||||
name="vn:components"
|
||||
size="xs"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('futureTickets.componentLack') }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
<QIcon
|
||||
v-if="row.hasRounding"
|
||||
color="primary"
|
||||
name="sync_problem"
|
||||
size="xs"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('futureTickets.rounding') }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
<TicketProblems :row />
|
||||
</span>
|
||||
</template>
|
||||
<template #column-id="{ row }">
|
||||
|
|
|
@ -108,13 +108,11 @@ const columns = computed(() => [
|
|||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'shippedDate',
|
||||
name: 'shipped',
|
||||
cardVisible: true,
|
||||
label: t('ticketList.shipped'),
|
||||
columnFilter: {
|
||||
component: 'date',
|
||||
alias: 't',
|
||||
inWhere: true,
|
||||
},
|
||||
format: ({ shippedDate }) => toDate(shippedDate),
|
||||
},
|
||||
|
@ -216,6 +214,7 @@ const columns = computed(() => [
|
|||
{
|
||||
title: t('components.smartCard.viewSummary'),
|
||||
icon: 'preview',
|
||||
isPrimary: true,
|
||||
action: (row, evt) => {
|
||||
if (evt && evt.ctrlKey) {
|
||||
const url = router.resolve({
|
||||
|
@ -253,7 +252,7 @@ const fetchAvailableAgencies = async (formData) => {
|
|||
|
||||
const { options, agency } = response;
|
||||
if (options) agenciesOptions.value = options;
|
||||
if (agency) formData.agencyModeId = agency;
|
||||
if (agency) formData.agencyModeId = agency.agencyModeFk;
|
||||
};
|
||||
|
||||
const fetchClient = async (formData) => {
|
||||
|
|
|
@ -9,6 +9,7 @@ import VnRow from 'components/ui/VnRow.vue';
|
|||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||
import VnInputTime from 'components/common/VnInputTime.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
|
@ -53,7 +54,16 @@ const warehousesOptionsIn = ref([]);
|
|||
<VnInputDate v-model="data.shipped" :label="t('globals.shipped')" />
|
||||
<VnInputDate v-model="data.landed" :label="t('globals.landed')" />
|
||||
</VnRow>
|
||||
|
||||
<VnRow>
|
||||
<VnInputDate
|
||||
v-model="data.availabled"
|
||||
:label="t('travel.summary.availabled')"
|
||||
/>
|
||||
<VnInputTime
|
||||
v-model="data.availabled"
|
||||
:label="t('travel.summary.availabledHour')"
|
||||
/>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnSelect
|
||||
:label="t('globals.warehouseOut')"
|
||||
|
@ -101,10 +111,3 @@ const warehousesOptionsIn = ref([]);
|
|||
</template>
|
||||
</FormModel>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
raidDays: El travel se desplaza automáticamente cada día para estar desde hoy al número de días indicado. Si se deja vacio no se moverá
|
||||
en:
|
||||
raidDays: The travel adjusts itself daily to match the number of days set, starting from today. If left blank, it won’t move
|
||||
</i18n>
|
||||
|
|
|
@ -11,6 +11,7 @@ export default {
|
|||
'agencyModeFk',
|
||||
'isRaid',
|
||||
'daysInForward',
|
||||
'availabled',
|
||||
],
|
||||
include: [
|
||||
{
|
||||
|
|
|
@ -10,6 +10,8 @@ import EntryDescriptorProxy from 'src/pages/Entry/Card/EntryDescriptorProxy.vue'
|
|||
import FetchData from 'src/components/FetchData.vue';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
import { toDate, toCurrency, toCelsius } from 'src/filters';
|
||||
import { toDateTimeFormat } from 'src/filters/date.js';
|
||||
import { dashIfEmpty } from 'src/filters';
|
||||
import axios from 'axios';
|
||||
import TravelDescriptorMenuItems from './TravelDescriptorMenuItems.vue';
|
||||
|
||||
|
@ -333,6 +335,12 @@ const getLink = (param) => `#/travel/${entityId.value}/${param}`;
|
|||
<VnLv :label="t('globals.reference')" :value="travel.ref" />
|
||||
<VnLv label="m³" :value="travel.m3" />
|
||||
<VnLv :label="t('globals.totalEntries')" :value="travel.totalEntries" />
|
||||
<VnLv
|
||||
:label="t('travel.summary.availabled')"
|
||||
:value="
|
||||
dashIfEmpty(toDateTimeFormat(travel.availabled))
|
||||
"
|
||||
/>
|
||||
</QCard>
|
||||
<QCard class="full-width">
|
||||
<VnTitle :text="t('travel.summary.entries')" />
|
||||
|
|
|
@ -209,7 +209,7 @@ const onThermographCreated = async (data) => {
|
|||
}"
|
||||
sort-by="thermographFk ASC"
|
||||
option-label="thermographFk"
|
||||
option-filter-value="id"
|
||||
option-filter-value="thermographFk"
|
||||
:disable="viewAction === 'edit'"
|
||||
:tooltip="t('New thermograph')"
|
||||
:roles-allowed-to-create="['logistic']"
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import { onMounted, ref, computed, watch } from 'vue';
|
||||
import { QBtn } from 'quasar';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue';
|
||||
import TravelDescriptorProxy from 'src/pages/Travel/Card/TravelDescriptorProxy.vue';
|
||||
|
@ -22,6 +23,8 @@ import VnPopup from 'src/components/common/VnPopup.vue';
|
|||
const stateStore = useStateStore();
|
||||
const { t } = useI18n();
|
||||
const { openReport } = usePrintService();
|
||||
const route = useRoute();
|
||||
const tableParams = ref();
|
||||
|
||||
const shippedFrom = ref(Date.vnNew());
|
||||
const landedTo = ref(Date.vnNew());
|
||||
|
@ -143,7 +146,7 @@ const columns = computed(() => [
|
|||
sortable: true,
|
||||
},
|
||||
{
|
||||
label: t('globals.pageTitles.supplier'),
|
||||
label: t('extraCommunity.cargoShip'),
|
||||
field: 'cargoSupplierNickname',
|
||||
name: 'cargoSupplierNickname',
|
||||
align: 'left',
|
||||
|
@ -171,7 +174,7 @@ const columns = computed(() => [
|
|||
? value.reduce((sum, entry) => {
|
||||
return sum + (entry.invoiceAmount || 0);
|
||||
}, 0)
|
||||
: 0
|
||||
: 0,
|
||||
),
|
||||
},
|
||||
{
|
||||
|
@ -200,7 +203,7 @@ const columns = computed(() => [
|
|||
sortable: true,
|
||||
},
|
||||
{
|
||||
label: t('kg'),
|
||||
label: t('extraCommunity.kg'),
|
||||
field: 'kg',
|
||||
name: 'kg',
|
||||
align: 'left',
|
||||
|
@ -208,7 +211,7 @@ const columns = computed(() => [
|
|||
sortable: true,
|
||||
},
|
||||
{
|
||||
label: t('physicKg'),
|
||||
label: t('extraCommunity.physicKg'),
|
||||
field: 'loadedKg',
|
||||
name: 'loadedKg',
|
||||
align: 'left',
|
||||
|
@ -232,7 +235,7 @@ const columns = computed(() => [
|
|||
sortable: true,
|
||||
},
|
||||
{
|
||||
label: t('shipped'),
|
||||
label: t('extraCommunity.shipped'),
|
||||
field: 'shipped',
|
||||
name: 'shipped',
|
||||
align: 'left',
|
||||
|
@ -249,7 +252,7 @@ const columns = computed(() => [
|
|||
sortable: true,
|
||||
},
|
||||
{
|
||||
label: t('landed'),
|
||||
label: t('extraCommunity.landed'),
|
||||
field: 'landed',
|
||||
name: 'landed',
|
||||
align: 'left',
|
||||
|
@ -258,7 +261,7 @@ const columns = computed(() => [
|
|||
format: (value) => toDate(value),
|
||||
},
|
||||
{
|
||||
label: t('notes'),
|
||||
label: t('extraCommunity.notes'),
|
||||
field: '',
|
||||
name: 'notes',
|
||||
align: 'center',
|
||||
|
@ -284,7 +287,7 @@ watch(
|
|||
if (!arrayData.store.data) return;
|
||||
onStoreDataChange();
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
{ deep: true, immediate: true },
|
||||
);
|
||||
|
||||
const openReportPdf = () => {
|
||||
|
@ -451,13 +454,24 @@ const getColor = (percentage) => {
|
|||
for (const { value, className } of travelKgPercentages.value)
|
||||
if (percentage > value) return className;
|
||||
};
|
||||
|
||||
const filteredEntries = (entries) => {
|
||||
if (!tableParams?.value?.entrySupplierFk) return entries;
|
||||
return entries?.filter(
|
||||
(entry) => entry.supplierFk === tableParams?.value?.entrySupplierFk,
|
||||
);
|
||||
};
|
||||
|
||||
watch(route, () => {
|
||||
tableParams.value = JSON.parse(route.query.table);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VnSearchbar
|
||||
data-key="ExtraCommunity"
|
||||
:limit="20"
|
||||
:label="t('searchExtraCommunity')"
|
||||
:label="t('extraCommunity.searchExtraCommunity')"
|
||||
/>
|
||||
<RightMenu>
|
||||
<template #right-panel>
|
||||
|
@ -521,7 +535,7 @@ const getColor = (percentage) => {
|
|||
? tableColumnComponents[col.name].event(
|
||||
rows[props.rowIndex][col.field],
|
||||
col.field,
|
||||
props.rowIndex
|
||||
props.rowIndex,
|
||||
)
|
||||
: {}
|
||||
"
|
||||
|
@ -546,7 +560,7 @@ const getColor = (percentage) => {
|
|||
},
|
||||
{
|
||||
link: ['id', 'cargoSupplierNickname'].includes(
|
||||
col.name
|
||||
col.name,
|
||||
),
|
||||
},
|
||||
]"
|
||||
|
@ -564,9 +578,8 @@ const getColor = (percentage) => {
|
|||
</component>
|
||||
</QTd>
|
||||
</QTr>
|
||||
|
||||
<QTr
|
||||
v-for="(entry, index) in props.row.entries"
|
||||
v-for="(entry, index) in filteredEntries(props.row.entries)"
|
||||
:key="index"
|
||||
:props="props"
|
||||
class="bg-vn-secondary-row cursor-pointer"
|
||||
|
@ -598,7 +611,7 @@ const getColor = (percentage) => {
|
|||
name="warning"
|
||||
color="negative"
|
||||
size="md"
|
||||
:title="t('requiresInspection')"
|
||||
:title="t('extraCommunity.requiresInspection')"
|
||||
>
|
||||
</QIcon>
|
||||
</QTd>
|
||||
|
@ -709,24 +722,3 @@ const getColor = (percentage) => {
|
|||
width: max-content;
|
||||
}
|
||||
</style>
|
||||
<i18n>
|
||||
en:
|
||||
searchExtraCommunity: Search for extra community shipping
|
||||
kg: BI. KG
|
||||
physicKg: Phy. KG
|
||||
shipped: W. shipped
|
||||
landed: W. landed
|
||||
requiresInspection: Requires inspection
|
||||
BIP: Boder Inspection Point
|
||||
notes: Notes
|
||||
es:
|
||||
searchExtraCommunity: Buscar por envío extra comunitario
|
||||
kg: KG Bloq.
|
||||
physicKg: KG físico
|
||||
shipped: F. envío
|
||||
landed: F. llegada
|
||||
notes: Notas
|
||||
Open as PDF: Abrir como PDF
|
||||
requiresInspection: Requiere inspección
|
||||
BIP: Punto de Inspección Fronteriza
|
||||
</i18n>
|
||||
|
|
|
@ -79,7 +79,7 @@ warehouses();
|
|||
<VnFilterPanel :data-key="props.dataKey" :search-button="true">
|
||||
<template #tags="{ tag, formatFn }">
|
||||
<div class="q-gutter-x-xs">
|
||||
<strong>{{ t(`params.${tag.label}`) }}: </strong>
|
||||
<strong>{{ t(`extraCommunity.filter.${tag.label}`) }}: </strong>
|
||||
<span>{{ formatFn(tag.value) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -92,7 +92,7 @@ warehouses();
|
|||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInput
|
||||
:label="t('params.reference')"
|
||||
:label="t('extraCommunity.filter.reference')"
|
||||
v-model="params.reference"
|
||||
is-outlined
|
||||
/>
|
||||
|
@ -103,7 +103,7 @@ warehouses();
|
|||
<QInput
|
||||
v-model="params.totalEntries"
|
||||
type="number"
|
||||
:label="t('params.totalEntries')"
|
||||
:label="t('extraCommunity.filter.totalEntries')"
|
||||
dense
|
||||
outlined
|
||||
rounded
|
||||
|
@ -133,10 +133,10 @@ warehouses();
|
|||
<QItem>
|
||||
<QItemSection>
|
||||
<VnSelect
|
||||
:label="t('params.agencyModeFk')"
|
||||
:label="t('extraCommunity.filter.agencyModeFk')"
|
||||
v-model="params.agencyModeFk"
|
||||
:options="agenciesOptions"
|
||||
option-value="agencyFk"
|
||||
option-value="id"
|
||||
option-label="name"
|
||||
hide-selected
|
||||
dense
|
||||
|
@ -148,7 +148,7 @@ warehouses();
|
|||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate
|
||||
:label="t('params.shippedFrom')"
|
||||
:label="t('extraCommunity.filter.shippedFrom')"
|
||||
v-model="params.shippedFrom"
|
||||
@update:model-value="searchFn()"
|
||||
is-outlined
|
||||
|
@ -158,7 +158,7 @@ warehouses();
|
|||
<QItem>
|
||||
<QItemSection>
|
||||
<VnInputDate
|
||||
:label="t('params.landedTo')"
|
||||
:label="t('extraCommunity.filter.landedTo')"
|
||||
v-model="params.landedTo"
|
||||
@update:model-value="searchFn()"
|
||||
is-outlined
|
||||
|
@ -168,7 +168,7 @@ warehouses();
|
|||
<QItem v-if="warehousesByContinent[params.continent]">
|
||||
<QItemSection>
|
||||
<VnSelect
|
||||
:label="t('params.warehouseOutFk')"
|
||||
:label="t('extraCommunity.filter.warehouseOutFk')"
|
||||
v-model="params.warehouseOutFk"
|
||||
:options="warehousesByContinent[params.continent]"
|
||||
option-value="id"
|
||||
|
@ -183,7 +183,7 @@ warehouses();
|
|||
<QItem v-else>
|
||||
<QItemSection>
|
||||
<VnSelect
|
||||
:label="t('params.warehouseOutFk')"
|
||||
:label="t('extraCommunity.filter.warehouseOutFk')"
|
||||
v-model="params.warehouseOutFk"
|
||||
:options="warehousesOptions"
|
||||
option-value="id"
|
||||
|
@ -198,7 +198,7 @@ warehouses();
|
|||
<QItem>
|
||||
<QItemSection>
|
||||
<VnSelect
|
||||
:label="t('params.warehouseInFk')"
|
||||
:label="t('extraCommunity.filter.warehouseInFk')"
|
||||
v-model="params.warehouseInFk"
|
||||
:options="warehousesOptions"
|
||||
option-value="id"
|
||||
|
@ -213,6 +213,7 @@ warehouses();
|
|||
<QItem>
|
||||
<QItemSection>
|
||||
<VnSelectSupplier
|
||||
:label="t('extraCommunity.cargoShip')"
|
||||
v-model="params.cargoSupplierFk"
|
||||
hide-selected
|
||||
dense
|
||||
|
@ -221,10 +222,21 @@ warehouses();
|
|||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnSelectSupplier
|
||||
v-model="params.entrySupplierFk"
|
||||
hide-selected
|
||||
dense
|
||||
outlined
|
||||
rounded
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<VnSelect
|
||||
:label="t('params.continent')"
|
||||
:label="t('extraCommunity.filter.continent')"
|
||||
v-model="params.continent"
|
||||
:options="continentsOptions"
|
||||
option-value="code"
|
||||
|
@ -240,30 +252,3 @@ warehouses();
|
|||
</template>
|
||||
</VnFilterPanel>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
en:
|
||||
params:
|
||||
id: Id
|
||||
reference: Reference
|
||||
totalEntries: Total entries
|
||||
agencyModeFk: Agency
|
||||
warehouseInFk: Warehouse In
|
||||
warehouseOutFk: Warehouse Out
|
||||
shippedFrom: Shipped from
|
||||
landedTo: Landed to
|
||||
cargoSupplierFk: Supplier
|
||||
continent: Continent out
|
||||
es:
|
||||
params:
|
||||
id: Id
|
||||
reference: Referencia
|
||||
totalEntries: Ent. totales
|
||||
agencyModeFk: Agencia
|
||||
warehouseInFk: Alm. entrada
|
||||
warehouseOutFk: Alm. salida
|
||||
shippedFrom: Llegada desde
|
||||
landedTo: Llegada hasta
|
||||
cargoSupplierFk: Proveedor
|
||||
continent: Cont. Salida
|
||||
</i18n>
|
||||
|
|
|
@ -10,6 +10,9 @@ import { getDateQBadgeColor } from 'src/composables/getDateQBadgeColor.js';
|
|||
import TravelFilter from './TravelFilter.vue';
|
||||
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
|
||||
import VnSection from 'src/components/common/VnSection.vue';
|
||||
import VnInputTime from 'src/components/common/VnInputTime.vue';
|
||||
import VnInputDate from 'src/components/common/VnInputDate.vue';
|
||||
import { toDateTimeFormat } from 'src/filters/date';
|
||||
|
||||
const { viewSummary } = useSummaryDialog();
|
||||
const router = useRouter();
|
||||
|
@ -167,6 +170,17 @@ const columns = computed(() => [
|
|||
cardVisible: true,
|
||||
create: true,
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'availabled',
|
||||
label: t('travel.summary.availabled'),
|
||||
component: 'input',
|
||||
columnClass: 'expand',
|
||||
columnField: {
|
||||
component: null,
|
||||
},
|
||||
format: (row, dashIfEmpty) => dashIfEmpty(toDateTimeFormat(row.availabled)),
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
label: '',
|
||||
|
@ -269,6 +283,16 @@ const columns = computed(() => [
|
|||
:class="{ 'is-active': row.isReceived }"
|
||||
/>
|
||||
</template>
|
||||
<template #more-create-dialog="{ data }">
|
||||
<VnInputDate
|
||||
v-model="data.availabled"
|
||||
:label="t('travel.summary.availabled')"
|
||||
/>
|
||||
<VnInputTime
|
||||
v-model="data.availabled"
|
||||
:label="t('travel.summary.availabledHour')"
|
||||
/>
|
||||
</template>
|
||||
<template #moreFilterPanel="{ params }">
|
||||
<VnInputNumber
|
||||
:label="t('params.scopeDays')"
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
extraCommunity:
|
||||
cargoShip: Cargo ship
|
||||
searchExtraCommunity: Search for extra community shipping
|
||||
kg: BI. KG
|
||||
physicKg: Phy. KG
|
||||
shipped: W. shipped
|
||||
landed: W. landed
|
||||
requiresInspection: Requires inspection
|
||||
BIP: Boder Inspection Point
|
||||
notes: Notes
|
||||
filter:
|
||||
id: Id
|
||||
reference: Reference
|
||||
totalEntries: Total entries
|
||||
agencyModeFk: Agency
|
||||
warehouseInFk: Warehouse In
|
||||
warehouseOutFk: Warehouse Out
|
||||
shippedFrom: Shipped from
|
||||
landedTo: Landed to
|
||||
cargoSupplierFk: Cargo supplier
|
||||
continent: Continent out
|
||||
entrySupplierFk: Supplier
|
|
@ -0,0 +1,23 @@
|
|||
extraCommunity:
|
||||
cargoShip: Carguera
|
||||
searchExtraCommunity: Buscar por envío extra comunitario
|
||||
kg: KG Bloq.
|
||||
physicKg: KG físico
|
||||
shipped: F. envío
|
||||
landed: F. llegada
|
||||
notes: Notas
|
||||
Open as PDF: Abrir como PDF
|
||||
requiresInspection: Requiere inspección
|
||||
BIP: Punto de Inspección Fronteriza
|
||||
filter:
|
||||
id: Id
|
||||
reference: Referencia
|
||||
totalEntries: Ent. totales
|
||||
agencyModeFk: Agencia
|
||||
warehouseInFk: Alm. entrada
|
||||
warehouseOutFk: Alm. salida
|
||||
shippedFrom: Llegada desde
|
||||
landedTo: Llegada hasta
|
||||
cargoSupplierFk: Carguera
|
||||
continent: Cont. Salida
|
||||
entrySupplierFk: Proveedor
|
|
@ -17,6 +17,12 @@ const maritalStatus = [
|
|||
{ code: 'M', name: t('Married') },
|
||||
{ code: 'S', name: t('Single') },
|
||||
];
|
||||
async function setAdvancedSummary(data) {
|
||||
const advanced = (await useAdvancedSummary('Workers', data.id)) ?? {};
|
||||
Object.assign(form.value.formData, advanced);
|
||||
await nextTick();
|
||||
if (form.value) form.value.hasChanges = false;
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<FetchData
|
||||
|
@ -36,18 +42,22 @@ const maritalStatus = [
|
|||
:url-update="`Workers/${$route.params.id}`"
|
||||
auto-load
|
||||
model="Worker"
|
||||
@on-fetch="
|
||||
async (data) => {
|
||||
Object.assign(data, (await useAdvancedSummary('Workers', data.id)) ?? {});
|
||||
await $nextTick();
|
||||
if (form) form.hasChanges = false;
|
||||
}
|
||||
"
|
||||
@on-fetch="setAdvancedSummary"
|
||||
>
|
||||
<template #form="{ data }">
|
||||
<VnRow>
|
||||
<VnInput :label="t('Name')" clearable v-model="data.firstName" />
|
||||
<VnInput :label="t('Last name')" clearable v-model="data.lastName" />
|
||||
<VnInput
|
||||
:label="t('Name')"
|
||||
clearable
|
||||
v-model="data.firstName"
|
||||
:required="true"
|
||||
/>
|
||||
<VnInput
|
||||
:label="t('Last name')"
|
||||
clearable
|
||||
v-model="data.lastName"
|
||||
:required="true"
|
||||
/>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnInput v-model="data.phone" :label="t('Business phone')" clearable />
|
||||
|
|
|
@ -3,7 +3,7 @@ import { ref, computed } from 'vue';
|
|||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
import VnTable from 'components/VnTable/VnTable.vue';
|
||||
import { toDate } from 'src/filters';
|
||||
import { dashIfEmpty, toDate } from 'src/filters';
|
||||
import { useQuasar } from 'quasar';
|
||||
import axios from 'axios';
|
||||
|
||||
|
@ -15,7 +15,7 @@ const quasar = useQuasar();
|
|||
|
||||
async function reactivateWorker() {
|
||||
const hasToReactive = tableRef.value.CrudModelRef.formData.find(
|
||||
(data) => !data.ended
|
||||
(data) => !data.ended,
|
||||
);
|
||||
if (hasToReactive) {
|
||||
quasar
|
||||
|
@ -35,28 +35,44 @@ async function reactivateWorker() {
|
|||
}
|
||||
}
|
||||
const columns = computed(() => [
|
||||
{
|
||||
name: 'id',
|
||||
label: t('Id'),
|
||||
align: 'left',
|
||||
isId: true,
|
||||
cardVisible: true,
|
||||
width: '40px',
|
||||
},
|
||||
{
|
||||
name: 'isHourlyLabor',
|
||||
label: t('worker.business.tableVisibleColumns.hourlyLabor'),
|
||||
align: 'left',
|
||||
component: 'checkbox',
|
||||
cardVisible: true,
|
||||
width: '60px',
|
||||
},
|
||||
{
|
||||
name: 'started',
|
||||
label: t('worker.business.tableVisibleColumns.started'),
|
||||
align: 'left',
|
||||
format: ({ started }) => toDate(started),
|
||||
component: 'date',
|
||||
cardVisible: true,
|
||||
create: true,
|
||||
width: '90px',
|
||||
},
|
||||
|
||||
{
|
||||
name: 'ended',
|
||||
label: t('worker.business.tableVisibleColumns.ended'),
|
||||
align: 'left',
|
||||
format: ({ ended }) => toDate(ended),
|
||||
component: 'date',
|
||||
cardVisible: true,
|
||||
create: true,
|
||||
width: '90px',
|
||||
},
|
||||
{
|
||||
label: t('worker.business.tableVisibleColumns.company'),
|
||||
align: 'left',
|
||||
toolTip: t('worker.business.tableVisibleColumns.company'),
|
||||
name: 'companyCodeFk',
|
||||
component: 'select',
|
||||
attrs: {
|
||||
|
@ -65,23 +81,23 @@ const columns = computed(() => [
|
|||
optionLabel: 'code',
|
||||
optionValue: 'code',
|
||||
},
|
||||
cardVisible: true,
|
||||
create: true,
|
||||
width: '60px',
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'reasonEndFk',
|
||||
component: 'select',
|
||||
label: t('worker.business.tableVisibleColumns.reasonEnd'),
|
||||
toolTip: t('worker.business.tableVisibleColumns.reasonEnd'),
|
||||
attrs: {
|
||||
url: 'BusinessReasonEnds',
|
||||
fields: ['id', 'reason'],
|
||||
optionLabel: 'reason',
|
||||
},
|
||||
cardVisible: true,
|
||||
format: ({ reason }, dashIfEmpty) => dashIfEmpty(reason),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'departmentFk',
|
||||
component: 'select',
|
||||
label: t('worker.business.tableVisibleColumns.department'),
|
||||
|
@ -89,15 +105,19 @@ const columns = computed(() => [
|
|||
url: 'Departments',
|
||||
fields: ['id', 'name'],
|
||||
optionLabel: 'name',
|
||||
optionValue: 'id',
|
||||
},
|
||||
cardVisible: true,
|
||||
create: true,
|
||||
width: '80px',
|
||||
format: ({ departmentName }, dashIfEmpty) => dashIfEmpty(departmentName),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'workerBusinessProfessionalCategoryFk',
|
||||
component: 'select',
|
||||
label: t('worker.business.tableVisibleColumns.professionalCategory'),
|
||||
toolTip: t('worker.business.tableVisibleColumns.professionalCategory'),
|
||||
attrs: {
|
||||
url: 'WorkerBusinessProfessionalCategories',
|
||||
fields: ['id', 'description', 'code'],
|
||||
|
@ -105,6 +125,9 @@ const columns = computed(() => [
|
|||
},
|
||||
cardVisible: true,
|
||||
create: true,
|
||||
width: '100px',
|
||||
format: ({ professionalDescription }, dashIfEmpty) =>
|
||||
dashIfEmpty(professionalDescription),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
|
@ -118,6 +141,8 @@ const columns = computed(() => [
|
|||
},
|
||||
cardVisible: true,
|
||||
create: true,
|
||||
format: ({ calendarTypeDescription }, dashIfEmpty) =>
|
||||
dashIfEmpty(calendarTypeDescription),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
|
@ -131,6 +156,8 @@ const columns = computed(() => [
|
|||
},
|
||||
cardVisible: true,
|
||||
create: true,
|
||||
width: '100px',
|
||||
format: ({ workCenterName }, dashIfEmpty) => dashIfEmpty(workCenterName),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
|
@ -144,6 +171,7 @@ const columns = computed(() => [
|
|||
},
|
||||
cardVisible: true,
|
||||
create: true,
|
||||
format: ({ payrollDescription }, dashIfEmpty) => dashIfEmpty(payrollDescription),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
|
@ -157,6 +185,7 @@ const columns = computed(() => [
|
|||
},
|
||||
cardVisible: true,
|
||||
create: true,
|
||||
format: ({ occupationName }, dashIfEmpty) => dashIfEmpty(occupationName),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
|
@ -165,6 +194,7 @@ const columns = computed(() => [
|
|||
component: 'input',
|
||||
cardVisible: true,
|
||||
create: true,
|
||||
width: '50px',
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
|
@ -177,6 +207,22 @@ const columns = computed(() => [
|
|||
},
|
||||
cardVisible: true,
|
||||
create: true,
|
||||
format: ({ workerBusinessTypeName }, dashIfEmpty) =>
|
||||
dashIfEmpty(workerBusinessTypeName),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
name: 'workerBusinessAgreementFk',
|
||||
label: t('worker.business.tableVisibleColumns.workerBusinessAgreementName'),
|
||||
component: 'select',
|
||||
attrs: {
|
||||
url: 'WorkerBusinessAgreements',
|
||||
fields: ['id', 'name'],
|
||||
},
|
||||
cardVisible: true,
|
||||
create: true,
|
||||
format: ({ workerBusinessAgreementName }, dashIfEmpty) =>
|
||||
dashIfEmpty(workerBusinessAgreementName),
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
|
@ -185,6 +231,7 @@ const columns = computed(() => [
|
|||
component: 'input',
|
||||
cardVisible: true,
|
||||
create: true,
|
||||
width: '70px',
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
|
@ -193,6 +240,7 @@ const columns = computed(() => [
|
|||
component: 'input',
|
||||
cardVisible: true,
|
||||
create: true,
|
||||
width: '70px',
|
||||
},
|
||||
{
|
||||
name: 'notes',
|
||||
|
@ -208,27 +256,27 @@ const columns = computed(() => [
|
|||
<VnTable
|
||||
ref="tableRef"
|
||||
data-key="WorkerBusiness"
|
||||
:url="`Workers/${entityId}/Business`"
|
||||
:url="`Workers/${entityId}/getWorkerBusiness`"
|
||||
save-url="/Businesses/crud"
|
||||
:create="{
|
||||
urlCreate: `Workers/${entityId}/Business`,
|
||||
title: 'Create business',
|
||||
title: t('Create business'),
|
||||
onDataSaved: () => tableRef.reload(),
|
||||
formInitialData: {},
|
||||
}"
|
||||
order="id DESC"
|
||||
:columns="columns"
|
||||
default-mode="card"
|
||||
auto-load
|
||||
:disable-option="{ table: true }"
|
||||
:disable-option="{ card: true }"
|
||||
:right-search="false"
|
||||
card-class="grid-two q-gutter-x-xl q-gutter-y-md q-pr-lg q-py-lg"
|
||||
:is-editable="true"
|
||||
:use-model="true"
|
||||
:right-search-icon="false"
|
||||
@save-changes="(data) => reactivateWorker(data)"
|
||||
/>
|
||||
</template>
|
||||
<i18n>
|
||||
es:
|
||||
Do you want to reactivate the user?: desea reactivar el usuario?
|
||||
Create business: Crear contrato
|
||||
</i18n>
|
||||
|
|
|
@ -111,6 +111,7 @@ const handlePhotoUpdated = (evt = false) => {
|
|||
<template #body="{ entity }">
|
||||
<VnLv :label="t('globals.user')" :value="entity.user?.name" />
|
||||
<VnLv
|
||||
class="ellipsis-text"
|
||||
:label="t('globals.params.email')"
|
||||
:value="entity.user?.emailUser?.email"
|
||||
copy
|
||||
|
@ -177,6 +178,12 @@ const handlePhotoUpdated = (evt = false) => {
|
|||
.photo {
|
||||
height: 256px;
|
||||
}
|
||||
.ellipsis-text {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue