Merge pull request 'Merge dev-test' (!123) from dev into test
gitea/salix-front/pipeline/head This commit looks good
Details
gitea/salix-front/pipeline/head This commit looks good
Details
Reviewed-on: #123 Reviewed-by: Javi Gallego <jgallego@verdnatura.es>
This commit is contained in:
commit
745fcfe2c9
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -5,6 +5,16 @@ 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).
|
||||
|
||||
## [2352.01] - 2023-12-28
|
||||
|
||||
### 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
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "salix-front",
|
||||
"version": "23.48.01",
|
||||
"version": "23.52.01",
|
||||
"description": "Salix frontend",
|
||||
"productName": "Salix",
|
||||
"author": "Verdnatura",
|
||||
|
|
|
@ -269,7 +269,7 @@ watch(formUrl, async () => {
|
|||
</VnPaginate>
|
||||
<SkeletonTable v-if="!formData" />
|
||||
<Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
|
||||
<QBtnGroup push class="q-gutter-x-sm">
|
||||
<QBtnGroup push style="column-gap: 10px">
|
||||
<slot name="moreBeforeActions" />
|
||||
<QBtn
|
||||
:label="tMobile('globals.remove')"
|
||||
|
|
|
@ -25,27 +25,17 @@ const pinnedModulesRef = ref();
|
|||
|
||||
<template>
|
||||
<QHeader class="bg-dark" color="white" elevated>
|
||||
<QToolbar class="q-py-sm q-px-md">
|
||||
<QBtn
|
||||
@click="stateStore.toggleLeftDrawer()"
|
||||
icon="menu"
|
||||
class="q-mr-sm"
|
||||
round
|
||||
dense
|
||||
flat
|
||||
>
|
||||
<QToolbar
|
||||
class="q-py-sm q-px-md"
|
||||
:class="{ 'q-gutter-x-sm': !quasar.platform.is.mobile }"
|
||||
>
|
||||
<QBtn @click="stateStore.toggleLeftDrawer()" icon="menu" round dense flat>
|
||||
<QTooltip bottom anchor="bottom right">
|
||||
{{ t('globals.collapseMenu') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
<RouterLink to="/">
|
||||
<QBtn
|
||||
class="q-ml-xs"
|
||||
color="primary"
|
||||
flat
|
||||
round
|
||||
v-if="!quasar.platform.is.mobile"
|
||||
>
|
||||
<QBtn color="primary" flat round v-if="!quasar.platform.is.mobile">
|
||||
<QAvatar square size="md">
|
||||
<QImg
|
||||
src="~/assets/salix_icon.svg"
|
||||
|
|
|
@ -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,10 @@ const $props = defineProps({
|
|||
type: String,
|
||||
default: '',
|
||||
},
|
||||
isClearable: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
const { optionLabel, options } = toRefs($props);
|
||||
const myOptions = ref([]);
|
||||
|
@ -81,11 +85,10 @@ const value = computed({
|
|||
map-options
|
||||
use-input
|
||||
@filter="filterHandler"
|
||||
hide-selected
|
||||
fill-input
|
||||
ref="vnSelectRef"
|
||||
>
|
||||
<template #append>
|
||||
<template v-if="isClearable" #append>
|
||||
<QIcon name="close" @click.stop="value = null" class="cursor-pointer" />
|
||||
</template>
|
||||
<template v-for="(_, slotName) in $slots" #[slotName]="slotData">
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
<script setup>
|
||||
import { onMounted, useSlots, ref, watch } from 'vue';
|
||||
import { onMounted, useSlots, ref, watch, computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import axios from 'axios';
|
||||
import SkeletonDescriptor from 'components/ui/SkeletonDescriptor.vue';
|
||||
import { useArrayData } from 'composables/useArrayData';
|
||||
|
||||
const $props = defineProps({
|
||||
url: {
|
||||
|
@ -25,33 +26,37 @@ const $props = defineProps({
|
|||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
dataKey: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
|
||||
const slots = useSlots();
|
||||
const { t } = useI18n();
|
||||
const entity = ref();
|
||||
|
||||
const entity = computed(() => useArrayData($props.dataKey).store.data);
|
||||
onMounted(async () => {
|
||||
await fetch();
|
||||
await getData();
|
||||
watch(
|
||||
() => $props.url,
|
||||
async (newUrl, lastUrl) => {
|
||||
if (newUrl == lastUrl) return;
|
||||
entity.value = null;
|
||||
await getData();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
const emit = defineEmits(['onFetch']);
|
||||
|
||||
async function fetch() {
|
||||
const params = {};
|
||||
|
||||
if ($props.filter) params.filter = JSON.stringify($props.filter);
|
||||
|
||||
const { data } = await axios.get($props.url, { params });
|
||||
entity.value = data;
|
||||
|
||||
async function getData() {
|
||||
const arrayData = useArrayData($props.dataKey, {
|
||||
url: $props.url,
|
||||
filter: $props.filter,
|
||||
skip: 0,
|
||||
});
|
||||
const { data } = await arrayData.fetch({ append: false });
|
||||
emit('onFetch', data);
|
||||
}
|
||||
|
||||
watch($props, async () => {
|
||||
entity.value = null;
|
||||
await fetch();
|
||||
});
|
||||
const emit = defineEmits(['onFetch']);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -41,15 +41,11 @@ 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;
|
||||
|
@ -78,10 +74,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 +86,7 @@ const tags = computed(() => {
|
|||
});
|
||||
|
||||
async function remove(key) {
|
||||
delete userParams.value[key];
|
||||
delete store.userParams[key];
|
||||
userParams.value[key] = null;
|
||||
await search();
|
||||
}
|
||||
|
||||
|
@ -200,7 +196,7 @@ function formatValue(value) {
|
|||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
es:
|
||||
No filters applied: No se han aplicado filtros
|
||||
Applied filters: Filtros aplicados
|
||||
Remove filters: Eliminar filtros
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
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(store?.exprBuilder(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);
|
||||
|
|
|
@ -45,3 +45,9 @@ body.body--dark {
|
|||
.bg-vn-dark {
|
||||
background-color: var(--vn-dark);
|
||||
}
|
||||
|
||||
.vn-card {
|
||||
background-color: var(--vn-gray);
|
||||
color: var(--vn-text);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
|
|
@ -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 };
|
|
@ -274,6 +274,7 @@ export default {
|
|||
development: 'Development',
|
||||
log: 'Audit logs',
|
||||
notes: 'Notes',
|
||||
action: 'Action',
|
||||
},
|
||||
list: {
|
||||
customer: 'Customer',
|
||||
|
|
|
@ -273,6 +273,7 @@ export default {
|
|||
photos: 'Fotos',
|
||||
log: 'Registros de auditoría',
|
||||
notes: 'Notas',
|
||||
action: 'Acción',
|
||||
},
|
||||
list: {
|
||||
customer: 'Cliente',
|
||||
|
|
|
@ -0,0 +1,518 @@
|
|||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import axios from 'axios';
|
||||
import { useStateStore } from 'src/stores/useStateStore';
|
||||
import { toDate, toPercentage, toCurrency } from 'filters/index';
|
||||
import { tMobile } from 'src/composables/tMobile';
|
||||
import CrudModel from 'src/components/CrudModel.vue';
|
||||
import FetchData from 'src/components/FetchData.vue';
|
||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||
import VnConfirm from 'src/components/ui/VnConfirm.vue';
|
||||
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
|
||||
import { useArrayData } from 'composables/useArrayData';
|
||||
|
||||
const { t } = useI18n();
|
||||
const quasar = useQuasar();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const stateStore = computed(() => useStateStore());
|
||||
const claim = ref(null);
|
||||
const claimRef = ref();
|
||||
const claimId = route.params.id;
|
||||
const dialogDestination = ref(false);
|
||||
const claimDestinationFk = ref(null);
|
||||
const resolvedStateId = ref(null);
|
||||
const claimActionsForm = ref();
|
||||
const rows = ref([]);
|
||||
const selectedRows = ref([]);
|
||||
const destinationTypes = ref([]);
|
||||
const totalClaimed = ref(null);
|
||||
const DEFAULT_MAX_RESPONSABILITY = 5;
|
||||
const DEFAULT_MIN_RESPONSABILITY = 1;
|
||||
const arrayData = useArrayData('claimData');
|
||||
const marker_labels = [
|
||||
{ value: DEFAULT_MIN_RESPONSABILITY, label: t('claim.summary.company') },
|
||||
{ value: DEFAULT_MAX_RESPONSABILITY, label: t('claim.summary.person') },
|
||||
];
|
||||
|
||||
const columns = computed(() => [
|
||||
{
|
||||
name: 'Id',
|
||||
label: t('Id item'),
|
||||
field: (row) => row.itemFk,
|
||||
},
|
||||
{
|
||||
name: 'ticket',
|
||||
label: t('Ticket'),
|
||||
field: (row) => row.ticketFk,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'destination',
|
||||
label: t('Destination'),
|
||||
field: (row) => row.claimDestinationFk,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'Landed',
|
||||
label: t('Landed'),
|
||||
field: (row) => toDate(row.landed),
|
||||
},
|
||||
{
|
||||
name: 'quantity',
|
||||
label: t('Quantity'),
|
||||
field: (row) => row.quantity,
|
||||
},
|
||||
{
|
||||
name: 'concept',
|
||||
label: t('Description'),
|
||||
field: (row) => row.concept,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'price',
|
||||
label: t('Price'),
|
||||
field: (row) => row.price,
|
||||
format: (value) => value,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'discount',
|
||||
label: t('Discount'),
|
||||
field: (row) => row.discount,
|
||||
format: (value) => toPercentage(value / 100),
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'total',
|
||||
label: t('Total'),
|
||||
field: (row) => row.total,
|
||||
format: (value) => value,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'delete',
|
||||
},
|
||||
]);
|
||||
|
||||
onMounted(() => {
|
||||
getTotal();
|
||||
});
|
||||
|
||||
function setData(data) {
|
||||
rows.value = data;
|
||||
getTotal();
|
||||
}
|
||||
function getTotal() {
|
||||
if (rows.value.length) {
|
||||
totalClaimed.value = rows.value.reduce((total, row) => total + row.total, 0);
|
||||
}
|
||||
}
|
||||
|
||||
async function updateDestinations(claimDestinationFk) {
|
||||
await updateDestination(claimDestinationFk, selectedRows.value, { reload: true });
|
||||
}
|
||||
|
||||
async function updateDestination(claimDestinationFk, row, options = {}) {
|
||||
if (claimDestinationFk) {
|
||||
await axios.post('Claims/updateClaimDestination', {
|
||||
claimDestinationFk,
|
||||
rows: Array.isArray(row) ? row : [row],
|
||||
});
|
||||
options.reload && claimActionsForm.value.reload();
|
||||
}
|
||||
}
|
||||
|
||||
async function regularizeClaim() {
|
||||
const query = `Claims/${claimId}/regularizeClaim`;
|
||||
|
||||
await axios.post(query);
|
||||
if (claim.value.responsibility >= Math.ceil(DEFAULT_MAX_RESPONSABILITY) / 2) {
|
||||
await claimRef.value.fetch();
|
||||
quasar
|
||||
.dialog({
|
||||
component: VnConfirm,
|
||||
componentProps: {
|
||||
title: t('confirmGreuges'),
|
||||
message: t('confirmGreugesMessage'),
|
||||
},
|
||||
})
|
||||
.onOk(async () => await onUpdateGreugeAccept());
|
||||
} else {
|
||||
quasar.notify({
|
||||
message: t('globals.dataSaved'),
|
||||
type: 'positive',
|
||||
});
|
||||
}
|
||||
await arrayData.fetch({ append: false });
|
||||
}
|
||||
|
||||
async function updateGreuge(greuges) {
|
||||
const { data } = await axios.post(`Greuges`, greuges);
|
||||
quasar.notify({
|
||||
message: t('globals.dataSaved'),
|
||||
type: 'positive',
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
async function onUpdateGreugeAccept() {
|
||||
const greugeTypeFreightId = await getGreugeTypeId();
|
||||
const freightPickUpPrice = await getGreugeConfig();
|
||||
|
||||
await updateGreuge({
|
||||
clientFk: claim.value.clientFk,
|
||||
description: `${t('ClaimGreugeDescription')} ${claimId}`.toUpperCase(),
|
||||
amount: freightPickUpPrice,
|
||||
greugeTypeFk: greugeTypeFreightId,
|
||||
ticketFk: claim.value.ticketFk,
|
||||
});
|
||||
quasar.notify({
|
||||
message: t('globals.dataSaved'),
|
||||
type: 'positive',
|
||||
});
|
||||
}
|
||||
|
||||
async function getGreugeTypeId() {
|
||||
const params = { filter: { where: { code: 'freightPickUp' } } };
|
||||
const query = `GreugeTypes/findOne`;
|
||||
const { data } = await axios.get(query, { params });
|
||||
return data.id;
|
||||
}
|
||||
|
||||
async function getGreugeConfig() {
|
||||
const query = `GreugeConfigs/findOne`;
|
||||
const { data } = await axios.get(query);
|
||||
return data.freightPickUpPrice;
|
||||
}
|
||||
|
||||
async function save(data) {
|
||||
const query = `Claims/${claimId}/updateClaimAction`;
|
||||
await axios.patch(query, data);
|
||||
}
|
||||
|
||||
async function importToNewRefundTicket() {
|
||||
const query = `ClaimBeginnings/${claimId}/importToNewRefundTicket`;
|
||||
await axios.post(query);
|
||||
claimActionsForm.value.reload();
|
||||
quasar.notify({
|
||||
message: t('globals.dataSaved'),
|
||||
type: 'positive',
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<FetchData
|
||||
ref="claimRef"
|
||||
:url="`Claims/${claimId}`"
|
||||
@on-fetch="(data) => (claim = data)"
|
||||
auto-load
|
||||
/>
|
||||
<FetchData
|
||||
url="ClaimStates/findOne"
|
||||
@on-fetch="(data) => (resolvedStateId = data.id)"
|
||||
auto-load
|
||||
:where="{ code: 'resolved' }"
|
||||
/>
|
||||
<FetchData
|
||||
url="ClaimDestinations"
|
||||
auto-load
|
||||
@on-fetch="(data) => (destinationTypes = data)"
|
||||
/>
|
||||
<template v-if="stateStore.isHeaderMounted()">
|
||||
<Teleport to="#actions-append">
|
||||
<div class="row q-gutter-x-sm">
|
||||
<QBtn
|
||||
flat
|
||||
@click="stateStore.toggleRightDrawer()"
|
||||
round
|
||||
dense
|
||||
icon="menu"
|
||||
>
|
||||
<QTooltip bottom anchor="bottom right">
|
||||
{{ t('globals.collapseMenu') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
<QDrawer
|
||||
v-model="stateStore.rightDrawer"
|
||||
side="right"
|
||||
:width="300"
|
||||
show-if-above
|
||||
v-if="claim"
|
||||
>
|
||||
<QCard class="totalClaim vn-card q-my-md q-pa-sm">
|
||||
{{ `${t('Total claimed')}: ${toCurrency(totalClaimed)}` }}
|
||||
</QCard>
|
||||
<QCard class="vn-card q-mb-md q-pa-sm">
|
||||
<QItem class="justify-between">
|
||||
<QItemLabel class="slider-container">
|
||||
<p class="text-primary">
|
||||
{{ t('claim.summary.actions') }}
|
||||
</p>
|
||||
<QSlider
|
||||
class="responsibility { 'background-color:primary': quasar.platform.is.mobile }"
|
||||
v-model="claim.responsibility"
|
||||
:label-value="t('claim.summary.responsibility')"
|
||||
@change="(value) => save({ responsibility: value })"
|
||||
label-always
|
||||
color="primary"
|
||||
markers
|
||||
:marker-labels="marker_labels"
|
||||
:min="DEFAULT_MIN_RESPONSABILITY"
|
||||
:max="DEFAULT_MAX_RESPONSABILITY"
|
||||
/>
|
||||
</QItemLabel>
|
||||
</QItem>
|
||||
</QCard>
|
||||
<QItemLabel class="mana q-mb-md">
|
||||
<QCheckbox
|
||||
v-model="claim.isChargedToMana"
|
||||
@update:model-value="(value) => save({ isChargedToMana: value })"
|
||||
/>
|
||||
<span>{{ t('mana') }}</span>
|
||||
</QItemLabel>
|
||||
</QDrawer>
|
||||
<Teleport to="#st-data" v-if="stateStore.isSubToolbarShown()"> </Teleport>
|
||||
<CrudModel
|
||||
v-if="claim"
|
||||
data-key="ClaimEnds"
|
||||
url="ClaimEnds/filter"
|
||||
save-url="ClaimEnds/crud"
|
||||
ref="claimActionsForm"
|
||||
v-model:selected="selectedRows"
|
||||
:filter="{ where: { claimFk: claimId } }"
|
||||
:default-remove="true"
|
||||
:default-save="false"
|
||||
:default-reset="false"
|
||||
@on-fetch="setData"
|
||||
auto-load
|
||||
>
|
||||
<template #body>
|
||||
<QTable
|
||||
:columns="columns"
|
||||
:rows="rows"
|
||||
:dense="$q.screen.lt.md"
|
||||
row-key="id"
|
||||
selection="multiple"
|
||||
v-model:selected="selectedRows"
|
||||
:grid="$q.screen.lt.md"
|
||||
>
|
||||
<template #body-cell-ticket="{ value }">
|
||||
<QTd align="center">
|
||||
<span class="link">
|
||||
{{ value }}
|
||||
<TicketDescriptorProxy :id="value" />
|
||||
</span>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-destination="{ row }">
|
||||
<QTd>
|
||||
<VnSelectFilter
|
||||
v-model="row.claimDestinationFk"
|
||||
:options="destinationTypes"
|
||||
option-label="description"
|
||||
option-value="id"
|
||||
:autofocus="true"
|
||||
dense
|
||||
input-debounce="0"
|
||||
hide-selected
|
||||
@update:model-value="(value) => updateDestination(value, row)"
|
||||
/>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-price="{ value }">
|
||||
<QTd align="center">
|
||||
{{ toCurrency(value) }}
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-total="{ value }">
|
||||
<QTd align="center">
|
||||
{{ toCurrency(value) }}
|
||||
</QTd>
|
||||
</template>
|
||||
<!-- View for grid mode -->
|
||||
<template #item="props">
|
||||
<div class="q-mb-md col-12 grid-style-transition">
|
||||
<QCard>
|
||||
<QCardSection class="row justify-between">
|
||||
<QCheckbox v-model="props.selected" />
|
||||
<QBtn color="primary" icon="delete" flat round />
|
||||
</QCardSection>
|
||||
|
||||
<QSeparator inset />
|
||||
<QList dense>
|
||||
<QItem v-for="column of props.cols" :key="column.name">
|
||||
<QItemSection>
|
||||
<QItemLabel caption>
|
||||
{{ column.label }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
<QItemSection side>
|
||||
<QItemLabel v-if="column.name === 'destination'">
|
||||
{{ column.value.description }}
|
||||
</QItemLabel>
|
||||
<QItemLabel v-else>
|
||||
{{ column.value }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</QList>
|
||||
</QCard>
|
||||
</div>
|
||||
</template>
|
||||
</QTable>
|
||||
</template>
|
||||
<template #moreBeforeActions>
|
||||
<QBtn
|
||||
color="primary"
|
||||
text-color="white"
|
||||
:unelevated="true"
|
||||
:label="tMobile('Regularize')"
|
||||
:title="t('Regularize')"
|
||||
icon="check"
|
||||
@click="regularizeClaim"
|
||||
:disable="claim.claimStateFk == resolvedStateId"
|
||||
/>
|
||||
|
||||
<QBtn
|
||||
color="primary"
|
||||
text-color="white"
|
||||
:unelevated="true"
|
||||
:disable="!selectedRows.length"
|
||||
:label="tMobile('Change destination')"
|
||||
:title="t('Change destination')"
|
||||
icon="swap_horiz"
|
||||
@click="dialogDestination = !dialogDestination"
|
||||
/>
|
||||
<QBtn
|
||||
color="primary"
|
||||
text-color="white"
|
||||
:unelevated="true"
|
||||
:label="tMobile('Import claim')"
|
||||
:title="t('Import claim')"
|
||||
icon="Upload"
|
||||
@click="importToNewRefundTicket"
|
||||
:disable="claim.claimStateFk == resolvedStateId"
|
||||
/>
|
||||
</template>
|
||||
</CrudModel>
|
||||
<QDialog v-model="dialogDestination">
|
||||
<QCard>
|
||||
<QCardSection>
|
||||
<QItem class="q-pa-sm">
|
||||
<span class="q-dialog__title text-white">
|
||||
{{ t('dialog title') }}
|
||||
</span>
|
||||
<QBtn icon="close" flat round dense v-close-popup />
|
||||
</QItem>
|
||||
</QCardSection>
|
||||
<QItemSection>
|
||||
<VnSelectFilter
|
||||
class="q-pa-sm"
|
||||
v-model="claimDestinationFk"
|
||||
:options="destinationTypes"
|
||||
option-label="description"
|
||||
option-value="id"
|
||||
:autofocus="true"
|
||||
dense
|
||||
input-debounce="0"
|
||||
hide-selected
|
||||
/>
|
||||
</QItemSection>
|
||||
<QCardActions class="justify-end q-mr-sm">
|
||||
<QBtn flat :label="t('globals.close')" color="primary" v-close-popup />
|
||||
<QBtn
|
||||
:disable="!claimDestinationFk"
|
||||
:label="t('globals.save')"
|
||||
color="primary"
|
||||
v-close-popup
|
||||
@click="updateDestinations(claimDestinationFk)"
|
||||
/>
|
||||
</QCardActions>
|
||||
</QCard>
|
||||
</QDialog>
|
||||
<!-- <QDialog v-model="dialogGreuge">
|
||||
<QCardSection>
|
||||
<QItem class="q-pa-sm">
|
||||
<span class="q-pa-sm q-dialog__title text-white">
|
||||
{{ t('dialogGreuge title') }}
|
||||
</span>
|
||||
<QBtn class="q-pa-sm" icon="close" flat round dense v-close-popup />
|
||||
</QItem>
|
||||
<QCardActions class="justify-end q-mr-sm">
|
||||
<QBtn flat :label="t('globals.close')" color="primary" v-close-popup />
|
||||
<QBtn
|
||||
:label="t('globals.save')"
|
||||
color="primary"
|
||||
v-close-popup
|
||||
@click="onUpdateGreugeAccept"
|
||||
/>
|
||||
</QCardActions>
|
||||
</QCardSection>
|
||||
</QDialog> -->
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.slider-container {
|
||||
width: 50%;
|
||||
}
|
||||
@media (max-width: $breakpoint-xs) {
|
||||
.slider-container {
|
||||
width: 90%;
|
||||
}
|
||||
}
|
||||
.q-table {
|
||||
.q-item {
|
||||
min-height: min-content;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
.q-dialog {
|
||||
.q-btn {
|
||||
height: min-content;
|
||||
}
|
||||
}
|
||||
.responsibility {
|
||||
max-width: 100%;
|
||||
margin-left: 40px;
|
||||
}
|
||||
|
||||
.mana {
|
||||
float: inline-start;
|
||||
}
|
||||
</style>
|
||||
<i18n>
|
||||
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
|
||||
</i18n>
|
|
@ -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}`);
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()">
|
||||
|
@ -42,18 +37,6 @@ onMounted(async () => {
|
|||
<ClaimDescriptor />
|
||||
<QSeparator />
|
||||
<LeftMenu source="card" />
|
||||
<QSeparator />
|
||||
<QList>
|
||||
<QItem
|
||||
active-class="text-primary"
|
||||
clickable
|
||||
v-ripple
|
||||
:href="`${salixUrl}/action`"
|
||||
>
|
||||
<QItemSection avatar><QIcon name="vn:actions"></QIcon></QItemSection>
|
||||
<QItemSection>{{ t('Action') }}</QItemSection>
|
||||
</QItem>
|
||||
</QList>
|
||||
</QScrollArea>
|
||||
</QDrawer>
|
||||
<QPageContainer>
|
||||
|
|
|
@ -62,13 +62,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 +88,7 @@ const setData = (entity) => {
|
|||
:title="data.title"
|
||||
:subtitle="data.subtitle"
|
||||
@on-fetch="setData"
|
||||
data-key="claimData"
|
||||
>
|
||||
<template #menu="{ entity }">
|
||||
<ClaimDescriptorMenu :claim="entity" />
|
||||
|
@ -120,14 +126,14 @@ const setData = (entity) => {
|
|||
<VnLv :label="t('claim.card.commercial')">
|
||||
<template #value>
|
||||
<span class="link">
|
||||
{{ entity.client.salesPersonUser.name }}
|
||||
<WorkerDescriptorProxy :id="entity.client.salesPersonFk" />
|
||||
{{ entity.client?.salesPersonUser?.name }}
|
||||
<WorkerDescriptorProxy :id="entity.client?.salesPersonFk" />
|
||||
</span>
|
||||
</template>
|
||||
</VnLv>
|
||||
<VnLv
|
||||
:label="t('claim.card.province')"
|
||||
:value="entity.ticket.address.province.name"
|
||||
:value="entity.ticket?.address?.province?.name"
|
||||
/>
|
||||
<VnLv :label="t('claim.card.zone')" :value="entity.ticket?.zone?.name" />
|
||||
</template>
|
||||
|
|
|
@ -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`;
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<FetchData
|
||||
|
@ -148,7 +145,7 @@ function goToAction() {
|
|||
:data-required="{ claimFk: route.params.id }"
|
||||
v-model:selected="selected"
|
||||
auto-load
|
||||
@save-changes="goToAction"
|
||||
@save-changes="$router.push(`/claim/${route.params.id}/action`)"
|
||||
:default-save="false"
|
||||
>
|
||||
<template #body="{ rows }">
|
||||
|
@ -175,6 +172,7 @@ function goToAction() {
|
|||
:option-label="col.optionLabel"
|
||||
:autofocus="col.tabIndex == 1"
|
||||
input-debounce="0"
|
||||
hide-selected
|
||||
>
|
||||
<template #option="scope" v-if="col.name == 'worker'">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
|
@ -213,6 +211,7 @@ function goToAction() {
|
|||
dense
|
||||
input-debounce="0"
|
||||
:autofocus="col.tabIndex == 1"
|
||||
hide-selected
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
|
|
|
@ -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() {
|
|||
</script>
|
||||
<template>
|
||||
<Teleport to="#st-data" v-if="stateStore.isSubToolbarShown()">
|
||||
<QToolbar class="bg-dark text-white">
|
||||
<QToolbar>
|
||||
<div class="row q-gutter-md">
|
||||
<div>
|
||||
{{ t('Amount') }}
|
||||
|
|
|
@ -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([
|
||||
|
|
|
@ -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 VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps({
|
||||
|
@ -60,7 +61,7 @@ const states = ref();
|
|||
<QSkeleton type="QInput" class="full-width" />
|
||||
</QItemSection>
|
||||
<QItemSection v-if="workers">
|
||||
<QSelect
|
||||
<VnSelectFilter
|
||||
:label="t('Salesperson')"
|
||||
v-model="params.salesPersonFk"
|
||||
@update:model-value="searchFn()"
|
||||
|
@ -79,7 +80,7 @@ const states = ref();
|
|||
<QSkeleton type="QInput" class="full-width" />
|
||||
</QItemSection>
|
||||
<QItemSection v-if="workers">
|
||||
<QSelect
|
||||
<VnSelectFilter
|
||||
:label="t('Attender')"
|
||||
v-model="params.attenderFk"
|
||||
@update:model-value="searchFn()"
|
||||
|
@ -98,7 +99,7 @@ const states = ref();
|
|||
<QSkeleton type="QInput" class="full-width" />
|
||||
</QItemSection>
|
||||
<QItemSection v-if="workers">
|
||||
<QSelect
|
||||
<VnSelectFilter
|
||||
:label="t('Responsible')"
|
||||
v-model="params.claimResponsibleFk"
|
||||
@update:model-value="searchFn()"
|
||||
|
@ -117,7 +118,7 @@ const states = ref();
|
|||
<QSkeleton type="QInput" class="full-width" />
|
||||
</QItemSection>
|
||||
<QItemSection v-if="states">
|
||||
<QSelect
|
||||
<VnSelectFilter
|
||||
:label="t('State')"
|
||||
v-model="params.claimStateFk"
|
||||
@update:model-value="searchFn()"
|
||||
|
|
|
@ -17,12 +17,14 @@ 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}` });
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ const setData = (entity) => (data.value = useCardDescription(entity.name, entity
|
|||
:title="data.title"
|
||||
:subtitle="data.subtitle"
|
||||
@on-fetch="setData"
|
||||
data-key="customerData"
|
||||
>
|
||||
<template #body="{ entity }">
|
||||
<VnLv v-if="entity.salesPersonUser" :label="t('customer.card.salesPerson')">
|
||||
|
|
|
@ -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 VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps({
|
||||
|
@ -63,7 +64,7 @@ const zones = ref();
|
|||
<QSkeleton type="QInput" class="full-width" />
|
||||
</QItemSection>
|
||||
<QItemSection v-if="workers">
|
||||
<QSelect
|
||||
<VnSelectFilter
|
||||
:label="t('Salesperson')"
|
||||
v-model="params.salesPersonFk"
|
||||
@update:model-value="searchFn()"
|
||||
|
@ -82,7 +83,7 @@ const zones = ref();
|
|||
<QSkeleton type="QInput" class="full-width" />
|
||||
</QItemSection>
|
||||
<QItemSection v-if="provinces">
|
||||
<QSelect
|
||||
<VnSelectFilter
|
||||
:label="t('Province')"
|
||||
v-model="params.provinceFk"
|
||||
@update:model-value="searchFn()"
|
||||
|
@ -91,6 +92,7 @@ const zones = ref();
|
|||
option-label="name"
|
||||
emit-value
|
||||
map-options
|
||||
:input-debounce="0"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
|
@ -124,7 +126,7 @@ const zones = ref();
|
|||
<QSkeleton type="QInput" class="full-width" />
|
||||
</QItemSection>
|
||||
<QItemSection v-if="zones">
|
||||
<QSelect
|
||||
<VnSelectFilter
|
||||
:label="t('Zone')"
|
||||
v-model="params.zoneFk"
|
||||
@update:model-value="searchFn()"
|
||||
|
|
|
@ -57,6 +57,7 @@ const setData = (entity) => (data.value = useCardDescription(entity.ref, entity.
|
|||
:title="data.title"
|
||||
:subtitle="data.subtitle"
|
||||
@on-fetch="setData"
|
||||
data-key="invoiceOutData"
|
||||
>
|
||||
<template #body="{ entity }">
|
||||
<VnLv :label="t('invoiceOut.card.issued')" :value="toDate(entity.issued)" />
|
||||
|
|
|
@ -81,6 +81,7 @@ const setData = (entity) =>
|
|||
:filter="filter"
|
||||
:title="data.title"
|
||||
:subtitle="data.subtitle"
|
||||
data-key="ticketData"
|
||||
@on-fetch="setData"
|
||||
>
|
||||
<template #menu="{ entity }">
|
||||
|
|
|
@ -68,7 +68,7 @@ function viewSummary(id) {
|
|||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256">
|
||||
<QScrollArea class="fit text-grey-8">
|
||||
<TicketFilter data-key="TicketList" />
|
||||
</QScrollArea>
|
||||
|
|
|
@ -47,19 +47,22 @@ 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}`;
|
||||
}
|
||||
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);
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<CardDescriptor
|
||||
module="Worker"
|
||||
data-key="workerData"
|
||||
:url="`Workers/${entityId}`"
|
||||
:filter="filter"
|
||||
:title="data.title"
|
||||
|
@ -90,8 +93,8 @@ const setData = (entity) =>
|
|||
</QImg>
|
||||
</template>
|
||||
<template #body="{ entity }">
|
||||
<VnLv :label="t('worker.card.name')" :value="entity.user.nickname" />
|
||||
<VnLv :label="t('worker.card.email')" :value="entity.user.email"> </VnLv>
|
||||
<VnLv :label="t('worker.card.name')" :value="entity.user?.nickname" />
|
||||
<VnLv :label="t('worker.card.email')" :value="entity.user?.email"> </VnLv>
|
||||
<VnLv
|
||||
:label="t('worker.list.department')"
|
||||
:value="entity.department ? entity.department.department.name : null"
|
||||
|
|
|
@ -19,6 +19,7 @@ export default {
|
|||
'ClaimLog',
|
||||
'ClaimNotes',
|
||||
'ClaimDevelopment',
|
||||
'ClaimAction',
|
||||
],
|
||||
},
|
||||
children: [
|
||||
|
@ -130,6 +131,15 @@ export default {
|
|||
},
|
||||
component: () => import('src/pages/Claim/Card/ClaimNotes.vue'),
|
||||
},
|
||||
{
|
||||
name: 'ClaimAction',
|
||||
path: 'action',
|
||||
meta: {
|
||||
title: 'action',
|
||||
icon: 'vn:actions',
|
||||
},
|
||||
component: () => import('src/pages/Claim/Card/ClaimAction.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
|
@ -18,7 +18,8 @@ export const useArrayDataStore = defineStore('arrayDataStore', () => {
|
|||
skip: 0,
|
||||
order: '',
|
||||
data: ref(),
|
||||
isLoading: false
|
||||
isLoading: false,
|
||||
exprBuilder: null,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/// <reference types="cypress" />
|
||||
describe('ClaimAction', () => {
|
||||
const claimId = 2;
|
||||
|
||||
const firstRow = 'tbody > :nth-child(1)';
|
||||
const destinationRow = '.q-item__section > .q-field';
|
||||
|
||||
beforeEach(() => {
|
||||
cy.viewport(1920, 1080);
|
||||
cy.login('developer');
|
||||
cy.visit(`/#/claim/${claimId}/action`);
|
||||
});
|
||||
|
||||
it('should import claim', () => {
|
||||
cy.get('[title="Import claim"]').click();
|
||||
});
|
||||
|
||||
it('should change destination', () => {
|
||||
const rowData = [true, null, null, 'Bueno'];
|
||||
cy.fillRow(firstRow, rowData);
|
||||
});
|
||||
|
||||
it('should change destination from other button', () => {
|
||||
const rowData = [true];
|
||||
|
||||
cy.fillRow(firstRow, rowData);
|
||||
cy.get('[title="Change destination"]').click();
|
||||
cy.selectOption(destinationRow, 'Confeccion');
|
||||
cy.get('.q-card > .q-card__actions > .q-btn--standard').click();
|
||||
});
|
||||
|
||||
it('should regularize', () => {
|
||||
cy.get('[title="Regularize"]').click();
|
||||
cy.clickConfirm();
|
||||
});
|
||||
|
||||
it('should remove the line', () => {
|
||||
cy.fillRow(firstRow, [true]);
|
||||
cy.removeCard();
|
||||
cy.clickConfirm();
|
||||
|
||||
cy.reload();
|
||||
cy.get(firstRow).should('not.exist');
|
||||
});
|
||||
});
|
|
@ -88,7 +88,8 @@ Cypress.Commands.add('addCard', () => {
|
|||
cy.get('.q-page-sticky > div > .q-btn').click();
|
||||
});
|
||||
Cypress.Commands.add('clickConfirm', () => {
|
||||
cy.get('.q-btn--unelevated > .q-btn__content > .block').click();
|
||||
cy.waitForElement('.q-dialog__inner > .q-card');
|
||||
cy.get('.q-card__actions > .q-btn--unelevated > .q-btn__content > .block').click();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('notificationHas', (selector, text) => {
|
||||
|
|
Loading…
Reference in New Issue