8627-devToTest #1421

Merged
alexm merged 768 commits from 8627-devToTest into test 2025-02-18 12:37:37 +00:00
27 changed files with 1165 additions and 750 deletions
Showing only changes of commit f33c4d42bf - Show all commits

View File

@ -51,7 +51,5 @@ export default boot(({ app }) => {
await useCau(response, message);
};
app.directive('shortcut', keyShortcut);
app.mixin(qFormMixin);
app.mixin(mainShortcutMixin);
app.provide('app', app);
});

View File

@ -1,31 +0,0 @@
<script setup>
import { computed } from 'vue';
const props = defineProps({
colors: {
type: Array,
required: true,
validator: (value) => value.length <= 3,
},
});
</script>
<template>
<div class="color-div">
<div
v-for="(color, index) in colors"
:key="index"
:style="{
backgroundColor: color,
height: '10px',
}"
>
&nbsp;
</div>
</div>
</template>
<style scoped>
.color-div {
display: flex;
flex-direction: column;
}
</style>

View File

@ -1,6 +1,6 @@
<script setup>
import { markRaw, computed } from 'vue';
import { QIcon, QCheckbox } from 'quasar';
import { QIcon, QCheckbox, QToggle } from 'quasar';
import { dashIfEmpty } from 'src/filters';
/* basic input */
@ -48,6 +48,10 @@ const $props = defineProps({
type: Boolean,
default: null,
},
eventHandlers: {
type: Object,
default: null,
},
});
const defaultSelect = {
@ -141,6 +145,9 @@ const defaultComponents = {
userLink: {
component: markRaw(VnUserLink),
},
toggle: {
component: markRaw(QToggle),
},
};
const value = computed(() => {
@ -187,6 +194,7 @@ const components = computed(() => {
...(component.attrs || {}),
autofocus: $props.autofocus,
},
event: { ...component?.event, ...$props?.eventHandlers },
};
return acc;
}, {});
@ -200,7 +208,6 @@ const components = computed(() => {
:components="components"
:value="{ row, model }"
v-model="model"
@blur="emit('blur')"
/>
<VnComponent
v-if="col.component"
@ -208,7 +215,6 @@ const components = computed(() => {
:components="components"
:value="{ row, model }"
v-model="model"
@blur="emit('blur')"
/>
<span :title="value" v-else>{{ value }}</span>
<VnComponent
@ -217,7 +223,7 @@ const components = computed(() => {
:components="components"
:value="{ row, model }"
v-model="model"
@blur="emit('blur')"
/>
<slot name="append" />
</div>
</template>

View File

@ -58,7 +58,7 @@ const selectComponent = {
component: markRaw(VnSelect),
event: updateEvent,
attrs: {
class: 'q-px-sm q-pb-xs q-pt-none fit',
class: 'q-pt-none fit test',
dense: true,
filled: !$props.showTitle,
},
@ -120,6 +120,7 @@ const components = {
};
async function addFilter(value, name) {
console.log('test');
value ??= undefined;
if (value && typeof value === 'object') value = model.value;
value = value === '' ? undefined : value;
@ -168,3 +169,12 @@ 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;
}
</style>

View File

@ -1,23 +1,36 @@
<script setup>
import { ref, onBeforeMount, onMounted, computed, watch, useAttrs, nextTick } from 'vue';
import {
ref,
onBeforeMount,
onMounted,
computed,
watch,
h,
render,
inject,
useAttrs,
} from 'vue';
import { useArrayData } from 'src/composables/useArrayData';
import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router';
import { useQuasar } from 'quasar';
import { useStateStore } from 'stores/useStateStore';
import { useFilterParams } from 'src/composables/useFilterParams';
import { dashIfEmpty } from 'src/filters';
import CrudModel from 'src/components/CrudModel.vue';
import FormModelPopup from 'components/FormModelPopup.vue';
import VnTableColumn from 'components/VnTable/VnColumn.vue';
import VnColumn from 'components/VnTable/VnColumn.vue';
import VnFilter from 'components/VnTable/VnFilter.vue';
import VnTableChip from 'components/VnTable/VnChip.vue';
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 { dashIfEmpty } from 'src/filters';
const arrayData = useArrayData(useAttrs()['data-key']);
const $props = defineProps({
columns: {
type: Array,
@ -115,6 +128,10 @@ const $props = defineProps({
type: Boolean,
default: false,
},
withFilters: {
type: Boolean,
default: true,
},
});
const { t } = useI18n();
const stateStore = useStateStore();
@ -136,7 +153,12 @@ const createForm = ref();
const tableRef = ref();
const params = ref(useFilterParams($attrs['data-key']).params);
const orders = ref(useFilterParams($attrs['data-key']).orders);
const app = inject('app');
const editingRow = ref(null);
const editingField = ref(null);
const isTableMode = computed(() => mode.value == TABLE_MODE);
const showRightIcon = computed(() => $props.rightSearch || $props.rightSearchIcon);
const tableModes = [
{
icon: 'view_column',
@ -156,7 +178,6 @@ onBeforeMount(() => {
const urlParams = route.query[$props.searchUrl];
hasParams.value = urlParams && Object.keys(urlParams).length !== 0;
});
onMounted(() => {
mode.value =
quasar.platform.is.mobile && !$props.disableOption?.card
@ -185,9 +206,6 @@ watch(
{ immediate: true }
);
const isTableMode = computed(() => mode.value == TABLE_MODE);
const showRightIcon = computed(() => $props.rightSearch || $props.rightSearchIcon);
function splitColumns(columns) {
splittedColumns.value = {
columns: [],
@ -306,99 +324,210 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
}
}
const editingRow = ref(null);
const editingField = ref(null);
function isEditableColumn(column) {
const isEditableCol = column?.isEditable ?? true;
const isVisible = column?.visible ?? true;
const hasComponent = column?.component;
const handleClick = (event) => {
return $props.isEditable && isVisible && hasComponent && isEditableCol;
}
function hasEditableFormat(column) {
if (isEditableColumn(column)) return 'editable-text';
}
const handleClick = async (event) => {
const clickedElement = event.target.closest('td');
if (!clickedElement) return;
const rowIndex = clickedElement.getAttribute('data-row-index');
console.log('HandleRowIndex: ', rowIndex);
const colField = clickedElement.getAttribute('data-col-field');
console.log('HandleColField: ', colField);
if (rowIndex !== null && colField) {
startEditing(Number(rowIndex), colField);
}
};
const vnEditableCell = ref(null);
const startEditing = async (rowId, field) => {
const col = $props.columns.find((col) => col.name === field);
if (col?.isEditable === false) return;
editingRow.value = rowId;
editingField.value = field;
if (col.component === 'checkbox') {
await nextTick();
const inputElement = vnEditableCell.value?.$el?.querySelector('span > div');
inputElement.focus();
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();
}
};
const stopEditing = (rowIndex, field) => {
async function startEditing(rowId, field, clickedElement) {
console.log('startEditing: ', field);
if (rowId === editingRow.value && field === editingField.value) return;
editingRow.value = rowId;
editingField.value = field;
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}"]`
);
Array.from(clickedElement.childNodes).forEach((child) => {
child.style.visibility = 'hidden';
child.style.position = 'absolute';
});
console.log('row[column.name]: ', row[column.name]);
const node = h(VnColumn, {
row: row,
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); */
},
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);
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); */
},
},
});
node.appContext = app._context;
render(node, clickedElement);
if (column.component === 'checkbox') node.el?.querySelector('span > div').focus();
}
function stopEditing(rowIndex, field, clickedElement) {
console.log('stopEditing: ', field);
if (clickedElement) {
render(null, clickedElement);
Array.from(clickedElement.childNodes).forEach((child) => {
child.style.visibility = 'visible';
child.style.position = '';
});
}
if (editingRow.value !== rowIndex || editingField.value !== field) return;
editingRow.value = null;
editingField.value = null;
};
}
const handleTab = async (rowIndex, colName) => {
function handleBlur(rowIndex, field, clickedElement) {
console.log('handleBlur: ', field);
stopEditing(rowIndex, field, clickedElement);
}
async function handleTabNavigation(rowIndex, colName, direction) {
const columns = $props.columns;
const totalColumns = columns.length;
let currentColumnIndex = columns.findIndex((col) => col.name === colName);
let newColumnIndex = currentColumnIndex + 1;
while (
columns[newColumnIndex]?.visible === false ||
columns[newColumnIndex]?.isEditable === false ||
!columns[newColumnIndex]?.component
) {
newColumnIndex++;
if (newColumnIndex >= columns.length) newColumnIndex = 0;
}
if (currentColumnIndex >= newColumnIndex) rowIndex++;
let iterations = 0;
let newColumnIndex = currentColumnIndex;
await startEditing(rowIndex, columns[newColumnIndex].name);
};
do {
iterations++;
newColumnIndex = (newColumnIndex + direction + totalColumns) % totalColumns;
const handleShiftTab = async (rowIndex, colName) => {
console.log('handleShiftTab: ');
const columns = $props.columns;
const currentColumnIndex = columns.findIndex((col) => col.name === colName);
if (isEditableColumn(columns[newColumnIndex])) break;
} while (iterations < totalColumns);
if (currentColumnIndex === -1) return;
let prevColumnIndex = currentColumnIndex - 1;
let prevRowIndex = rowIndex;
while (prevColumnIndex >= 0 && columns[prevColumnIndex]?.isEditable === false) {
prevColumnIndex--;
}
if (prevColumnIndex < 0) {
prevColumnIndex = columns.length - 1;
prevRowIndex -= 1;
while (prevRowIndex >= 0 && columns[prevColumnIndex]?.isEditable === false) {
prevColumnIndex--;
if (prevColumnIndex < 0) {
prevColumnIndex = columns.length - 1;
prevRowIndex--;
}
}
}
if (prevRowIndex < 0) {
stopEditing(rowIndex, colName);
if (iterations >= totalColumns) {
console.warn('No editable columns found.');
return;
}
await startEditing(prevRowIndex, columns[prevColumnIndex]?.name);
console.log('finishHandleShiftTab');
};
if (direction === 1 && newColumnIndex <= currentColumnIndex) {
rowIndex++;
} 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);
}
const handleTabKey = async (event, rowIndex, colName) => {
if (event.shiftKey) await handleShiftTab(rowIndex, colName);
else await handleTab(rowIndex, colName);
};
function getCheckboxIcon(value) {
switch (typeof value) {
case 'boolean':
@ -408,25 +537,35 @@ function getCheckboxIcon(value) {
? 'indeterminate_check_box'
: 'unknown_med';
case 'number':
return value > 0 ? 'check' : 'close';
return value === 0 ? 'close' : 'check';
case 'object':
return value === null ? 'help_outline' : 'unknown_med';
case 'undefined':
return 'help_outline';
default:
return 'unknown_med';
return 'indeterminate_check_box';
}
}
function shouldDisplayReadonly(col, rowIndex) {
return (
!col?.component ||
editingRow.value !== rowIndex ||
editingField.value !== col?.name
);
}
/* 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';
}
} */
</script>
<template>
<QDrawer
v-if="$props.rightSearch"
@ -477,7 +616,7 @@ function shouldDisplayReadonly(col, rowIndex) {
@row-click="(_, row) => rowClickFunction && rowClickFunction(row)"
@update:selected="emit('update:selected', $event)"
@selection="(details) => handleSelection(details, rows)"
@click="handleClick"
v-on="isEditable ? { click: handleClick } : {}"
>
<template #top-left v-if="!$props.withoutHeader">
<slot name="top-left"> </slot>
@ -496,13 +635,6 @@ function shouldDisplayReadonly(col, rowIndex) {
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
@ -512,7 +644,9 @@ function shouldDisplayReadonly(col, rowIndex) {
>
<div
class="no-padding"
:style="$props.columnSearch ? 'height: 75px' : ''"
:style="
withFilters && $props.columnSearch ? 'height: 75px' : ''
"
>
<div class="text-center" style="height: 30px">
<QTooltip v-if="col.toolTip">{{ col.toolTip }}</QTooltip>
@ -525,13 +659,17 @@ function shouldDisplayReadonly(col, rowIndex) {
/>
</div>
<VnFilter
v-if="$props.columnSearch && col.columnSearch !== false"
v-if="
$props.columnSearch &&
col.columnSearch !== false &&
withFilters
"
:column="col"
:show-title="true"
:data-key="$attrs['data-key']"
v-model="params[columnName(col)]"
:search-url="searchUrl"
class="full-width"
class="full-width test"
/>
</div>
</QTh>
@ -550,7 +688,6 @@ function shouldDisplayReadonly(col, rowIndex) {
</template>
<template #body-cell="{ col, row, rowIndex }">
<QTd
auto-width
class="no-margin q-px-xs"
v-if="col.visible ?? true"
:style="{
@ -566,7 +703,6 @@ function shouldDisplayReadonly(col, rowIndex) {
:data-col-field="col?.name"
>
<div
v-if="shouldDisplayReadonly(col, rowIndex)"
class="no-padding no-margin"
style="
overflow: hidden;
@ -584,18 +720,12 @@ function shouldDisplayReadonly(col, rowIndex) {
v-if="col?.component === 'checkbox'"
:name="getCheckboxIcon(row[col?.name])"
style="color: var(--vn-text-color)"
size="var(--font-size)"
:class="
isEditable &&
(col?.component ? 'editable-text' : '')
"
:class="hasEditableFormat(col)"
size="17px"
/>
<span
v-else
:class="
isEditable &&
(col?.component ? 'editable-text' : '')
"
:class="hasEditableFormat(col)"
:style="col?.style ? col.style(row) : null"
style="bottom: 0"
>
@ -607,27 +737,6 @@ function shouldDisplayReadonly(col, rowIndex) {
</span>
</slot>
</div>
<div v-else>
<VnTableColumn
ref="vnEditableCell"
:column="col"
:row="row"
:is-editable="col.isEditable ?? isEditable"
v-model="row[col.name]"
component-prop="columnField"
class="cell-input q-px-xs"
@blur="stopEditing(rowIndex, col?.name)"
@keyup.enter="stopEditing(rowIndex, col?.name)"
@keydown.tab.prevent="
handleTabKey($event, rowIndex, col?.name)
"
@keydown.shift.tab.prevent="
handleShiftTab(rowIndex, col?.name)
"
@keydown.escape="stopEditing(rowIndex, col?.name)"
:autofocus="true"
/>
</div>
</QTd>
</template>
<template #body-cell-tableActions="{ col, row }">
@ -751,7 +860,7 @@ function shouldDisplayReadonly(col, rowIndex) {
:row="row"
:row-index="index"
>
<VnTableColumn
<VnColumn
:column="col"
:row="row"
:is-editable="false"
@ -792,7 +901,8 @@ function shouldDisplayReadonly(col, rowIndex) {
</component>
</template>
<template #bottom-row="{ cols }" v-if="$props.footer">
<QTr v-if="rows.length" style="height: 30px">
<QTr v-if="rows.length" style="height: 45px">
<QTh v-if="table.selection" />
<QTh
v-for="col of cols.filter((cols) => cols.visible ?? true)"
:key="col?.id"
@ -838,7 +948,7 @@ function shouldDisplayReadonly(col, rowIndex) {
:column-name="column.name"
:label="column.label"
>
<VnTableColumn
<VnColumn
:column="column"
:row="{}"
default="input"

View File

@ -0,0 +1,32 @@
<script setup>
const $props = defineProps({
colors: {
type: String,
default: '{"value":[]}',
},
});
const colorArray = JSON.parse($props.colors)?.value;
const maxHeight = 30;
const colorHeight = maxHeight / colorArray?.length;
</script>
<template>
<div class="color-div" :style="{ height: `${maxHeight}px` }">
<div
v-for="(color, index) in colorArray"
:key="index"
:style="{
backgroundColor: `#${color}`,
height: `${colorHeight}px`,
}"
>
&nbsp;
</div>
</div>
</template>
<style scoped>
.color-div {
display: flex;
flex-direction: column;
}
</style>

View File

@ -45,6 +45,7 @@ function toValueAttrs(attrs) {
}
</script>
<template>
<slot name="test" />
<span
v-for="toComponent of componentArray"
:key="toComponent.name"
@ -57,6 +58,7 @@ function toValueAttrs(attrs) {
v-on="mix(toComponent).event ?? {}"
v-model="model"
@blur="emit('blur')"
@mouse-down="() => console.log('mouse-down')"
/>
</span>
</template>

View File

@ -70,10 +70,6 @@ const focus = () => {
vnInputRef.value.focus();
};
defineExpose({
focus,
});
const mixinRules = [
requiredFieldRule,
...($attrs.rules ?? []),

View File

@ -6,7 +6,7 @@ import { useRequired } from 'src/composables/useRequired';
import dataByOrder from 'src/utils/dataByOrder';
import { QItemLabel } from 'quasar';
const emit = defineEmits(['update:modelValue', 'update:options', 'remove', 'blur']);
const emit = defineEmits(['update:modelValue', 'update:options', 'remove']);
const $attrs = useAttrs();
const { t } = useI18n();
@ -327,7 +327,6 @@ function handleKeyDown(event) {
:option-value="optionValue"
v-bind="{ ...$attrs, ...styleAttrs }"
@filter="filterHandler"
@blur="() => emit('blur')"
:emit-value="nullishToTrue($attrs['emit-value'])"
:map-options="nullishToTrue($attrs['map-options'])"
:use-input="nullishToTrue($attrs['use-input'])"

View File

@ -34,7 +34,6 @@ const isAllowedToCreate = computed(() => {
return role.hasAny($props.rolesAllowedToCreate);
});
</script>
<template>
<VnSelect
v-model="value"
@ -63,7 +62,6 @@ const isAllowedToCreate = computed(() => {
</template>
</VnSelect>
</template>
<style lang="scss" scoped>
.default-icon {
cursor: pointer;

View File

@ -76,7 +76,6 @@ a {
text-decoration: underline;
}
// Removes chrome autofill background
input:-webkit-autofill,
select:-webkit-autofill {
color: var(--vn-text-color);

View File

@ -392,16 +392,26 @@ entry:
list:
newEntry: New entry
tableVisibleColumns:
created: Creation
supplierFk: Supplier
isBooked: Booked
isConfirmed: Confirmed
isExcludedFromAvailable: Exclude from inventory
isOrdered: Ordered
isConfirmed: Ready to label
isReceived: Received
isRaid: Raid
landed: Date
supplierFk: Supplier
reference: Ref/Alb/Guide
invoiceNumber: Invoice
agencyModeId: Agency
isBooked: Booked
companyFk: Company
travelFk: Travel
isExcludedFromAvailable: Inventory
evaNotes: Notes
warehouseOutFk: Origin
warehouseInFk: Destiny
entryTypeDescription: Entry type
invoiceAmount: Import
travelFk: Travel
summary:
invoiceAmount: Amount
commission: Commission
currency: Currency
invoiceNumber: Invoice number
@ -454,7 +464,10 @@ entry:
ektFk: Ekt
packingOut: Package out
landing: Landing
isExcludedFromAvailable: Es inventory
isExcludedFromAvailable: Exclude from inventory
isRaid: Raid
invoiceNumber: Invoice
reference: Ref/Alb/Guide
ticket:
card:
customerId: Customer ID

View File

@ -392,16 +392,26 @@ entry:
list:
newEntry: Nueva entrada
tableVisibleColumns:
created: Creación
supplierFk: Proveedor
isBooked: Asentado
isConfirmed: Confirmado
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
isExcludedFromAvailable: Inventario
evaNotes: Notas
warehouseOutFk: Origen
warehouseInFk: Destino
entryTypeDescription: Tipo entrada
invoiceAmount: Importe
summary:
invoiceAmount: Importe
commission: Comisión
currency: Moneda
invoiceNumber: Núm. factura
@ -455,7 +465,10 @@ entry:
ektFk: Ekt
packingOut: Embalaje envíos
landing: Llegada
isExcludedFromAvailable: Es inventario
isExcludedFromAvailable: Excluir del inventario
isRaid: Redada
invoiceNumber: Nº Factura
reference: Ref/Alb/Guía
ticket:
card:
customerId: ID cliente

View File

@ -100,17 +100,6 @@ const columns = computed(() => [
columnFilter: {
component: 'number',
},
columnField: {
component: null,
after: {
component: markRaw(VnLinkPhone),
attrs: ({ model }) => {
return {
'phone-number': model,
};
},
},
},
},
{
align: 'left',

View File

@ -247,8 +247,14 @@ function handleLocation(data, location) {
:label="t('Longitude')"
clearable
v-model="data.longitude"
:decimal-places="6"
/>
<VnInputNumber
:label="t('Latitude')"
clearable
v-model="data.latitude"
:decimal-places="6"
/>
<VnInputNumber :label="t('Latitude')" clearable v-model="data.latitude" />
</VnRow>
<h4 class="q-mb-xs">{{ t('Notes') }}</h4>
<VnRow

View File

@ -1,68 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Checkbox Focus with Button</title>
<style>
body {
font-family: Arial, sans-serif;
}
.checkbox-container {
display: flex;
align-items: center;
margin-bottom: 20px;
}
input[type='checkbox'] {
width: 20px;
height: 20px;
}
label {
margin-left: 10px;
}
/* Estilos para el foco */
input[type='checkbox']:focus {
outline: 2px solid blue;
outline-offset: 2px;
}
</style>
</head>
<body>
<div class="checkbox-container">
<input type="checkbox" id="myCheckbox" />
<label for="myCheckbox">Acepto los términos y condiciones</label>
</div>
<!-- Botón para enfocar el checkbox -->
<button id="focusButton">Dar foco al checkbox</button>
<script>
const checkbox = document.getElementById('myCheckbox');
const focusButton = document.getElementById('focusButton');
// Manejador de eventos para cuando el checkbox recibe el foco
checkbox.addEventListener('focus', () => {
console.log('El checkbox tiene el foco');
});
// Manejador de eventos para cuando el checkbox pierde el foco
checkbox.addEventListener('blur', () => {
console.log('El checkbox perdió el foco');
});
// Manejador de eventos para cuando se cambia el estado del checkbox
checkbox.addEventListener('change', (event) => {
if (event.target.checked) {
console.log('El checkbox está marcado');
} else {
console.log('El checkbox no está marcado');
}
});
// Dar foco al checkbox cuando se presiona el botón
focusButton.addEventListener('click', () => {
checkbox.focus();
});
</script>
</body>
</html>

View File

@ -9,6 +9,7 @@ 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';
@ -51,28 +52,6 @@ const onFilterTravelSelected = (formData, id) => {
>
<template #form="{ data }">
<VnRow>
<VnSelect
:label="t('globals.supplier')"
v-model="data.supplierFk"
url="Suppliers"
option-value="id"
option-label="nickname"
:fields="['id', 'nickname']"
hide-selected
:required="true"
map-options
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>{{ scope.opt?.name }}</QItemLabel>
<QItemLabel caption>
{{ scope.opt?.nickname }}, {{ scope.opt?.id }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
<VnSelectDialog
:label="t('entry.basicData.travel')"
v-model="data.travelFk"
@ -105,9 +84,36 @@ const onFilterTravelSelected = (formData, id) => {
</QItem>
</template>
</VnSelectDialog>
<VnSelect
:label="t('globals.supplier')"
v-model="data.supplierFk"
url="Suppliers"
option-value="id"
option-label="nickname"
:fields="['id', 'nickname']"
hide-selected
:required="true"
map-options
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>{{ scope.opt?.name }}</QItemLabel>
<QItemLabel caption>
{{ scope.opt?.nickname }}, {{ scope.opt?.id }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
</VnRow>
<VnRow>
<VnInput v-model="data.reference" :label="t('globals.reference')" />
<VnInputNumber
v-model="data.invoiceAmount"
:label="t('entry.summary.invoiceAmount')"
:positive="false"
/>
</VnRow>
<VnRow>
<VnInput

View File

@ -2,21 +2,36 @@
import { useStateStore } from 'stores/useStateStore';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { onMounted, ref } from 'vue';
import { h, onMounted, ref } from 'vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
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 'components/ui/FetchedTags.vue';
import VnColor from 'src/components/VnColor.vue';
import FetchedTags from 'src/components/ui/FetchedTags.vue';
import VnColor from 'src/components/common/VnColor.vue';
import { QCheckbox } from 'quasar';
const $props = defineProps({
id: {
type: Number,
default: null,
},
editableMode: {
type: Boolean,
default: true,
},
});
const { t } = useI18n();
const stateStore = useStateStore();
const route = useRoute();
const selectedRows = ref([]);
const entityId = ref($props.id ?? route.params.id);
console.log('entityId: ', entityId.value);
const footer = ref({});
const columns = [
{
name: 'buyFk',
name: 'id',
isId: true,
visible: false,
isEditable: false,
@ -26,6 +41,7 @@ const columns = [
label: 'Nv',
name: 'isIgnored',
component: 'checkbox',
toggleIndeterminate: false,
width: '35px',
},
{
@ -33,6 +49,7 @@ const columns = [
label: 'Id',
name: 'itemFk',
component: 'input',
isEditable: false,
create: true,
width: '45px',
},
@ -52,7 +69,8 @@ const columns = [
},
{
align: 'center',
label: t('Size'),
label: t('Siz.'),
toolTip: t('Size'),
name: 'size',
width: '35px',
isEditable: false,
@ -63,8 +81,18 @@ const columns = [
{
align: 'center',
label: t('Sti.'),
toolTip: t('Printed Stickers/Stickers'),
name: 'stickers',
component: 'number',
attrs: {
positive: false,
},
cellEvent: {
'update:modelValue': (value, oldValue, row) => {
row['quantity'] = value * row['packing'];
row['amount'] = row['quantity'] * row['buyingValue'];
},
},
width: '35px',
},
{
@ -92,11 +120,49 @@ const columns = [
label: 'Pack',
name: 'packing',
component: 'number',
cellEvent: {
'update:modelValue': (value, oldValue, row) => {
console.log('oldValue: ', oldValue);
const oldPacking = oldValue === 1 || oldValue === null ? 1 : oldValue;
row['weight'] = (row['weight'] * value) / oldPacking;
row['quantity'] = row['stickers'] * value;
row['amount'] = row['quantity'] * row['buyingValue'];
},
},
width: '35px',
style: (row) => {
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',
name: 'groupingMode',
component: 'toggle',
attrs: {
trueValue: 'grouping',
falseValue: 'packing',
indeterminateValue: null,
},
width: '35px',
},
{
align: 'center',
@ -113,17 +179,31 @@ const columns = [
label: t('Quantity'),
name: 'quantity',
component: 'number',
width: '50px',
style: (row) => {
if (row?.quantity !== row?.stickers * row?.packing)
return { color: 'var(--q-negative)' };
attrs: {
positive: false,
},
cellEvent: {
'update:modelValue': (value, oldValue, row) => {
row['amount'] = value * row['buyingValue'];
},
},
width: '50px',
style: getQuantityStyle,
},
{
align: 'center',
label: 'Cost.',
label: t('Cost'),
toolTip: t('Buying value'),
name: 'buyingValue',
component: 'number',
attrs: {
positive: false,
},
cellEvent: {
'update:modelValue': (value, oldValue, row) => {
row['amount'] = row['quantity'] * value;
},
},
width: '50px',
},
{
@ -131,13 +211,17 @@ const columns = [
label: t('Amount'),
name: 'amount',
width: '50px',
style: () => {
return { color: 'var(--vn-label-color)' };
component: 'number',
attrs: {
positive: false,
},
isEditable: false,
style: getAmountStyle,
},
{
align: 'center',
label: t('Package'),
label: t('Pack.'),
toolTip: t('Package'),
name: 'price2',
component: 'number',
width: '35px',
@ -147,13 +231,40 @@ const columns = [
label: t('Box'),
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
*/
},
},
width: '35px',
},
{
align: 'center',
label: 'Min.',
toolTip: t('Minimum price'),
name: 'minPrice',
component: 'number',
isEditable: false,
width: '35px',
style: (row) => {
if (row?.hasMinPrice)
@ -163,21 +274,27 @@ const columns = [
{
align: 'center',
label: t('P.Sen'),
toolTip: t('Packing sent'),
name: 'packingOut',
component: 'number',
isEditable: false,
width: '40px',
},
{
align: 'center',
label: t('Com.'),
toolTip: t('Comment'),
name: 'comment',
component: 'input',
isEditable: false,
width: '55px',
},
{
align: 'center',
label: 'Prod.',
toolTip: t('Producer'),
name: 'subName',
isEditable: false,
width: '45px',
style: () => {
return { color: 'var(--vn-label-color)' };
@ -185,43 +302,148 @@ const columns = [
},
{
align: 'center',
label: 'Tags',
label: t('Tags'),
name: 'tags',
width: '120px',
width: '125px',
columnSearch: false,
},
{
align: 'center',
label: 'Comp.',
toolTip: t('Company'),
name: 'company_name',
component: 'input',
isEditable: false,
width: '35px',
},
];
function getQuantityStyle(row) {
if (row?.quantity !== row?.stickers * row?.packing)
return { color: 'var(--q-negative)' };
}
function getAmountStyle(row) {
if (row?.isChecked) return { color: 'var(--q-positive)' };
return { color: 'var(--vn-label-color)' };
}
onMounted(() => {
console.log('viewMode: ', $props.editableMode);
stateStore.rightDrawer = false;
});
</script>
<template>
<VnSubToolbar />
<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"
>
<QTooltip>{{ t('tableActions.invertQuantitySign') }}</QTooltip>
</QBtn>
<QBtn
icon="price_check"
color="primary"
flat
@click="console.log('request_quote')"
>
<QTooltip>{{ t('tableActions.checkAmount') }}</QTooltip>
</QBtn>
<QBtn
icon="price_check"
color="primary"
flat
@click="console.log('request_quote')"
>
<QTooltip>{{ t('tableActions.setMinPrice') }}</QTooltip>
</QBtn>
</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];
}
"
auto-load
/>
<VnTable
ref="tableRef"
data-key="EntryBuys"
:url="`Entries/${route.params.id}/getBuys`"
:url="`Entries/${entityId}/getBuyList`"
save-url="Buys/crud"
:disable-option="{ card: true }"
v-model:selected="selectedRows"
:table="{
'row-key': 'id',
selection: 'multiple',
}"
:table="
editableMode
? {
'row-key': 'id',
selection: 'multiple',
}
: {}
"
:is-editable="editableMode"
:without-header="!editableMode"
:with-filters="editableMode"
:right-search="false"
:row-click="false"
:columns="columns"
class="buyList"
is-editable
table-height="84vh"
auto-load
footer
>
<template #column-hex>
<VnColor :colors="['#ff0000', '#ffff00', '#ff0000']" style="height: 100%" />
<template #column-hex="{ row }">
<VnColor :colors="row?.hexJson" style="height: 100%" />
</template>
<template #column-name="{ row }">
<span class="link">
@ -233,29 +455,57 @@ onMounted(() => {
<FetchedTags :item="row" :columns="3" />
</template>
<template #column-stickers="{ row }">
<span style="color: var(--vn-label-color)">{{ row.printedStickers }}</span>
<span>/{{ row.stickers }}</span>
<span :class="editableMode ? 'editable-text' : ''">
<span style="color: var(--vn-label-color)">{{
row.printedStickers
}}</span>
<span>/{{ row.stickers }}</span>
</span>
</template>
<template #column-footer-stickers>
<div>
<span style="color: var(--vn-label-color)">{{
footer?.printedStickers
}}</span>
<span>/{{ footer?.stickers }}</span>
</div>
</template>
<template #column-footer-weight>
{{ footer?.weight }}
</template>
<template #column-footer-quantity>
<span :style="getQuantityStyle(footer)">
{{ footer?.quantity }}
</span>
</template>
<template #column-footer-amount>
<span :style="getAmountStyle(footer)">
{{ footer?.amount }}
</span>
</template>
</VnTable>
</template>
<style lang="scss" scoped>
.q-checkbox__inner--dark {
&__inner {
border-radius: 0% !important;
background-color: rosybrown;
}
}
</style>
<i18n>
es:
Article: Artículo3
Size: Med.
Article: Artículo
Siz.: Med.
Size: Medida
Sti.: Eti.
Bucket: Cubo
Quantity: Cantidad
Amount: Importe
Pack.: Paq.
Package: Paquete
Box: Caja
P.Sen: P.Env
Packing sent: Packing envíos
Com.: Ref.
Comment: Referencia
Minimum price: Precio mínimo
Printed Stickers/Stickers: Etiquetas impresas/Etiquetas
Cost: Cost.
Buying value: Coste
Producer: Productor
Company: Compañia
Tags: Etiquetas
</i18n>

View File

@ -1,17 +1,17 @@
<script setup>
import { ref, computed, onMounted } from 'vue';
import { ref, computed, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import CardDescriptor from 'components/ui/CardDescriptor.vue';
import VnLv from 'src/components/ui/VnLv.vue';
import TravelDescriptorProxy from 'src/pages/Travel/Card/TravelDescriptorProxy.vue';
import { toDate } from 'src/filters';
import { usePrintService } from 'composables/usePrintService';
import { getUrl } from 'src/composables/getUrl';
import filter from './EntryFilter.js';
import TravelDescriptorProxy from 'src/pages/Travel/Card/TravelDescriptorProxy.vue';
import axios from 'axios';
import { useRouter } from 'vue-router';
const { push } = useRouter();
const $props = defineProps({
id: {
@ -55,9 +55,22 @@ const getEntryRedirectionFilter = (entry) => {
});
};
const showEntryReport = () => {
openReport(`Entries/${route.params.id}/entry-order-pdf`);
};
function showEntryReport() {
openReport(`Entries/${entityId.value}/entry-order-pdf`);
}
function recalculateRates() {
console.log('recalculateRates');
}
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>
<template>
@ -73,14 +86,39 @@ const showEntryReport = () => {
<QItem v-ripple clickable @click="showEntryReport(entity)">
<QItemSection>{{ t('Show entry report') }}</QItemSection>
</QItem>
<QItem v-ripple clickable @click="recalculateRates(entity)">
<QItemSection>{{ t('Recalculate rates') }}</QItemSection>
</QItem>
<QItem v-ripple clickable @click="cloneEntry(entity)">
<QItemSection>{{ t('Clone') }}</QItemSection>
</QItem>
<QItem v-ripple clickable @click="deleteEntry(entity)">
<QItemSection>{{ t('Delete') }}</QItemSection>
</QItem>
</template>
<template #body="{ entity }">
<VnLv :label="t('globals.agency')" :value="entity.travel?.agency?.name" />
<VnLv :label="t('shipped')" :value="toDate(entity.travel?.shipped)" />
<VnLv :label="t('landed')" :value="toDate(entity.travel?.landed)" />
<VnLv :label="t('Travel')">
<template #value>
<span class="link" v-if="entity?.travelFk">
{{ entity.travel?.agency?.name }}
{{ entity.travel?.warehouseOut?.code }} &rarr;
{{ entity.travel?.warehouseIn?.code }}
<TravelDescriptorProxy :id="entity?.travelFk" />
</span>
</template>
</VnLv>
<VnLv
:label="t('globals.warehouseOut')"
:value="entity.travel?.warehouseOut?.name"
:label="t('entry.summary.travelShipped')"
:value="toDate(entity.travel?.shipped)"
/>
<VnLv
:label="t('entry.summary.travelLanded')"
:value="toDate(entity.travel?.landed)"
/>
<VnLv :label="t('entry.summary.currency')" :value="entity?.currency?.code" />
<VnLv
:label="t('entry.summary.invoiceAmount')"
:value="entity?.invoiceAmount"
/>
</template>
<template #icons="{ entity }">
@ -107,6 +145,14 @@ const showEntryReport = () => {
}}</QTooltip
>
</QIcon>
<QIcon
v-if="!entity?.travelFk"
name="vn:deletedTicket"
size="xs"
color="primary"
>
<QTooltip>{{ t('This entry is deleted') }}</QTooltip>
</QIcon>
</QCardActions>
</template>
<template #actions="{ entity }">
@ -153,6 +199,7 @@ const showEntryReport = () => {
</template>
<i18n>
es:
Travel: Envío
Supplier card: Ficha del proveedor
All travels with current agency: Todos los envíos con la agencia actual
All entries with current supplier: Todas las entradas con el proveedor actual
@ -162,4 +209,5 @@ es:
Virtual entry: Es una redada
shipped: Enviado
landed: Recibido
This entry is deleted: Esta entrada está eliminada
</i18n>

View File

@ -9,6 +9,7 @@ export default {
'shipped',
'agencyModeFk',
'warehouseOutFk',
'warehouseInFk',
'daysInForward',
],
include: [
@ -21,13 +22,13 @@ export default {
{
relation: 'warehouseOut',
scope: {
fields: ['name'],
fields: ['name', 'code'],
},
},
{
relation: 'warehouseIn',
scope: {
fields: ['name'],
fields: ['name', 'code'],
},
},
],
@ -39,5 +40,11 @@ export default {
fields: ['id', 'nickname'],
},
},
{
relation: 'currency',
scope: {
fields: ['id', 'code'],
},
},
],
};

View File

@ -2,15 +2,15 @@
import { onMounted, ref, computed } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { toDate } from 'src/filters';
import { getUrl } from 'src/composables/getUrl';
import axios from 'axios';
import CardSummary from 'components/ui/CardSummary.vue';
import VnLv from 'src/components/ui/VnLv.vue';
import TravelDescriptorProxy from 'src/pages/Travel/Card/TravelDescriptorProxy.vue';
import { toDate, toCurrency } from 'src/filters';
import { getUrl } from 'src/composables/getUrl';
import axios from 'axios';
import FetchedTags from 'src/components/ui/FetchedTags.vue';
import EntryBuys from './EntryBuys.vue';
import VnTitle from 'src/components/common/VnTitle.vue';
import VnToSummary from 'src/components/ui/VnToSummary.vue';
const route = useRoute();
@ -30,117 +30,6 @@ const entry = ref();
const entryBuys = ref([]);
const entryUrl = ref();
onMounted(async () => {
entryUrl.value = (await getUrl('entry/')) + entityId.value;
});
const tableColumnComponents = {
quantity: {
component: () => 'span',
props: () => {},
},
stickers: {
component: () => 'span',
props: () => {},
event: () => {},
},
packagingFk: {
component: () => 'span',
props: () => {},
event: () => {},
},
weight: {
component: () => 'span',
props: () => {},
event: () => {},
},
packing: {
component: () => 'span',
props: () => {},
event: () => {},
},
grouping: {
component: () => 'span',
props: () => {},
event: () => {},
},
buyingValue: {
component: () => 'span',
props: () => {},
event: () => {},
},
amount: {
component: () => 'span',
props: () => {},
event: () => {},
},
pvp: {
component: () => 'span',
props: () => {},
event: () => {},
},
};
const entriesTableColumns = computed(() => {
return [
{
label: t('globals.quantity'),
field: 'quantity',
name: 'quantity',
align: 'left',
},
{
label: t('entry.summary.stickers'),
field: 'stickers',
name: 'stickers',
align: 'left',
},
{
label: t('entry.summary.package'),
field: 'packagingFk',
name: 'packagingFk',
align: 'left',
},
{
label: t('globals.weight'),
field: 'weight',
name: 'weight',
align: 'left',
},
{
label: t('entry.summary.packing'),
field: 'packing',
name: 'packing',
align: 'left',
},
{
label: t('entry.summary.grouping'),
field: 'grouping',
name: 'grouping',
align: 'left',
},
{
label: t('entry.summary.buyingValue'),
field: 'buyingValue',
name: 'buyingValue',
align: 'left',
format: (value) => toCurrency(value),
},
{
label: t('entry.summary.import'),
name: 'amount',
align: 'left',
format: (_, row) => toCurrency(row.buyingValue * row.quantity),
},
{
label: t('entry.summary.pvp'),
name: 'pvp',
align: 'left',
format: (_, row) => toCurrency(row.price2) + ' / ' + toCurrency(row.price3),
},
];
});
async function setEntryData(data) {
if (data) entry.value = data;
await fetchEntryBuys();
@ -150,8 +39,11 @@ const fetchEntryBuys = async () => {
const { data } = await axios.get(`Entries/${entry.value.id}/getBuys`);
if (data) entryBuys.value = data;
};
</script>
onMounted(async () => {
entryUrl.value = (await getUrl('entry/')) + entityId.value;
});
</script>
<template>
<CardSummary
ref="summaryRef"
@ -168,34 +60,64 @@ const fetchEntryBuys = async () => {
/>
</template>
<template #header>
<span>{{ entry.id }} - {{ entry.supplier.nickname }}</span>
<span>{{ entry.id }} - {{ entry?.supplier?.nickname }}</span>
</template>
<template #body>
<QCard class="vn-one">
<router-link
:to="{ name: 'EntryBasicData', params: { id: entityId } }"
class="header header-link"
>
{{ t('globals.summary.basicData') }}
<QIcon name="open_in_new" />
</router-link>
<VnTitle
:url="`#/entry/{{ entityId }}/basicData`"
:text="t('globals.summary.basicData')"
/>
<div class="card-group">
<div class="card-content">
<VnLv
:label="t('entry.summary.commission')"
:value="entry.commission"
:value="entry?.commission"
/>
<VnLv
:label="t('entry.summary.currency')"
:value="entry.currency?.name"
:value="entry?.currency?.name"
/>
<VnLv :label="t('globals.company')" :value="entry.company.code" />
<VnLv :label="t('globals.reference')" :value="entry.reference" />
<VnLv
:label="t('globals.company')"
:value="entry?.company?.code"
/>
<VnLv :label="t('globals.reference')" :value="entry?.reference" />
<VnLv
:label="t('entry.summary.invoiceNumber')"
:value="entry.invoiceNumber"
:value="entry?.invoiceNumber"
/>
</div>
<div class="card-content">
<QCheckbox
:label="t('entry.summary.ordered')"
v-model="entry.isOrdered"
:disable="true"
/>
<QCheckbox
:label="t('globals.confirmed')"
v-model="entry.isConfirmed"
:disable="true"
/>
<QCheckbox
:label="t('entry.summary.booked')"
v-model="entry.isBooked"
:disable="true"
/>
<QCheckbox
:label="t('entry.summary.excludedFromAvailable')"
v-model="entry.isExcludedFromAvailable"
:disable="true"
/>
</div>
</div>
</QCard>
<QCard class="vn-one" v-if="entry?.travelFk">
<VnTitle
:url="`#/travel/{{ entry.travel.id }}/summary`"
:text="t('globals.summary.basicData')"
/>
<div class="card-group">
<div class="card-content">
<VnLv :label="t('entry.summary.travelReference')">
<template #value>
@ -210,18 +132,23 @@ const fetchEntryBuys = async () => {
:value="entry.travel.agency?.name"
/>
<VnLv
:label="t('shipped')"
:label="t('entry.summary.travelShipped')"
:value="toDate(entry.travel.shipped)"
/>
<VnLv
:label="t('globals.warehouseOut')"
:value="entry.travel.warehouseOut?.name"
/>
<VnLv :label="t('landed')" :value="toDate(entry.travel.landed)" />
<VnLv
:label="t('entry.summary.travelLanded')"
:value="toDate(entry.travel.landed)"
/>
<VnLv
:label="t('globals.warehouseIn')"
:value="entry.travel.warehouseIn?.name"
/>
</div>
<div class="card-content">
<QCheckbox
:label="t('entry.summary.travelDelivered')"
v-model="entry.travel.isDelivered"
@ -235,59 +162,35 @@ const fetchEntryBuys = async () => {
</div>
</div>
</QCard>
<QCard class="vn-one">
<router-link
:to="{ name: 'TravelSummary', params: { id: entry.travel.id } }"
class="header header-link"
>
{{ t('Travel data') }}
<QIcon name="open_in_new" />
</router-link>
<QCheckbox
:label="t('entry.summary.ordered')"
v-model="entry.isOrdered"
:disable="true"
/>
<QCheckbox
:label="t('globals.confirmed')"
v-model="entry.isConfirmed"
:disable="true"
/>
<QCheckbox
:label="t('entry.summary.booked')"
v-model="entry.isBooked"
:disable="true"
/>
<QCheckbox
:label="t('entry.summary.excludedFromAvailable')"
v-model="entry.isExcludedFromAvailable"
:disable="true"
<QCard class="vn-max">
<VnTitle
:url="`#/entry/{{ entityId }}/buys`"
:text="t('entry.summary.buys')"
/>
<EntryBuys v-if="entityId" :id="entityId" :editable-mode="false" />
</QCard>
</template>
</CardSummary>
</template>
<style lang="scss" scoped>
.separation-row {
background-color: var(--vn-section-color) !important;
}
.card-group {
display: flex;
flex-direction: column;
}
.card-content {
margin-bottom: 16px; /* Para dar espacio entre las secciones */
display: flex;
flex-direction: column;
text-overflow: ellipsis;
}
@media (min-width: 1010px) {
.card-group {
flex-direction: row; /* Coloca los contenidos en fila cuando el ancho es mayor a 600px */
flex-direction: row;
}
.card-content {
flex: 1; /* Haz que las secciones ocupen el mismo espacio */
margin-right: 16px; /* Espaciado entre las dos primeras tarjetas */
flex: 1;
margin-right: 16px;
}
}
</style>
@ -295,4 +198,5 @@ const fetchEntryBuys = async () => {
<i18n>
es:
Travel data: Datos envío
InvoiceIn data: Datos factura
</i18n>

View File

@ -18,6 +18,7 @@ const props = defineProps({
const currenciesOptions = ref([]);
const companiesOptions = ref([]);
const entryFilterPanel = ref();
</script>
<template>
@ -37,7 +38,7 @@ const companiesOptions = ref([]);
@on-fetch="(data) => (currenciesOptions = data)"
auto-load
/>
<VnFilterPanel :data-key="props.dataKey" :search-button="true">
<VnFilterPanel ref="entryFilterPanel" :data-key="props.dataKey" :search-button="true">
<template #tags="{ tag, formatFn }">
<div class="q-gutter-x-xs">
<strong>{{ t(`params.${tag.label}`) }}: </strong>
@ -47,70 +48,82 @@ const companiesOptions = ref([]);
<template #body="{ params, searchFn }">
<QItem>
<QItemSection>
<VnInput
v-model="params.search"
:label="t('entryFilter.filter.search')"
is-outlined
/>
<QCheckbox
:label="t('params.isExcludedFromAvailable')"
v-model="params.isExcludedFromAvailable"
toggle-indeterminate
>
<QTooltip>
{{
t(
'entry.list.tableVisibleColumns.isExcludedFromAvailable'
)
}}
</QTooltip>
</QCheckbox>
</QItemSection>
<QItemSection>
<QCheckbox
:label="t('params.isOrdered')"
v-model="params.isOrdered"
toggle-indeterminate
>
<QTooltip>
{{ t('entry.list.tableVisibleColumns.isOrdered') }}
</QTooltip>
</QCheckbox>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInput
v-model="params.reference"
:label="t('entryFilter.filter.reference')"
is-outlined
/>
<QCheckbox
:label="t('params.isReceived')"
v-model="params.isReceived"
toggle-indeterminate
>
<QTooltip>
{{ t('entry.list.tableVisibleColumns.isReceived') }}
</QTooltip>
</QCheckbox>
</QItemSection>
<QItemSection>
<QCheckbox
:label="t('params.isRaid')"
v-model="params.isRaid"
toggle-indeterminate
>
<QTooltip>
{{ t('entry.list.tableVisibleColumns.isRaid') }}
</QTooltip>
</QCheckbox>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInput
v-model="params.invoiceNumber"
:label="t('params.invoiceNumber')"
is-outlined
/>
<QCheckbox
:label="t('entry.list.tableVisibleColumns.isConfirmed')"
v-model="params.isConfirmed"
toggle-indeterminate
>
<QTooltip>
{{ t('entry.list.tableVisibleColumns.isConfirmed') }}
</QTooltip>
</QCheckbox>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInput
v-model="params.travelFk"
:label="t('params.travelFk')"
is-outlined
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnSelect
:label="t('params.companyFk')"
v-model="params.companyFk"
<VnInputDate
:label="t('params.landed')"
v-model="params.landed"
@update:model-value="searchFn()"
:options="companiesOptions"
option-value="id"
option-label="code"
hide-selected
dense
outlined
rounded
is-outlined
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnSelect
:label="t('params.currencyFk')"
v-model="params.currencyFk"
@update:model-value="searchFn()"
:options="currenciesOptions"
option-value="id"
option-label="name"
hide-selected
dense
outlined
rounded
/>
<VnInput v-model="params.id" label="Id" is-outlined />
</QItemSection>
</QItem>
<QItem>
@ -143,56 +156,90 @@ const companiesOptions = ref([]);
</QItem>
<QItem>
<QItemSection>
<VnInputDate
:label="t('params.created')"
v-model="params.created"
@update:model-value="searchFn()"
<VnInput
v-model="params.invoiceNumber"
:label="t('params.invoiceNumber')"
is-outlined
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInputDate
:label="t('params.from')"
v-model="params.from"
@update:model-value="searchFn()"
<VnInput
v-model="params.reference"
:label="t('entryFilter.filter.reference')"
is-outlined
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInputDate
:label="t('params.to')"
v-model="params.to"
<VnSelect
:label="t('params.agencyModeId')"
v-model="params.agencyModeId"
@update:model-value="searchFn()"
url="AgencyModes"
:fields="['id', 'name']"
hide-selected
dense
outlined
rounded
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInput
v-model="params.evaNotes"
:label="t('params.evaNotes')"
is-outlined
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QCheckbox
:label="t('params.isBooked')"
v-model="params.isBooked"
toggle-indeterminate
/>
</QItemSection>
<QItemSection>
<QCheckbox
:label="t('params.isConfirmed')"
v-model="params.isConfirmed"
toggle-indeterminate
<VnSelect
:label="t('params.warehouseOutFk')"
v-model="params.warehouseOutFk"
@update:model-value="searchFn()"
url="Warehouses"
:fields="['id', 'name']"
hide-selected
dense
outlined
rounded
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QCheckbox
:label="t('params.isOrdered')"
v-model="params.isOrdered"
toggle-indeterminate
<VnSelect
:label="t('params.warehouseInFk')"
v-model="params.warehouseInFk"
@update:model-value="searchFn()"
url="Warehouses"
:fields="['id', 'name']"
hide-selected
dense
outlined
rounded
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnSelect
:label="t('params.entryTypeCode')"
v-model="params.entryTypeCode"
@update:model-value="searchFn()"
url="EntryTypes"
:fields="['code', 'description']"
option-value="code"
option-label="description"
hide-selected
dense
outlined
rounded
/>
</QItemSection>
</QItem>
@ -203,30 +250,38 @@ const companiesOptions = ref([]);
<i18n>
en:
params:
invoiceNumber: Invoice number
travelFk: Travel
companyFk: Company
currencyFk: Currency
supplierFk: Supplier
from: From
to: To
created: Created
isBooked: Booked
isConfirmed: Confirmed
isExcludedFromAvailable: Inventory
isOrdered: Ordered
isReceived: Received
isConfirmed: Confirmed
isRaid: Raid
landed: Date
id: Id
supplierFk: Supplier
invoiceNumber: Invoice number
reference: Ref/Alb/Guide
agencyModeId: Agency mode
evaNotes: Notes
warehouseOutFk: Origin
warehouseInFk: Destiny
entryTypeCode: Entry type
hasToShowDeletedEntries: Show deleted entries
es:
params:
invoiceNumber: Núm. factura
travelFk: Envío
companyFk: Empresa
currencyFk: Moneda
supplierFk: Proveedor
from: Desde
to: Hasta
created: Fecha creación
isBooked: Asentado
isConfirmed: Confirmado
isExcludedFromAvailable: Inventario
isOrdered: Pedida
isConfirmed: Confirmado
isReceived: Recibida
isRaid: Raid
landed: Fecha
id: Id
supplierFk: Proveedor
invoiceNumber: Núm. factura
reference: Ref/Alb/Guía
agencyModeId: Modo agencia
evaNotes: Notas
warehouseOutFk: Origen
warehouseInFk: Destino
entryTypeCode: Tipo de entrada
hasToShowDeletedEntries: Mostrar entradas eliminadas
</i18n>

View File

@ -1,23 +1,24 @@
<script setup>
import { onMounted, ref, computed } from 'vue';
import axios from 'axios';
import { ref, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { useState } from 'src/composables/useState';
import { onBeforeMount } from 'vue';
import EntryFilter from './EntryFilter.vue';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import { useStateStore } from 'stores/useStateStore';
import VnTable from 'components/VnTable/VnTable.vue';
import RightMenu from 'src/components/common/RightMenu.vue';
import { toDate } from 'src/filters';
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
import EntrySummary from './Card/EntrySummary.vue';
import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue';
import TravelDescriptorProxy from 'src/pages/Travel/Card/TravelDescriptorProxy.vue';
const stateStore = useStateStore();
const { t } = useI18n();
const tableRef = ref();
const defaultEntry = ref({});
const state = useState();
const user = state.getUser();
const { viewSummary } = useSummaryDialog();
const entryFilter = {
const entryQueryFilter = {
include: [
{
relation: 'suppliers',
@ -42,11 +43,50 @@ const entryFilter = {
const columns = computed(() => [
{
name: 'status',
columnFilter: false,
align: 'center',
label: 'Ex',
toolTip: t('entry.list.tableVisibleColumns.isExcludedFromAvailable'),
name: 'isExcludedFromAvailable',
component: 'checkbox',
width: '35px',
},
{
align: 'left',
align: 'center',
label: 'Pe',
toolTip: t('entry.list.tableVisibleColumns.isOrdered'),
name: 'isOrdered',
component: 'checkbox',
width: '35px',
},
{
align: 'center',
label: 'Le',
toolTip: t('entry.list.tableVisibleColumns.isConfirmed'),
name: 'isConfirmed',
component: 'checkbox',
width: '35px',
},
{
align: 'center',
label: 'Re',
toolTip: t('entry.list.tableVisibleColumns.isReceived'),
name: 'isReceived',
component: 'checkbox',
width: '35px',
},
{
align: 'center',
label: t('entry.list.tableVisibleColumns.landed'),
name: 'landed',
component: 'date',
columnField: {
component: null,
},
format: (row, dashIfEmpty) => dashIfEmpty(toDate(row.landed)),
width: '105px',
},
{
align: 'right',
label: t('globals.id'),
name: 'id',
isId: true,
@ -54,30 +94,6 @@ const columns = computed(() => [
condition: () => true,
},
},
{
align: 'left',
label: t('globals.reference'),
name: 'reference',
isTitle: true,
component: 'input',
columnField: {
component: null,
},
create: true,
cardVisible: true,
},
{
align: 'left',
label: t('entry.list.tableVisibleColumns.created'),
name: 'created',
create: true,
cardVisible: true,
component: 'date',
columnField: {
component: null,
},
format: (row, dashIfEmpty) => dashIfEmpty(toDate(row.created)),
},
{
align: 'left',
label: t('entry.list.tableVisibleColumns.supplierFk'),
@ -89,117 +105,168 @@ const columns = computed(() => [
url: 'suppliers',
fields: ['id', 'name'],
},
columnField: {
component: null,
},
format: (row, dashIfEmpty) => dashIfEmpty(row.supplierName),
},
{
align: 'center',
label: t('entry.list.tableVisibleColumns.isBooked'),
name: 'isBooked',
cardVisible: true,
create: true,
component: 'checkbox',
},
{
align: 'center',
label: t('entry.list.tableVisibleColumns.isConfirmed'),
name: 'isConfirmed',
cardVisible: true,
create: true,
component: 'checkbox',
},
{
align: 'center',
label: t('entry.list.tableVisibleColumns.isOrdered'),
name: 'isOrdered',
cardVisible: true,
create: true,
component: 'checkbox',
align: 'left',
label: t('entry.list.tableVisibleColumns.invoiceNumber'),
name: 'invoiceNumber',
component: 'input',
},
{
align: 'left',
label: t('entry.list.tableVisibleColumns.companyFk'),
name: 'companyFk',
label: t('entry.list.tableVisibleColumns.reference'),
name: 'reference',
isTitle: true,
component: 'input',
columnField: {
component: null,
},
cardVisible: true,
},
{
align: 'left',
label: 'AWB',
name: 'awbCode',
component: 'input',
},
{
align: 'left',
label: t('entry.list.tableVisibleColumns.agencyModeId'),
name: 'agencyModeId',
cardVisible: true,
component: 'select',
attrs: {
url: 'companies',
fields: ['id', 'code'],
optionLabel: 'code',
optionValue: 'id',
url: 'agencyModes',
fields: ['id', 'name'],
},
columnField: {
component: null,
},
create: true,
format: (row, dashIfEmpty) => dashIfEmpty(row.companyCode),
format: (row, dashIfEmpty) => dashIfEmpty(row.agencyModeName),
},
{
align: 'left',
label: t('entry.list.tableVisibleColumns.travelFk'),
name: 'travelFk',
label: t('entry.list.tableVisibleColumns.evaNotes'),
name: 'evaNotes',
component: 'input',
},
{
align: 'left',
label: t('entry.list.tableVisibleColumns.warehouseOutFk'),
name: 'warehouseOutFk',
cardVisible: true,
component: 'select',
attrs: {
url: 'travels',
fields: ['id', 'ref'],
optionLabel: 'ref',
optionValue: 'id',
url: 'warehouses',
fields: ['id', 'name'],
},
columnField: {
component: null,
},
create: true,
format: (row, dashIfEmpty) => dashIfEmpty(row.travelRef),
format: (row, dashIfEmpty) => dashIfEmpty(row.warehouseOutName),
},
{
align: 'left',
label: t('entry.list.tableVisibleColumns.invoiceAmount'),
name: 'invoiceAmount',
label: t('entry.list.tableVisibleColumns.warehouseInFk'),
name: 'warehouseInFk',
cardVisible: true,
component: 'select',
attrs: {
url: 'warehouses',
fields: ['id', 'name'],
},
columnField: {
component: null,
},
format: (row, dashIfEmpty) => dashIfEmpty(row.warehouseInName),
},
{
align: 'center',
label: t('entry.list.tableVisibleColumns.isExcludedFromAvailable'),
name: 'isExcludedFromAvailable',
chip: {
color: null,
condition: (value) => value,
icon: 'vn:inventory',
},
align: 'left',
label: t('entry.list.tableVisibleColumns.entryTypeDescription'),
name: 'entryTypeCode',
cardVisible: true,
columnFilter: {
inWhere: true,
},
component: 'checkbox',
},
{
align: 'center',
label: t('entry.list.tableVisibleColumns.isRaid'),
name: 'isRaid',
chip: {
color: null,
condition: (value) => value,
icon: 'vn:net',
},
columnFilter: {
inWhere: true,
},
component: 'checkbox',
},
{
align: 'right',
name: 'tableActions',
actions: [
{
title: t('components.smartCard.viewSummary'),
icon: 'preview',
action: (row) => viewSummary(row.id, EntrySummary),
isPrimary: true,
component: 'select',
attrs: {
optionValue: 'code',
optionLabel: 'description',
url: 'entryTypes',
fields: ['code', 'description'],
},
],
},
format: (row, dashIfEmpty) => dashIfEmpty(row.entryTypeDescription),
},
{
name: 'dated',
label: t('entry.list.tableVisibleColumns.dated'),
component: 'date',
cardVisible: false,
visible: false,
create: true,
},
{
name: 'companyFk',
label: t('entry.list.tableVisibleColumns.companyFk'),
cardVisible: false,
visible: false,
create: true,
component: 'select',
attrs: {
optionValue: 'id',
optionLabel: 'code',
url: 'Companies',
},
},
{
name: 'travelFk',
label: t('entry.list.tableVisibleColumns.travelFk'),
cardVisible: false,
visible: false,
create: true,
},
]);
function getBadgeAttrs(row) {
const date = row.landed;
let today = Date.vnNew();
today.setHours(0, 0, 0, 0);
let timeTicket = new Date(date);
timeTicket.setHours(0, 0, 0, 0);
let timeDiff = today - timeTicket;
if (timeDiff > 0) return { color: 'warning', 'text-color': 'black' };
switch (row.entryTypeCode) {
case 'regularization':
case 'life':
case 'internal':
case 'inventory':
if (!row.isOrdered || !row.isConfirmed)
return { color: 'negative', 'text-color': 'black' };
break;
case 'product':
case 'packaging':
case 'devaluation':
case 'payment':
case 'transport':
if (
row.invoiceAmount === null ||
(row.invoiceNumber === null && row.reference === null) ||
!row.isOrdered ||
!row.isConfirmed
)
return { color: 'negative', 'text-color': 'black' };
break;
default:
break;
}
if (timeDiff < 0) return { color: 'info', 'text-color': 'black' };
return { color: 'transparent' };
}
onBeforeMount(async () => {
defaultEntry.value = (await axios.get('EntryConfigs/findOne')).data;
});
</script>
<template>
<VnSearchbar
@ -214,40 +281,35 @@ const columns = computed(() => [
</template>
</RightMenu>
<VnTable
v-if="defaultEntry.defaultSupplierFk"
ref="tableRef"
data-key="EntryList"
url="Entries/filter"
:filter="entryFilter"
:filter="entryQueryFilter"
:create="{
urlCreate: 'Entries',
title: t('Create entry'),
onDataSaved: ({ id }) => tableRef.redirect(id),
formInitialData: {},
formInitialData: {
supplierFk: defaultEntry.defaultSupplierFk,
dated: Date.vnNew(),
companyFk: user?.companyFk,
},
}"
order="id DESC"
:columns="columns"
redirect="entry"
:right-search="false"
>
<template #column-status="{ row }">
<div class="row q-gutter-xs">
<QIcon
v-if="!!row.isExcludedFromAvailable"
name="vn:inventory"
color="primary"
>
<QTooltip>{{
t('entry.list.tableVisibleColumns.isExcludedFromAvailable')
}}</QTooltip>
</QIcon>
<QIcon v-if="!!row.isRaid" name="vn:net" color="primary">
<QTooltip>
{{
t('globals.raid', { daysInForward: row.daysInForward })
}}</QTooltip
>
</QIcon>
</div>
<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>
@ -255,12 +317,6 @@ const columns = computed(() => [
<SupplierDescriptorProxy :id="row.supplierFk" />
</span>
</template>
<template #column-travelFk="{ row }">
<span class="link" @click.stop>
{{ row.travelRef }}
<TravelDescriptorProxy :id="row.travelFk" />
</span>
</template>
</VnTable>
</template>

View File

@ -36,6 +36,10 @@ const $props = defineProps({
type: Number,
default: null,
},
proxyRender: {
type: Boolean,
default: false,
},
});
const { openCloneDialog } = cloneItem();
@ -171,7 +175,7 @@ const openRegularizeStockForm = () => {
</QCardActions>
</template>
<template #actions="{}">
<QCardActions class="row justify-center">
<QCardActions class="row justify-center" v-if="proxyRender">
<QBtn
:to="{
name: 'ItemDiary',
@ -184,6 +188,16 @@ const openRegularizeStockForm = () => {
>
<QTooltip>{{ t('item.descriptor.itemDiary') }}</QTooltip>
</QBtn>
<QBtn
:to="{
name: 'ItemLastEntries',
}"
size="md"
icon="vn:regentry"
color="primary"
>
<QTooltip>{{ t('item.descriptor.itemLastEntries') }}</QTooltip>
</QBtn>
</QCardActions>
</template>
</CardDescriptor>

View File

@ -4,7 +4,7 @@ import ItemSummary from './ItemSummary.vue';
const $props = defineProps({
id: {
type: Number,
type: [Number, String],
required: true,
},
dated: {
@ -30,6 +30,7 @@ const $props = defineProps({
:dated="dated"
:sale-fk="saleFk"
:warehouse-fk="warehouseFk"
:proxy-render="true"
/>
</QPopupProxy>
</template>

View File

@ -117,6 +117,7 @@ item:
available: Available
warehouseText: 'Calculated on the warehouse of { warehouseName }'
itemDiary: Item diary
itemLastEntries: Last entries
producer: Producer
clone:
title: All its properties will be copied

View File

@ -119,6 +119,7 @@ item:
available: Disponible
warehouseText: 'Calculado sobre el almacén de { warehouseName }'
itemDiary: Registro de compra-venta
itemLastEntries: Últimas entradas
producer: Productor
clone:
title: Todas sus propiedades serán copiadas