refactor: refs #6897 enhance localization entries, clean up unused code, and improve component structure
This commit is contained in:
parent
84c92b8a98
commit
b258c4eaac
|
@ -64,6 +64,10 @@ const $props = defineProps({
|
|||
type: Function,
|
||||
default: null,
|
||||
},
|
||||
beforeSaveFn: {
|
||||
type: Function,
|
||||
default: null,
|
||||
},
|
||||
goTo: {
|
||||
type: String,
|
||||
default: '',
|
||||
|
@ -149,7 +153,7 @@ function filter(value, update, filterOptions) {
|
|||
(ref) => {
|
||||
ref.setOptionIndex(-1);
|
||||
ref.moveOptionSelection(1, true);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -176,7 +180,11 @@ async function saveChanges(data) {
|
|||
hasChanges.value = false;
|
||||
return;
|
||||
}
|
||||
const changes = data || getChanges();
|
||||
let changes = data || getChanges();
|
||||
if ($props.beforeSaveFn) {
|
||||
changes = await $props.beforeSaveFn(changes, getChanges);
|
||||
}
|
||||
|
||||
try {
|
||||
await axios.post($props.saveUrl || $props.url + '/crud', changes);
|
||||
} finally {
|
||||
|
@ -215,7 +223,7 @@ async function remove(data) {
|
|||
|
||||
if (preRemove.length) {
|
||||
newData = newData.filter(
|
||||
(form) => !preRemove.some((index) => index == form.$index)
|
||||
(form) => !preRemove.some((index) => index == form.$index),
|
||||
);
|
||||
const changes = getChanges();
|
||||
if (!changes.creates?.length && !changes.updates?.length)
|
||||
|
@ -374,6 +382,8 @@ watch(formUrl, async () => {
|
|||
@click="onSubmit"
|
||||
:disable="!hasChanges"
|
||||
:title="t('globals.save')"
|
||||
v-shortcut="'s'"
|
||||
shortcut="s"
|
||||
data-cy="crudModelDefaultSaveBtn"
|
||||
/>
|
||||
<slot name="moreAfterActions" />
|
||||
|
|
|
@ -97,7 +97,7 @@ const $props = defineProps({
|
|||
});
|
||||
const emit = defineEmits(['onFetch', 'onDataSaved']);
|
||||
const modelValue = computed(
|
||||
() => $props.model ?? `formModel_${route?.meta?.title ?? route.name}`
|
||||
() => $props.model ?? `formModel_${route?.meta?.title ?? route.name}`,
|
||||
).value;
|
||||
const componentIsRendered = ref(false);
|
||||
const arrayData = useArrayData(modelValue);
|
||||
|
@ -148,7 +148,7 @@ onMounted(async () => {
|
|||
JSON.stringify(newVal) !== JSON.stringify(originalData.value);
|
||||
isResetting.value = false;
|
||||
},
|
||||
{ deep: true }
|
||||
{ deep: true },
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -156,7 +156,7 @@ onMounted(async () => {
|
|||
if (!$props.url)
|
||||
watch(
|
||||
() => arrayData.store.data,
|
||||
(val) => updateAndEmit('onFetch', val)
|
||||
(val) => updateAndEmit('onFetch', val),
|
||||
);
|
||||
|
||||
watch(
|
||||
|
@ -165,7 +165,7 @@ watch(
|
|||
originalData.value = null;
|
||||
reset();
|
||||
await fetch();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
onBeforeRouteLeave((to, from, next) => {
|
||||
|
@ -254,7 +254,7 @@ function filter(value, update, filterOptions) {
|
|||
(ref) => {
|
||||
ref.setOptionIndex(-1);
|
||||
ref.moveOptionSelection(1, true);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import FormModel from 'components/FormModel.vue';
|
||||
|
@ -15,23 +15,30 @@ defineProps({
|
|||
type: String,
|
||||
default: '',
|
||||
},
|
||||
showSaveAndContinueBtn: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const formModelRef = ref(null);
|
||||
const closeButton = ref(null);
|
||||
|
||||
const isSaveAndContinue = ref(false);
|
||||
const onDataSaved = (formData, requestResponse) => {
|
||||
if (closeButton.value) closeButton.value.click();
|
||||
if (closeButton.value && isSaveAndContinue) closeButton.value.click();
|
||||
emit('onDataSaved', formData, requestResponse);
|
||||
};
|
||||
|
||||
const isLoading = computed(() => formModelRef.value?.isLoading);
|
||||
const reset = computed(() => formModelRef.value?.reset);
|
||||
|
||||
defineExpose({
|
||||
isLoading,
|
||||
onDataSaved,
|
||||
isSaveAndContinue,
|
||||
reset,
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -51,6 +58,19 @@ defineExpose({
|
|||
<p>{{ subtitle }}</p>
|
||||
<slot name="form-inputs" :data="data" :validate="validate" />
|
||||
<div class="q-mt-lg row justify-end">
|
||||
<QBtn
|
||||
v-if="showSaveAndContinueBtn"
|
||||
:label="t('globals.isSaveAndContinue')"
|
||||
:title="t('globals.isSaveAndContinue')"
|
||||
type="submit"
|
||||
color="primary"
|
||||
class="q-ml-sm"
|
||||
:disabled="isLoading"
|
||||
:loading="isLoading"
|
||||
data-cy="FormModelPopup_isSaveAndContinue"
|
||||
z-max
|
||||
@click="() => (isSaveAndContinue = true)"
|
||||
/>
|
||||
<QBtn
|
||||
:label="t('globals.cancel')"
|
||||
:title="t('globals.cancel')"
|
||||
|
@ -59,10 +79,15 @@ defineExpose({
|
|||
flat
|
||||
:disabled="isLoading"
|
||||
:loading="isLoading"
|
||||
@click="emit('onDataCanceled')"
|
||||
v-close-popup
|
||||
data-cy="FormModelPopup_cancel"
|
||||
v-close-popup
|
||||
z-max
|
||||
@click="
|
||||
() => {
|
||||
isSaveAndContinue = false;
|
||||
emit('onDataCanceled');
|
||||
}
|
||||
"
|
||||
/>
|
||||
<QBtn
|
||||
:label="t('globals.save')"
|
||||
|
@ -74,6 +99,7 @@ defineExpose({
|
|||
:loading="isLoading"
|
||||
data-cy="FormModelPopup_save"
|
||||
z-max
|
||||
@click="() => (isSaveAndContinue = false)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -12,6 +12,7 @@ import VnInputDate from 'components/common/VnInputDate.vue';
|
|||
import VnInputTime from 'components/common/VnInputTime.vue';
|
||||
import VnComponent from 'components/common/VnComponent.vue';
|
||||
import VnUserLink from 'components/ui/VnUserLink.vue';
|
||||
import VnSelectEnum from '../common/VnSelectEnum.vue';
|
||||
|
||||
const model = defineModel(undefined, { required: true });
|
||||
const emit = defineEmits(['blur']);
|
||||
|
@ -59,6 +60,7 @@ const defaultSelect = {
|
|||
row: $props.row,
|
||||
disable: !$props.isEditable,
|
||||
class: 'fit',
|
||||
'emit-value': false,
|
||||
},
|
||||
forceAttrs: {
|
||||
label: $props.showLabel && $props.column.label,
|
||||
|
@ -139,6 +141,10 @@ const defaultComponents = {
|
|||
component: markRaw(VnSelect),
|
||||
...defaultSelect,
|
||||
},
|
||||
selectEnum: {
|
||||
component: markRaw(VnSelectEnum),
|
||||
...defaultSelect,
|
||||
},
|
||||
icon: {
|
||||
component: markRaw(QIcon),
|
||||
},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup>
|
||||
import { markRaw, computed } from 'vue';
|
||||
import { QCheckbox } from 'quasar';
|
||||
import { QCheckbox, QToggle } from 'quasar';
|
||||
import { useArrayData } from 'composables/useArrayData';
|
||||
|
||||
/* basic input */
|
||||
|
@ -34,7 +34,7 @@ defineExpose({ addFilter, props: $props });
|
|||
const model = defineModel(undefined, { required: true });
|
||||
const arrayData = useArrayData(
|
||||
$props.dataKey,
|
||||
$props.searchUrl ? { searchUrl: $props.searchUrl } : null
|
||||
$props.searchUrl ? { searchUrl: $props.searchUrl } : null,
|
||||
);
|
||||
const columnFilter = computed(() => $props.column?.columnFilter);
|
||||
|
||||
|
@ -51,7 +51,7 @@ const defaultAttrs = {
|
|||
};
|
||||
|
||||
const forceAttrs = {
|
||||
label: $props.showTitle ? '' : columnFilter.value?.label ?? $props.column.label,
|
||||
label: $props.showTitle ? '' : (columnFilter.value?.label ?? $props.column.label),
|
||||
};
|
||||
|
||||
const selectComponent = {
|
||||
|
@ -117,10 +117,19 @@ const components = {
|
|||
},
|
||||
select: selectComponent,
|
||||
rawSelect: selectComponent,
|
||||
toggle: {
|
||||
component: markRaw(QToggle),
|
||||
event: updateEvent,
|
||||
attrs: {
|
||||
class: $props.showTitle ? 'q-py-sm' : 'q-px-md q-py-xs fit',
|
||||
'toggle-indeterminate': true,
|
||||
size: 'sm',
|
||||
},
|
||||
forceAttrs,
|
||||
},
|
||||
};
|
||||
|
||||
async function addFilter(value, name) {
|
||||
console.log('test');
|
||||
value ??= undefined;
|
||||
if (value && typeof value === 'object') value = model.value;
|
||||
value = value === '' ? undefined : value;
|
||||
|
@ -133,19 +142,8 @@ async function addFilter(value, name) {
|
|||
await arrayData.addFilter({ params: { [field]: value } });
|
||||
}
|
||||
|
||||
function alignRow() {
|
||||
switch ($props.column.align) {
|
||||
case 'left':
|
||||
return 'justify-start items-start';
|
||||
case 'right':
|
||||
return 'justify-end items-end';
|
||||
default:
|
||||
return 'flex-center';
|
||||
}
|
||||
}
|
||||
|
||||
const showFilter = computed(
|
||||
() => $props.column?.columnFilter !== false && $props.column.name != 'tableActions'
|
||||
() => $props.column?.columnFilter !== false && $props.column.name != 'tableActions',
|
||||
);
|
||||
|
||||
const onTabPressed = async () => {
|
||||
|
@ -153,12 +151,7 @@ const onTabPressed = async () => {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<div
|
||||
v-if="showFilter"
|
||||
class="full-width"
|
||||
:class="alignRow()"
|
||||
style="max-height: 45px; overflow: hidden"
|
||||
>
|
||||
<div v-if="showFilter" class="full-width flex-center" style="overflow: hidden">
|
||||
<VnTableColumn
|
||||
:column="$props.column"
|
||||
default="input"
|
||||
|
@ -170,10 +163,6 @@ const onTabPressed = async () => {
|
|||
</div>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
/* label.test {
|
||||
padding-bottom: 0px !important;
|
||||
background-color: red;
|
||||
} */
|
||||
label.test > .q-field__inner > .q-field__control {
|
||||
padding: inherit;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import {
|
|||
ref,
|
||||
onBeforeMount,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
computed,
|
||||
watch,
|
||||
h,
|
||||
|
@ -29,6 +30,7 @@ import VnVisibleColumn from 'src/components/VnTable/VnVisibleColumn.vue';
|
|||
import VnLv from 'components/ui/VnLv.vue';
|
||||
import VnTableOrder from 'src/components/VnTable/VnOrder.vue';
|
||||
import VnTableFilter from './VnTableFilter.vue';
|
||||
import { getColAlign } from 'src/composables/getColAlign';
|
||||
|
||||
const arrayData = useArrayData(useAttrs()['data-key']);
|
||||
const $props = defineProps({
|
||||
|
@ -56,10 +58,6 @@ const $props = defineProps({
|
|||
type: [Function, Boolean],
|
||||
default: null,
|
||||
},
|
||||
rowCtrlClick: {
|
||||
type: [Function, Boolean],
|
||||
default: null,
|
||||
},
|
||||
redirect: {
|
||||
type: String,
|
||||
default: null,
|
||||
|
@ -150,6 +148,7 @@ const showForm = ref(false);
|
|||
const splittedColumns = ref({ columns: [] });
|
||||
const columnsVisibilitySkipped = ref();
|
||||
const createForm = ref();
|
||||
const createRef = ref(null);
|
||||
const tableRef = ref();
|
||||
const params = ref(useFilterParams($attrs['data-key']).params);
|
||||
const orders = ref(useFilterParams($attrs['data-key']).orders);
|
||||
|
@ -159,6 +158,7 @@ const editingRow = ref(null);
|
|||
const editingField = ref(null);
|
||||
const isTableMode = computed(() => mode.value == TABLE_MODE);
|
||||
const showRightIcon = computed(() => $props.rightSearch || $props.rightSearchIcon);
|
||||
const originalCreateData = $props?.create?.formInitialData;
|
||||
const tableModes = [
|
||||
{
|
||||
icon: 'view_column',
|
||||
|
@ -178,7 +178,8 @@ onBeforeMount(() => {
|
|||
const urlParams = route.query[$props.searchUrl];
|
||||
hasParams.value = urlParams && Object.keys(urlParams).length !== 0;
|
||||
});
|
||||
onMounted(() => {
|
||||
onMounted(async () => {
|
||||
if ($props.isEditable) document.addEventListener('click', clickHandler);
|
||||
mode.value =
|
||||
quasar.platform.is.mobile && !$props.disableOption?.card
|
||||
? CARD_MODE
|
||||
|
@ -199,6 +200,9 @@ onMounted(() => {
|
|||
};
|
||||
}
|
||||
});
|
||||
onUnmounted(async () => {
|
||||
if ($props.isEditable) document.removeEventListener('click', clickHandler);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => $props.columns,
|
||||
|
@ -250,16 +254,6 @@ const rowClickFunction = computed(() => {
|
|||
return () => {};
|
||||
});
|
||||
|
||||
const rowCtrlClickFunction = computed(() => {
|
||||
if ($props.rowCtrlClick != undefined) return $props.rowCtrlClick;
|
||||
if ($props.redirect)
|
||||
return (evt, { id }) => {
|
||||
stopEventPropagation(evt);
|
||||
window.open(`/#/${$props.redirect}/${id}`, '_blank');
|
||||
};
|
||||
return () => {};
|
||||
});
|
||||
|
||||
function redirectFn(id) {
|
||||
router.push({ path: `/${$props.redirect}/${id}` });
|
||||
}
|
||||
|
@ -281,10 +275,6 @@ function columnName(col) {
|
|||
return name;
|
||||
}
|
||||
|
||||
function getColAlign(col) {
|
||||
return 'text-' + (col.align ?? 'left');
|
||||
}
|
||||
|
||||
const emit = defineEmits(['onFetch', 'update:selected', 'saveChanges']);
|
||||
defineExpose({
|
||||
create: createForm,
|
||||
|
@ -336,126 +326,114 @@ function hasEditableFormat(column) {
|
|||
if (isEditableColumn(column)) return 'editable-text';
|
||||
}
|
||||
|
||||
const handleClick = async (event) => {
|
||||
const clickHandler = async (event) => {
|
||||
const clickedElement = event.target.closest('td');
|
||||
|
||||
if (!clickedElement) return;
|
||||
const isDateElement = event.target.closest('.q-date');
|
||||
const isTimeElement = event.target.closest('.q-time');
|
||||
|
||||
const rowIndex = clickedElement.getAttribute('data-row-index');
|
||||
console.log('HandleRowIndex: ', rowIndex);
|
||||
const colField = clickedElement.getAttribute('data-col-field');
|
||||
console.log('HandleColField: ', colField);
|
||||
if (isDateElement || isTimeElement) return;
|
||||
|
||||
if (rowIndex !== null && colField) {
|
||||
console.log('handleClick STARTEDEDITING');
|
||||
const column = $props.columns.find((col) => col.name === colField);
|
||||
console.log('isEditableColumn(column): ', isEditableColumn(column));
|
||||
if (!isEditableColumn(column)) return;
|
||||
await startEditing(Number(rowIndex), colField, clickedElement);
|
||||
if (column.component !== 'checkbox') console.log();
|
||||
if (clickedElement === null) {
|
||||
destroyInput(editingRow.value, editingField.value);
|
||||
return;
|
||||
}
|
||||
const rowIndex = clickedElement.getAttribute('data-row-index');
|
||||
const colField = clickedElement.getAttribute('data-col-field');
|
||||
const column = $props.columns.find((col) => col.name === colField);
|
||||
|
||||
if (editingRow.value != null && editingField.value != null) {
|
||||
if (editingRow.value == rowIndex && editingField.value == colField) {
|
||||
return;
|
||||
} else {
|
||||
destroyInput(editingRow.value, editingField.value);
|
||||
if (isEditableColumn(column))
|
||||
await renderInput(Number(rowIndex), colField, clickedElement);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (isEditableColumn(column))
|
||||
await renderInput(Number(rowIndex), colField, clickedElement);
|
||||
};
|
||||
|
||||
async function startEditing(rowId, field, clickedElement) {
|
||||
console.log('startEditing: ', field);
|
||||
if (rowId === editingRow.value && field === editingField.value) return;
|
||||
editingRow.value = rowId;
|
||||
async function handleTabKey(event, rowIndex, colField) {
|
||||
if (editingRow.value == rowIndex && editingField.value == colField)
|
||||
destroyInput(editingRow.value, editingField.value);
|
||||
|
||||
const direction = event.shiftKey ? -1 : 1;
|
||||
const { nextRowIndex, nextColumnName } = await handleTabNavigation(
|
||||
rowIndex,
|
||||
colField,
|
||||
direction,
|
||||
);
|
||||
|
||||
if (nextRowIndex < 0 || nextRowIndex >= arrayData.store.data.length) return;
|
||||
|
||||
event.preventDefault();
|
||||
await renderInput(nextRowIndex, nextColumnName, null);
|
||||
}
|
||||
const selectRegex = /select/;
|
||||
async function renderInput(rowId, field, clickedElement) {
|
||||
editingField.value = field;
|
||||
editingRow.value = rowId;
|
||||
|
||||
const column = $props.columns.find((col) => col.name === field);
|
||||
console.log('LaVerdaderacolumn: ', column);
|
||||
const row = CrudModelRef.value.formData[rowId];
|
||||
const oldValue = CrudModelRef.value.formData[rowId][column?.name];
|
||||
console.log('changes: ', CrudModelRef.value.getChanges());
|
||||
|
||||
if (!clickedElement)
|
||||
clickedElement = document.querySelector(
|
||||
`[data-row-index="${rowId}"][data-col-field="${field}"]`
|
||||
`[data-row-index="${rowId}"][data-col-field="${field}"]`,
|
||||
);
|
||||
|
||||
Array.from(clickedElement.childNodes).forEach((child) => {
|
||||
child.style.visibility = 'hidden';
|
||||
child.style.position = 'absolute';
|
||||
child.style.position = 'relative';
|
||||
});
|
||||
|
||||
console.log('row[column.name]: ', row[column.name]);
|
||||
const isSelect = selectRegex.test(column?.component);
|
||||
if (isSelect) column.attrs = { ...column.attrs, 'emit-value': false };
|
||||
|
||||
const node = h(VnColumn, {
|
||||
row: row,
|
||||
class: 'temp-input',
|
||||
column: column,
|
||||
modelValue: row[column.name],
|
||||
componentProp: 'columnField',
|
||||
autofocus: true,
|
||||
focusOnMount: true,
|
||||
eventHandlers: {
|
||||
'update:modelValue': (value) => {
|
||||
console.log('update:modelValue: ', value);
|
||||
row[column.name] = value;
|
||||
|
||||
column?.cellEvent?.['update:modelValue']?.(value, oldValue, row);
|
||||
},
|
||||
onMouseDown: (event) => {
|
||||
console.log('mouseDown: ', field);
|
||||
if (column.component === 'checkbox') event.stopPropagation();
|
||||
},
|
||||
blur: () => {
|
||||
/* const focusElement = document.activeElement;
|
||||
const rowIndex = focusElement.getAttribute('data-row-index');
|
||||
const colField = focusElement.getAttribute('data-col-field');
|
||||
console.log('rowIndex: ', rowIndex);
|
||||
console.log('colField: ', colField);
|
||||
console.log('editingField.value: ', editingField.value);
|
||||
console.log('editingRow.value: ', editingRow.value);
|
||||
|
||||
handleBlur(rowId, field, clickedElement);
|
||||
column?.cellEvent?.blur?.(row); */
|
||||
'update:modelValue': async (value) => {
|
||||
if (isSelect) {
|
||||
row[column.name] = value[column.name.attrs?.optionValue ?? 'id'];
|
||||
row[column?.name + 'textValue'] =
|
||||
value[column.name.attrs?.optionLabel ?? 'name'];
|
||||
await column?.cellEvent?.['update:modelValue']?.(
|
||||
value,
|
||||
oldValue,
|
||||
row,
|
||||
);
|
||||
} else row[column.name] = value;
|
||||
await column?.cellEvent?.['update:modelValue']?.(value, oldValue, row);
|
||||
},
|
||||
keyup: async (event) => {
|
||||
console.log('keyup: ', field);
|
||||
if (event.key === 'Enter') handleBlur(rowId, field, clickedElement);
|
||||
},
|
||||
keydown: async (event) => {
|
||||
switch (event.key) {
|
||||
case 'Tab':
|
||||
console.log('TabTest: ', field);
|
||||
await handleTabKey(event, rowId, field);
|
||||
event.stopPropagation();
|
||||
if (column.component === 'checkbox')
|
||||
handleBlur(rowId, field, clickedElement);
|
||||
break;
|
||||
case 'Escape':
|
||||
console.log('Escape: ', field);
|
||||
stopEditing(rowId, field, clickedElement);
|
||||
destroyInput(rowId, field, clickedElement);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
click: (event) => {
|
||||
/* event.stopPropagation();
|
||||
console.log('click: ', field);
|
||||
|
||||
if (column.component === 'checkbox') {
|
||||
const allowNull = column?.toggleIndeterminate ?? true;
|
||||
const currentValue = row[column.name];
|
||||
|
||||
let newValue;
|
||||
|
||||
if (allowNull) {
|
||||
if (currentValue === null) {
|
||||
newValue = true;
|
||||
} else if (currentValue) {
|
||||
newValue = false;
|
||||
} else {
|
||||
newValue = null;
|
||||
}
|
||||
} else {
|
||||
newValue = !currentValue;
|
||||
}
|
||||
row[column.name] = newValue;
|
||||
|
||||
column?.cellEvent?.['update:modelValue']?.(newValue, row);
|
||||
}
|
||||
column?.cellEvent?.['click']?.(event, row); */
|
||||
column?.cellEvent?.['click']?.(event, row);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -463,11 +441,15 @@ async function startEditing(rowId, field, clickedElement) {
|
|||
node.appContext = app._context;
|
||||
render(node, clickedElement);
|
||||
|
||||
if (column.component === 'checkbox') node.el?.querySelector('span > div').focus();
|
||||
if (['checkbox', 'toggle', undefined].includes(column?.component))
|
||||
node.el?.querySelector('span > div').focus();
|
||||
}
|
||||
|
||||
function stopEditing(rowIndex, field, clickedElement) {
|
||||
console.log('stopEditing: ', field);
|
||||
function destroyInput(rowIndex, field, clickedElement) {
|
||||
if (!clickedElement)
|
||||
clickedElement = document.querySelector(
|
||||
`[data-row-index="${rowIndex}"][data-col-field="${field}"]`,
|
||||
);
|
||||
if (clickedElement) {
|
||||
render(null, clickedElement);
|
||||
Array.from(clickedElement.childNodes).forEach((child) => {
|
||||
|
@ -481,8 +463,7 @@ function stopEditing(rowIndex, field, clickedElement) {
|
|||
}
|
||||
|
||||
function handleBlur(rowIndex, field, clickedElement) {
|
||||
console.log('handleBlur: ', field);
|
||||
stopEditing(rowIndex, field, clickedElement);
|
||||
destroyInput(rowIndex, field, clickedElement);
|
||||
}
|
||||
|
||||
async function handleTabNavigation(rowIndex, colName, direction) {
|
||||
|
@ -501,7 +482,6 @@ async function handleTabNavigation(rowIndex, colName, direction) {
|
|||
} while (iterations < totalColumns);
|
||||
|
||||
if (iterations >= totalColumns) {
|
||||
console.warn('No editable columns found.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -510,61 +490,37 @@ async function handleTabNavigation(rowIndex, colName, direction) {
|
|||
} else if (direction === -1 && newColumnIndex >= currentColumnIndex) {
|
||||
rowIndex--;
|
||||
}
|
||||
console.log('next: ', columns[newColumnIndex].name, 'rowIndex: ', rowIndex);
|
||||
return { nextRowIndex: rowIndex, nextColumnName: columns[newColumnIndex].name };
|
||||
}
|
||||
|
||||
async function handleTabKey(event, rowIndex, colName) {
|
||||
const direction = event.shiftKey ? -1 : 1;
|
||||
const { nextRowIndex, nextColumnName } = await handleTabNavigation(
|
||||
rowIndex,
|
||||
colName,
|
||||
direction
|
||||
);
|
||||
|
||||
if (nextRowIndex < 0 || nextRowIndex >= arrayData.store.data.length) return;
|
||||
|
||||
event.preventDefault();
|
||||
await startEditing(nextRowIndex, nextColumnName, null);
|
||||
}
|
||||
|
||||
function getCheckboxIcon(value) {
|
||||
switch (typeof value) {
|
||||
case 'boolean':
|
||||
return value ? 'check' : 'close';
|
||||
case 'string':
|
||||
return value.toLowerCase() === 'partial'
|
||||
? 'indeterminate_check_box'
|
||||
: 'unknown_med';
|
||||
case 'number':
|
||||
return value === 0 ? 'close' : 'check';
|
||||
case 'object':
|
||||
return value === null ? 'help_outline' : 'unknown_med';
|
||||
case 'undefined':
|
||||
return 'help_outline';
|
||||
return 'indeterminate_check_box';
|
||||
default:
|
||||
return 'indeterminate_check_box';
|
||||
}
|
||||
}
|
||||
function getToggleIcon(value) {
|
||||
if (value === null) return 'help_outline';
|
||||
return value ? 'toggle_on' : 'toggle_off';
|
||||
}
|
||||
|
||||
/* function getCheckboxIcon(value) {
|
||||
switch (typeof value) {
|
||||
case 'boolean':
|
||||
return value ? 'check_box' : 'check_box_outline_blank';
|
||||
case 'string':
|
||||
return value.toLowerCase() === 'partial'
|
||||
? 'indeterminate_check_box'
|
||||
: 'unknown_med';
|
||||
case 'number':
|
||||
return value === 0 ? 'check_box_outline_blank' : 'check_box';
|
||||
case 'object':
|
||||
return value === null ? 'help_outline' : 'unknown_med';
|
||||
case 'undefined':
|
||||
return 'help_outline';
|
||||
default:
|
||||
return 'indeterminate_check_box';
|
||||
function formatColumnValue(col, row, dashIfEmpty) {
|
||||
if (col?.format) {
|
||||
if (selectRegex.test(col?.component) && row[col?.name + 'textValue']) {
|
||||
return dashIfEmpty(row[col?.name + 'textValue']);
|
||||
} else {
|
||||
return col.format(row, dashIfEmpty);
|
||||
}
|
||||
} else {
|
||||
return dashIfEmpty(row[col?.name]);
|
||||
}
|
||||
} */
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<QDrawer
|
||||
|
@ -628,7 +584,6 @@ function getCheckboxIcon(value) {
|
|||
@row-click="(_, row) => rowClickFunction && rowClickFunction(row)"
|
||||
@update:selected="emit('update:selected', $event)"
|
||||
@selection="(details) => handleSelection(details, rows)"
|
||||
v-on="isEditable ? { click: handleClick } : {}"
|
||||
>
|
||||
<template #top-left v-if="!$props.withoutHeader">
|
||||
<slot name="top-left"> </slot>
|
||||
|
@ -647,6 +602,14 @@ function getCheckboxIcon(value) {
|
|||
dense
|
||||
:options="tableModes.filter((mode) => !mode.disable)"
|
||||
/>
|
||||
|
||||
<QBtn
|
||||
v-if="showRightIcon"
|
||||
icon="filter_alt"
|
||||
class="bg-vn-section-color q-ml-sm"
|
||||
dense
|
||||
@click="stateStore.toggleRightDrawer()"
|
||||
/>
|
||||
</template>
|
||||
<template #header-cell="{ col }">
|
||||
<QTh
|
||||
|
@ -665,7 +628,7 @@ function getCheckboxIcon(value) {
|
|||
<VnTableOrder
|
||||
v-model="orders[col.orderBy ?? col.name]"
|
||||
:name="col.orderBy ?? col.name"
|
||||
:label="col?.label"
|
||||
:label="col?.labelAbbreviation ?? col?.label"
|
||||
:data-key="$attrs['data-key']"
|
||||
:search-url="searchUrl"
|
||||
/>
|
||||
|
@ -707,15 +670,14 @@ function getCheckboxIcon(value) {
|
|||
position: 'relative',
|
||||
}"
|
||||
:class="[
|
||||
getColAlign(col),
|
||||
col.columnClass,
|
||||
'body-cell no-margin no-padding',
|
||||
'body-cell no-margin no-padding text-center',
|
||||
]"
|
||||
:data-row-index="rowIndex"
|
||||
:data-col-field="col?.name"
|
||||
>
|
||||
<div
|
||||
class="no-padding no-margin"
|
||||
class="no-padding no-margin peter"
|
||||
style="
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
@ -729,7 +691,18 @@ function getCheckboxIcon(value) {
|
|||
:row-index="rowIndex"
|
||||
>
|
||||
<QIcon
|
||||
v-if="col?.component === 'checkbox'"
|
||||
v-if="col?.component === 'toggle'"
|
||||
:name="
|
||||
col?.getIcon
|
||||
? col.getIcon(row[col?.name])
|
||||
: getToggleIcon(row[col?.name])
|
||||
"
|
||||
style="color: var(--vn-text-color)"
|
||||
:class="hasEditableFormat(col)"
|
||||
size="17px"
|
||||
/>
|
||||
<QIcon
|
||||
v-else-if="col?.component === 'checkbox'"
|
||||
:name="getCheckboxIcon(row[col?.name])"
|
||||
style="color: var(--vn-text-color)"
|
||||
:class="hasEditableFormat(col)"
|
||||
|
@ -741,11 +714,7 @@ function getCheckboxIcon(value) {
|
|||
:style="col?.style ? col.style(row) : null"
|
||||
style="bottom: 0"
|
||||
>
|
||||
{{
|
||||
col?.format
|
||||
? col.format(row, dashIfEmpty)
|
||||
: dashIfEmpty(row[col?.name])
|
||||
}}
|
||||
{{ formatColumnValue(col, row, dashIfEmpty) }}
|
||||
</span>
|
||||
</slot>
|
||||
</div>
|
||||
|
@ -898,7 +867,6 @@ function getCheckboxIcon(value) {
|
|||
v-for="col of cols.filter((cols) => cols.visible ?? true)"
|
||||
:key="col?.id"
|
||||
class="text-center"
|
||||
:class="getColAlign(col)"
|
||||
>
|
||||
<slot :name="`column-footer-${col.name}`" />
|
||||
</QTh>
|
||||
|
@ -944,32 +912,53 @@ function getCheckboxIcon(value) {
|
|||
{{ createForm?.title }}
|
||||
</QTooltip>
|
||||
</QPageSticky>
|
||||
<QDialog v-model="showForm" transition-show="scale" transition-hide="scale">
|
||||
<QDialog
|
||||
v-model="showForm"
|
||||
transition-show="scale"
|
||||
transition-hide="scale"
|
||||
:full-width="create?.isFullWidth ?? false"
|
||||
@before-hide="
|
||||
() => {
|
||||
if (createRef.isSaveAndContinue) {
|
||||
showForm = true;
|
||||
createForm.formInitialData = { ...create.formInitialData };
|
||||
}
|
||||
}
|
||||
"
|
||||
>
|
||||
<FormModelPopup
|
||||
ref="createRef"
|
||||
v-bind="createForm"
|
||||
:model="$attrs['data-key'] + 'Create'"
|
||||
@on-data-saved="(_, res) => createForm.onDataSaved(res)"
|
||||
>
|
||||
<template #form-inputs="{ data }">
|
||||
<div class="grid-create">
|
||||
<slot
|
||||
v-for="column of splittedColumns.create"
|
||||
:key="column.name"
|
||||
:name="`column-create-${column.name}`"
|
||||
:data="data"
|
||||
:column-name="column.name"
|
||||
:label="column.label"
|
||||
>
|
||||
<VnColumn
|
||||
:column="column"
|
||||
:row="{}"
|
||||
default="input"
|
||||
v-model="data[column.name]"
|
||||
:show-label="true"
|
||||
component-prop="columnCreate"
|
||||
/>
|
||||
</slot>
|
||||
<slot name="more-create-dialog" :data="data" />
|
||||
<div :class="create?.containerClass">
|
||||
<div>
|
||||
<slot name="previous-create-dialog" :data="data" />
|
||||
</div>
|
||||
<div class="grid-create" :style="create?.columnGridStyle">
|
||||
<slot
|
||||
v-for="column of splittedColumns.create"
|
||||
:key="column.name"
|
||||
:name="`column-create-${column.name}`"
|
||||
:data="data"
|
||||
:column-name="column.name"
|
||||
:label="column.label"
|
||||
>
|
||||
<VnColumn
|
||||
:column="column"
|
||||
:row="{}"
|
||||
default="input"
|
||||
v-model="data[column.name]"
|
||||
:show-label="true"
|
||||
component-prop="columnCreate"
|
||||
/>
|
||||
</slot>
|
||||
</div>
|
||||
<div>
|
||||
<slot name="more-create-dialog" :data="data" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</FormModelPopup>
|
||||
|
@ -1021,6 +1010,7 @@ es:
|
|||
.body-cell {
|
||||
padding-left: 2px !important;
|
||||
padding-right: 2px !important;
|
||||
position: relative;
|
||||
}
|
||||
.bg-chip-secondary {
|
||||
background-color: var(--vn-page-color);
|
||||
|
@ -1047,11 +1037,14 @@ es:
|
|||
.grid-create {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, max-content));
|
||||
max-width: 100%;
|
||||
grid-gap: 20px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px; /* Espacio entre los divs */
|
||||
}
|
||||
.flex-one {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
|
@ -1162,4 +1155,11 @@ es:
|
|||
.q-table__middle.q-virtual-scroll.q-virtual-scroll--vertical.scroll {
|
||||
background-color: var(--vn-section-color);
|
||||
}
|
||||
.temp-input {
|
||||
top: 0;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -29,25 +29,29 @@ function columnName(col) {
|
|||
<VnFilterPanel v-bind="$attrs" :search-button="true" :disable-submit-event="true">
|
||||
<template #body="{ params, orders }">
|
||||
<div
|
||||
class="row no-wrap flex-center"
|
||||
class="container"
|
||||
v-for="col of columns.filter((c) => c.columnFilter ?? true)"
|
||||
:key="col.id"
|
||||
>
|
||||
<VnFilter
|
||||
ref="tableFilterRef"
|
||||
:column="col"
|
||||
:data-key="$attrs['data-key']"
|
||||
v-model="params[columnName(col)]"
|
||||
:search-url="searchUrl"
|
||||
/>
|
||||
<VnTableOrder
|
||||
v-if="col?.columnFilter !== false && col?.name !== 'tableActions'"
|
||||
v-model="orders[col.orderBy ?? col.name]"
|
||||
:name="col.orderBy ?? col.name"
|
||||
:data-key="$attrs['data-key']"
|
||||
:search-url="searchUrl"
|
||||
:vertical="true"
|
||||
/>
|
||||
<div class="filter">
|
||||
<VnFilter
|
||||
ref="tableFilterRef"
|
||||
:column="col"
|
||||
:data-key="$attrs['data-key']"
|
||||
v-model="params[columnName(col)]"
|
||||
:search-url="searchUrl"
|
||||
/>
|
||||
</div>
|
||||
<div class="order">
|
||||
<VnTableOrder
|
||||
v-if="col?.columnFilter !== false && col?.name !== 'tableActions'"
|
||||
v-model="orders[col.orderBy ?? col.name]"
|
||||
:name="col.orderBy ?? col.name"
|
||||
:data-key="$attrs['data-key']"
|
||||
:search-url="searchUrl"
|
||||
:vertical="true"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<slot
|
||||
name="moreFilterPanel"
|
||||
|
@ -67,3 +71,21 @@ function columnName(col) {
|
|||
</template>
|
||||
</VnFilterPanel>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 45px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.filter {
|
||||
width: 70%;
|
||||
height: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
.order {
|
||||
width: 10%;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -58,7 +58,6 @@ function toValueAttrs(attrs) {
|
|||
v-on="mix(toComponent).event ?? {}"
|
||||
v-model="model"
|
||||
@blur="emit('blur')"
|
||||
@mouse-down="() => console.log('mouse-down')"
|
||||
/>
|
||||
</span>
|
||||
</template>
|
||||
|
|
|
@ -140,7 +140,7 @@ const handleUppercase = () => {
|
|||
hide-bottom-space
|
||||
:data-cy="$attrs.dataCy ?? $attrs.label + '_input'"
|
||||
>
|
||||
<template #prepend>
|
||||
<template #prepend v-if="$slots.prepend">
|
||||
<slot name="prepend" />
|
||||
</template>
|
||||
<template #append>
|
||||
|
@ -165,15 +165,15 @@ const handleUppercase = () => {
|
|||
}
|
||||
"
|
||||
></QIcon>
|
||||
|
||||
|
||||
<QIcon
|
||||
name="match_case"
|
||||
size="xs"
|
||||
v-if="!$attrs.disabled && !($attrs.readonly) && $props.uppercase"
|
||||
v-if="!$attrs.disabled && !$attrs.readonly && $props.uppercase"
|
||||
@click="handleUppercase"
|
||||
class="uppercase-icon"
|
||||
/>
|
||||
|
||||
|
||||
<slot name="append" v-if="$slots.append && !$attrs.disabled" />
|
||||
<QIcon v-if="info" name="info">
|
||||
<QTooltip max-width="350px">
|
||||
|
@ -194,4 +194,4 @@ const handleUppercase = () => {
|
|||
inputMin: Debe ser mayor a {value}
|
||||
maxLength: El valor excede los {value} carácteres
|
||||
inputMax: Debe ser menor a {value}
|
||||
</i18n>
|
||||
</i18n>
|
||||
|
|
|
@ -171,7 +171,8 @@ onMounted(() => {
|
|||
});
|
||||
|
||||
const arrayDataKey =
|
||||
$props.dataKey ?? ($props.url?.length > 0 ? $props.url : $attrs.name ?? $attrs.label);
|
||||
$props.dataKey ??
|
||||
($props.url?.length > 0 ? $props.url : ($attrs.name ?? $attrs.label));
|
||||
|
||||
const arrayData = useArrayData(arrayDataKey, {
|
||||
url: $props.url,
|
||||
|
@ -220,7 +221,7 @@ async function fetchFilter(val) {
|
|||
optionFilterValue.value ??
|
||||
(new RegExp(/\d/g).test(val)
|
||||
? optionValue.value
|
||||
: optionFilter.value ?? optionLabel.value);
|
||||
: (optionFilter.value ?? optionLabel.value));
|
||||
|
||||
let defaultWhere = {};
|
||||
if ($props.filterOptions.length) {
|
||||
|
@ -239,7 +240,7 @@ async function fetchFilter(val) {
|
|||
|
||||
const { data } = await arrayData.applyFilter(
|
||||
{ filter: filterOptions },
|
||||
{ updateRouter: false }
|
||||
{ updateRouter: false },
|
||||
);
|
||||
setOptions(data);
|
||||
return data;
|
||||
|
@ -272,7 +273,7 @@ async function filterHandler(val, update) {
|
|||
ref.setOptionIndex(-1);
|
||||
ref.moveOptionSelection(1, true);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -308,7 +309,7 @@ function handleKeyDown(event) {
|
|||
if (inputValue) {
|
||||
const matchingOption = myOptions.value.find(
|
||||
(option) =>
|
||||
option[optionLabel.value].toLowerCase() === inputValue.toLowerCase()
|
||||
option[optionLabel.value].toLowerCase() === inputValue.toLowerCase(),
|
||||
);
|
||||
|
||||
if (matchingOption) {
|
||||
|
@ -320,11 +321,11 @@ function handleKeyDown(event) {
|
|||
}
|
||||
|
||||
const focusableElements = document.querySelectorAll(
|
||||
'a:not([disabled]), button:not([disabled]), input:not([disabled]), textarea:not([disabled]), select:not([disabled]), details:not([disabled]), [tabindex]:not([tabindex="-1"]):not([disabled])'
|
||||
'a:not([disabled]), button:not([disabled]), input:not([disabled]), textarea:not([disabled]), select:not([disabled]), details:not([disabled]), [tabindex]:not([tabindex="-1"]):not([disabled])',
|
||||
);
|
||||
const currentIndex = Array.prototype.indexOf.call(
|
||||
focusableElements,
|
||||
event.target
|
||||
event.target,
|
||||
);
|
||||
if (currentIndex >= 0 && currentIndex < focusableElements.length - 1) {
|
||||
focusableElements[currentIndex + 1].focus();
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
<script setup>
|
||||
import VnSelectDialog from './VnSelectDialog.vue';
|
||||
import FilterTravelForm from 'src/components/FilterTravelForm.vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { toDate } from 'src/filters';
|
||||
const { t } = useI18n();
|
||||
|
||||
const $props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
onFilterTravelSelected: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<VnSelectDialog
|
||||
:label="t('entry.basicData.travel')"
|
||||
v-bind="$attrs"
|
||||
url="Travels/filter"
|
||||
:fields="['id', 'warehouseInName']"
|
||||
option-value="id"
|
||||
option-label="warehouseInName"
|
||||
map-options
|
||||
hide-selected
|
||||
:required="true"
|
||||
action-icon="filter_alt"
|
||||
>
|
||||
<template #form>
|
||||
<FilterTravelForm @travel-selected="onFilterTravelSelected(data, $event)" />
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>
|
||||
{{ scope.opt?.agencyModeName }} -
|
||||
{{ scope.opt?.warehouseInName }}
|
||||
({{ toDate(scope.opt?.shipped) }}) →
|
||||
{{ scope.opt?.warehouseOutName }}
|
||||
({{ toDate(scope.opt?.landed) }})
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelectDialog>
|
||||
</template>
|
|
@ -114,7 +114,7 @@ async function clearFilters() {
|
|||
arrayData.resetPagination();
|
||||
// Filtrar los params no removibles
|
||||
const removableFilters = Object.keys(userParams.value).filter((param) =>
|
||||
$props.unremovableParams.includes(param)
|
||||
$props.unremovableParams.includes(param),
|
||||
);
|
||||
const newParams = {};
|
||||
// Conservar solo los params que no son removibles
|
||||
|
@ -162,13 +162,13 @@ const formatTags = (tags) => {
|
|||
|
||||
const tags = computed(() => {
|
||||
const filteredTags = tagsList.value.filter(
|
||||
(tag) => !($props.customTags || []).includes(tag.label)
|
||||
(tag) => !($props.customTags || []).includes(tag.label),
|
||||
);
|
||||
return formatTags(filteredTags);
|
||||
});
|
||||
|
||||
const customTags = computed(() =>
|
||||
tagsList.value.filter((tag) => ($props.customTags || []).includes(tag.label))
|
||||
tagsList.value.filter((tag) => ($props.customTags || []).includes(tag.label)),
|
||||
);
|
||||
|
||||
async function remove(key) {
|
||||
|
@ -191,7 +191,9 @@ const getLocale = (label) => {
|
|||
if (te(globalLocale)) return t(globalLocale);
|
||||
else if (te(t(`params.${param}`)));
|
||||
else {
|
||||
const camelCaseModuleName = route.meta.moduleName.charAt(0).toLowerCase() + route.meta.moduleName.slice(1);
|
||||
const camelCaseModuleName =
|
||||
route.meta.moduleName.charAt(0).toLowerCase() +
|
||||
route.meta.moduleName.slice(1);
|
||||
return t(`${camelCaseModuleName}.params.${param}`);
|
||||
}
|
||||
};
|
||||
|
@ -290,6 +292,9 @@ const getLocale = (label) => {
|
|||
/>
|
||||
</template>
|
||||
<style scoped lang="scss">
|
||||
.q-field__label.no-pointer-events.absolute.ellipsis {
|
||||
margin-left: 6px !important;
|
||||
}
|
||||
.list {
|
||||
width: 256px;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
import { useQuasar } from 'quasar';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
import axios from 'axios';
|
||||
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||
|
||||
export async function checkEntryLock(entryFk, userFk) {
|
||||
const { t } = useI18n();
|
||||
const quasar = useQuasar();
|
||||
const { push } = useRouter();
|
||||
const { data } = await axios.get(`Entries/${entryFk}`, {
|
||||
params: {
|
||||
filter: JSON.stringify({
|
||||
fields: ['id', 'locked', 'lockerUserFk'],
|
||||
include: { relation: 'user', scope: { fields: ['id', 'nickname'] } },
|
||||
}),
|
||||
},
|
||||
});
|
||||
const entryConfig = await axios.get('EntryConfigs/findOne');
|
||||
|
||||
if (data?.lockerUserFk && data?.locked) {
|
||||
const now = new Date().getTime();
|
||||
const lockedTime = new Date(data.locked).getTime();
|
||||
const timeDiff = (now - lockedTime) / 1000;
|
||||
const isMaxTimeLockExceeded = entryConfig.data.maxLockTime > timeDiff;
|
||||
if (data?.lockerUserFk !== userFk && isMaxTimeLockExceeded) {
|
||||
quasar
|
||||
.dialog({
|
||||
component: VnConfirm,
|
||||
componentProps: {
|
||||
title: t('entry.lock.title'),
|
||||
message: t('entry.lock.message', {
|
||||
userName: data?.user?.nickname,
|
||||
time: timeDiff / 60,
|
||||
}),
|
||||
},
|
||||
})
|
||||
.onOk(
|
||||
async () =>
|
||||
await axios.patch(`Entries/${entryFk}`, {
|
||||
locked: Date.vnNow(),
|
||||
lockerUserFk: userFk,
|
||||
}),
|
||||
)
|
||||
.onCancel(() => push({ path: `summary` }));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
export function getColAlign(col) {
|
||||
let align;
|
||||
|
||||
switch (col.component) {
|
||||
case 'number':
|
||||
align = 'right';
|
||||
break;
|
||||
case 'date':
|
||||
case 'checkbox':
|
||||
align = 'center';
|
||||
break;
|
||||
default:
|
||||
align = col?.align;
|
||||
}
|
||||
|
||||
if (/^is[A-Z]/.test(col.name) || /^has[A-Z]/.test(col.name)) align = 'center';
|
||||
|
||||
return 'text-' + (align ?? 'center');
|
||||
}
|
|
@ -315,9 +315,6 @@ input::-webkit-inner-spin-button {
|
|||
max-width: fit-content;
|
||||
}
|
||||
|
||||
.row > .column:has(.q-checkbox) {
|
||||
max-width: fit-content;
|
||||
}
|
||||
.q-field__inner {
|
||||
.q-field__control {
|
||||
min-height: auto !important;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
globals:
|
||||
lang:
|
||||
es: Spanish
|
||||
en: English
|
||||
en: English
|
||||
language: Language
|
||||
quantity: Quantity
|
||||
entity: Entity
|
||||
|
@ -33,6 +33,7 @@ globals:
|
|||
reset: Reset
|
||||
close: Close
|
||||
cancel: Cancel
|
||||
isSaveAndContinue: Save and continue
|
||||
clone: Clone
|
||||
confirm: Confirm
|
||||
assign: Assign
|
||||
|
@ -476,6 +477,27 @@ entry:
|
|||
isRaid: Raid
|
||||
invoiceNumber: Invoice
|
||||
reference: Ref/Alb/Guide
|
||||
params:
|
||||
isExcludedFromAvailable: Excluir del inventario
|
||||
isOrdered: Pedida
|
||||
isConfirmed: Lista para etiquetar
|
||||
isReceived: Recibida
|
||||
isRaid: Redada
|
||||
landed: Fecha
|
||||
supplierFk: Proveedor
|
||||
invoiceNumber: Nº Factura
|
||||
reference: Ref/Alb/Guía
|
||||
agencyModeId: Agencia
|
||||
isBooked: Asentado
|
||||
companyFk: Empresa
|
||||
travelFk: Envio
|
||||
evaNotes: Notas
|
||||
warehouseOutFk: Origen
|
||||
warehouseInFk: Destino
|
||||
entryTypeDescription: Tipo entrada
|
||||
invoiceAmount: Importe
|
||||
dated: Fecha
|
||||
|
||||
ticket:
|
||||
params:
|
||||
ticketFk: Ticket ID
|
||||
|
|
|
@ -33,6 +33,7 @@ globals:
|
|||
reset: Restaurar
|
||||
close: Cerrar
|
||||
cancel: Cancelar
|
||||
isSaveAndContinue: Guardar y continuar
|
||||
clone: Clonar
|
||||
confirm: Confirmar
|
||||
assign: Asignar
|
||||
|
|
|
@ -1,30 +1,31 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRole } from 'src/composables/useRole';
|
||||
import { useState } from 'src/composables/useState';
|
||||
import { checkEntryLock } from 'src/composables/checkEntryLock';
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import FormModel from 'components/FormModel.vue';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
|
||||
import VnSelectDialog from 'src/components/common/VnSelectDialog.vue';
|
||||
import FilterTravelForm from 'src/components/FilterTravelForm.vue';
|
||||
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
|
||||
import { toDate } from 'src/filters';
|
||||
import VnSelectTravelExtended from 'src/components/common/VnSelectTravelExtended.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
const { hasAny } = useRole();
|
||||
const isAdministrative = () => hasAny(['administrative']);
|
||||
const state = useState();
|
||||
const user = state.getUser().fn();
|
||||
|
||||
const companiesOptions = ref([]);
|
||||
const currenciesOptions = ref([]);
|
||||
|
||||
const onFilterTravelSelected = (formData, id) => {
|
||||
formData.travelFk = id;
|
||||
};
|
||||
onMounted(() => {
|
||||
checkEntryLock(route.params.id, user.id);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -52,37 +53,11 @@ const onFilterTravelSelected = (formData, id) => {
|
|||
>
|
||||
<template #form="{ data }">
|
||||
<VnRow>
|
||||
<VnSelectDialog
|
||||
:label="t('entry.basicData.travel')"
|
||||
<VnSelectTravelExtended
|
||||
:data="data"
|
||||
v-model="data.travelFk"
|
||||
url="Travels/filter"
|
||||
:fields="['id', 'warehouseInName']"
|
||||
option-value="id"
|
||||
option-label="warehouseInName"
|
||||
map-options
|
||||
hide-selected
|
||||
:required="true"
|
||||
action-icon="filter_alt"
|
||||
>
|
||||
<template #form>
|
||||
<FilterTravelForm
|
||||
@travel-selected="onFilterTravelSelected(data, $event)"
|
||||
/>
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>
|
||||
{{ scope.opt?.agencyModeName }} -
|
||||
{{ scope.opt?.warehouseInName }}
|
||||
({{ toDate(scope.opt?.shipped) }}) →
|
||||
{{ scope.opt?.warehouseOutName }}
|
||||
({{ toDate(scope.opt?.landed) }})
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelectDialog>
|
||||
:onFilterTravelSelected="(data, result) => (data.travelFk = result)"
|
||||
/>
|
||||
<VnSelect
|
||||
:label="t('globals.supplier')"
|
||||
v-model="data.supplierFk"
|
||||
|
|
|
@ -2,14 +2,21 @@
|
|||
import { useStateStore } from 'stores/useStateStore';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { h, onMounted, ref } from 'vue';
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import { useState } from 'src/composables/useState';
|
||||
|
||||
import FetchData from 'src/components/FetchData.vue';
|
||||
import VnTable from 'src/components/VnTable/VnTable.vue';
|
||||
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
|
||||
import FetchedTags from 'src/components/ui/FetchedTags.vue';
|
||||
import VnColor from 'src/components/common/VnColor.vue';
|
||||
import { QCheckbox } from 'quasar';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import ItemDescriptor from 'src/pages/Item/Card/ItemDescriptor.vue';
|
||||
import axios from 'axios';
|
||||
import VnSelectEnum from 'src/components/common/VnSelectEnum.vue';
|
||||
import { checkEntryLock } from 'src/composables/checkEntryLock';
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
|
@ -21,28 +28,67 @@ const $props = defineProps({
|
|||
},
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const state = useState();
|
||||
const user = state.getUser().fn();
|
||||
const stateStore = useStateStore();
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const selectedRows = ref([]);
|
||||
const entityId = ref($props.id ?? route.params.id);
|
||||
console.log('entityId: ', entityId.value);
|
||||
|
||||
const entryBuysRef = ref();
|
||||
const footerFetchDataRef = ref();
|
||||
const footer = ref({});
|
||||
const columns = [
|
||||
{
|
||||
align: 'center',
|
||||
labelAbbreviation: 'NV',
|
||||
label: t('Ignore'),
|
||||
toolTip: t('Ignored for available'),
|
||||
name: 'isIgnored',
|
||||
component: 'checkbox',
|
||||
toggleIndeterminate: false,
|
||||
create: true,
|
||||
width: '25px',
|
||||
},
|
||||
{
|
||||
label: t('Buyer'),
|
||||
name: 'workerFk',
|
||||
component: 'select',
|
||||
attrs: {
|
||||
url: 'Workers/search',
|
||||
fields: ['id', 'nickname'],
|
||||
optionLabel: 'nickname',
|
||||
optionValue: 'id',
|
||||
},
|
||||
visible: false,
|
||||
},
|
||||
{
|
||||
label: t('Family'),
|
||||
name: 'itemTypeFk',
|
||||
component: 'select',
|
||||
attrs: {
|
||||
url: 'itemTypes',
|
||||
fields: ['id', 'name'],
|
||||
optionLabel: 'name',
|
||||
optionValue: 'id',
|
||||
},
|
||||
visible: false,
|
||||
},
|
||||
{
|
||||
name: 'id',
|
||||
isId: true,
|
||||
visible: false,
|
||||
isEditable: false,
|
||||
columnFilter: false,
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
label: 'Nv',
|
||||
name: 'isIgnored',
|
||||
component: 'checkbox',
|
||||
toggleIndeterminate: false,
|
||||
width: '35px',
|
||||
name: 'entryFk',
|
||||
isId: true,
|
||||
visible: false,
|
||||
isEditable: false,
|
||||
disable: true,
|
||||
create: true,
|
||||
columnFilter: false,
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
|
@ -50,26 +96,47 @@ const columns = [
|
|||
name: 'itemFk',
|
||||
component: 'input',
|
||||
isEditable: false,
|
||||
create: true,
|
||||
width: '45px',
|
||||
width: '40px',
|
||||
},
|
||||
{
|
||||
label: '',
|
||||
labelAbbreviation: '',
|
||||
label: 'Color',
|
||||
name: 'hex',
|
||||
columnSearch: false,
|
||||
isEditable: false,
|
||||
width: '5px',
|
||||
component: 'select',
|
||||
attrs: {
|
||||
url: 'Inks',
|
||||
fields: ['id', 'name'],
|
||||
},
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
label: t('Article'),
|
||||
name: 'name',
|
||||
width: '100px',
|
||||
component: 'select',
|
||||
attrs: {
|
||||
url: 'Items',
|
||||
fields: ['id', 'name'],
|
||||
optionLabel: 'name',
|
||||
optionValue: 'id',
|
||||
},
|
||||
width: '85px',
|
||||
isEditable: false,
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
label: t('Siz.'),
|
||||
label: t('Article'),
|
||||
name: 'itemFk',
|
||||
visible: false,
|
||||
create: true,
|
||||
columnFilter: false,
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
labelAbbreviation: t('Siz.'),
|
||||
label: t('Size'),
|
||||
toolTip: t('Size'),
|
||||
name: 'size',
|
||||
width: '35px',
|
||||
|
@ -80,15 +147,17 @@ const columns = [
|
|||
},
|
||||
{
|
||||
align: 'center',
|
||||
label: t('Sti.'),
|
||||
labelAbbreviation: t('Sti.'),
|
||||
label: t('Printed Stickers/Stickers'),
|
||||
toolTip: t('Printed Stickers/Stickers'),
|
||||
name: 'stickers',
|
||||
component: 'number',
|
||||
create: true,
|
||||
attrs: {
|
||||
positive: false,
|
||||
},
|
||||
cellEvent: {
|
||||
'update:modelValue': (value, oldValue, row) => {
|
||||
'update:modelValue': async (value, oldValue, row) => {
|
||||
row['quantity'] = value * row['packing'];
|
||||
row['amount'] = row['quantity'] * row['buyingValue'];
|
||||
},
|
||||
|
@ -105,7 +174,8 @@ const columns = [
|
|||
fields: ['id', 'volume'],
|
||||
optionLabel: 'id',
|
||||
},
|
||||
width: '60px',
|
||||
create: true,
|
||||
width: '40px',
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
|
@ -117,12 +187,14 @@ const columns = [
|
|||
},
|
||||
{
|
||||
align: 'center',
|
||||
label: 'Pack',
|
||||
labelAbbreviation: 'Pack',
|
||||
label: 'Packing',
|
||||
toolTip: 'Packing',
|
||||
name: 'packing',
|
||||
component: 'number',
|
||||
create: true,
|
||||
cellEvent: {
|
||||
'update:modelValue': (value, oldValue, row) => {
|
||||
console.log('oldValue: ', oldValue);
|
||||
'update:modelValue': async (value, oldValue, row) => {
|
||||
const oldPacking = oldValue === 1 || oldValue === null ? 1 : oldValue;
|
||||
row['weight'] = (row['weight'] * value) / oldPacking;
|
||||
row['quantity'] = row['stickers'] * value;
|
||||
|
@ -134,42 +206,44 @@ const columns = [
|
|||
if (row.groupingMode === 'grouping')
|
||||
return { color: 'var(--vn-label-color)' };
|
||||
},
|
||||
/* append: {
|
||||
name: 'groupingMode',
|
||||
h: (row) =>
|
||||
h(QCheckbox, {
|
||||
'data-name': 'groupingMode',
|
||||
modelValue: row['groupingMode'] === 'packing',
|
||||
size: 'sm',
|
||||
'onUpdate:modelValue': (value) => {
|
||||
console.log('entra');
|
||||
if (value) row['groupingMode'] = 'packing';
|
||||
else row['groupingMode'] = 'grouping';
|
||||
},
|
||||
onClick: (event) => {
|
||||
console.log('eventOnClick: ', event);
|
||||
},
|
||||
}),
|
||||
}, */
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
label: 'Group',
|
||||
labelAbbreviation: 'GM',
|
||||
label: t('Grouping selector'),
|
||||
toolTip: t('Grouping selector'),
|
||||
name: 'groupingMode',
|
||||
component: 'toggle',
|
||||
attrs: {
|
||||
'toggle-indeterminate': true,
|
||||
trueValue: 'grouping',
|
||||
falseValue: 'packing',
|
||||
indeterminateValue: null,
|
||||
},
|
||||
width: '35px',
|
||||
size: 'xs',
|
||||
width: '30px',
|
||||
create: true,
|
||||
rightFilter: false,
|
||||
getIcon: (value) => {
|
||||
switch (value) {
|
||||
case 'grouping':
|
||||
return 'toggle_on';
|
||||
case 'packing':
|
||||
return 'toggle_off';
|
||||
default:
|
||||
return 'minimize';
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
label: 'Group',
|
||||
labelAbbreviation: 'Group',
|
||||
label: 'Grouping',
|
||||
toolTip: 'Grouping',
|
||||
name: 'grouping',
|
||||
component: 'number',
|
||||
width: '35px',
|
||||
create: true,
|
||||
style: (row) => {
|
||||
if (row.groupingMode === 'packing') return { color: 'var(--vn-label-color)' };
|
||||
},
|
||||
|
@ -183,34 +257,37 @@ const columns = [
|
|||
positive: false,
|
||||
},
|
||||
cellEvent: {
|
||||
'update:modelValue': (value, oldValue, row) => {
|
||||
'update:modelValue': async (value, oldValue, row) => {
|
||||
row['amount'] = value * row['buyingValue'];
|
||||
},
|
||||
},
|
||||
width: '50px',
|
||||
width: '45px',
|
||||
create: true,
|
||||
style: getQuantityStyle,
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
label: t('Cost'),
|
||||
labelAbbreviation: t('Cost'),
|
||||
label: t('Buying value'),
|
||||
toolTip: t('Buying value'),
|
||||
name: 'buyingValue',
|
||||
create: true,
|
||||
component: 'number',
|
||||
attrs: {
|
||||
positive: false,
|
||||
},
|
||||
cellEvent: {
|
||||
'update:modelValue': (value, oldValue, row) => {
|
||||
'update:modelValue': async (value, oldValue, row) => {
|
||||
row['amount'] = row['quantity'] * value;
|
||||
},
|
||||
},
|
||||
width: '50px',
|
||||
width: '45px',
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
label: t('Amount'),
|
||||
name: 'amount',
|
||||
width: '50px',
|
||||
width: '45px',
|
||||
component: 'number',
|
||||
attrs: {
|
||||
positive: false,
|
||||
|
@ -220,11 +297,13 @@ const columns = [
|
|||
},
|
||||
{
|
||||
align: 'center',
|
||||
label: t('Pack.'),
|
||||
labelAbbreviation: t('Pack.'),
|
||||
label: t('Package'),
|
||||
toolTip: t('Package'),
|
||||
name: 'price2',
|
||||
component: 'number',
|
||||
width: '35px',
|
||||
create: true,
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
|
@ -232,48 +311,45 @@ const columns = [
|
|||
name: 'price3',
|
||||
component: 'number',
|
||||
cellEvent: {
|
||||
'update:modelValue': (value, row) => {
|
||||
/*
|
||||
Call db.execV("UPDATE vn.item SET " & _
|
||||
"typeFk = # " & _
|
||||
",producerFk = # " & _
|
||||
",minPrice = # " & _
|
||||
",box = # " & _
|
||||
",hasMinPrice = # " & _
|
||||
",comment = # " & _
|
||||
"WHERE id = # " _
|
||||
, Me.tipo_id _
|
||||
, Me.producer_id _
|
||||
, Me.PVP _
|
||||
, Me.caja _
|
||||
, Me.Min _
|
||||
, Nz(Me.reference, 0) _
|
||||
, Me.Id_Article _
|
||||
)
|
||||
Me.Tarifa2 = Me.Tarifa2 * (Me.Tarifa3 / Me.Tarifa3.OldValue)
|
||||
Call actualizar_compra
|
||||
Me.sincro = True
|
||||
*/
|
||||
'update:modelValue': async (value, oldValue, row) => {
|
||||
row['price2'] = row['price2'] * (value / oldValue);
|
||||
},
|
||||
},
|
||||
width: '35px',
|
||||
create: true,
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
label: 'Min.',
|
||||
labelAbbreviation: 'Min.',
|
||||
label: t('Minimum price'),
|
||||
toolTip: t('Minimum price'),
|
||||
name: 'minPrice',
|
||||
component: 'number',
|
||||
isEditable: false,
|
||||
cellEvent: {
|
||||
'update:modelValue': async (value, oldValue, row) => {
|
||||
await axios.patch(`Items/${row['itemFk']}`, {
|
||||
minPrice: value,
|
||||
});
|
||||
},
|
||||
},
|
||||
width: '35px',
|
||||
style: (row) => {
|
||||
if (row?.hasMinPrice)
|
||||
return { backgroundColor: 'var(--q-positive)', color: 'black' };
|
||||
if (!row?.hasMinPrice) return { color: 'var(--vn-label-color)' };
|
||||
},
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
label: t('P.Sen'),
|
||||
labelAbbreviation: 'CM',
|
||||
label: t('Check min price'),
|
||||
toolTip: t('Check min price'),
|
||||
name: 'hasMinPrice',
|
||||
component: 'checkbox',
|
||||
width: '25px',
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
labelAbbreviation: t('P.Sen'),
|
||||
label: t('Packing sent'),
|
||||
toolTip: t('Packing sent'),
|
||||
name: 'packingOut',
|
||||
component: 'number',
|
||||
|
@ -282,16 +358,18 @@ const columns = [
|
|||
},
|
||||
{
|
||||
align: 'center',
|
||||
label: t('Com.'),
|
||||
labelAbbreviation: t('Com.'),
|
||||
label: t('Comment'),
|
||||
toolTip: t('Comment'),
|
||||
name: 'comment',
|
||||
component: 'input',
|
||||
isEditable: false,
|
||||
width: '55px',
|
||||
width: '50px',
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
label: 'Prod.',
|
||||
labelAbbreviation: 'Prod.',
|
||||
label: t('Producer'),
|
||||
toolTip: t('Producer'),
|
||||
name: 'subName',
|
||||
isEditable: false,
|
||||
|
@ -309,7 +387,8 @@ const columns = [
|
|||
},
|
||||
{
|
||||
align: 'center',
|
||||
label: 'Comp.',
|
||||
labelAbbreviation: 'Comp.',
|
||||
label: t('Company'),
|
||||
toolTip: t('Company'),
|
||||
name: 'company_name',
|
||||
component: 'input',
|
||||
|
@ -327,97 +406,162 @@ function getAmountStyle(row) {
|
|||
return { color: 'var(--vn-label-color)' };
|
||||
}
|
||||
|
||||
async function beforeSave(data, getChanges) {
|
||||
try {
|
||||
const changes = data.updates;
|
||||
if (!changes) return data;
|
||||
const patchPromises = [];
|
||||
|
||||
for (const change of changes) {
|
||||
let patchData = {};
|
||||
|
||||
if ('hasMinPrice' in change.data) {
|
||||
patchData.hasMinPrice = change.data?.hasMinPrice;
|
||||
delete change.data.hasMinPrice;
|
||||
}
|
||||
if ('minPrice' in change.data) {
|
||||
patchData.minPrice = change.data?.minPrice;
|
||||
delete change.data.minPrice;
|
||||
}
|
||||
|
||||
if (Object.keys(patchData).length > 0) {
|
||||
const promise = axios
|
||||
.get('Buys/findOne', {
|
||||
params: {
|
||||
filter: {
|
||||
fields: ['itemFk'],
|
||||
where: { id: change.where.id },
|
||||
},
|
||||
},
|
||||
})
|
||||
.then((buy) => {
|
||||
return axios.patch(`Items/${buy.data.itemFk}`, patchData);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error processing change: ', change, error);
|
||||
});
|
||||
|
||||
patchPromises.push(promise);
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(patchPromises);
|
||||
|
||||
data.updates = changes.filter((change) => Object.keys(change.data).length > 0);
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Error in beforeSave:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function invertQuantitySign(rows, sign) {
|
||||
for (const row of rows) {
|
||||
row.quantity = row.quantity * sign;
|
||||
}
|
||||
}
|
||||
function setIsChecked(rows, value) {
|
||||
for (const row of rows) {
|
||||
row.isChecked = value;
|
||||
}
|
||||
footerFetchDataRef.value.fetch();
|
||||
}
|
||||
|
||||
async function setBuyUltimate(itemFk, data) {
|
||||
if (!itemFk) return;
|
||||
const buyUltimate = await axios.get(`Entries/getBuyUltimate`, {
|
||||
params: {
|
||||
itemFk,
|
||||
warehouseFk: user.warehouseFk,
|
||||
date: Date.vnNew(),
|
||||
},
|
||||
});
|
||||
const buyUltimateData = buyUltimate.data[0];
|
||||
|
||||
const allowedKeys = columns
|
||||
.filter((col) => col.create === true)
|
||||
.map((col) => col.name);
|
||||
|
||||
allowedKeys.forEach((key) => {
|
||||
if (buyUltimateData.hasOwnProperty(key) && key !== 'entryFk') {
|
||||
data[key] = buyUltimateData[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
console.log('viewMode: ', $props.editableMode);
|
||||
stateStore.rightDrawer = false;
|
||||
if ($props.editableMode) checkEntryLock(entityId.value, user.id);
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<QToggle
|
||||
toggle-indeterminate
|
||||
toggle-order="ft"
|
||||
v-model="cyan"
|
||||
label="'ft' order + toggle-indeterminate"
|
||||
color="cyan"
|
||||
/>
|
||||
<Teleport to="#st-data" v-if="stateStore?.isSubToolbarShown() && editableMode">
|
||||
<QBtnGroup push style="column-gap: 1px">
|
||||
<QBtn icon="calculate" color="primary" flat @click="console.log('calculate')">
|
||||
<QTooltip>{{ t('tableActions.openBucketCalculator') }}</QTooltip>
|
||||
</QBtn>
|
||||
<QBtnDropdown
|
||||
icon="box_edit"
|
||||
color="primary"
|
||||
flat
|
||||
tool-tip="test"
|
||||
@click="console.log('request_quote')"
|
||||
:title="t('tableActions.setSaleMode')"
|
||||
>
|
||||
<div>
|
||||
<QList>
|
||||
<QItem clickable v-close-popup @click="setSaleMode('packing')">
|
||||
<QItemSection>
|
||||
<QItemLabel>Packing</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem clickable v-close-popup @click="setSaleMode('packing')">
|
||||
<QItemSection>
|
||||
<QItemLabel>Grouping</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem label="Grouping" />
|
||||
</QList>
|
||||
</div>
|
||||
</QBtnDropdown>
|
||||
<QBtn
|
||||
icon="invert_colors"
|
||||
color="primary"
|
||||
flat
|
||||
@click="console.log('price_check')"
|
||||
>
|
||||
<QTooltip>{{ t('tableActions.openCalculator') }}</QTooltip>
|
||||
</QBtn>
|
||||
<QBtn
|
||||
icon="exposure_neg_1"
|
||||
color="primary"
|
||||
flat
|
||||
@click="console.log('request_quote')"
|
||||
title="test"
|
||||
:title="t('Invert quantity value')"
|
||||
:disable="!selectedRows.length"
|
||||
>
|
||||
<QTooltip>{{ t('tableActions.invertQuantitySign') }}</QTooltip>
|
||||
</QBtn>
|
||||
<QBtn
|
||||
<QList>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<QBtn flat @click="invertQuantitySign(selectedRows, -1)">
|
||||
<span style="font-size: medium">-1</span>
|
||||
</QBtn>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<QBtn flat @click="invertQuantitySign(selectedRows, 1)">
|
||||
<span style="font-size: medium">1</span>
|
||||
</QBtn>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</QList>
|
||||
</QBtnDropdown>
|
||||
<QBtnDropdown
|
||||
icon="price_check"
|
||||
color="primary"
|
||||
flat
|
||||
@click="console.log('request_quote')"
|
||||
:title="t('Check buy amount')"
|
||||
:disable="!selectedRows.length"
|
||||
>
|
||||
<QTooltip>{{ t('tableActions.checkAmount') }}</QTooltip>
|
||||
</QBtn>
|
||||
<QBtn
|
||||
icon="price_check"
|
||||
color="primary"
|
||||
flat
|
||||
@click="console.log('request_quote')"
|
||||
>
|
||||
<QTooltip>{{ t('tableActions.setMinPrice') }}</QTooltip>
|
||||
</QBtn>
|
||||
<QTooltip>{{}}</QTooltip>
|
||||
<QList>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<QBtn
|
||||
icon="check"
|
||||
flat
|
||||
@click="setIsChecked(selectedRows, true)"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<QItemSection>
|
||||
<QBtn
|
||||
icon="close"
|
||||
flat
|
||||
@click="setIsChecked(selectedRows, false)"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</QList>
|
||||
</QBtnDropdown>
|
||||
</QBtnGroup>
|
||||
</Teleport>
|
||||
<FetchData
|
||||
ref="footerFetchDataRef"
|
||||
:url="`Entries/${entityId}/getBuyList`"
|
||||
:params="{ groupBy: 'GROUP BY b.entryFk' }"
|
||||
@on-fetch="
|
||||
(data) => {
|
||||
console.log('data: ', data);
|
||||
footer = data[0];
|
||||
}
|
||||
"
|
||||
@on-fetch="(data) => (footer = data[0])"
|
||||
auto-load
|
||||
/>
|
||||
<VnTable
|
||||
ref="tableRef"
|
||||
ref="entryBuysRef"
|
||||
data-key="EntryBuys"
|
||||
:url="`Entries/${entityId}/getBuyList`"
|
||||
save-url="Buys/crud"
|
||||
|
@ -431,19 +575,40 @@ onMounted(() => {
|
|||
}
|
||||
: {}
|
||||
"
|
||||
:create="
|
||||
editableMode
|
||||
? {
|
||||
urlCreate: 'Buys',
|
||||
title: t('Create buy'),
|
||||
onDataSaved: () => {
|
||||
entryBuysRef.reload();
|
||||
footerFetchDataRef.fetch();
|
||||
},
|
||||
formInitialData: { entryFk: entityId, isIgnored: false },
|
||||
isFullWidth: true,
|
||||
containerClass: 'form-container',
|
||||
showSaveAndContinueBtn: true,
|
||||
columnGridStyle: {
|
||||
'max-width': '50%',
|
||||
flex: 1,
|
||||
},
|
||||
}
|
||||
: null
|
||||
"
|
||||
:is-editable="editableMode"
|
||||
:without-header="!editableMode"
|
||||
:with-filters="editableMode"
|
||||
:right-search="false"
|
||||
:right-search="editableMode"
|
||||
:row-click="false"
|
||||
:columns="columns"
|
||||
:beforeSaveFn="beforeSave"
|
||||
class="buyList"
|
||||
table-height="84vh"
|
||||
auto-load
|
||||
footer
|
||||
>
|
||||
<template #column-hex="{ row }">
|
||||
<VnColor :colors="row?.hexJson" style="height: 100%" />
|
||||
<VnColor :colors="row?.hexJson" style="height: 100%; min-width: 2000px" />
|
||||
</template>
|
||||
<template #column-name="{ row }">
|
||||
<span class="link">
|
||||
|
@ -456,9 +621,9 @@ onMounted(() => {
|
|||
</template>
|
||||
<template #column-stickers="{ row }">
|
||||
<span :class="editableMode ? 'editable-text' : ''">
|
||||
<span style="color: var(--vn-label-color)">{{
|
||||
row.printedStickers
|
||||
}}</span>
|
||||
<span style="color: var(--vn-label-color)">
|
||||
{{ row.printedStickers }}
|
||||
</span>
|
||||
<span>/{{ row.stickers }}</span>
|
||||
</span>
|
||||
</template>
|
||||
|
@ -483,6 +648,36 @@ onMounted(() => {
|
|||
{{ footer?.amount }}
|
||||
</span>
|
||||
</template>
|
||||
<template #column-create-itemFk="{ data }">
|
||||
<VnSelect
|
||||
url="Items"
|
||||
v-model="data.itemFk"
|
||||
:label="t('Article')"
|
||||
:fields="['id', 'name']"
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
@update:modelValue="
|
||||
async (value) => {
|
||||
setBuyUltimate(value, data);
|
||||
}
|
||||
"
|
||||
:required="true"
|
||||
/>
|
||||
</template>
|
||||
<template #column-create-groupingMode="{ data }">
|
||||
<VnSelectEnum
|
||||
:label="t('Grouping mode')"
|
||||
v-model="data.groupingMode"
|
||||
schema="vn"
|
||||
table="buy"
|
||||
column="groupingMode"
|
||||
option-value="groupingMode"
|
||||
option-label="groupingMode"
|
||||
/>
|
||||
</template>
|
||||
<template #previous-create-dialog="{ data }">
|
||||
<ItemDescriptor :id="data.itemFk" />
|
||||
</template>
|
||||
</VnTable>
|
||||
</template>
|
||||
<i18n>
|
||||
|
@ -508,4 +703,18 @@ es:
|
|||
Producer: Productor
|
||||
Company: Compañia
|
||||
Tags: Etiquetas
|
||||
Grouping mode: Modo de agrupación
|
||||
C.min: P.min
|
||||
Ignore: Ignorar
|
||||
Ignored for available: Ignorado para disponible
|
||||
Grouping selector: Selector de grouping
|
||||
Check min price: Marcar precio mínimo
|
||||
Create buy: Crear compra
|
||||
Invert quantity value: Invertir valor de cantidad
|
||||
Check buy amount: Marcar como correcta la cantidad de compra
|
||||
</i18n>
|
||||
<style lang="scss" scoped>
|
||||
.test {
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -10,6 +10,9 @@ import filter from './EntryFilter.js';
|
|||
import TravelDescriptorProxy from 'src/pages/Travel/Card/TravelDescriptorProxy.vue';
|
||||
import axios from 'axios';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useQuasar } from 'quasar';
|
||||
|
||||
const quasar = useQuasar();
|
||||
const { push } = useRouter();
|
||||
|
||||
const $props = defineProps({
|
||||
|
@ -56,17 +59,24 @@ const getEntryRedirectionFilter = (entry) => {
|
|||
function showEntryReport() {
|
||||
openReport(`Entries/${entityId.value}/entry-order-pdf`);
|
||||
}
|
||||
function recalculateRates() {
|
||||
console.log('recalculateRates');
|
||||
async function recalculateRates(entity) {
|
||||
const entryConfig = await axios.get('EntryConfigs/findOne');
|
||||
if (entryConfig.data?.inventorySupplierFk === entity.supplierFk) {
|
||||
quasar.notify({
|
||||
type: 'negative',
|
||||
message: t('Cannot recalculate prices because this is an inventory entry'),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await axios.post(`Entries/${entityId.value}/recalcEntryPrices`);
|
||||
}
|
||||
async function cloneEntry() {
|
||||
console.log('cloneEntry');
|
||||
await axios
|
||||
.post(`Entries/${entityId.value}/cloneEntry`)
|
||||
.then((response) => push(`/entry/${response.data[0].vNewEntryFk}`));
|
||||
}
|
||||
async function deleteEntry() {
|
||||
console.log('deleteEntry');
|
||||
await axios.post(`Entries/${entityId.value}/deleteEntry`).then(() => push(`/entry/`));
|
||||
}
|
||||
</script>
|
||||
|
@ -118,6 +128,10 @@ async function deleteEntry() {
|
|||
:label="t('entry.summary.invoiceAmount')"
|
||||
:value="entity?.invoiceAmount"
|
||||
/>
|
||||
<VnLv
|
||||
:label="t('entry.summary.entryType')"
|
||||
:value="entity?.entryType?.description"
|
||||
/>
|
||||
</template>
|
||||
<template #icons="{ entity }">
|
||||
<QCardActions class="q-gutter-x-md">
|
||||
|
@ -163,21 +177,6 @@ async function deleteEntry() {
|
|||
>
|
||||
<QTooltip>{{ t('Supplier card') }}</QTooltip>
|
||||
</QBtn>
|
||||
<QBtn
|
||||
:to="{
|
||||
name: 'TravelMain',
|
||||
query: {
|
||||
params: JSON.stringify({
|
||||
agencyModeFk: entity.travel?.agencyModeFk,
|
||||
}),
|
||||
},
|
||||
}"
|
||||
size="md"
|
||||
icon="local_airport"
|
||||
color="primary"
|
||||
>
|
||||
<QTooltip>{{ t('All travels with current agency') }}</QTooltip>
|
||||
</QBtn>
|
||||
<QBtn
|
||||
:to="{
|
||||
name: 'EntryMain',
|
||||
|
@ -207,4 +206,5 @@ es:
|
|||
shipped: Enviado
|
||||
landed: Recibido
|
||||
This entry is deleted: Esta entrada está eliminada
|
||||
Cannot recalculate prices because this is an inventory entry: No se pueden recalcular los precios porque es una entrada de inventario
|
||||
</i18n>
|
||||
|
|
|
@ -46,5 +46,11 @@ export default {
|
|||
fields: ['id', 'code'],
|
||||
},
|
||||
},
|
||||
{
|
||||
relation: 'entryType',
|
||||
scope: {
|
||||
fields: ['code', 'description'],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -167,7 +167,12 @@ onMounted(async () => {
|
|||
:url="`#/entry/{{ entityId }}/buys`"
|
||||
:text="t('entry.summary.buys')"
|
||||
/>
|
||||
<EntryBuys v-if="entityId" :id="entityId" :editable-mode="false" />
|
||||
<EntryBuys
|
||||
v-if="entityId"
|
||||
:id="entityId"
|
||||
:editable-mode="false"
|
||||
:isEditable="false"
|
||||
/>
|
||||
</QCard>
|
||||
</template>
|
||||
</CardSummary>
|
||||
|
|
|
@ -9,6 +9,7 @@ import { onBeforeMount } from 'vue';
|
|||
import EntryFilter from './EntryFilter.vue';
|
||||
import VnTable from 'components/VnTable/VnTable.vue';
|
||||
import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue';
|
||||
import VnSelectTravelExtended from 'src/components/common/VnSelectTravelExtended.vue';
|
||||
import { toDate } from 'src/filters';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
@ -274,45 +275,56 @@ onBeforeMount(async () => {
|
|||
<template #advanced-menu>
|
||||
<EntryFilter :data-key="dataKey" />
|
||||
</template>
|
||||
</VnSection>
|
||||
<VnTable
|
||||
v-if="defaultEntry.defaultSupplierFk"
|
||||
ref="tableRef"
|
||||
:data-key="dataKey"
|
||||
url="Entries/filter"
|
||||
:filter="entryQueryFilter"
|
||||
:create="{
|
||||
urlCreate: 'Entries',
|
||||
title: t('Create entry'),
|
||||
onDataSaved: ({ id }) => tableRef.redirect(id),
|
||||
formInitialData: {
|
||||
supplierFk: defaultEntry.defaultSupplierFk,
|
||||
dated: Date.vnNew(),
|
||||
companyFk: user?.companyFk,
|
||||
},
|
||||
}"
|
||||
order="id DESC"
|
||||
:columns="columns"
|
||||
redirect="entry"
|
||||
:right-search="false"
|
||||
>
|
||||
<template #column-landed="{ row }">
|
||||
<QBadge
|
||||
v-if="row?.travelFk"
|
||||
v-bind="getBadgeAttrs(row)"
|
||||
class="q-pa-sm"
|
||||
style="font-size: 14px"
|
||||
<template #body>
|
||||
<VnTable
|
||||
v-if="defaultEntry.defaultSupplierFk"
|
||||
ref="tableRef"
|
||||
:data-key="dataKey"
|
||||
url="Entries/filter"
|
||||
:filter="entryQueryFilter"
|
||||
:create="{
|
||||
urlCreate: 'Entries',
|
||||
title: t('Create entry'),
|
||||
onDataSaved: ({ id }) => tableRef.redirect(id),
|
||||
formInitialData: {
|
||||
supplierFk: defaultEntry.defaultSupplierFk,
|
||||
dated: Date.vnNew(),
|
||||
companyFk: user?.companyFk,
|
||||
},
|
||||
}"
|
||||
order="id DESC"
|
||||
:columns="columns"
|
||||
redirect="entry"
|
||||
:right-search="false"
|
||||
>
|
||||
{{ toDate(row.landed) }}
|
||||
</QBadge>
|
||||
<template #column-landed="{ row }">
|
||||
<QBadge
|
||||
v-if="row?.travelFk"
|
||||
v-bind="getBadgeAttrs(row)"
|
||||
class="q-pa-sm"
|
||||
style="font-size: 14px"
|
||||
>
|
||||
{{ toDate(row.landed) }}
|
||||
</QBadge>
|
||||
</template>
|
||||
<template #column-supplierFk="{ row }">
|
||||
<span class="link" @click.stop>
|
||||
{{ row.supplierName }}
|
||||
<SupplierDescriptorProxy :id="row.supplierFk" />
|
||||
</span>
|
||||
</template>
|
||||
<template #column-create-travelFk="{ data }">
|
||||
<VnSelectTravelExtended
|
||||
:data="data"
|
||||
v-model="data.travelFk"
|
||||
:onFilterTravelSelected="
|
||||
(data, result) => (data.travelFk = result)
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
</VnTable>
|
||||
</template>
|
||||
<template #column-supplierFk="{ row }">
|
||||
<span class="link" @click.stop>
|
||||
{{ row.supplierName }}
|
||||
<SupplierDescriptorProxy :id="row.supplierFk" />
|
||||
</span>
|
||||
</template>
|
||||
</VnTable>
|
||||
</VnSection>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
entry:
|
||||
lock:
|
||||
title: Lock entry
|
||||
message: This entry has been locked by {userName} for {time} minutes. Do you want to unlock it?
|
||||
list:
|
||||
newEntry: New entry
|
||||
tableVisibleColumns:
|
||||
|
@ -20,11 +23,13 @@ entry:
|
|||
entryTypeDescription: Entry type
|
||||
invoiceAmount: Import
|
||||
travelFk: Travel
|
||||
dated: Dated
|
||||
inventoryEntry: Inventory entry
|
||||
summary:
|
||||
commission: Commission
|
||||
currency: Currency
|
||||
invoiceNumber: Invoice number
|
||||
invoiceAmount: Invoice amount
|
||||
ordered: Ordered
|
||||
booked: Booked
|
||||
excludedFromAvailable: Inventory
|
||||
|
@ -42,6 +47,7 @@ entry:
|
|||
buyingValue: Buying value
|
||||
import: Import
|
||||
pvp: PVP
|
||||
entryType: Entry type
|
||||
basicData:
|
||||
travel: Travel
|
||||
currency: Currency
|
||||
|
@ -78,11 +84,48 @@ entry:
|
|||
landing: Landing
|
||||
isExcludedFromAvailable: Es inventory
|
||||
params:
|
||||
toShipped: To
|
||||
fromShipped: From
|
||||
daysOnward: Days onward
|
||||
daysAgo: Days ago
|
||||
warehouseInFk: Warehouse in
|
||||
isExcludedFromAvailable: Exclude from inventory
|
||||
isOrdered: Ordered
|
||||
isConfirmed: Ready to label
|
||||
isReceived: Received
|
||||
isIgnored: Ignored
|
||||
isRaid: Raid
|
||||
landed: Date
|
||||
supplierFk: Supplier
|
||||
reference: Ref/Alb/Guide
|
||||
invoiceNumber: Invoice
|
||||
agencyModeId: Agency
|
||||
isBooked: Booked
|
||||
companyFk: Company
|
||||
evaNotes: Notes
|
||||
warehouseOutFk: Origin
|
||||
warehouseInFk: Destiny
|
||||
entryTypeDescription: Entry type
|
||||
invoiceAmount: Import
|
||||
travelFk: Travel
|
||||
dated: Dated
|
||||
itemFk: Item id
|
||||
hex: Color
|
||||
name: Item name
|
||||
size: Size
|
||||
stickers: Stickers
|
||||
packagingFk: Packaging
|
||||
weight: Kg
|
||||
groupingMode: Grouping selector
|
||||
grouping: Grouping
|
||||
quantity: Quantity
|
||||
buyingValue: Buying value
|
||||
price2: Package
|
||||
price3: Box
|
||||
minPrice: Minumum price
|
||||
hasMinPrice: Has minimum price
|
||||
packingOut: Packing out
|
||||
comment: Comment
|
||||
subName: Supplier name
|
||||
tags: Tags
|
||||
company_name: Company name
|
||||
itemTypeFk: Item type
|
||||
workerFk: Worker id
|
||||
search: Search entries
|
||||
searchInfo: You can search by entry reference
|
||||
entryFilter:
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
entry:
|
||||
lock:
|
||||
title: Entrada bloqueada
|
||||
message: Esta entrada ha sido bloqueada por {userName} hace {time} minutos. ¿Quieres desbloquearla?
|
||||
|
||||
list:
|
||||
newEntry: Nueva entrada
|
||||
tableVisibleColumns:
|
||||
|
@ -20,11 +24,13 @@ entry:
|
|||
warehouseInFk: Destino
|
||||
entryTypeDescription: Tipo entrada
|
||||
invoiceAmount: Importe
|
||||
dated: Fecha
|
||||
inventoryEntry: Es inventario
|
||||
summary:
|
||||
commission: Comisión
|
||||
currency: Moneda
|
||||
invoiceNumber: Núm. factura
|
||||
invoiceAmount: Importe
|
||||
ordered: Pedida
|
||||
booked: Contabilizada
|
||||
excludedFromAvailable: Inventario
|
||||
|
@ -43,6 +49,7 @@ entry:
|
|||
buyingValue: Coste
|
||||
import: Importe
|
||||
pvp: PVP
|
||||
entryType: Tipo entrada
|
||||
basicData:
|
||||
travel: Envío
|
||||
currency: Moneda
|
||||
|
@ -78,14 +85,52 @@ entry:
|
|||
packingOut: Embalaje envíos
|
||||
landing: Llegada
|
||||
isExcludedFromAvailable: Es inventario
|
||||
params:
|
||||
toShipped: Hasta
|
||||
fromShipped: Desde
|
||||
warehouseInFk: Alm. entrada
|
||||
daysOnward: Días adelante
|
||||
daysAgo: Días atras
|
||||
|
||||
search: Buscar entradas
|
||||
searchInfo: Puedes buscar por referencia de entrada
|
||||
params:
|
||||
isExcludedFromAvailable: Excluir del inventario
|
||||
isOrdered: Pedida
|
||||
isConfirmed: Lista para etiquetar
|
||||
isReceived: Recibida
|
||||
isRaid: Redada
|
||||
isIgnored: Ignorado
|
||||
landed: Fecha
|
||||
supplierFk: Proveedor
|
||||
invoiceNumber: Nº Factura
|
||||
reference: Ref/Alb/Guía
|
||||
agencyModeId: Agencia
|
||||
isBooked: Asentado
|
||||
companyFk: Empresa
|
||||
travelFk: Envio
|
||||
evaNotes: Notas
|
||||
warehouseOutFk: Origen
|
||||
warehouseInFk: Destino
|
||||
entryTypeDescription: Tipo entrada
|
||||
invoiceAmount: Importe
|
||||
dated: Fecha
|
||||
itemFk: Id artículo
|
||||
hex: Color
|
||||
name: Nombre artículo
|
||||
size: Medida
|
||||
stickers: Etiquetas
|
||||
packagingFk: Embalaje
|
||||
weight: Kg
|
||||
groupinMode: Selector de grouping
|
||||
grouping: Grouping
|
||||
quantity: Quantity
|
||||
buyingValue: Precio de compra
|
||||
price2: Paquete
|
||||
price3: Caja
|
||||
minPrice: Precio mínimo
|
||||
hasMinPrice: Tiene precio mínimo
|
||||
packingOut: Packing out
|
||||
comment: Referencia
|
||||
subName: Nombre proveedor
|
||||
tags: Etiquetas
|
||||
company_name: Nombre empresa
|
||||
itemTypeFk: Familia
|
||||
workerFk: Comprador
|
||||
entryFilter:
|
||||
params:
|
||||
invoiceNumber: Núm. factura
|
||||
|
|
|
@ -272,7 +272,6 @@ const createInvoiceInCorrection = async () => {
|
|||
>
|
||||
<template #option="{ itemProps, opt }">
|
||||
<QItem v-bind="itemProps">
|
||||
{{ console.log('opt: ', opt) }}
|
||||
<QItemSection>
|
||||
<QItemLabel
|
||||
>{{ opt.id }} -
|
||||
|
|
|
@ -28,6 +28,7 @@ const cols = computed(() => [
|
|||
name: 'isBooked',
|
||||
label: t('invoicein.isBooked'),
|
||||
columnFilter: false,
|
||||
component: 'checkbox',
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
|
@ -176,7 +177,9 @@ const cols = computed(() => [
|
|||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{ scope.opt?.nickname }}</QItemLabel>
|
||||
<QItemLabel caption> #{{ scope.opt?.id }}, {{ scope.opt?.name }} </QItemLabel>
|
||||
<QItemLabel caption>
|
||||
#{{ scope.opt?.id }}, {{ scope.opt?.name }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
|
|
|
@ -157,7 +157,7 @@ const openTab = (id) =>
|
|||
openConfirmationModal(
|
||||
$t('globals.deleteConfirmTitle'),
|
||||
$t('salesOrdersTable.deleteConfirmMessage'),
|
||||
removeOrders
|
||||
removeOrders,
|
||||
)
|
||||
"
|
||||
>
|
||||
|
|
|
@ -50,7 +50,6 @@ const columns = computed(() => [
|
|||
name: 'isAnyVolumeAllowed',
|
||||
component: 'checkbox',
|
||||
cardVisible: true,
|
||||
disable: true,
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
|
@ -80,7 +79,8 @@ const columns = computed(() => [
|
|||
url="Agencies"
|
||||
order="name"
|
||||
:columns="columns"
|
||||
:right-search="false"
|
||||
is-editable="false"
|
||||
:right-search="true"
|
||||
:use-model="true"
|
||||
redirect="agency"
|
||||
default-mode="card"
|
||||
|
|
|
@ -68,7 +68,7 @@ const columns = computed(() => [
|
|||
},
|
||||
useLike: false,
|
||||
cardVisible: true,
|
||||
format: (row, dashIfEmpty) => dashIfEmpty(row.travelRef),
|
||||
format: (row, dashIfEmpty) => dashIfEmpty(row.workerUserName),
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
|
@ -87,6 +87,7 @@ const columns = computed(() => [
|
|||
},
|
||||
},
|
||||
columnClass: 'expand',
|
||||
format: (row, dashIfEmpty) => dashIfEmpty(row.agencyName),
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
|
@ -108,6 +109,7 @@ const columns = computed(() => [
|
|||
columnFilter: {
|
||||
inWhere: true,
|
||||
},
|
||||
format: (row, dashIfEmpty) => dashIfEmpty(row.vehiclePlateNumber),
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
|
@ -117,7 +119,7 @@ const columns = computed(() => [
|
|||
cardVisible: true,
|
||||
create: true,
|
||||
component: 'date',
|
||||
format: ({ created }) => toDate(created),
|
||||
format: ({ dated }) => toDate(dated),
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
|
@ -127,7 +129,7 @@ const columns = computed(() => [
|
|||
cardVisible: true,
|
||||
create: true,
|
||||
component: 'date',
|
||||
format: ({ from }) => toDate(from),
|
||||
format: ({ from }) => from,
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
|
@ -152,7 +154,7 @@ const columns = computed(() => [
|
|||
label: t('route.hourStarted'),
|
||||
component: 'time',
|
||||
columnFilter: false,
|
||||
format: ({ hourStarted }) => toHour(hourStarted),
|
||||
format: ({ started }) => toHour(started),
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
|
@ -160,7 +162,7 @@ const columns = computed(() => [
|
|||
label: t('route.hourFinished'),
|
||||
component: 'time',
|
||||
columnFilter: false,
|
||||
format: ({ hourFinished }) => toHour(hourFinished),
|
||||
format: ({ finished }) => toHour(finished),
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
|
|
Loading…
Reference in New Issue