diff --git a/CHANGELOG.md b/CHANGELOG.md index 243d67a34..1a679cdfc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,27 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2400.01] - 2024-01-04 + +### Added + +### Changed + +### Fixed + +## [2350.01] - 2023-12-14 + +### Added + +- (Carros) => Se añade contador de carros. #6545 +- (Reclamaciones) => Se añade la sección para hacer acciones sobre una reclamación. #5654 + +### Changed + +### Fixed + +- (Reclamaciones) => Se corrige el color de la barra según el tema y el evento de actualziar cantidades #6334 + ## [2253.01] - 2023-01-05 ### Added diff --git a/cypress.config.js b/cypress.config.js index 2b5b40d08..1934f833e 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -8,7 +8,7 @@ module.exports = defineConfig({ supportFile: 'test/cypress/support/index.js', videosFolder: 'test/cypress/videos', video: false, - specPattern: 'test/cypress/integration/*.spec.js', + specPattern: 'test/cypress/integration/**/*.spec.js', experimentalRunAllSpecs: true, component: { componentFolder: 'src', diff --git a/package-lock.json b/package-lock.json index a3a9dcc63..9db93eff3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,14 @@ { "name": "salix-front", - "version": "23.40.01", + "version": "23.52.01", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "salix-front", - "version": "0.0.1", + "version": "23.52.01", "dependencies": { - "@quasar/cli": "^2.2.1", + "@quasar/cli": "^2.3.0", "@quasar/extras": "^1.16.4", "axios": "^1.4.0", "chromium": "^3.0.3", @@ -946,9 +946,9 @@ } }, "node_modules/@quasar/cli": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@quasar/cli/-/cli-2.2.1.tgz", - "integrity": "sha512-PMwJ76IeeNRRBw+08hUMjhqGC6JKJ/t1zIb+IOiyR5D4rkBR26Ha/Z46OD3KfwUprq4Q8s4ieB1+d3VY8FhPKg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@quasar/cli/-/cli-2.3.0.tgz", + "integrity": "sha512-DNFDemicj3jXe5+Ib+5w9Bwj1U3yoHQkqn0bU/qysIl/p0MmGA1yqOfUF0V4fw/5or1dfCvStIA/oZxUcC+2pQ==", "dependencies": { "@quasar/ssl-certificate": "^1.0.0", "ci-info": "^3.8.0", diff --git a/package.json b/package.json index 799401c08..7c966dbb1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "salix-front", - "version": "23.48.01", + "version": "24.00.01", "description": "Salix frontend", "productName": "Salix", "author": "Verdnatura", @@ -9,7 +9,7 @@ "lint": "eslint --ext .js,.vue ./", "format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore", "test:e2e": "cypress open", - "test:e2e:ci": "cd ../salix && gulp docker && cd ../salix-front && cypress run --browser chromium", + "test:e2e:ci": "cd ../salix && gulp docker && cd ../salix-front && cypress run", "test": "echo \"See package.json => scripts for available tests.\" && exit 0", "test:unit": "vitest", "test:unit:ci": "vitest run" @@ -53,4 +53,4 @@ "vite": "^4.3.5", "vitest": "^0.31.1" } -} \ No newline at end of file +} diff --git a/quasar.config.js b/quasar.config.js index cbcbae4dc..755e96bd3 100644 --- a/quasar.config.js +++ b/quasar.config.js @@ -29,7 +29,7 @@ module.exports = configure(function (/* ctx */) { // app boot file (/src/boot) // --> boot files are part of "main.js" // https://v2.quasar.dev/quasar-cli/boot-files - boot: ['i18n', 'axios', 'vnDate'], + boot: ['i18n', 'axios', 'vnDate', 'validations'], // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css css: ['app.scss'], @@ -66,7 +66,9 @@ module.exports = configure(function (/* ctx */) { // publicPath: '/', // analyze: true, // env: {}, - // rawDefine: {} + rawDefine: { + 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV) + }, // ignorePublicFolder: true, // minify: false, // polyfillModulePreload: true, @@ -89,11 +91,12 @@ module.exports = configure(function (/* ctx */) { vitePlugins: [ [ - VueI18nPlugin, + VueI18nPlugin({ + runtimeOnly: false + }), { // if you want to use Vue I18n Legacy API, you need to set `compositionOnly: false` // compositionOnly: false, - // you need to set i18n resource including paths ! include: path.resolve(__dirname, './src/i18n/**'), }, diff --git a/src/boot/validations.js b/src/boot/validations.js new file mode 100644 index 000000000..31e232f86 --- /dev/null +++ b/src/boot/validations.js @@ -0,0 +1,6 @@ +import { boot } from 'quasar/wrappers'; +import { useValidationsStore } from 'src/stores/useValidationsStore'; + +export default boot(async ({ store }) => { + await useValidationsStore(store).fetchModels(); +}); diff --git a/src/boot/vnDate.js b/src/boot/vnDate.js index c78886b57..33d5ac27f 100644 --- a/src/boot/vnDate.js +++ b/src/boot/vnDate.js @@ -15,4 +15,14 @@ export default boot(() => { Date.vnNow = () => { return new Date(Date.vnUTC()).getTime(); }; + + Date.vnFirstDayOfMonth = () => { + const date = new Date(Date.vnUTC()); + return new Date(date.getFullYear(), date.getMonth(), 1); + }; + + Date.vnLastDayOfMonth = () => { + const date = new Date(Date.vnUTC()); + return new Date(date.getFullYear(), date.getMonth() + 1, 0); + }; }); diff --git a/src/components/CrudModel.vue b/src/components/CrudModel.vue index 96d52b98c..75353a35a 100644 --- a/src/components/CrudModel.vue +++ b/src/components/CrudModel.vue @@ -136,6 +136,10 @@ async function saveChanges(data) { hasChanges.value = false; isLoading.value = false; emit('saveChanges', data); + quasar.notify({ + type: 'positive', + message: t('globals.dataSaved'), + }); } async function insert() { @@ -269,7 +273,7 @@ watch(formUrl, async () => { - + {}, + }, + observeFormChanges: { + type: Boolean, + default: true, + description: + 'Esto se usa principalmente para permitir guardar sin hacer cambios (Útil para la feature de clonar ya que en este caso queremos poder guardar de primeras)', + }, }); const emit = defineEmits(['onFetch']); @@ -43,49 +63,72 @@ defineExpose({ save, }); -onMounted(async () => await fetch()); +onMounted(async () => { + // Podemos enviarle al form la estructura de data inicial sin necesidad de fetchearla + if ($props.formInitialData && !$props.autoLoad) { + state.set($props.model, $props.formInitialData); + } else { + await fetch(); + } + + // Disparamos el watcher del form después de que se haya cargado la data inicial, si así se desea + if ($props.observeFormChanges) { + startFormWatcher(); + } +}); onUnmounted(() => { state.unset($props.model); }); const isLoading = ref(false); -const hasChanges = ref(false); -const originalData = ref(); +// Si elegimos observar los cambios del form significa que inicialmente las actions estaran deshabilitadas +const hasChanges = ref(!$props.observeFormChanges); +const originalData = ref({...$props.formInitialData}); const formData = computed(() => state.get($props.model)); const formUrl = computed(() => $props.url); +const startFormWatcher = () => { + watch( + () => formData.value, + (val) => { + if (val) hasChanges.value = true; + }, + { deep: true } + ); +}; + function tMobile(...args) { if (!quasar.platform.is.mobile) return t(...args); } async function fetch() { const { data } = await axios.get($props.url, { - params: { filter: $props.filter }, + params: { filter: JSON.stringify($props.filter) }, }); state.set($props.model, data); originalData.value = data && JSON.parse(JSON.stringify(data)); - watch(formData.value, () => (hasChanges.value = true)); - emit('onFetch', state.get($props.model)); } async function save() { if (!hasChanges.value) { - return quasar.notify({ - type: 'negative', - message: t('globals.noChanges'), - }); + notify('globals.noChanges', 'negative'); + return; } isLoading.value = true; + try { - await axios.patch($props.urlUpdate || $props.url, formData.value); - } catch (err) { - if (err) { - isLoading.value = false; + if ($props.urlCreate) { + await axios.post($props.urlCreate, formData.value); + notify('globals.dataCreated', 'positive'); + } else { + await axios.patch($props.urlUpdate || $props.url, formData.value); } + } catch (err) { + notify('errors.create', 'negative'); } originalData.value = JSON.parse(JSON.stringify(formData.value)); @@ -97,11 +140,12 @@ function reset() { state.set($props.model, originalData.value); originalData.value = JSON.parse(JSON.stringify(originalData.value)); - watch(formData.value, () => (hasChanges.value = true)); - emit('onFetch', state.get($props.model)); - hasChanges.value = false; + if ($props.observeFormChanges) { + hasChanges.value = false; + } } + // eslint-disable-next-line vue/no-dupe-keys function filter(value, update, filterOptions) { update( @@ -124,7 +168,7 @@ watch(formUrl, async () => { }); - + {{ t('globals.changesToSave') }} @@ -182,6 +226,7 @@ watch(formUrl, async () => { max-width: 800px; width: 100%; } + .q-card { padding: 32px; } diff --git a/src/components/NavBar.vue b/src/components/NavBar.vue index 778f6bfb9..2368e078e 100644 --- a/src/components/NavBar.vue +++ b/src/components/NavBar.vue @@ -25,27 +25,17 @@ const pinnedModulesRef = ref(); - - + + {{ t('globals.collapseMenu') }} - + +import { ref, watch } from 'vue'; +import { QInput } from 'quasar'; + +const props = defineProps({ + modelValue: { + type: String, + default: '', + }, +}); + +const emit = defineEmits(['update:modelValue', 'accountShortToStandard']); + +let internalValue = ref(props.modelValue); + +watch( + () => props.modelValue, + (newVal) => { + internalValue.value = newVal; + } +); + +watch( + () => internalValue.value, + (newVal) => { + emit('update:modelValue', newVal); + accountShortToStandard(); + } +); + +function accountShortToStandard() { + internalValue.value = internalValue.value.replace( + '.', + '0'.repeat(11 - internalValue.value.length) + ); +} + + + + + diff --git a/src/components/common/VnInputDate.vue b/src/components/common/VnInputDate.vue new file mode 100644 index 000000000..a8e0d4a43 --- /dev/null +++ b/src/components/common/VnInputDate.vue @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + diff --git a/src/components/common/VnJsonValue.vue b/src/components/common/VnJsonValue.vue new file mode 100644 index 000000000..a2e858d0d --- /dev/null +++ b/src/components/common/VnJsonValue.vue @@ -0,0 +1,88 @@ + + + + + {{ t }} + + + + diff --git a/src/components/common/VnLog.vue b/src/components/common/VnLog.vue index 1213c8bbc..0949fb5cb 100644 --- a/src/components/common/VnLog.vue +++ b/src/components/common/VnLog.vue @@ -1,128 +1,634 @@ - - - {{ t('Audit logs') }} - - - - - - {{ log.userName }} - - {{ - toDate(log.created, { - dateStyle: 'medium', - timeStyle: 'short', - }) - }} - - - - {{ t(`actions.${log.action}`) }} - - {{ t(`models.${log.model}`) }} - - (workers = data)" + auto-load + /> + + (actions = data.map((item) => { + return { + locale: useFirstUpper(validations[item.changedModel].locale.name), + value: item.changedModel, + }; + })) + " + auto-load + /> + + + + {{ useFirstUpper(validations[props.model].locale.name) }} + #{{ originLog.originFk }} + + + + + + + + + + + + + + + + + - - - + #{{ modelLog.id }} + + {{ modelLog.showValue }} + + + + + + + + + + {{ toRelativeDate(log.creationDate) }} + + + - {{ t(col.label) }} - - - - - - - - - + + + + {{ modelLog.modelI18n }} + #{{ modelLog.id }} + + + + + {{ value.nameI18n }}: + + + + + + + + + + + + + + + + + + {{ prop.nameI18n }}: + + + , + + + + + + + {{ prop.nameI18n }}: + + + + #{{ prop.val.id }} + + + ← + + + #{{ prop.old.id }} + + + + + + + {{ log.description }} + + + + + + + - + {{ t('globals.collapseMenu') }} @@ -131,28 +637,390 @@ function actionColor(action) { - + + + + selectFilter('search')" + @focusout="() => selectFilter('search')" + @clear="() => selectFilter('search')" + > + + + {{ t('tooltips.search') }} + + + + + + selectFilter('action')" + hide-selected + /> + + + + + {{ t(`Users.${label}`) }} + + + + + + + + + + + + + + + + {{ opt.name }} + {{ opt.nickname }} + + + + + + + + + + + {{ + t('tooltips.changes') + }} + + + + + + + + + evt.target.blur()" + @clear="selectFilter('date', 'to')" + v-model="dateFrom" + clearable + clear-icon="close" + /> + + + evt.target.blur()" + @clear="selectFilter('date', 'from')" + v-model="dateTo" + clearable + clear-icon="close" + /> + + + + { + dateFromDialog = false; + dateFrom = date.formatDate(value, 'DD-MM-YYYY'); + selectFilter('date', 'from'); + } + " + /> + + + { + dateToDialog = false; + dateTo = date.formatDate(value, 'DD-MM-YYYY'); + selectFilter('date', 'to'); + } + " + /> + + + + - en: + to: To + pointRecord: View record at this point in time + recordChanges: show all record changes + tooltips: + search: Search by id or concept + changes: Search by changes actions: - insert: Creates - update: Updates - delete: Deletes - models: - Claim: Claim - ClaimDms: Document - ClaimBeginning: Claimed Sales - ClaimObservation: Observation + Creates: Creates + Edits: Edits + Deletes: Deletes + Accesses: Accesses + Users: + User: Usuario + All: Todo + System: Sistema properties: id: ID claimFk: Claim ID @@ -172,6 +1040,12 @@ en: responsibility: Responsibility packages: Packages es: + to: Hasta + pointRecord: Ver el registro en este punto + recordChanges: Mostrar todos los cambios realizados en el registro + tooltips: + search: Buscar por identificador o concepto + changes: Buscar por cambios. Los atributos deben buscarse por su nombre interno, para obtenerlo situar el cursor sobre el atributo. Audit logs: Registros de auditoría Property: Propiedad Before: Antes @@ -179,14 +1053,14 @@ es: Yes: Si Nothing: Nada actions: - insert: Crea - update: Actualiza - delete: Elimina - models: - Claim: Reclamación - ClaimDms: Documento - ClaimBeginning: Línea reclamada - ClaimObservation: Observación + Creates: Crea + Edits: Modifica + Deletes: Elimina + Accesses: Accede + Users: + User: Usuario + All: Todo + System: Sistema properties: id: ID claimFk: ID reclamación diff --git a/src/components/common/VnSelectFilter.vue b/src/components/common/VnSelectFilter.vue index 55395f260..568da613f 100644 --- a/src/components/common/VnSelectFilter.vue +++ b/src/components/common/VnSelectFilter.vue @@ -4,7 +4,7 @@ const emit = defineEmits(['update:modelValue', 'update:options']); const $props = defineProps({ modelValue: { - type: [String, Number], + type: [String, Number, Object], default: null, }, options: { @@ -15,6 +15,14 @@ const $props = defineProps({ type: String, default: '', }, + filterOptions: { + type: Array, + default: () => [], + }, + isClearable: { + type: Boolean, + default: true, + }, }); const { optionLabel, options } = toRefs($props); const myOptions = ref([]); @@ -28,18 +36,22 @@ function setOptions(data) { setOptions(options.value); const filter = (val, options) => { - const search = val.toLowerCase(); + const search = val.toString().toLowerCase(); - if (val === '') return options; + if (!search) return options; return options.filter((row) => { + if ($props.filterOptions.length) { + return $props.filterOptions.some((prop) => { + const propValue = String(row[prop]).toLowerCase(); + return propValue.includes(search); + }); + } + const id = row.id; - const name = row[$props.optionLabel].toLowerCase(); + const optionLabel = String(row[$props.optionLabel]).toLowerCase(); - const idMatches = id == search; - const nameMatches = name.indexOf(search) > -1; - - return idMatches || nameMatches; + return id == search || optionLabel.includes(search); }); }; @@ -85,11 +97,16 @@ const value = computed({ fill-input ref="vnSelectRef" > - - + + - + diff --git a/src/components/ui/CardDescriptor.vue b/src/components/ui/CardDescriptor.vue index f63b75de6..2669b53a9 100644 --- a/src/components/ui/CardDescriptor.vue +++ b/src/components/ui/CardDescriptor.vue @@ -1,8 +1,10 @@ - - - - - {{ t('components.cardDescriptor.mainList') }} - - - + + + + {{ t('components.smartCard.openSummary') }} + + {{ t('components.cardDescriptor.summary') }} - {{ t('components.cardDescriptor.moreOptions') }} @@ -211,8 +234,6 @@ watch($props, async () => { width: 256px; .header { display: flex; - justify-content: space-between; - align-items: stretch; } .icons { margin: 0 10px; diff --git a/src/components/ui/CardList.vue b/src/components/ui/CardList.vue index ed1df7668..7d7cee23e 100644 --- a/src/components/ui/CardList.vue +++ b/src/components/ui/CardList.vue @@ -1,66 +1,109 @@ + - - {{ $props.title ?? `#${$props.id}` }} + + + + {{ $props.title }} + + + {{ t('ID') }}: {{ $props.id }} + + + - - + + - + + @@ -73,11 +116,11 @@ const $props = defineProps({ background-color: var(--vn-gray); } .list-items { - width: 90%; -} -@media (max-width: $breakpoint-xs) { - .list-items { - width: 85%; - } + width: 75%; } + + + es: + ID: ID + diff --git a/src/components/ui/CardSummary.vue b/src/components/ui/CardSummary.vue index b54e44398..29363509d 100644 --- a/src/components/ui/CardSummary.vue +++ b/src/components/ui/CardSummary.vue @@ -27,7 +27,7 @@ defineExpose({ async function fetch() { const params = {}; - if (props.filter) params.filter = props.filter; + if (props.filter) params.filter = JSON.stringify(props.filter); const { data } = await axios.get(props.url, { params }); entity.value = data; @@ -73,6 +73,7 @@ watch(props, async () => { .cardSummary { width: 100%; + .summaryHeader { text-align: center; font-size: 20px; @@ -82,11 +83,14 @@ watch(props, async () => { .summaryBody { display: flex; flex-direction: row; + flex-wrap: wrap; justify-content: space-evenly; gap: 15px; padding: 15px; + background-color: var(--vn-gray); > .q-card.vn-one { + width: 350px; flex: 1; } > .q-card.vn-two { @@ -123,7 +127,6 @@ watch(props, async () => { width: max-content; overflow: hidden; white-space: nowrap; - text-overflow: ellipsis; } } .header { diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue index d78c3ba13..9cd5a052f 100644 --- a/src/components/ui/VnFilterPanel.vue +++ b/src/components/ui/VnFilterPanel.vue @@ -26,7 +26,7 @@ const props = defineProps({ }, }); -const emit = defineEmits(['refresh', 'clear']); +const emit = defineEmits(['refresh', 'clear', 'search']); const arrayData = useArrayData(props.dataKey); const store = arrayData.store; @@ -41,18 +41,15 @@ onMounted(() => { const isLoading = ref(false); async function search() { - for (const param in userParams.value) { - if (userParams.value[param] === '' || userParams.value[param] === null) { - delete userParams.value[param]; - delete store.userParams[param]; - } - } - const params = { ...userParams.value }; isLoading.value = true; - await arrayData.addFilter({ params }); + const params = { ...userParams.value }; + const { params: newParams } = await arrayData.addFilter({ params }); + userParams.value = newParams; + if (!props.showAll && !Object.values(params).length) store.data = []; isLoading.value = false; + emit('search'); } async function reload() { @@ -78,10 +75,11 @@ async function clearFilters() { const tags = computed(() => { const params = []; - for (const param in store.userParams) { + for (const param in userParams.value) { + if (!userParams.value[param]) continue; params.push({ label: param, - value: store.userParams[param], + value: userParams.value[param], }); } @@ -89,8 +87,7 @@ const tags = computed(() => { }); async function remove(key) { - delete userParams.value[key]; - delete store.userParams[key]; + userParams.value[key] = null; await search(); } @@ -106,6 +103,7 @@ function formatValue(value) { return `"${value}"`; } + @@ -119,32 +117,32 @@ function formatValue(value) { {{ t('Remove filters') }} {{ t('Refresh') }} - + @@ -172,35 +170,35 @@ function formatValue(value) { - - - - - - - - + + + + + + + + -es: +es: No filters applied: No se han aplicado filtros Applied filters: Filtros aplicados Remove filters: Eliminar filtros diff --git a/src/components/ui/VnLinkPhone.vue b/src/components/ui/VnLinkPhone.vue new file mode 100644 index 000000000..4445b99c9 --- /dev/null +++ b/src/components/ui/VnLinkPhone.vue @@ -0,0 +1,22 @@ + + + + + diff --git a/src/components/ui/VnLv.vue b/src/components/ui/VnLv.vue index 0f9b99713..9a17fca05 100644 --- a/src/components/ui/VnLv.vue +++ b/src/components/ui/VnLv.vue @@ -4,9 +4,10 @@ import { dashIfEmpty } from 'src/filters'; const $props = defineProps({ label: { type: String, default: null }, - value: { type: [Number, String, Boolean], default: null }, - titleLabel: { type: String, default: null }, - titleValue: { type: [Number, String, Boolean], default: null }, + value: { + type: [String, Boolean], + default: null, + }, info: { type: String, default: null }, dash: { type: Boolean, default: true }, }); @@ -16,7 +17,7 @@ const isBooleanValue = computed(() => typeof $props.value === 'boolean'); - {{ $props.label }} + {{ $props.label }} diff --git a/src/components/ui/VnPaginate.vue b/src/components/ui/VnPaginate.vue index d21d073f2..c75761462 100644 --- a/src/components/ui/VnPaginate.vue +++ b/src/components/ui/VnPaginate.vue @@ -50,6 +50,10 @@ const props = defineProps({ type: Boolean, default: true, }, + exprBuilder: { + type: Function, + default: null, + }, }); const emit = defineEmits(['onFetch', 'onPaginate']); @@ -68,6 +72,7 @@ const arrayData = useArrayData(props.dataKey, { limit: props.limit, order: props.order, userParams: props.userParams, + exprBuilder: props.exprBuilder, }); const store = arrayData.store; diff --git a/src/components/ui/VnSearchbar.vue b/src/components/ui/VnSearchbar.vue index 693d6fce2..35f6c1548 100644 --- a/src/components/ui/VnSearchbar.vue +++ b/src/components/ui/VnSearchbar.vue @@ -76,9 +76,9 @@ async function search() { const module = route.matched[1]; if (rows.length === 1) { const [firstRow] = rows; - await router.push({ path: `/${module.name}/${firstRow.id}` }); + await router.push({ path: `${module.path}/${firstRow.id}` }); } else if (route.matched.length > 3) { - await router.push({ path: `/${module.name}` }); + await router.push({ path: `/${module.path}` }); arrayData.updateStateParams(); } } diff --git a/src/composables/downloadFile.js b/src/composables/downloadFile.js new file mode 100644 index 000000000..877d2a254 --- /dev/null +++ b/src/composables/downloadFile.js @@ -0,0 +1,11 @@ +import { useSession } from 'src/composables/useSession'; +import { getUrl } from './getUrl'; + +const session = useSession(); +const token = session.getToken(); + +export async function downloadFile(dmsId) { + let appUrl = await getUrl('', 'lilium'); + appUrl = appUrl.replace('/#/', ''); + window.open(`${appUrl}/api/dms/${dmsId}/downloadFile?access_token=${token}`); +} diff --git a/src/composables/getUrl.js b/src/composables/getUrl.js index f2bd9ddb9..1e6aec4c6 100644 --- a/src/composables/getUrl.js +++ b/src/composables/getUrl.js @@ -1,11 +1,10 @@ import axios from 'axios'; -export async function getUrl(route, appName = 'salix') { - const filter = { - where: { and: [{ appName: appName }, { environment: process.env.NODE_ENV }] }, - }; +export async function getUrl(route, app = 'salix') { + let url; - const { data } = await axios.get('Urls/findOne', { params: { filter } }); - const url = data.url; - return route ? url + route : url; + await axios.get('Urls/getUrl', { params: { app } }).then((res) => { + url = res.data + route; + }); + return url; } diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js index 9aff0eaa8..523340cc9 100644 --- a/src/composables/useArrayData.js +++ b/src/composables/useArrayData.js @@ -2,6 +2,7 @@ import { onMounted, ref, computed } from 'vue'; import { useRouter, useRoute } from 'vue-router'; import axios from 'axios'; import { useArrayDataStore } from 'stores/useArrayDataStore'; +import { buildFilter } from 'filters/filterPanel'; const arrayDataStore = useArrayDataStore(); @@ -29,6 +30,10 @@ export function useArrayData(key, userOptions) { } }); + if (key && userOptions) { + setOptions(); + } + function setOptions() { const allowedOptions = [ 'url', @@ -39,10 +44,11 @@ export function useArrayData(key, userOptions) { 'skip', 'userParams', 'userFilter', + 'exprBuilder', ]; if (typeof userOptions === 'object') { for (const option in userOptions) { - const isEmpty = userOptions[option] == null || userOptions[option] == ''; + const isEmpty = userOptions[option] == null || userOptions[option] === ''; if (isEmpty || !allowedOptions.includes(option)) continue; if (Object.prototype.hasOwnProperty.call(store, option)) { @@ -64,16 +70,27 @@ export function useArrayData(key, userOptions) { skip: store.skip, }; - Object.assign(filter, store.userFilter); - Object.assign(store.filter, filter); + let exprFilter; + let userParams = { ...store.userParams }; + if (store?.exprBuilder) { + const where = buildFilter(userParams, (param, value) => { + const res = store.exprBuilder(param, value); + if (res) delete userParams[param]; + return res; + }); + exprFilter = where ? { where } : null; + } + Object.assign(filter, store.userFilter, exprFilter); + Object.assign(store.filter, filter); const params = { filter: JSON.stringify(store.filter), }; - Object.assign(params, store.userParams); + Object.assign(params, userParams); store.isLoading = true; + const response = await axios.get(store.url, { signal: canceller.signal, params, @@ -97,6 +114,7 @@ export function useArrayData(key, userOptions) { store.isLoading = false; canceller = null; + return response; } function destroy() { @@ -121,9 +139,30 @@ export function useArrayData(key, userOptions) { async function addFilter({ filter, params }) { if (filter) store.userFilter = Object.assign(store.userFilter, filter); - if (params) store.userParams = Object.assign(store.userParams, params); + + let userParams = Object.assign({}, store.userParams, params); + userParams = sanitizerParams(userParams, store?.exprBuilder); + + store.userParams = userParams; await fetch({ append: false }); + return { filter, params }; + } + + function sanitizerParams(params, exprBuilder) { + for (const param in params) { + if (params[param] === '' || params[param] === null) { + delete store.userParams[param]; + delete params[param]; + if (store.filter?.where) { + delete store.filter.where[Object.keys(exprBuilder ? exprBuilder(param) : param)[0]]; + if (Object.keys(store.filter.where).length === 0) { + delete store.filter.where; + } + } + } + } + return params; } async function loadMore() { @@ -147,10 +186,11 @@ export function useArrayData(key, userOptions) { if (store.userParams && Object.keys(store.userParams).length !== 0) query.params = JSON.stringify(store.userParams); - router.replace({ - path: route.path, - query: query, - }); + if (router) + router.replace({ + path: route.path, + query: query, + }); } const totalRows = computed(() => (store.data && store.data.length) || 0); diff --git a/src/composables/useColor.js b/src/composables/useColor.js new file mode 100644 index 000000000..b325e985f --- /dev/null +++ b/src/composables/useColor.js @@ -0,0 +1,35 @@ +export function djb2a(string) { + let hash = 5381; + for (let i = 0; i < string.length; i++) + hash = ((hash << 5) + hash) ^ string.charCodeAt(i); + return hash >>> 0; +} + +export function useColor(value) { + return '#' + colors[djb2a(value || '') % colors.length]; +} + +const colors = [ + 'b5b941', // Yellow + 'ae9681', // Peach + 'd78767', // Salmon + 'cc7000', // Orange bright + 'e2553d', // Coral + '8B0000', // Red dark + 'de4362', // Red crimson + 'FF1493', // Ping intense + 'be39a2', // Pink light + 'b754cf', // Purple middle + 'a87ba8', // Pink + '8a69cd', // Blue lavender + 'ab20ab', // Purple dark + '00b5b8', // Turquoise + '1fa8a1', // Green ocean + '5681cf', // Blue steel + '3399fe', // Blue sky + '6d9c3e', // Green chartreuse + '51bb51', // Green lime + '518b8b', // Gray board + '7e7e7e', // Gray + '5d5d5d', // Gray dark +]; diff --git a/src/composables/useNotify.js b/src/composables/useNotify.js new file mode 100644 index 000000000..2f0e1c257 --- /dev/null +++ b/src/composables/useNotify.js @@ -0,0 +1,22 @@ +import { Notify } from 'quasar'; +import { i18n } from 'src/boot/i18n'; + +export default function useNotify() { + const notify = (message, type, icon) => { + const defaultIcons = { + warning: 'warning', + negative: 'error', + positive: 'check', + }; + + Notify.create({ + message: i18n.global.t(message), + type: type, + icon: icon ? icon : defaultIcons[type], + }); + }; + + return { + notify, + }; +} diff --git a/src/composables/useState.js b/src/composables/useState.js index f0ff830ab..1c797e992 100644 --- a/src/composables/useState.js +++ b/src/composables/useState.js @@ -8,6 +8,7 @@ const user = ref({ nickname: '', lang: '', darkMode: null, + companyFk: null, }); const roles = ref([]); @@ -23,6 +24,7 @@ export function useState() { nickname: user.value.nickname, lang: user.value.lang, darkMode: user.value.darkMode, + companyFk: user.value.companyFk, }; }); } @@ -34,6 +36,7 @@ export function useState() { nickname: data.nickname, lang: data.lang, darkMode: data.darkMode, + companyFk: data.companyFk, }; } @@ -59,7 +62,6 @@ export function useState() { delete state.value[name]; } - return { getUser, setUser, @@ -69,6 +71,6 @@ export function useState() { get, unset, drawer, - headerMounted + headerMounted, }; } diff --git a/src/composables/useUserConfig.js b/src/composables/useUserConfig.js index 767ffb54e..692943978 100644 --- a/src/composables/useUserConfig.js +++ b/src/composables/useUserConfig.js @@ -1,14 +1,24 @@ import axios from 'axios'; import { useState } from './useState'; +import useNotify from './useNotify'; export function useUserConfig() { const state = useState(); + const { notify } = useNotify(); async function fetch() { - const { data } = await axios.get('UserConfigs/getUserConfig'); - const user = state.getUser().value; - user.darkMode = data.darkMode; - state.setUser(user); + try { + const { data } = await axios.get('UserConfigs/getUserConfig'); + const user = state.getUser().value; + user.darkMode = data.darkMode; + user.companyFk = data.companyFk; + state.setUser(user); + + return data; + } catch (error) { + notify('globals.errors.userConfig', 'negative'); + console.error('Error fetching user config:', error); + } } return { diff --git a/src/composables/useValidator.js b/src/composables/useValidator.js index 625f20911..364d11391 100644 --- a/src/composables/useValidator.js +++ b/src/composables/useValidator.js @@ -1,19 +1,12 @@ -import { ref } from 'vue'; import { useI18n } from 'vue-i18n'; -import axios from 'axios'; import validator from 'validator'; - -const models = ref(null); +import { useValidationsStore } from 'src/stores/useValidationsStore'; export function useValidator() { - if (!models.value) fetch(); - - function fetch() { - axios.get('Schemas/ModelInfo').then((response) => (models.value = response.data)); - } + const models = useValidationsStore().validations; function validate(propertyRule) { - const modelInfo = models.value; + const modelInfo = models; if (!modelInfo || !propertyRule) return; const rule = propertyRule.split('.'); @@ -84,5 +77,6 @@ export function useValidator() { return { validate, + models, }; } diff --git a/src/css/app.scss b/src/css/app.scss index 0f04c9ad8..02d4f8946 100644 --- a/src/css/app.scss +++ b/src/css/app.scss @@ -33,6 +33,7 @@ body.body--light { --vn-gray: #f5f5f5; --vn-label: #5f5f5f; --vn-dark: white; + --vn-light-gray: #e7e3e3; } body.body--dark { @@ -40,8 +41,15 @@ body.body--dark { --vn-gray: #313131; --vn-label: #a8a8a8; --vn-dark: #292929; + --vn-light-gray: #424242; } .bg-vn-dark { background-color: var(--vn-dark); } + +.vn-card { + background-color: var(--vn-gray); + color: var(--vn-text); + border-radius: 8px; +} diff --git a/src/filters/filterPanel.js b/src/filters/filterPanel.js new file mode 100644 index 000000000..ee91e6749 --- /dev/null +++ b/src/filters/filterPanel.js @@ -0,0 +1,94 @@ +/** + * Passes a loopback fields filter to an object. + * + * @param {Object} fields The fields object or array + * @return {Object} The fields as object + */ +function fieldsToObject(fields) { + let fieldsObj = {}; + + if (Array.isArray(fields)) { + for (let field of fields) fieldsObj[field] = true; + } else if (typeof fields == 'object') { + for (let field in fields) { + if (fields[field]) fieldsObj[field] = true; + } + } + + return fieldsObj; +} + +/** + * Merges two loopback fields filters. + * + * @param {Object|Array} src The source fields + * @param {Object|Array} dst The destination fields + * @return {Array} The merged fields as an array + */ +function mergeFields(src, dst) { + let fields = {}; + Object.assign(fields, fieldsToObject(src), fieldsToObject(dst)); + return Object.keys(fields); +} + +/** + * Merges two loopback where filters. + * + * @param {Object|Array} src The source where + * @param {Object|Array} dst The destination where + * @return {Array} The merged wheres + */ +function mergeWhere(src, dst) { + let and = []; + if (src) and.push(src); + if (dst) and.push(dst); + return simplifyOperation(and, 'and'); +} + +/** + * Merges two loopback filters returning the merged filter. + * + * @param {Object} src The source filter + * @param {Object} dst The destination filter + * @return {Object} The result filter + */ +function mergeFilters(src, dst) { + let res = Object.assign({}, dst); + + if (!src) return res; + + if (src.fields) res.fields = mergeFields(src.fields, res.fields); + if (src.where) res.where = mergeWhere(res.where, src.where); + if (src.include) res.include = src.include; + if (src.order) res.order = src.order; + if (src.limit) res.limit = src.limit; + if (src.offset) res.offset = src.offset; + if (src.skip) res.skip = src.skip; + + return res; +} + +function simplifyOperation(operation, operator) { + switch (operation.length) { + case 0: + return undefined; + case 1: + return operation[0]; + default: + return { [operator]: operation }; + } +} + +function buildFilter(params, builderFunc) { + let and = []; + + for (let param in params) { + let value = params[param]; + if (value == null) continue; + let expr = builderFunc(param, value); + if (expr) and.push(expr); + } + return simplifyOperation(and, 'and'); +} + +export { fieldsToObject, mergeFields, mergeWhere, mergeFilters, buildFilter }; diff --git a/src/filters/index.js b/src/filters/index.js index 158ce1009..b0c441641 100644 --- a/src/filters/index.js +++ b/src/filters/index.js @@ -2,6 +2,7 @@ import toLowerCase from './toLowerCase'; import toDate from './toDate'; import toDateString from './toDateString'; import toDateHour from './toDateHour'; +import toRelativeDate from './toRelativeDate'; import toCurrency from './toCurrency'; import toPercentage from './toPercentage'; import toLowerCamel from './toLowerCamel'; @@ -13,6 +14,7 @@ export { toDate, toDateString, toDateHour, + toRelativeDate, toCurrency, toPercentage, dashIfEmpty, diff --git a/src/filters/toCurrency.js b/src/filters/toCurrency.js index 5005e95bc..f820c0127 100644 --- a/src/filters/toCurrency.js +++ b/src/filters/toCurrency.js @@ -9,13 +9,10 @@ export default function (value, symbol = 'EUR', fractionSize = 2) { style: 'currency', currency: symbol, minimumFractionDigits: fractionSize, - maximumFractionDigits: fractionSize + maximumFractionDigits: fractionSize, }; const lang = locale.value == 'es' ? 'de' : locale.value; - return new Intl.NumberFormat(lang, options) - .format(value); - - -} \ No newline at end of file + return new Intl.NumberFormat(lang, options).format(value); +} diff --git a/src/filters/toRelativeDate.js b/src/filters/toRelativeDate.js new file mode 100644 index 000000000..76e67dbea --- /dev/null +++ b/src/filters/toRelativeDate.js @@ -0,0 +1,32 @@ +import { useI18n } from 'vue-i18n'; + +export default function formatDate(dateVal) { + const { t } = useI18n(); + const today = new Date(); + if (dateVal == null) return ''; + + const date = new Date(dateVal); + const dateZeroTime = new Date(dateVal); + dateZeroTime.setHours(0, 0, 0, 0); + const diff = Math.trunc( + (today.getTime() - dateZeroTime.getTime()) / (1000 * 3600 * 24) + ); + let format; + if (diff === 0) format = t('globals.today'); + else if (diff === 1) format = t('globals.yesterday'); + else if (diff > 1 && diff < 7) { + const options = { weekday: 'short' }; + format = date.toLocaleDateString(t('globals.dateFormat'), options); + } else if (today.getFullYear() === date.getFullYear()) { + const options = { day: 'numeric', month: 'short' }; + format = date.toLocaleDateString(t('globals.dateFormat'), options); + } else { + const options = { year: 'numeric', month: '2-digit', day: '2-digit' }; + format = date.toLocaleDateString(t('globals.dateFormat'), options); + } + + // Formatear la hora en HH:mm + const hours = date.getHours().toString().padStart(2, '0'); + const minutes = date.getMinutes().toString().padStart(2, '0'); + return `${format} ${hours}:${minutes}`; +} diff --git a/src/i18n/en/index.js b/src/i18n/en/index.js index 948332d46..5e7e116c5 100644 --- a/src/i18n/en/index.js +++ b/src/i18n/en/index.js @@ -5,6 +5,9 @@ export default { en: 'English', }, language: 'Language', + entity: 'Entity', + user: 'User', + details: 'Details', collapseMenu: 'Collapse left menu', backToDashboard: 'Return to dashboard', notifications: 'Notifications', @@ -13,8 +16,12 @@ export default { pinnedModules: 'Pinned modules', darkMode: 'Dark mode', logOut: 'Log out', + date: 'Date', dataSaved: 'Data saved', dataDeleted: 'Data deleted', + search: 'Search', + changes: 'Changes', + dataCreated: 'Data created', add: 'Add', create: 'Create', save: 'Save', @@ -36,13 +43,34 @@ export default { summary: { basicData: 'Basic data', }, + today: 'Today', + yesterday: 'Yesterday', + dateFormat: 'en-GB', + microsip: 'Open in MicroSIP', noSelectedRows: `You don't have any line selected`, + downloadCSVSuccess: 'CSV downloaded successfully', + // labels compartidos entre vistas + reference: 'Reference', + agency: 'Agency', + wareHouseOut: 'Warehouse Out', + wareHouseIn: 'Warehouse In', + landed: 'Landed', + shipped: 'Shipped', + totalEntries: 'Total entries', + amount: 'Amount', + packages: 'Packages', + download: 'Download', + selectRows: 'Select all { numberRows } row(s)', + allRows: 'All { numberRows } row(s)', + markAll: 'Mark all', }, errors: { statusUnauthorized: 'Access denied', statusInternalServerError: 'An internal server error has ocurred', statusBadGateway: 'It seems that the server has fall down', statusGatewayTimeout: 'Could not contact the server', + userConfig: 'Error fetching user config', + create: 'Error during creation', }, login: { title: 'Login', @@ -225,7 +253,6 @@ export default { invoice: 'Invoice', shipped: 'Shipped', landed: 'Landed', - packages: 'Packages', consigneePhone: 'Consignee phone', consigneeMobile: 'Consignee mobile', clientPhone: 'Client phone', @@ -242,7 +269,6 @@ export default { description: 'Description', price: 'Price', discount: 'Discount', - amount: 'Amount', packing: 'Packing', hasComponentLack: 'Component lack', itemShortage: 'Not visible', @@ -274,6 +300,7 @@ export default { development: 'Development', log: 'Audit logs', notes: 'Notes', + action: 'Action', }, list: { customer: 'Customer', @@ -334,7 +361,6 @@ export default { assignedTo: 'Assigned', created: 'Created', state: 'State', - packages: 'Packages', picked: 'Picked', returnOfMaterial: 'Return of material authorization (RMA)', }, @@ -346,8 +372,10 @@ export default { }, invoiceOut: { pageTitles: { - invoiceOuts: 'Invoices Out', + invoiceOuts: 'Invoice out', list: 'List', + negativeBases: 'Negative Bases', + globalInvoicing: 'Global invoicing', createInvoiceOut: 'Create invoice out', summary: 'Summary', basicData: 'Basic Data', @@ -356,7 +384,6 @@ export default { ref: 'Reference', issued: 'Issued', shortIssued: 'Issued', - amount: 'Amount', client: 'Client', created: 'Created', shortCreated: 'Created', @@ -366,7 +393,6 @@ export default { }, card: { issued: 'Issued', - amount: 'Amount', client: 'Client', company: 'Company', customerCard: 'Customer card', @@ -389,6 +415,138 @@ export default { shipped: 'Shipped', totalWithVat: 'Amount', }, + globalInvoices: { + errors: { + chooseValidClient: 'Choose a valid client', + chooseValidCompany: 'Choose a valid company', + chooseValidPrinter: 'Choose a valid printer', + fillDates: 'Invoice date and the max date should be filled', + invoiceDateLessThanMaxDate: 'Invoice date can not be less than max date', + invoiceWithFutureDate: 'Exists an invoice with a future date', + noTicketsToInvoice: 'There are not clients to invoice', + criticalInvoiceError: 'Critical invoicing error, process stopped', + }, + table: { + client: 'Client', + addressId: 'Address id', + streetAddress: 'Street', + }, + statusCard: { + percentageText: '{getPercentage}% {getAddressNumber} of {getNAddresses}', + pdfsNumberText: '{nPdfs} of {totalPdfs} PDFs', + }, + }, + negativeBases: { + from: 'From', + to: 'To', + company: 'Company', + country: 'Country', + clientId: 'Client Id', + client: 'Client', + amount: 'Amount', + base: 'Base', + ticketId: 'Ticket Id', + active: 'Active', + hasToInvoice: 'Has to Invoice', + verifiedData: 'Verified Data', + comercial: 'Comercial', + errors: { + downloadCsvFailed: 'CSV download failed', + }, + }, + }, + shelving: { + pageTitles: { + shelving: 'Shelving', + shelvingList: 'Shelving List', + create: 'Create', + summary: 'Summary', + basicData: 'Basic Data', + log: 'Logs', + }, + list: { + parking: 'Parking', + priority: 'Priority', + newShelving: 'New Shelving', + }, + summary: { + code: 'Code', + parking: 'Parking', + priority: 'Priority', + worker: 'Worker', + recyclable: 'Recyclable', + }, + basicData: { + code: 'Code', + parking: 'Parking', + priority: 'Priority', + recyclable: 'Recyclable', + }, + }, + invoiceIn: { + pageTitles: { + invoiceIns: 'Invoices In', + list: 'List', + createInvoiceIn: 'Create invoice in', + summary: 'Summary', + basicData: 'Basic data', + vat: 'VAT', + dueDay: 'Due day', + intrastat: 'Intrastat', + log: 'Logs', + }, + list: { + ref: 'Reference', + supplier: 'Supplier', + supplierRef: 'Supplier ref.', + serialNumber: 'Serial number', + serial: 'Serial', + file: 'File', + issued: 'Issued', + isBooked: 'Is booked', + awb: 'AWB', + amount: 'Amount', + }, + card: { + issued: 'Issued', + amount: 'Amount', + client: 'Client', + company: 'Company', + customerCard: 'Customer card', + ticketList: 'Ticket List', + vat: 'Vat', + dueDay: 'Due day', + intrastat: 'Intrastat', + }, + summary: { + supplier: 'Supplier', + supplierRef: 'Supplier ref.', + currency: 'Currency', + docNumber: 'Doc number', + issued: 'Expedition date', + operated: 'Operation date', + bookEntried: 'Entry date', + bookedDate: 'Booked date', + sage: 'Sage withholding', + vat: 'Undeductible VAT', + company: 'Company', + booked: 'Booked', + expense: 'Expense', + taxableBase: 'Taxable base', + rate: 'Rate', + sageVat: 'Sage vat', + sageTransaction: 'Sage transaction', + dueDay: 'Date', + bank: 'Bank', + amount: 'Amount', + foreignValue: 'Foreign value', + dueTotal: 'Due day', + noMatch: 'Do not match', + code: 'Code', + net: 'Net', + stems: 'Stems', + country: 'Country', + }, }, worker: { pageTitles: { @@ -448,6 +606,7 @@ export default { typesList: 'Types List', typeCreate: 'Create type', typeEdit: 'Edit type', + wagonCounter: 'Trolley counter', }, type: { name: 'Name', @@ -504,6 +663,81 @@ export default { }, }, }, + supplier: { + pageTitles: { + suppliers: 'Suppliers', + supplier: 'Supplier', + list: 'List', + create: 'Create', + summary: 'Summary', + }, + list: { + payMethod: 'Pay method', + payDeadline: 'Pay deadline', + payDay: 'Pay day', + account: 'Account', + newSupplier: 'New supplier', + }, + summary: { + responsible: 'Responsible', + notes: 'Notes', + verified: 'Verified', + isActive: 'Is active', + billingData: 'Billing data', + payMethod: 'Pay method', + payDeadline: 'Pay deadline', + payDay: 'Día de pago', + account: 'Account', + fiscalData: 'Fiscal data', + sageTaxType: 'Sage tax type', + sageTransactionType: 'Sage transaction type', + sageWithholding: 'Sage withholding', + supplierActivity: 'Supplier activity', + healthRegister: 'Healt register', + fiscalAddress: 'Fiscal address', + socialName: 'Social name', + taxNumber: 'Tax number', + street: 'Street', + city: 'City', + postCode: 'Postcode', + province: 'Province', + country: 'Country', + }, + create: { + supplierName: 'Supplier name', + }, + }, + travel: { + pageTitles: { + travel: 'Travels', + list: 'List', + create: 'Create', + summary: 'Summary', + extraCommunity: 'Extra community', + }, + summary: { + confirmed: 'Confirmed', + entryId: 'Entry Id', + freight: 'Freight', + package: 'Package', + delivered: 'Delivered', + received: 'Received', + entries: 'Entries', + cloneShipping: 'Clone travel', + CloneTravelAndEntries: 'Clone travel and his entries', + AddEntry: 'Add entry', + }, + variables: { + search: 'Id/Reference', + agencyModeFk: 'Agency', + warehouseInFk: ' Warehouse In', + warehouseOutFk: 'Warehouse Out', + landedFrom: 'Landed from', + landedTo: 'Landed to', + continent: 'Continent out', + totalEntries: 'Total entries', + }, + }, components: { topbar: {}, userPanel: { @@ -511,9 +745,11 @@ export default { logOut: 'Log Out', }, smartCard: { - openCard: 'View card', - openSummary: 'Open summary', - viewDescription: 'View description', + downloadFile: 'Download file', + clone: 'Clone', + openCard: 'View', + openSummary: 'Summary', + viewDescription: 'Description', }, cardDescriptor: { mainList: 'Main list', diff --git a/src/i18n/es/index.js b/src/i18n/es/index.js index 9b452ab22..4e570e7b4 100644 --- a/src/i18n/es/index.js +++ b/src/i18n/es/index.js @@ -5,6 +5,9 @@ export default { en: 'Inglés', }, language: 'Idioma', + entity: 'Entidad', + user: 'Usuario', + details: 'Detalles', collapseMenu: 'Contraer menú lateral', backToDashboard: 'Volver al tablón', notifications: 'Notificaciones', @@ -13,8 +16,12 @@ export default { pinnedModules: 'Módulos fijados', darkMode: 'Modo oscuro', logOut: 'Cerrar sesión', + date: 'Fecha', dataSaved: 'Datos guardados', dataDeleted: 'Datos eliminados', + search: 'Buscar', + changes: 'Cambios', + dataCreated: 'Datos creados', add: 'Añadir', create: 'Crear', save: 'Guardar', @@ -36,13 +43,33 @@ export default { summary: { basicData: 'Datos básicos', }, + today: 'Hoy', + yesterday: 'Ayer', + dateFormat: 'es-ES', noSelectedRows: `No tienes ninguna línea seleccionada`, + microsip: 'Abrir en MicroSIP', + downloadCSVSuccess: 'Descarga de CSV exitosa', + reference: 'Referencia', + agency: 'Agencia', + wareHouseOut: 'Alm. salida', + wareHouseIn: 'Alm. entrada', + landed: 'F. entrega', + shipped: 'F. envío', + totalEntries: 'Ent. totales', + amount: 'Importe', + packages: 'Bultos', + download: 'Descargar', + selectRows: 'Seleccionar las { numberRows } filas(s)', + allRows: 'Todo { numberRows } filas(s)', + markAll: 'Marcar todo', }, errors: { statusUnauthorized: 'Acceso denegado', statusInternalServerError: 'Ha ocurrido un error interno del servidor', statusBadGateway: 'Parece ser que el servidor ha caído', statusGatewayTimeout: 'No se ha podido contactar con el servidor', + userConfig: 'Error al obtener configuración de usuario', + create: 'Error al crear', }, login: { title: 'Inicio de sesión', @@ -224,7 +251,6 @@ export default { invoice: 'Factura', shipped: 'Enviado', landed: 'Entregado', - packages: 'Bultos', consigneePhone: 'Tel. consignatario', consigneeMobile: 'Móv. consignatario', clientPhone: 'Tel. cliente', @@ -241,7 +267,6 @@ export default { description: 'Descripción', price: 'Precio', discount: 'Descuento', - amount: 'Importe', packing: 'Encajado', hasComponentLack: 'Faltan componentes', itemShortage: 'No visible', @@ -273,6 +298,7 @@ export default { photos: 'Fotos', log: 'Registros de auditoría', notes: 'Notas', + action: 'Acción', }, list: { customer: 'Cliente', @@ -333,7 +359,6 @@ export default { assignedTo: 'Asignada a', created: 'Creada', state: 'Estado', - packages: 'Bultos', picked: 'Recogida', returnOfMaterial: 'Autorización de retorno de materiales (RMA)', }, @@ -348,6 +373,8 @@ export default { pageTitles: { invoiceOuts: 'Fact. emitidas', list: 'Listado', + negativeBases: 'Bases Negativas', + globalInvoicing: 'Facturación global', createInvoiceOut: 'Crear fact. emitida', summary: 'Resumen', basicData: 'Datos básicos', @@ -356,7 +383,6 @@ export default { ref: 'Referencia', issued: 'Fecha emisión', shortIssued: 'F. emisión', - amount: 'Importe', client: 'Cliente', created: 'Fecha creación', shortCreated: 'F. creación', @@ -366,7 +392,6 @@ export default { }, card: { issued: 'Fecha emisión', - amount: 'Importe', client: 'Cliente', company: 'Empresa', customerCard: 'Ficha del cliente', @@ -389,6 +414,138 @@ export default { shipped: 'F. envío', totalWithVat: 'Importe', }, + globalInvoices: { + errors: { + chooseValidClient: 'Selecciona un cliente válido', + chooseValidCompany: 'Selecciona una empresa válida', + chooseValidPrinter: 'Selecciona una impresora válida', + fillDates: + 'La fecha de la factura y la fecha máxima deben estar completas', + invoiceDateLessThanMaxDate: + 'La fecha de la factura no puede ser menor que la fecha máxima', + invoiceWithFutureDate: 'Existe una factura con una fecha futura', + noTicketsToInvoice: 'No hay clientes para facturar', + criticalInvoiceError: 'Error crítico en la facturación, proceso detenido', + }, + table: { + client: 'Cliente', + addressId: 'Id dirección', + streetAddress: 'Dirección fiscal', + }, + statusCard: { + percentageText: '{getPercentage}% {getAddressNumber} de {getNAddresses}', + pdfsNumberText: '{nPdfs} de {totalPdfs} PDFs', + }, + }, + negativeBases: { + from: 'Desde', + to: 'Hasta', + company: 'Empresa', + country: 'País', + clientId: 'Id cliente', + client: 'Cliente', + amount: 'Importe', + base: 'Base', + ticketId: 'Id ticket', + active: 'Activo', + hasToInvoice: 'Facturar', + verifiedData: 'Datos comprobados', + comercial: 'Comercial', + errors: { + downloadCsvFailed: 'Error al descargar CSV', + }, + }, + }, + shelving: { + pageTitles: { + shelving: 'Carros', + shelvingList: 'Listado de carros', + create: 'Crear', + summary: 'Resumen', + basicData: 'Datos básicos', + log: 'Registros de auditoría', + }, + list: { + parking: 'Parking', + priority: 'Prioridad', + newShelving: 'Nuevo Carro', + }, + summary: { + code: 'Código', + parking: 'Parking', + priority: 'Prioridad', + worker: 'Trabajador', + recyclable: 'Reciclable', + }, + basicData: { + code: 'Código', + parking: 'Parking', + priority: 'Prioridad', + recyclable: 'Reciclable', + }, + }, + invoiceIn: { + pageTitles: { + invoiceIns: 'Fact. recibidas', + list: 'Listado', + createInvoiceIn: 'Crear fact. recibida', + summary: 'Resumen', + basicData: 'Datos básicos', + vat: 'IVA', + dueDay: 'Vencimiento', + intrastat: 'Intrastat', + log: 'Registros de auditoría', + }, + list: { + ref: 'Referencia', + supplier: 'Proveedor', + supplierRef: 'Ref. proveedor', + serialNumber: 'Num. serie', + shortIssued: 'F. emisión', + serial: 'Serie', + file: 'Fichero', + issued: 'Fecha emisión', + isBooked: 'Conciliada', + awb: 'AWB', + amount: 'Importe', + }, + card: { + issued: 'Fecha emisión', + amount: 'Importe', + client: 'Cliente', + company: 'Empresa', + customerCard: 'Ficha del cliente', + ticketList: 'Listado de tickets', + vat: 'Iva', + dueDay: 'Fecha de vencimiento', + }, + summary: { + supplier: 'Proveedor', + supplierRef: 'Ref. proveedor', + currency: 'Divisa', + docNumber: 'Número documento', + issued: 'Fecha de expedición', + operated: 'Fecha operación', + bookEntried: 'Fecha asiento', + bookedDate: 'Fecha contable', + sage: 'Retención sage', + vat: 'Iva no deducible', + company: 'Empresa', + booked: 'Contabilizada', + expense: 'Gasto', + taxableBase: 'Base imp.', + rate: 'Tasa', + sageTransaction: 'Sage transación', + dueDay: 'Fecha', + bank: 'Caja', + amount: 'Importe', + foreignValue: 'Divisa', + dueTotal: 'Vencimiento', + code: 'Código', + net: 'Neto', + stems: 'Tallos', + country: 'País', + }, }, worker: { pageTitles: { @@ -448,6 +605,7 @@ export default { typesList: 'Listado tipos', typeCreate: 'Crear tipo', typeEdit: 'Editar tipo', + wagonCounter: 'Contador de carros', }, type: { name: 'Nombre', @@ -504,6 +662,81 @@ export default { }, }, }, + supplier: { + pageTitles: { + suppliers: 'Proveedores', + supplier: 'Proveedor', + list: 'Listado', + create: 'Crear', + summary: 'Resumen', + }, + list: { + payMethod: 'Método de pago', + payDeadline: 'Plazo de pago', + payDay: 'Día de pago', + account: 'Cuenta', + newSupplier: 'Nuevo proveedor', + }, + summary: { + responsible: 'Responsable', + notes: 'Notas', + verified: 'Verificado', + isActive: 'Está activo', + billingData: 'Forma de pago', + payMethod: 'Método de pago', + payDeadline: 'Plazo de pago', + payDay: 'Día de pago', + account: 'Cuenta', + fiscalData: 'Data fiscal', + sageTaxType: 'Tipo de impuesto Sage', + sageTransactionType: 'Tipo de transacción Sage', + sageWithholding: 'Retención sage', + supplierActivity: 'Actividad proveedor', + healthRegister: 'Pasaporte sanitario', + fiscalAddress: 'Dirección fiscal', + socialName: 'Razón social', + taxNumber: 'NIF/CIF', + street: 'Dirección', + city: 'Población', + postCode: 'Código postal', + province: 'Provincia', + country: 'País', + }, + create: { + supplierName: 'Nombre del proveedor', + }, + }, + travel: { + pageTitles: { + travel: 'Envíos', + list: 'Listado', + create: 'Crear', + summary: 'Resumen', + extraCommunity: 'Extra comunitarios', + }, + summary: { + confirmed: 'Confirmado', + entryId: 'Id entrada', + freight: 'Porte', + package: 'Embalaje', + delivered: 'Enviada', + received: 'Recibida', + entries: 'Entradas', + cloneShipping: 'Clonar envío', + CloneTravelAndEntries: 'Clonar travel y sus entradas', + AddEntry: 'Añadir entrada', + }, + variables: { + search: 'Id/Referencia', + agencyModeFk: 'Agencia', + warehouseInFk: 'Alm. entrada', + warehouseOutFk: ' Alm. salida', + landedFrom: 'Llegada desde', + landedTo: 'Llegada hasta', + continent: 'Cont. Salida', + totalEntries: 'Ent. totales', + }, + }, components: { topbar: {}, userPanel: { @@ -511,9 +744,11 @@ export default { logOut: 'Cerrar sesión', }, smartCard: { - openCard: 'Ver ficha', - openSummary: 'Abrir detalles', - viewDescription: 'Ver descripción', + downloadFile: 'Descargar archivo', + clone: 'Clonar', + openCard: 'Ficha', + openSummary: 'Detalles', + viewDescription: 'Descripción', }, cardDescriptor: { mainList: 'Listado principal', diff --git a/src/pages/Claim/Card/ClaimAction.vue b/src/pages/Claim/Card/ClaimAction.vue new file mode 100644 index 000000000..44575f1d6 --- /dev/null +++ b/src/pages/Claim/Card/ClaimAction.vue @@ -0,0 +1,518 @@ + + + (claim = data)" + auto-load + /> + (resolvedStateId = data.id)" + auto-load + :where="{ code: 'resolved' }" + /> + (destinationTypes = data)" + /> + + + + + + {{ t('globals.collapseMenu') }} + + + + + + + + {{ `${t('Total claimed')}: ${toCurrency(totalClaimed)}` }} + + + + + + {{ t('claim.summary.actions') }} + + save({ responsibility: value })" + label-always + color="primary" + markers + :marker-labels="marker_labels" + :min="DEFAULT_MIN_RESPONSABILITY" + :max="DEFAULT_MAX_RESPONSABILITY" + /> + + + + + save({ isChargedToMana: value })" + /> + {{ t('mana') }} + + + + + + + + + + {{ value }} + + + + + + + updateDestination(value, row)" + /> + + + + + {{ toCurrency(value) }} + + + + + {{ toCurrency(value) }} + + + + + + + + + + + + + + + + + {{ column.label }} + + + + + {{ column.value.description }} + + + {{ column.value }} + + + + + + + + + + + + + + + + + + + + + + {{ t('dialog title') }} + + + + + + + + + + + + + + + + + +en: + mana: Is paid with mana + dialog title: Change destination to all selected rows + confirmGreuges: Do you want to insert complaints? + confirmGreugesMessage: Insert complaints into the client's record + +es: + mana: Cargado al maná + Delivered: Descripción + Quantity: Cantidad + Claimed: Rec + Description: Descripción + Price: Precio + Discount: Dto. + Destination: Destino + Landed: F.entrega + Remove line: Eliminar línea + Total claimed: Total reclamado + Regularize: Regularizar + Change destination: Cambiar destino + Import claim: Importar reclamación + dialog title: Cambiar destino en todas las filas seleccionadas + Remove: Eliminar + dialogGreuge title: Insertar greuges en la ficha del cliente + ClaimGreugeDescription: Id reclamación + Id item: Id artículo + confirmGreuges: ¿Desea insertar greuges? + confirmGreugesMessage: Insertar greuges en la ficha del cliente + diff --git a/src/pages/Claim/Card/ClaimBasicData.vue b/src/pages/Claim/Card/ClaimBasicData.vue index 94e447e13..0f067f281 100644 --- a/src/pages/Claim/Card/ClaimBasicData.vue +++ b/src/pages/Claim/Card/ClaimBasicData.vue @@ -7,6 +7,7 @@ import { useSession } from 'src/composables/useSession'; import FetchData from 'components/FetchData.vue'; import FormModel from 'components/FormModel.vue'; import VnRow from 'components/ui/VnRow.vue'; +import VnInputDate from "components/common/VnInputDate.vue"; const route = useRoute(); const { t } = useI18n(); @@ -96,6 +97,7 @@ const statesFilter = { :url-update="`Claims/updateClaim/${route.params.id}`" :filter="claimFilter" model="claim" + auto-load > @@ -107,33 +109,7 @@ const statesFilter = { /> - - - - - - - - - - - - - + @@ -183,7 +159,7 @@ const statesFilter = { diff --git a/src/pages/Claim/Card/ClaimCard.vue b/src/pages/Claim/Card/ClaimCard.vue index 03b9889f0..a8c832967 100644 --- a/src/pages/Claim/Card/ClaimCard.vue +++ b/src/pages/Claim/Card/ClaimCard.vue @@ -22,11 +22,6 @@ const $props = defineProps({ const entityId = computed(() => { return $props.id || route.params.id; }); - -let salixUrl; -onMounted(async () => { - salixUrl = await getUrl(`claim/${entityId.value}`); -}); @@ -42,18 +37,6 @@ onMounted(async () => { - - - - - {{ t('Action') }} - - diff --git a/src/pages/Claim/Card/ClaimDescriptor.vue b/src/pages/Claim/Card/ClaimDescriptor.vue index af7e84d38..85ae9f7e1 100644 --- a/src/pages/Claim/Card/ClaimDescriptor.vue +++ b/src/pages/Claim/Card/ClaimDescriptor.vue @@ -7,6 +7,7 @@ import { useState } from 'src/composables/useState'; import TicketDescriptorProxy from 'pages/Ticket/Card/TicketDescriptorProxy.vue'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; +import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue'; import ClaimDescriptorMenu from 'pages/Claim/Card/ClaimDescriptorMenu.vue'; import CardDescriptor from 'components/ui/CardDescriptor.vue'; import VnLv from 'src/components/ui/VnLv.vue'; @@ -62,13 +63,18 @@ const filter = { ], }; +const STATE_COLOR = { + pending: 'positive', + managed: 'warning', + resolved: 'negative', +}; + function stateColor(code) { - if (code === 'pending') return 'positive'; - if (code === 'managed') return 'warning'; - if (code === 'resolved') return 'negative'; + return STATE_COLOR[code]; } const data = ref(useCardDescription()); const setData = (entity) => { + if (!entity) return; data.value = useCardDescription(entity.client.name, entity.id); state.set('ClaimDescriptor', entity); }; @@ -83,6 +89,7 @@ const setData = (entity) => { :title="data.title" :subtitle="data.subtitle" @on-fetch="setData" + data-key="claimData" > @@ -113,23 +120,23 @@ const setData = (entity) => { {{ entity.worker.user.name }} - + - {{ entity.client.salesPersonUser.name }} - + {{ entity.client?.salesPersonUser?.name }} + - + diff --git a/src/pages/Claim/Card/ClaimDescriptorMenu.vue b/src/pages/Claim/Card/ClaimDescriptorMenu.vue index 5688613d6..d88c3d120 100644 --- a/src/pages/Claim/Card/ClaimDescriptorMenu.vue +++ b/src/pages/Claim/Card/ClaimDescriptorMenu.vue @@ -37,7 +37,7 @@ function confirmPickupOrder() { data: { address: customer.email, }, - send: sendPickupOrder, + promise: sendPickupOrder, }, }); } diff --git a/src/pages/Claim/Card/ClaimDevelopment.vue b/src/pages/Claim/Card/ClaimDevelopment.vue index 09422f7a1..0ab3c6c90 100644 --- a/src/pages/Claim/Card/ClaimDevelopment.vue +++ b/src/pages/Claim/Card/ClaimDevelopment.vue @@ -7,6 +7,7 @@ import FetchData from 'components/FetchData.vue'; import VnSelectFilter from 'components/common/VnSelectFilter.vue'; import { getUrl } from 'composables/getUrl'; import { tMobile } from 'composables/tMobile'; +import router from 'src/router'; const route = useRoute(); @@ -102,10 +103,6 @@ const columns = computed(() => [ tabIndex: 5, }, ]); - -function goToAction() { - location.href = `${salixUrl}/action`; -} @@ -175,6 +172,7 @@ function goToAction() { :option-label="col.optionLabel" :autofocus="col.tabIndex == 1" input-debounce="0" + hide-selected > @@ -213,6 +211,7 @@ function goToAction() { dense input-debounce="0" :autofocus="col.tabIndex == 1" + hide-selected /> diff --git a/src/pages/Claim/Card/ClaimLines.vue b/src/pages/Claim/Card/ClaimLines.vue index c03291b85..fa7fb123f 100644 --- a/src/pages/Claim/Card/ClaimLines.vue +++ b/src/pages/Claim/Card/ClaimLines.vue @@ -46,7 +46,7 @@ async function onFetchClaim(data) { const amount = ref(0); const amountClaimed = ref(0); async function onFetch(rows) { - if (!rows || rows.length) return; + if (!rows || !rows.length) return; amount.value = rows.reduce( (acumulator, { sale }) => acumulator + sale.price * sale.quantity, 0 @@ -155,7 +155,7 @@ function showImportDialog() { - + {{ t('Amount') }} diff --git a/src/pages/Claim/Card/ClaimPhoto.vue b/src/pages/Claim/Card/ClaimPhoto.vue index 483dbffc1..6ac116ce0 100644 --- a/src/pages/Claim/Card/ClaimPhoto.vue +++ b/src/pages/Claim/Card/ClaimPhoto.vue @@ -26,6 +26,7 @@ const client = ref({}); const inputFile = ref(); const files = ref({}); +const spinnerRef = ref(); const claimDmsRef = ref(); const dmsType = ref({}); const config = ref({}); @@ -118,11 +119,11 @@ async function create() { clientId: client.value.id, }).toUpperCase(), }; - + spinnerRef.value.show(); await axios.post(query, formData, { params: dms, }); - + spinnerRef.value.hide(); quasar.notify({ message: t('globals.dataSaved'), type: 'positive', @@ -234,7 +235,9 @@ function onDrag() { - + + + diff --git a/src/pages/Claim/Card/ClaimSummary.vue b/src/pages/Claim/Card/ClaimSummary.vue index dc5ec9544..64d7b721b 100644 --- a/src/pages/Claim/Card/ClaimSummary.vue +++ b/src/pages/Claim/Card/ClaimSummary.vue @@ -85,10 +85,15 @@ const detailsColumns = ref([ }, ]); +const STATE_COLOR = { + pending: 'positive', + + managed: 'warning', + + resolved: 'negative', +}; function stateColor(code) { - if (code === 'pending') return 'green'; - if (code === 'managed') return 'orange'; - if (code === 'resolved') return 'red'; + return STATE_COLOR[code]; } const developmentColumns = ref([ diff --git a/src/pages/Claim/ClaimFilter.vue b/src/pages/Claim/ClaimFilter.vue index 5918712fd..c9716074d 100644 --- a/src/pages/Claim/ClaimFilter.vue +++ b/src/pages/Claim/ClaimFilter.vue @@ -3,6 +3,8 @@ import { ref } from 'vue'; import { useI18n } from 'vue-i18n'; import FetchData from 'components/FetchData.vue'; import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; +import VnSelectFilter from 'components/common/VnSelectFilter.vue'; +import VnInputDate from 'components/common/VnInputDate.vue'; const { t } = useI18n(); const props = defineProps({ @@ -60,7 +62,7 @@ const states = ref(); - - - - --> - - - - - - - - - - - - - + diff --git a/src/pages/Claim/ClaimList.vue b/src/pages/Claim/ClaimList.vue index 79afc7e91..fbf28bc8e 100644 --- a/src/pages/Claim/ClaimList.vue +++ b/src/pages/Claim/ClaimList.vue @@ -5,24 +5,27 @@ import { useQuasar } from 'quasar'; import { useStateStore } from 'stores/useStateStore'; import { toDate } from 'filters/index'; import VnPaginate from 'src/components/ui/VnPaginate.vue'; -import ClaimSummaryDialog from './Card/ClaimSummaryDialog.vue'; -import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue'; import VnSearchbar from 'components/ui/VnSearchbar.vue'; import ClaimFilter from './ClaimFilter.vue'; import VnLv from 'src/components/ui/VnLv.vue'; import CardList from 'src/components/ui/CardList.vue'; +import ClaimSummaryDialog from './Card/ClaimSummaryDialog.vue'; +import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue'; +import WorkerDescriptorProxy from '../Worker/Card/WorkerDescriptorProxy.vue'; const stateStore = useStateStore(); const router = useRouter(); const quasar = useQuasar(); const { t } = useI18n(); +const STATE_COLOR = { + pending: 'positive', + managed: 'warning', + resolved: 'negative', +}; function stateColor(code) { - if (code === 'pending') return 'green'; - if (code === 'managed') return 'orange'; - if (code === 'resolved') return 'red'; + return STATE_COLOR[code]; } - function navigate(id) { router.push({ path: `/claim/${id}` }); } @@ -35,6 +38,15 @@ function viewSummary(id) { }, }); } + +function viewDescriptor(id) { + quasar.dialog({ + component: CustomerDescriptorProxy, + componentProps: { + id, + }, + }); +} @@ -77,32 +89,37 @@ function viewSummary(id) { > - - + + + + {{ row.clientName }} + + + + + + + + {{ row.workerName }} + + + + - + {{ row.stateDescription }} @@ -110,26 +127,26 @@ function viewSummary(id) { + - - {{ t('components.smartCard.openCard') }} - - - - - {{ t('components.smartCard.openSummary') }} - - - - - {{ t('components.smartCard.viewDescription') }} - - + diff --git a/src/pages/Customer/Card/CustomerBasicData.vue b/src/pages/Customer/Card/CustomerBasicData.vue index 08c661f0b..47cb6e337 100644 --- a/src/pages/Customer/Card/CustomerBasicData.vue +++ b/src/pages/Customer/Card/CustomerBasicData.vue @@ -60,7 +60,7 @@ const filterOptions = { auto-load /> - + diff --git a/src/pages/Customer/Card/CustomerDescriptor.vue b/src/pages/Customer/Card/CustomerDescriptor.vue index 23e6228ee..282a4b958 100644 --- a/src/pages/Customer/Card/CustomerDescriptor.vue +++ b/src/pages/Customer/Card/CustomerDescriptor.vue @@ -14,6 +14,10 @@ const $props = defineProps({ required: false, default: null, }, + summary: { + type: Object, + default: null, + }, }); const route = useRoute(); @@ -34,6 +38,8 @@ const setData = (entity) => (data.value = useCardDescription(entity.name, entity :title="data.title" :subtitle="data.subtitle" @on-fetch="setData" + :summary="$props.summary" + data-key="customerData" > diff --git a/src/pages/Customer/Card/CustomerDescriptorProxy.vue b/src/pages/Customer/Card/CustomerDescriptorProxy.vue index 84a69c026..0341f66a0 100644 --- a/src/pages/Customer/Card/CustomerDescriptorProxy.vue +++ b/src/pages/Customer/Card/CustomerDescriptorProxy.vue @@ -1,5 +1,6 @@ + - + diff --git a/src/pages/Customer/Card/CustomerSummary.vue b/src/pages/Customer/Card/CustomerSummary.vue index 081bdd157..6693274ac 100644 --- a/src/pages/Customer/Card/CustomerSummary.vue +++ b/src/pages/Customer/Card/CustomerSummary.vue @@ -6,6 +6,7 @@ import { toCurrency, toPercentage, toDate } from 'src/filters'; import CardSummary from 'components/ui/CardSummary.vue'; import { getUrl } from 'src/composables/getUrl'; import VnLv from 'src/components/ui/VnLv.vue'; +import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue'; const route = useRoute(); const { t } = useI18n(); @@ -68,8 +69,18 @@ const creditWarning = computed(() => { - - + + + {{ t('customer.summary.phone') }} + + + + + + {{ t('customer.summary.mobile') }} + + + - - @@ -124,7 +126,7 @@ const zones = ref(); - - - + + + {{ t('customer.list.phone') }} + + + - - {{ t('components.smartCard.openCard') }} - - + class="bg-vn-dark" + outline + /> - - {{ t('components.smartCard.openSummary') }} - - + color="primary" + style="margin-top: 15px" + /> diff --git a/src/pages/Customer/CustomerPaymentsFilter.vue b/src/pages/Customer/CustomerPaymentsFilter.vue index 56fb52d79..7ee584d7f 100644 --- a/src/pages/Customer/CustomerPaymentsFilter.vue +++ b/src/pages/Customer/CustomerPaymentsFilter.vue @@ -1,6 +1,7 @@ + + (suppliers = data)" + /> + (currencies = data)" + /> + (companies = data)" + /> + (dmsTypes = data)" + /> + (warehouses = data)" + /> + (allowedContentTypes = data)" + /> + (userConfig = data)" + auto-load + /> + + + + + + + + + {{ + `${scope.opt.id} - ${scope.opt.nickname}` + }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ t('Edit document') }} + + + {{ t('Create document') }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ t('Edit document') }} + + + + + + + + + + + + + + + + + + + + + + {{ t('Select a file') }} + + + + + {{ + `${t( + 'Allowed content types' + )}: ${allowedContentTypes.join(', ')}` + }} + + + + + + + + + + + + + + + + + + + + + + {{ t('Create document') }} + + + + + + + + + + + + + + + + + + + + + + {{ t('Select a file') }} + + + + + {{ + `${t( + 'Allowed content types' + )}: ${allowedContentTypes.join(', ')}` + }} + + + + + + + + + + + + + + + + + + + en: + supplierFk: Supplier + es: + supplierFk: Proveedor + Supplier ref: Ref. proveedor + Expedition date: Fecha expedición + Operation date: Fecha operación + Undeductible VAT: Iva no deducible + Document: Documento + Download file: Descargar archivo + Entry date: Fecha asiento + Accounted date: Fecha contable + Currency: Moneda + Company: Empresa + Edit document: Editar documento + Reference: Referencia + Type: Tipo + Description: Descripción + Generate identifier for original file: Generar identificador para archivo original + Required field: Campo obligatorio + File: Fichero + Create document: Crear documento + Select a file: Seleccione un fichero + Allowed content types: Tipos de archivo permitidos + The company can't be empty: La empresa no puede estar vacía + The warehouse can't be empty: El almacén no puede estar vacío + The DMS Type can't be empty: El dms no puede estar vacío + The description can't be empty: La descripción no puede estar vacía + The files can't be empty: Los archivos no pueden estar vacíos + diff --git a/src/pages/InvoiceIn/Card/InvoiceInCard.vue b/src/pages/InvoiceIn/Card/InvoiceInCard.vue new file mode 100644 index 000000000..4d06cb41e --- /dev/null +++ b/src/pages/InvoiceIn/Card/InvoiceInCard.vue @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + es: + Search invoice: Buscar factura emitida + You can search by invoice reference: Puedes buscar por referencia de la factura + diff --git a/src/pages/InvoiceIn/Card/InvoiceInDescriptor.vue b/src/pages/InvoiceIn/Card/InvoiceInDescriptor.vue new file mode 100644 index 000000000..4e37102e9 --- /dev/null +++ b/src/pages/InvoiceIn/Card/InvoiceInDescriptor.vue @@ -0,0 +1,327 @@ + + + + (config = data)" + /> + + + + + {{ t('To book') }} + + + {{ t('Delete invoice') }} + + + {{ t('Clone invoice') }} + + + {{ t('Show agricultural receipt as PDF') }} + + + {{ t('Send agricultural receipt as PDF') }}... + + + {{ t('components.smartCard.downloadFile') }} + + + + + + + + + + + + + + {{ t('invoiceOut.card.ticketList') }} + + + + + + + +es: + To book: Contabilizar + Are you sure you want to book this invoice?: Estas seguro de querer asentar esta factura? + Delete invoice: Eliminar factura + Are you sure you want to delete this invoice?: Estas seguro de querer eliminar esta factura? + Invoice deleted: Factura eliminada + Clone invoice: Clonar factura + Invoice cloned: Factura clonada + Show agricultural receipt as PDF: Ver recibo agrícola como PDF + Send agricultural receipt as PDF: Enviar recibo agrícola como PDF + Are you sure you want to send it?: Estás seguro que quieres enviarlo? + Send PDF invoice: Enviar factura a PDF + diff --git a/src/pages/InvoiceIn/Card/InvoiceInDueDay.vue b/src/pages/InvoiceIn/Card/InvoiceInDueDay.vue new file mode 100644 index 000000000..e240e9a8c --- /dev/null +++ b/src/pages/InvoiceIn/Card/InvoiceInDueDay.vue @@ -0,0 +1,294 @@ + + + (banks = data)" /> + (areRows = !!data.length)" + > + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ + `${scope.opt.id}: ${scope.opt.bank}` + }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ + `${scope.opt.id}: ${scope.opt.bank}` + }} + + + + + + + + + + + + + + + + + + + + + + + + + es: + Date: Fecha + Bank: Caja + Amount: Importe + Foreign value: Divisa + diff --git a/src/pages/InvoiceIn/Card/InvoiceInIntrastat.vue b/src/pages/InvoiceIn/Card/InvoiceInIntrastat.vue new file mode 100644 index 000000000..58f521534 --- /dev/null +++ b/src/pages/InvoiceIn/Card/InvoiceInIntrastat.vue @@ -0,0 +1,280 @@ + + + (countries = data)" + sort-by="country" + /> + (intrastats = data)" + /> + + + + + + + + + + + + + + + + + (invoceInIntrastat = data)" + > + + + + + + + + + + + + + {{ `${scope.opt.id}: ${scope.opt.description}` }} + + + + + + + + + + + + + + + + + + + + + + + {{ + `${scope.opt.id}: ${scope.opt.description}` + }} + + + + + + + + + + + + + + + + + + + + + + + + + + en: + amount: Amount + net: Net + stems: Stems + country: Country + es: + Code: Código + amount: Cantidad + net: Neto + stems: Tallos + country: País + Total amount: Total importe + Total net: Total neto + Total stems: Total tallos + diff --git a/src/pages/InvoiceIn/Card/InvoiceInSummary.vue b/src/pages/InvoiceIn/Card/InvoiceInSummary.vue new file mode 100644 index 000000000..d7039fe3d --- /dev/null +++ b/src/pages/InvoiceIn/Card/InvoiceInSummary.vue @@ -0,0 +1,428 @@ + + + + setData(data)" + > + + {{ invoiceIn.id }} - {{ invoiceIn.supplier.name }} + + + + + + + {{ t('invoiceIn.pageTitles.basicData') }} + + + + + + + + + + + + {{ t('invoiceIn.pageTitles.basicData') }} + + + + + + + + + + + + {{ t('invoiceIn.pageTitles.basicData') }} + + + + + + + + + + + + {{ t('invoiceIn.pageTitles.basicData') }} + + + + + + + + + + + {{ toCurrency(invoiceIn.totals.totalDueDay) }} + + + + + + + + + + {{ t('invoiceIn.card.vat') }} + + + + + + + {{ t(col.label) }} + + + + + + + {{ toCurrency(invoiceIn.totals.totalTaxableBase) }} + + + {{ + toCurrency(getTaxTotal(invoiceIn.invoiceInTax)) + }} + + + + + + + + + {{ t('invoiceIn.card.dueDay') }} + + + + + + + {{ t(col.label) }} + + + + + + + + {{ toCurrency(invoiceIn.totals.totalDueDay) }} + + + + + + + + + {{ t('invoiceIn.card.intrastat') }} + + + + + + + {{ t(col.label) }} + + + + + + + {{ toCurrency(intrastatTotals.amount) }} + {{ intrastatTotals.net }} + {{ intrastatTotals.stems }} + + + + + + + + + + + es: + Search invoice: Buscar factura emitida + You can search by invoice reference: Puedes buscar por referencia de la factura + diff --git a/src/pages/InvoiceIn/Card/InvoiceInSummaryDialog.vue b/src/pages/InvoiceIn/Card/InvoiceInSummaryDialog.vue new file mode 100644 index 000000000..8657bf6ac --- /dev/null +++ b/src/pages/InvoiceIn/Card/InvoiceInSummaryDialog.vue @@ -0,0 +1,29 @@ + + + + + + + + + diff --git a/src/pages/InvoiceIn/Card/InvoiceInVat.vue b/src/pages/InvoiceIn/Card/InvoiceInVat.vue new file mode 100644 index 000000000..d8e742706 --- /dev/null +++ b/src/pages/InvoiceIn/Card/InvoiceInVat.vue @@ -0,0 +1,500 @@ + + + (expenses = data)" + /> + (sageTaxTypes = data)" /> + (sageTransactionTypes = data)" + /> + + + + + + + + + {{ `${scope.opt.id}: ${scope.opt.name}` }} + + + + + + + {{ t('Create expense') }} + + + + + + + + + + + + + + + + + + + + + + {{ scope.opt.vat }} + + {{ `#${scope.opt.id}` }} + + + + + + + + + + + + + + {{ + scope.opt.transaction + }} + + {{ `#${scope.opt.id}` }} + + + + + + + + + + + + + + + + + + + + + + + + + {{ `${scope.opt.id}: ${scope.opt.name}` }} + + + + + + + + + + + + + + + + + {{ + scope.opt.vat + }} + + {{ `#${scope.opt.id}` }} + + + + + + + + + + + + {{ + scope.opt.transaction + }} + + {{ `#${scope.opt.id}` }} + + + + + + + + {{ toCurrency(taxRate(props.row)) }} + + + + + + + + + + + + + + + + + + {{ t('New expense') }} + + + + + + + + + + + + + + + + + + + + + + + + + +es: + Expense: Gasto + Create expense: Crear gasto + Add tax: Crear gasto + Taxable base: Base imp. + Sage tax: Sage iva + Sage transaction: Sage transacción + Rate: Tasa + Foreign value: Divisa + New expense: Nuevo gasto + Code: Código + It's a withholding: Es una retención + Descripction: Descripción + The code can't be empty: El código no puede estar vacío + The description can't be empty: La descripción no puede estar vacía + The code have to be a number: El código debe ser un número. + diff --git a/src/pages/InvoiceIn/InvoiceInFilter.vue b/src/pages/InvoiceIn/InvoiceInFilter.vue new file mode 100644 index 000000000..6348d4167 --- /dev/null +++ b/src/pages/InvoiceIn/InvoiceInFilter.vue @@ -0,0 +1,307 @@ + + + + (suppliers = data)" + /> + + + + {{ t(`params.${tag.label}`) }}: + {{ formatFn(tag.value) }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +en: + params: + search: ID + supplierRef: Supplier ref. + supplierFk: Supplier + fi: Supplier fiscal id + clientFk: Customer + amount: Amount + created: Created + awb: AWB + dued: Dued + serialNumber: Serial Number + serial: Serial + account: Account + isBooked: is booked +es: + params: + search: Contiene + supplierRef: Ref. proveedor + supplierFk: Proveedor + clientFk: Cliente + fi: CIF proveedor + serialNumber: Num. serie + serial: Serie + awb: AWB + amount: Importe + issued: Emitida + isBooked: Conciliada + account: Cuenta + created: Creada + dued: Vencida + From: Desde + To: Hasta + Amount: Importe + Issued: Fecha factura + Id or supplier: Id o proveedor + More options: Más opciones + diff --git a/src/pages/InvoiceIn/InvoiceInList.vue b/src/pages/InvoiceIn/InvoiceInList.vue new file mode 100644 index 000000000..692251bff --- /dev/null +++ b/src/pages/InvoiceIn/InvoiceInList.vue @@ -0,0 +1,179 @@ + + + + + + + + + + + + {{ t('globals.collapseMenu') }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ t('components.smartCard.openCard') }} + + + + + {{ t('components.smartCard.openSummary') }} + + + + + {{ t('components.smartCard.downloadFile') }} + + + + + + + + + + + + + + + + +es: + Search invoice: Buscar factura emitida + You can search by invoice reference: Puedes buscar por referencia de la factura + diff --git a/src/pages/InvoiceIn/InvoiceInMain.vue b/src/pages/InvoiceIn/InvoiceInMain.vue new file mode 100644 index 000000000..66ce78f23 --- /dev/null +++ b/src/pages/InvoiceIn/InvoiceInMain.vue @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/src/pages/InvoiceOut/Card/InvoiceOutDescriptor.vue b/src/pages/InvoiceOut/Card/InvoiceOutDescriptor.vue index f6abd77e0..9c1bb3649 100644 --- a/src/pages/InvoiceOut/Card/InvoiceOutDescriptor.vue +++ b/src/pages/InvoiceOut/Card/InvoiceOutDescriptor.vue @@ -7,6 +7,7 @@ import CardDescriptor from 'components/ui/CardDescriptor.vue'; import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue'; import VnLv from 'src/components/ui/VnLv.vue'; import useCardDescription from 'src/composables/useCardDescription'; +import InvoiceOutDescriptorMenu from './InvoiceOutDescriptorMenu.vue'; const $props = defineProps({ id: { @@ -57,13 +58,14 @@ const setData = (entity) => (data.value = useCardDescription(entity.ref, entity. :title="data.title" :subtitle="data.subtitle" @on-fetch="setData" + data-key="invoiceOutData" > + + + - + diff --git a/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue b/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue new file mode 100644 index 000000000..ab8b6470b --- /dev/null +++ b/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue @@ -0,0 +1,40 @@ + + + + + {{ t('Transfer invoice to') }} + + + {{ t('See invoice') }} + + + {{ t('Send invoice') }} + + + {{ t('Delete invoice') }} + + + {{ t('Post invoice') }} + + + {{ t('Regenerate invoice PDF') }} + + + {{ t('Pass') }} + + + + +es: + Transfer invoice to: Transferir factura a + See invoice: Ver factura + Send invoice: Enviar factura + Delete invoice: Eliminar factura + Post invoice: Asentar factura + Regenerate invoice PDF: Regenerar PDF factura + Pass: Abono + diff --git a/src/pages/InvoiceOut/InvoiceOutFilter.vue b/src/pages/InvoiceOut/InvoiceOutFilter.vue index 1ecaf828b..5d965816f 100644 --- a/src/pages/InvoiceOut/InvoiceOutFilter.vue +++ b/src/pages/InvoiceOut/InvoiceOutFilter.vue @@ -3,6 +3,7 @@ import { ref } from 'vue'; import { useI18n } from 'vue-i18n'; import FetchData from 'components/FetchData.vue'; import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; +import VnInputDate from 'components/common/VnInputDate.vue'; const { t } = useI18n(); const props = defineProps({ @@ -43,66 +44,72 @@ function setWorkers(data) { - - - - - - - - - - - - - - - - - - - - - - + outlined + rounded + v-model="params.clientFk" + /> + + + + + + + + + + - - - - + /> - - - - + /> @@ -110,115 +117,35 @@ function setWorkers(data) { - - - - - - - - - - - - - - + :label="t('Issued')" + dense + outlined + rounded + /> - - - - - - - - - - - - - - + :label="t('Created')" + dense + outlined + rounded + /> - - - - - - - - - - - - - - + diff --git a/src/pages/InvoiceOut/InvoiceOutGlobal.vue b/src/pages/InvoiceOut/InvoiceOutGlobal.vue new file mode 100644 index 000000000..7284ca04e --- /dev/null +++ b/src/pages/InvoiceOut/InvoiceOutGlobal.vue @@ -0,0 +1,198 @@ + + + + + + + + + + + + + {{ t(`status.${status}`) }} + {{ + t('invoiceOut.globalInvoices.statusCard.percentageText', { + getPercentage: getPercentage, + getAddressNumber: getAddressNumber, + getNAddresses: getNAddresses, + }) + }} + {{ + t('invoiceOut.globalInvoices.statusCard.pdfsNumberText', { + nPdfs: nPdfs, + totalPdfs: totalPdfs, + }) + }} + + + + + + + + {{ props.value }} + + + + + + + + + + + + + +en: + status: + packageInvoicing: Build packaging tickets + invoicing: Invoicing client + stopping: Stopping process + done: Ended process + of: of + +es: + status: + packageInvoicing: Generación de tickets de empaque + invoicing: Facturando a cliente + stopping: Deteniendo proceso + done: Proceso detenido + of: de + diff --git a/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue b/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue new file mode 100644 index 000000000..c15d56671 --- /dev/null +++ b/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue @@ -0,0 +1,201 @@ + + + + onFetchCompanies(data)" auto-load /> + onFetchPrinters(data)" auto-load /> + onFetchClients(data)" auto-load /> + + + + + + + + + + + + + + + + + + + + +en: + invoiceDate: Invoice date + maxShipped: Max date + allClients: All clients + oneClient: One client + company: Company + printer: Printer + invoiceOut: Invoice out + client: Client + stop: Stop + +es: + invoiceDate: Fecha de factura + maxShipped: Fecha límite + allClients: Todos los clientes + oneClient: Un solo cliente + company: Empresa + printer: Impresora + invoiceOut: Facturar + client: Cliente + stop: Parar + diff --git a/src/pages/InvoiceOut/InvoiceOutList.vue b/src/pages/InvoiceOut/InvoiceOutList.vue index 137dedba8..292fa2e0a 100644 --- a/src/pages/InvoiceOut/InvoiceOutList.vue +++ b/src/pages/InvoiceOut/InvoiceOutList.vue @@ -1,8 +1,8 @@ {{ t('globals.collapseMenu') }} @@ -64,71 +121,128 @@ function viewSummary(id) { - - - - - - - - - - - - - - - - - - {{ t('components.smartCard.openCard') }} - - - - - {{ t('components.smartCard.openSummary') }} - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -140,7 +254,14 @@ function viewSummary(id) { +en: + searchInvoice: Search issued invoice + fileDenied: Browser denied file download... + fileAllowed: Successful download of CSV file + youCanSearchByInvoiceReference: You can search by invoice reference es: - Search invoice: Buscar factura emitida - You can search by invoice reference: Puedes buscar por referencia de la factura + searchInvoice: Buscar factura emitida + fileDenied: El navegador denegó la descarga de archivos... + fileAllowed: Descarga exitosa de archivo CSV + youCanSearchByInvoiceReference: Puedes buscar por referencia de la factura diff --git a/src/pages/InvoiceOut/InvoiceOutNegativeBases.vue b/src/pages/InvoiceOut/InvoiceOutNegativeBases.vue new file mode 100644 index 000000000..63385380e --- /dev/null +++ b/src/pages/InvoiceOut/InvoiceOutNegativeBases.vue @@ -0,0 +1,356 @@ + + + + + + + + + + + + + + + + {{ rows.length }} {{ t('results') }} + + + + + + + + + + {{ t(`invoiceOut.negativeBases.${col.label}`) }} + + + + + + + + + {{ props.value }} + + + + + + + + + + + + + diff --git a/src/pages/Route/Cmr/CmrFilter.vue b/src/pages/Route/Cmr/CmrFilter.vue index b8948652a..bab300672 100644 --- a/src/pages/Route/Cmr/CmrFilter.vue +++ b/src/pages/Route/Cmr/CmrFilter.vue @@ -3,6 +3,7 @@ import { ref } from 'vue'; import { useI18n } from 'vue-i18n'; import FetchData from 'components/FetchData.vue'; import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; +import VnInputDate from "components/common/VnInputDate.vue"; const { t } = useI18n(); const props = defineProps({ @@ -111,35 +112,7 @@ const countries = ref(); - - - - - - - - - - - - - + @@ -156,7 +129,7 @@ const countries = ref(); country: Country clientFk: Client id shipped: Preparation date - + es: params: cmrFk: Id cmr diff --git a/src/pages/Shelving/Card/ShelvingCard.vue b/src/pages/Shelving/Card/ShelvingCard.vue new file mode 100644 index 000000000..221740486 --- /dev/null +++ b/src/pages/Shelving/Card/ShelvingCard.vue @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + diff --git a/src/pages/Shelving/Card/ShelvingDescriptor.vue b/src/pages/Shelving/Card/ShelvingDescriptor.vue new file mode 100644 index 000000000..34c374117 --- /dev/null +++ b/src/pages/Shelving/Card/ShelvingDescriptor.vue @@ -0,0 +1,71 @@ + + + + + + + + + + + {{ entity.worker?.user?.nickname }} + + + + + + + + + + diff --git a/src/pages/Shelving/Card/ShelvingDescriptorMenu.vue b/src/pages/Shelving/Card/ShelvingDescriptorMenu.vue new file mode 100644 index 000000000..6290cda75 --- /dev/null +++ b/src/pages/Shelving/Card/ShelvingDescriptorMenu.vue @@ -0,0 +1,61 @@ + + + + + + + {{ t('deleteShelving') }} + + + + +{ + "en": { + "deleteShelving": "Delete Shelving" + }, + "es": { + "deleteShelving": "Eliminar carro" + } +} + diff --git a/src/pages/Shelving/Card/ShelvingDescriptorProxy.vue b/src/pages/Shelving/Card/ShelvingDescriptorProxy.vue new file mode 100644 index 000000000..8023271b0 --- /dev/null +++ b/src/pages/Shelving/Card/ShelvingDescriptorProxy.vue @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/pages/Shelving/Card/ShelvingFilter.vue b/src/pages/Shelving/Card/ShelvingFilter.vue new file mode 100644 index 000000000..423402f89 --- /dev/null +++ b/src/pages/Shelving/Card/ShelvingFilter.vue @@ -0,0 +1,120 @@ + + + + + + + + + {{ t(`params.${tag.label}`) }}: + {{ formatFn(tag.value) }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +en: + params: + parkingFk: Parking + userFk: Worker + isRecyclable: Recyclable +es: + params: + parkingFk: Parking + userFk: Trabajador + isRecyclable: Reciclable + diff --git a/src/pages/Shelving/Card/ShelvingForm.vue b/src/pages/Shelving/Card/ShelvingForm.vue new file mode 100644 index 000000000..e30ac1939 --- /dev/null +++ b/src/pages/Shelving/Card/ShelvingForm.vue @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + filter(value, update, parkingSelectFilter) + " + :rules="validate('Shelving.parkingFk')" + :input-debounce="0" + /> + + + + + + + + + + + + + diff --git a/src/pages/Shelving/Card/ShelvingLog.vue b/src/pages/Shelving/Card/ShelvingLog.vue new file mode 100644 index 000000000..8f355d2ca --- /dev/null +++ b/src/pages/Shelving/Card/ShelvingLog.vue @@ -0,0 +1,6 @@ + + + + diff --git a/src/pages/Shelving/Card/ShelvingSearchbar.vue b/src/pages/Shelving/Card/ShelvingSearchbar.vue new file mode 100644 index 000000000..89a4de01e --- /dev/null +++ b/src/pages/Shelving/Card/ShelvingSearchbar.vue @@ -0,0 +1,20 @@ + + + + + + + + +es: + Search shelving: Buscar carros + You can search by shelving reference: Puedes buscar por referencia del carro + diff --git a/src/pages/Shelving/Card/ShelvingSummary.vue b/src/pages/Shelving/Card/ShelvingSummary.vue new file mode 100644 index 000000000..f1f914385 --- /dev/null +++ b/src/pages/Shelving/Card/ShelvingSummary.vue @@ -0,0 +1,110 @@ + + + + + + + + + {{ t('globals.collapseMenu') }} + + + + + + + + + {{ entity.code }} + + + + + {{ t('shelving.pageTitles.basicData') }} + + + + + + + + + + + + + + + + diff --git a/src/pages/Shelving/Card/ShelvingSummaryDialog.vue b/src/pages/Shelving/Card/ShelvingSummaryDialog.vue new file mode 100644 index 000000000..783e47395 --- /dev/null +++ b/src/pages/Shelving/Card/ShelvingSummaryDialog.vue @@ -0,0 +1,29 @@ + + + + + + + + + diff --git a/src/pages/Shelving/ShelvingList.vue b/src/pages/Shelving/ShelvingList.vue new file mode 100644 index 000000000..71b3a6ccf --- /dev/null +++ b/src/pages/Shelving/ShelvingList.vue @@ -0,0 +1,138 @@ + + + + + + + + + + + + {{ t('globals.collapseMenu') }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ t('shelving.list.newShelving') }} + + + + + + + diff --git a/src/pages/Shelving/ShelvingMain.vue b/src/pages/Shelving/ShelvingMain.vue new file mode 100644 index 000000000..66ce78f23 --- /dev/null +++ b/src/pages/Shelving/ShelvingMain.vue @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/src/pages/Supplier/Card/SupplierCard.vue b/src/pages/Supplier/Card/SupplierCard.vue new file mode 100644 index 000000000..cf94c9e8d --- /dev/null +++ b/src/pages/Supplier/Card/SupplierCard.vue @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/pages/Supplier/Card/SupplierDescriptor.vue b/src/pages/Supplier/Card/SupplierDescriptor.vue new file mode 100644 index 000000000..910d36489 --- /dev/null +++ b/src/pages/Supplier/Card/SupplierDescriptor.vue @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + diff --git a/src/pages/Supplier/Card/SupplierDescriptorProxy.vue b/src/pages/Supplier/Card/SupplierDescriptorProxy.vue new file mode 100644 index 000000000..b730a39dd --- /dev/null +++ b/src/pages/Supplier/Card/SupplierDescriptorProxy.vue @@ -0,0 +1,16 @@ + + + + + + + diff --git a/src/pages/Supplier/Card/SupplierSummary.vue b/src/pages/Supplier/Card/SupplierSummary.vue new file mode 100644 index 000000000..e9fbffe96 --- /dev/null +++ b/src/pages/Supplier/Card/SupplierSummary.vue @@ -0,0 +1,188 @@ + + + + setData(data)" + > + + + + + + + {{ supplier.name }} - {{ supplier.id }} + + + + + + {{ t('globals.summary.basicData') }} + + + {{ t('globals.summary.basicData') }} + + + + + + {{ dashIfEmpty(supplier.worker?.user?.nickname) }} + + + + + + + {{ dashIfEmpty(supplier.note) }} + + + + + + + + + + + + + + + + + {{ t('supplier.summary.billingData') }} + + + {{ t('supplier.summary.billingData') }} + + + + + + + + {{ t('supplier.summary.fiscalData') }} + + + {{ t('supplier.summary.fiscalData') }} + + + + + + + + + {{ t('supplier.summary.fiscalAddress') }} + + + {{ t('supplier.summary.fiscalAddress') }} + + + + + + + + + + + + diff --git a/src/pages/Supplier/Card/SupplierSummaryDialog.vue b/src/pages/Supplier/Card/SupplierSummaryDialog.vue new file mode 100644 index 000000000..6353caa38 --- /dev/null +++ b/src/pages/Supplier/Card/SupplierSummaryDialog.vue @@ -0,0 +1,29 @@ + + + + + + + + + diff --git a/src/pages/Supplier/SupplierCreate.vue b/src/pages/Supplier/SupplierCreate.vue new file mode 100644 index 000000000..9b792483b --- /dev/null +++ b/src/pages/Supplier/SupplierCreate.vue @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/pages/Supplier/SupplierList.vue b/src/pages/Supplier/SupplierList.vue new file mode 100644 index 000000000..ea7bc3cb0 --- /dev/null +++ b/src/pages/Supplier/SupplierList.vue @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ t('supplier.list.newSupplier') }} + + + + + + + + +en: + Search suppliers: Search suppliers + +es: + Search suppliers: Buscar proveedores + diff --git a/src/pages/Supplier/SupplierMain.vue b/src/pages/Supplier/SupplierMain.vue new file mode 100644 index 000000000..66ce78f23 --- /dev/null +++ b/src/pages/Supplier/SupplierMain.vue @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/src/pages/Ticket/Card/TicketDescriptor.vue b/src/pages/Ticket/Card/TicketDescriptor.vue index 641ffee0c..d2a407874 100644 --- a/src/pages/Ticket/Card/TicketDescriptor.vue +++ b/src/pages/Ticket/Card/TicketDescriptor.vue @@ -81,6 +81,7 @@ const setData = (entity) => :filter="filter" :title="data.title" :subtitle="data.subtitle" + data-key="ticketData" @on-fetch="setData" > diff --git a/src/pages/Ticket/Card/TicketSummary.vue b/src/pages/Ticket/Card/TicketSummary.vue index ce46d1d47..cfc35c032 100644 --- a/src/pages/Ticket/Card/TicketSummary.vue +++ b/src/pages/Ticket/Card/TicketSummary.vue @@ -10,6 +10,7 @@ import FetchedTags from 'components/ui/FetchedTags.vue'; import InvoiceOutDescriptorProxy from 'pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue'; import WorkerDescriptorProxy from 'pages/Worker/Card/WorkerDescriptorProxy.vue'; import VnLv from 'src/components/ui/VnLv.vue'; +import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue'; import { getUrl } from 'src/composables/getUrl'; onUpdated(() => summaryRef.value.fetch()); @@ -172,7 +173,7 @@ async function changeState(value) { :label="t('ticket.summary.agency')" :value="ticket.agencyMode.name" /> - + - + {{ dashIfEmpty(ticket.refFk) }} - - - - - + + + + {{ t('ticket.summary.consigneePhone') }} + + + + + + {{ t('ticket.summary.consigneeMobile') }} + + + + + + {{ t('ticket.summary.clientPhone') }} + + + + + + {{ t('ticket.summary.clientMobile') }} + + + {{ t('ticket.summary.description') }} {{ t('ticket.summary.price') }} {{ t('ticket.summary.discount') }} - {{ t('ticket.summary.amount') }} + {{ t('globals.amount') }} {{ t('ticket.summary.packing') }} @@ -391,7 +400,7 @@ async function changeState(value) { v-if="ticket.packagings.length > 0 || ticket.services.length > 0" > - {{ t('ticket.summary.packages') }} + {{ t('globals.packages') }} @@ -422,7 +431,7 @@ async function changeState(value) { {{ t('ticket.summary.description') }} {{ t('ticket.summary.price') }} {{ t('ticket.summary.taxClass') }} - {{ t('ticket.summary.amount') }} + {{ t('globals.amount') }} diff --git a/src/pages/Ticket/TicketFilter.vue b/src/pages/Ticket/TicketFilter.vue index aa98b9564..69f0b1c11 100644 --- a/src/pages/Ticket/TicketFilter.vue +++ b/src/pages/Ticket/TicketFilter.vue @@ -4,6 +4,7 @@ import { useI18n } from 'vue-i18n'; import FetchData from 'components/FetchData.vue'; import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; import toDateString from 'filters/toDateString'; +import VnInputDate from "components/common/VnInputDate.vue"; const { t } = useI18n(); const props = defineProps({ @@ -71,69 +72,10 @@ const warehouses = ref(); - - - - - - - - - - - - - - + - - - - - - - - - - - - - - + diff --git a/src/pages/Ticket/TicketList.vue b/src/pages/Ticket/TicketList.vue index b9f0f57e7..7d7429630 100644 --- a/src/pages/Ticket/TicketList.vue +++ b/src/pages/Ticket/TicketList.vue @@ -68,7 +68,7 @@ function viewSummary(id) { - + @@ -87,6 +87,7 @@ function viewSummary(id) { v-for="row of rows" :key="row.id" :id="row.id" + :title="`${row.nickname} (${row.id})`" @click="navigate(row.id)" > @@ -120,11 +121,11 @@ function viewSummary(id) { /> - - - {{ t('components.smartCard.openSummary') }} - - + diff --git a/src/pages/Travel/Card/TravelCard.vue b/src/pages/Travel/Card/TravelCard.vue new file mode 100644 index 000000000..6309c14ab --- /dev/null +++ b/src/pages/Travel/Card/TravelCard.vue @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/pages/Travel/Card/TravelDescriptor.vue b/src/pages/Travel/Card/TravelDescriptor.vue new file mode 100644 index 000000000..6a45710ad --- /dev/null +++ b/src/pages/Travel/Card/TravelDescriptor.vue @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + diff --git a/src/pages/Travel/Card/TravelDescriptorProxy.vue b/src/pages/Travel/Card/TravelDescriptorProxy.vue new file mode 100644 index 000000000..ab5c42d7e --- /dev/null +++ b/src/pages/Travel/Card/TravelDescriptorProxy.vue @@ -0,0 +1,16 @@ + + + + + + + diff --git a/src/pages/Travel/Card/TravelSummary.vue b/src/pages/Travel/Card/TravelSummary.vue new file mode 100644 index 000000000..ad4659b11 --- /dev/null +++ b/src/pages/Travel/Card/TravelSummary.vue @@ -0,0 +1,323 @@ + + + + setTravelData(data)" + > + + + + + + + {{ travel.ref }} - {{ travel.id }} + + + + + + {{ t('components.cardDescriptor.moreOptions') }} + + + + + {{ option.name }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ t('travel.summary.entries') }} + + + + + + + {{ props.value }} + {{ + props.col.toolTip + }} + + + + + + + + + diff --git a/src/pages/Travel/Card/TravelSummaryDialog.vue b/src/pages/Travel/Card/TravelSummaryDialog.vue new file mode 100644 index 000000000..8351d6e5b --- /dev/null +++ b/src/pages/Travel/Card/TravelSummaryDialog.vue @@ -0,0 +1,29 @@ + + + + + + + + + diff --git a/src/pages/Travel/ExtraCommunity.vue b/src/pages/Travel/ExtraCommunity.vue new file mode 100644 index 000000000..cef3ed998 --- /dev/null +++ b/src/pages/Travel/ExtraCommunity.vue @@ -0,0 +1,419 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ col.value }} + + + + + + + + {{ col.value }} + + + + + + + + + + {{ entry.id }} + + + + {{ + entry.supplierName + }} + + + + {{ + toCurrency(entry.invoiceAmount) + }} + {{ entry.reference }} + {{ entry.stickers }} + + {{ entry.loadedkg }} + {{ entry.volumeKg }} + + + + + + + + + + + + + +en: + searchExtraCommunity: Search for extra community shipping + kg: BI. KG + physicKg: Phy. KG + shipped: W. shipped + landed: W. landed + +es: + searchExtraCommunity: Buscar por envío extra comunitario + kg: KG Bloq. + physicKg: KG físico + shipped: F. envío + landed: F. llegada + diff --git a/src/pages/Travel/ExtraCommunityFilter.vue b/src/pages/Travel/ExtraCommunityFilter.vue new file mode 100644 index 000000000..2d3629de4 --- /dev/null +++ b/src/pages/Travel/ExtraCommunityFilter.vue @@ -0,0 +1,263 @@ + + + + updateFilterOptions(data, 'warehouses')" + auto-load + /> + updateFilterOptions(data, 'continents')" + auto-load + /> + updateFilterOptions(data, 'agencies')" + auto-load + /> + updateFilterOptions(data, 'suppliers')" + auto-load + /> + + + + + {{ t(`params.${tag.label}`) }}: + {{ formatFn(tag.value) }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +en: + params: + ref: 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: + ref: Referencia + totalEntries: Ent. totales + agencyModeFk: Agencia + warehouseInFk: Alm. entrada + warehouseOutFk: Alm. salida + shippedFrom: Llegada desde + landedTo: Llegada hasta + cargoSupplierFk: Proveedor + continent: Cont. Salida + diff --git a/src/pages/Travel/TravelCreate.vue b/src/pages/Travel/TravelCreate.vue new file mode 100644 index 000000000..434bbb238 --- /dev/null +++ b/src/pages/Travel/TravelCreate.vue @@ -0,0 +1,179 @@ + + + + onFetchAgencies(data)" auto-load /> + onFetchWarehouses(data)" auto-load /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/pages/Travel/TravelFilter.vue b/src/pages/Travel/TravelFilter.vue new file mode 100644 index 000000000..2c2d80488 --- /dev/null +++ b/src/pages/Travel/TravelFilter.vue @@ -0,0 +1,311 @@ + + + + updateFilterOptions(data, 'warehouses')" + auto-load + /> + updateFilterOptions(data, 'continents')" + auto-load + /> + updateFilterOptions(data, 'agencies')" + auto-load + /> + + + + + {{ t(`params.${tag.label}`) }}: + {{ formatFn(tag.value) }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +en: + params: + search: Id/Reference + agencyModeFk: Agency + warehouseInFk: Warehouse In + warehouseOutFk: Warehouse Out + scopeDays: Days onward + landedFrom: Landed from + landedTo: Landed to + continent: Continent out + totalEntries: Total entries +es: + params: + search: Id/Referencia + agencyModeFk: Agencia + warehouseInFk: Alm. entrada + warehouseOutFk: Alm. salida + scopeDays: Días adelante + landedFrom: Llegada desde + landedTo: Llegada hasta + continent: Cont. Salida + totalEntries: Ent. totales + + diff --git a/src/pages/Travel/TravelList.vue b/src/pages/Travel/TravelList.vue new file mode 100644 index 000000000..d62485a4c --- /dev/null +++ b/src/pages/Travel/TravelList.vue @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ t('supplier.list.newSupplier') }} + + + + + + + + +en: + addEntry: Add entry + + +es: + addEntry: Añadir entrada + + + diff --git a/src/pages/Travel/TravelMain.vue b/src/pages/Travel/TravelMain.vue new file mode 100644 index 000000000..66ce78f23 --- /dev/null +++ b/src/pages/Travel/TravelMain.vue @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/src/pages/Wagon/Type/WagonTypeCreate.vue b/src/pages/Wagon/Type/WagonTypeCreate.vue index adfd41049..50be4169c 100644 --- a/src/pages/Wagon/Type/WagonTypeCreate.vue +++ b/src/pages/Wagon/Type/WagonTypeCreate.vue @@ -239,8 +239,8 @@ function exceedMaxHeight(pos) { - - + + - + - - - - - - + + + + + + @@ -357,45 +357,55 @@ function exceedMaxHeight(pos) { justify-content: center; align-items: flex-start; } -.q-card { + +.q-form { width: 70%; } + .q-dialog { .q-card { width: 100%; } } + .wheels { margin-left: 5%; display: flex; justify-content: space-around; } + .wagon-tray { display: flex; height: 6rem; + .position { - width: 15%; + width: 20%; border-right: 1rem solid gray; display: flex; align-items: flex-end; justify-content: flex-end; padding-right: 1rem; } + .shelving { display: flex; width: 75%; + .shelving-half { width: 50%; height: 100%; + .shelving-up { height: 80%; width: 100%; } + .shelving-down { height: 20%; width: 100%; } } + .shelving-divisible { width: 1%; height: 100%; @@ -403,6 +413,7 @@ function exceedMaxHeight(pos) { border-right: 0.5rem dashed grey; } } + .action-button { width: 10%; border-left: 1rem solid gray; diff --git a/src/pages/Wagon/Type/WagonTypeList.vue b/src/pages/Wagon/Type/WagonTypeList.vue index f364da008..8e4ace744 100644 --- a/src/pages/Wagon/Type/WagonTypeList.vue +++ b/src/pages/Wagon/Type/WagonTypeList.vue @@ -5,6 +5,8 @@ import VnPaginate from 'src/components/ui/VnPaginate.vue'; import { useArrayData } from 'src/composables/useArrayData'; import { useI18n } from 'vue-i18n'; import { useRouter } from 'vue-router'; +import CardList from 'components/ui/CardList.vue'; +import VnLv from 'components/ui/VnLv.vue'; const quasar = useQuasar(); const arrayData = useArrayData('WagonTypeList'); @@ -48,43 +50,28 @@ async function remove(row) { auto-load > - - - - {{ row.name }} - #{{ row.id }} - - - - - - {{ t('components.smartCard.openCard') }} - - - - - {{ t('wagon.list.remove') }} - - - - - + + + + + + diff --git a/src/pages/Wagon/WagonCounter.vue b/src/pages/Wagon/WagonCounter.vue new file mode 100644 index 000000000..bd5d2ca67 --- /dev/null +++ b/src/pages/Wagon/WagonCounter.vue @@ -0,0 +1,154 @@ + + + + + + + {{ props.title }} + + + + {{ props.count }} + + + + {{ t('Add 30') }} + + + {{ t('Add 10') }} + + + + + {{ t('Subtract 1') }} + + + {{ t('Flush') }} + + + + + + + + + +es: + Subtract 1: Quitar 1 + Add 30: Añadir 30 + Add 10: Añadir 10 + Flush: Vaciar + Are you sure?: ¿Estás seguro? + It will set to 0: Se pondrá a 0 + The counter will be reset to zero: Se pondrá el contador a cero + diff --git a/src/pages/Wagon/WagonCreate.vue b/src/pages/Wagon/WagonCreate.vue index 123e01d36..0bd6f87be 100644 --- a/src/pages/Wagon/WagonCreate.vue +++ b/src/pages/Wagon/WagonCreate.vue @@ -86,9 +86,9 @@ function filterType(val, update) { - - - + + + - + - - - - - - + + + + + + @@ -176,7 +176,8 @@ function filterType(val, update) { justify-content: center; align-items: flex-start; } -.q-card { + +.q-form { width: 70%; } diff --git a/src/pages/Wagon/WagonList.vue b/src/pages/Wagon/WagonList.vue index 5e512aa92..77d9da0bf 100644 --- a/src/pages/Wagon/WagonList.vue +++ b/src/pages/Wagon/WagonList.vue @@ -5,6 +5,8 @@ import VnPaginate from 'src/components/ui/VnPaginate.vue'; import { useArrayData } from 'src/composables/useArrayData'; import { useI18n } from 'vue-i18n'; import { useRouter } from 'vue-router'; +import CardList from 'components/ui/CardList.vue'; +import VnLv from 'components/ui/VnLv.vue'; const quasar = useQuasar(); const arrayData = useArrayData('WagonList'); @@ -55,69 +57,40 @@ async function remove(row) { auto-load > - - - - {{ row.label }} - #{{ row.id }} - - - - - {{ t('wagon.list.plate') }} - - {{ row.plate }} - - - - - - {{ t('wagon.list.volume') }} - - {{ row.volume }} - - - - - - {{ t('wagon.list.type') }} - - {{ row.type.name }} - - - - - - - - - {{ t('components.smartCard.openCard') }} - - - - - {{ t('wagon.list.remove') }} - - - - - + + + + + + + + + + + diff --git a/src/pages/Worker/Card/WorkerDescriptor.vue b/src/pages/Worker/Card/WorkerDescriptor.vue index ba64a5abb..f089c0022 100644 --- a/src/pages/Worker/Card/WorkerDescriptor.vue +++ b/src/pages/Worker/Card/WorkerDescriptor.vue @@ -5,13 +5,19 @@ import { useI18n } from 'vue-i18n'; import { useSession } from 'src/composables/useSession'; import CardDescriptor from 'src/components/ui/CardDescriptor.vue'; import VnLv from 'src/components/ui/VnLv.vue'; +import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue'; import useCardDescription from 'src/composables/useCardDescription'; + const $props = defineProps({ id: { type: Number, required: false, default: null, }, + summary: { + type: Object, + default: null, + }, }); const route = useRoute(); @@ -47,23 +53,27 @@ const filter = { ], }; -const sip = computed(() => worker.value.sip && worker.value.sip.extension); +const sip = computed(() => worker.value?.sip && worker.value.sip.extension); function getWorkerAvatar() { const token = getToken(); - return `/api/Images/user/160x160/${route.params.id}/download?access_token=${token}`; + return `/api/Images/user/160x160/${entityId.value}/download?access_token=${token}`; } const data = ref(useCardDescription()); -const setData = (entity) => - (data.value = useCardDescription(entity.user.nickname, entity.id)); +const setData = (entity) => { + if (!entity) return; + data.value = useCardDescription(entity.user.nickname, entity.id); +}; { worker = data; @@ -90,14 +100,24 @@ const setData = (entity) => - - + + - - + + + {{ t('worker.card.phone') }} + + + + + + {{ t('worker.summary.sipExtension') }} + + + diff --git a/src/pages/Worker/Card/WorkerDescriptorProxy.vue b/src/pages/Worker/Card/WorkerDescriptorProxy.vue index 8d372b6de..98b06ad6e 100644 --- a/src/pages/Worker/Card/WorkerDescriptorProxy.vue +++ b/src/pages/Worker/Card/WorkerDescriptorProxy.vue @@ -1,5 +1,6 @@
+ {{ t('claim.summary.actions') }} +
{{ props.title }}