Merge branch 'dev' into 8930-createVehicleSectionOnInvoiceIn
gitea/salix-front/pipeline/pr-dev This commit looks good
Details
gitea/salix-front/pipeline/pr-dev This commit looks good
Details
This commit is contained in:
commit
cabee31109
|
@ -131,11 +131,10 @@ async function fetch(data) {
|
||||||
const rows = keyData ? data[keyData] : data;
|
const rows = keyData ? data[keyData] : data;
|
||||||
resetData(rows);
|
resetData(rows);
|
||||||
emit('onFetch', rows);
|
emit('onFetch', rows);
|
||||||
$props.insertOnLoad && await insert();
|
$props.insertOnLoad && (await insert());
|
||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function resetData(data) {
|
function resetData(data) {
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
if (data && Array.isArray(data)) {
|
if (data && Array.isArray(data)) {
|
||||||
|
@ -146,15 +145,22 @@ function resetData(data) {
|
||||||
formData.value = JSON.parse(JSON.stringify(data));
|
formData.value = JSON.parse(JSON.stringify(data));
|
||||||
|
|
||||||
if (watchChanges.value) watchChanges.value(); //destroy watcher
|
if (watchChanges.value) watchChanges.value(); //destroy watcher
|
||||||
watchChanges.value = watch(formData, (nVal) => {
|
watchChanges.value = watch(
|
||||||
|
formData,
|
||||||
|
(nVal) => {
|
||||||
hasChanges.value = false;
|
hasChanges.value = false;
|
||||||
const filteredNewData = nVal.filter(row => !isRowEmpty(row) || row[$props.primaryKey]);
|
const filteredNewData = nVal.filter(
|
||||||
const filteredOriginal = originalData.value.filter(row => row[$props.primaryKey]);
|
(row) => !isRowEmpty(row) || row[$props.primaryKey],
|
||||||
|
);
|
||||||
|
const filteredOriginal = originalData.value.filter(
|
||||||
|
(row) => row[$props.primaryKey],
|
||||||
|
);
|
||||||
|
|
||||||
const changes = getDifferences(filteredOriginal, filteredNewData);
|
const changes = getDifferences(filteredOriginal, filteredNewData);
|
||||||
hasChanges.value = !isEmpty(changes);
|
hasChanges.value = !isEmpty(changes);
|
||||||
|
},
|
||||||
}, { deep: true });
|
{ deep: true },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
async function reset() {
|
async function reset() {
|
||||||
await fetch(originalData.value);
|
await fetch(originalData.value);
|
||||||
|
@ -185,7 +191,6 @@ async function onSubmit() {
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
|
|
||||||
await saveChanges($props.saveFn ? formData.value : null);
|
await saveChanges($props.saveFn ? formData.value : null);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onSubmitAndGo() {
|
async function onSubmitAndGo() {
|
||||||
|
@ -194,8 +199,8 @@ async function onSubmitAndGo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveChanges(data) {
|
async function saveChanges(data) {
|
||||||
formData.value = formData.value.filter(row =>
|
formData.value = formData.value.filter(
|
||||||
row[$props.primaryKey] || !isRowEmpty(row)
|
(row) => row[$props.primaryKey] || !isRowEmpty(row),
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($props.saveFn) {
|
if ($props.saveFn) {
|
||||||
|
@ -228,7 +233,7 @@ async function saveChanges(data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function insert(pushData = { ...$props.dataRequired, ...$props.dataDefault }) {
|
async function insert(pushData = { ...$props.dataRequired, ...$props.dataDefault }) {
|
||||||
formData.value = formData.value.filter(row => !isRowEmpty(row));
|
formData.value = formData.value.filter((row) => !isRowEmpty(row));
|
||||||
|
|
||||||
const lastRow = formData.value.at(-1);
|
const lastRow = formData.value.at(-1);
|
||||||
const isLastRowEmpty = lastRow ? isRowEmpty(lastRow) : false;
|
const isLastRowEmpty = lastRow ? isRowEmpty(lastRow) : false;
|
||||||
|
@ -239,20 +244,18 @@ async function insert(pushData = { ...$props.dataRequired, ...$props.dataDefault
|
||||||
const nRow = Object.assign({ $index }, pushData);
|
const nRow = Object.assign({ $index }, pushData);
|
||||||
formData.value.push(nRow);
|
formData.value.push(nRow);
|
||||||
|
|
||||||
const hasChange = Object.keys(nRow).some(key => !isChange(nRow, key));
|
const hasChange = Object.keys(nRow).some((key) => !isChange(nRow, key));
|
||||||
if (hasChange) hasChanges.value = true;
|
if (hasChange) hasChanges.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isRowEmpty(row) {
|
function isRowEmpty(row) {
|
||||||
return Object.keys(row).every(key => isChange(row, key));
|
return Object.keys(row).every((key) => isChange(row, key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function isChange(row, key) {
|
function isChange(row, key) {
|
||||||
return !row[key] || key == '$index' || Object.hasOwn($props.dataRequired || {}, key);
|
return !row[key] || key == '$index' || Object.hasOwn($props.dataRequired || {}, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function remove(data) {
|
async function remove(data) {
|
||||||
if (!data.length)
|
if (!data.length)
|
||||||
return quasar.notify({
|
return quasar.notify({
|
||||||
|
@ -270,7 +273,9 @@ async function remove(data) {
|
||||||
(form) => !preRemove.some((index) => index == form.$index),
|
(form) => !preRemove.some((index) => index == form.$index),
|
||||||
);
|
);
|
||||||
formData.value = newData;
|
formData.value = newData;
|
||||||
hasChanges.value = JSON.stringify(removeIndexField(formData.value)) !== JSON.stringify(removeIndexField(originalData.value));
|
hasChanges.value =
|
||||||
|
JSON.stringify(removeIndexField(formData.value)) !==
|
||||||
|
JSON.stringify(removeIndexField(originalData.value));
|
||||||
}
|
}
|
||||||
if (ids.length) {
|
if (ids.length) {
|
||||||
quasar
|
quasar
|
||||||
|
@ -286,7 +291,7 @@ async function remove(data) {
|
||||||
})
|
})
|
||||||
.onOk(async () => {
|
.onOk(async () => {
|
||||||
newData = newData.filter((form) => !ids.some((id) => id == form[pk]));
|
newData = newData.filter((form) => !ids.some((id) => id == form[pk]));
|
||||||
fetch(newData);
|
await reload();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -254,17 +254,13 @@ async function save() {
|
||||||
old: originalData.value,
|
old: originalData.value,
|
||||||
});
|
});
|
||||||
if ($props.reload) await arrayData.fetch({});
|
if ($props.reload) await arrayData.fetch({});
|
||||||
|
if ($props.goTo) push({ path: $props.goTo });
|
||||||
hasChanges.value = false;
|
hasChanges.value = false;
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveAndGo() {
|
|
||||||
await save();
|
|
||||||
push({ path: $props.goTo });
|
|
||||||
}
|
|
||||||
|
|
||||||
function reset() {
|
function reset() {
|
||||||
formData.value = JSON.parse(JSON.stringify(originalData.value));
|
formData.value = JSON.parse(JSON.stringify(originalData.value));
|
||||||
updateAndEmit('onFetch', { val: originalData.value });
|
updateAndEmit('onFetch', { val: originalData.value });
|
||||||
|
@ -385,7 +381,7 @@ defineExpose({
|
||||||
<QBtnDropdown
|
<QBtnDropdown
|
||||||
data-cy="saveAndContinueDefaultBtn"
|
data-cy="saveAndContinueDefaultBtn"
|
||||||
v-if="$props.goTo"
|
v-if="$props.goTo"
|
||||||
@click="saveAndGo"
|
@click="submitForm"
|
||||||
:label="
|
:label="
|
||||||
tMobile('globals.saveAndContinue') +
|
tMobile('globals.saveAndContinue') +
|
||||||
' ' +
|
' ' +
|
||||||
|
@ -405,7 +401,7 @@ defineExpose({
|
||||||
<QItem
|
<QItem
|
||||||
clickable
|
clickable
|
||||||
v-close-popup
|
v-close-popup
|
||||||
@click="save"
|
@click="submitForm"
|
||||||
:title="t('globals.save')"
|
:title="t('globals.save')"
|
||||||
>
|
>
|
||||||
<QItemSection>
|
<QItemSection>
|
||||||
|
|
|
@ -185,6 +185,7 @@ const col = computed(() => {
|
||||||
newColumn.attrs = { ...newColumn.component?.attrs, autofocus: $props.autofocus };
|
newColumn.attrs = { ...newColumn.component?.attrs, autofocus: $props.autofocus };
|
||||||
newColumn.event = { ...newColumn.component?.event, ...$props?.eventHandlers };
|
newColumn.event = { ...newColumn.component?.event, ...$props?.eventHandlers };
|
||||||
}
|
}
|
||||||
|
|
||||||
return newColumn;
|
return newColumn;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ const arrayData = defineModel({
|
||||||
function handler(event) {
|
function handler(event) {
|
||||||
const clickedElement = event.target.closest('td');
|
const clickedElement = event.target.closest('td');
|
||||||
if (!clickedElement) return;
|
if (!clickedElement) return;
|
||||||
|
event.preventDefault();
|
||||||
target.value = event.target;
|
target.value = event.target;
|
||||||
qmenuRef.value.show();
|
qmenuRef.value.show();
|
||||||
colField.value = clickedElement.getAttribute('data-col-field');
|
colField.value = clickedElement.getAttribute('data-col-field');
|
||||||
|
|
|
@ -151,6 +151,10 @@ const $props = defineProps({
|
||||||
type: String,
|
type: String,
|
||||||
default: 'vnTable',
|
default: 'vnTable',
|
||||||
},
|
},
|
||||||
|
selectionFn: {
|
||||||
|
type: Function,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
@ -218,10 +222,7 @@ onBeforeMount(() => {
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
if ($props.isEditable) document.addEventListener('click', clickHandler);
|
if ($props.isEditable) document.addEventListener('click', clickHandler);
|
||||||
document.addEventListener('contextmenu', (event) => {
|
document.addEventListener('contextmenu', contextMenuRef.value.handler);
|
||||||
event.preventDefault();
|
|
||||||
contextMenuRef.value.handler(event);
|
|
||||||
});
|
|
||||||
mode.value =
|
mode.value =
|
||||||
quasar.platform.is.mobile && !$props.disableOption?.card
|
quasar.platform.is.mobile && !$props.disableOption?.card
|
||||||
? CARD_MODE
|
? CARD_MODE
|
||||||
|
@ -338,10 +339,10 @@ function stopEventPropagation(event) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
function reload(params) {
|
async function reload(params) {
|
||||||
selected.value = [];
|
selected.value = [];
|
||||||
selectAll.value = false;
|
selectAll.value = false;
|
||||||
CrudModelRef.value.reload(params);
|
await CrudModelRef.value.reload(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
function columnName(col) {
|
function columnName(col) {
|
||||||
|
@ -395,12 +396,14 @@ function hasEditableFormat(column) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const clickHandler = async (event) => {
|
const clickHandler = async (event) => {
|
||||||
const clickedElement = event.target.closest('td');
|
const el = event.target;
|
||||||
const isDateElement = event.target.closest('.q-date');
|
const clickedElement = el.closest('td');
|
||||||
const isTimeElement = event.target.closest('.q-time');
|
const isDateElement = el.closest('.q-date');
|
||||||
const isQSelectDropDown = event.target.closest('.q-select__dropdown-icon');
|
const isTimeElement = el.closest('.q-time');
|
||||||
|
const isQSelectDropDown = el.closest('.q-select__dropdown-icon');
|
||||||
|
const isDialog = el.closest('.q-dialog');
|
||||||
|
|
||||||
if (isDateElement || isTimeElement || isQSelectDropDown) return;
|
if (isDateElement || isTimeElement || isQSelectDropDown || isDialog) return;
|
||||||
|
|
||||||
if (clickedElement === null) {
|
if (clickedElement === null) {
|
||||||
await destroyInput(editingRow.value, editingField.value);
|
await destroyInput(editingRow.value, editingField.value);
|
||||||
|
@ -447,6 +450,7 @@ async function renderInput(rowId, field, clickedElement) {
|
||||||
const row = CrudModelRef.value.formData[rowId];
|
const row = CrudModelRef.value.formData[rowId];
|
||||||
const oldValue = CrudModelRef.value.formData[rowId][column?.name];
|
const oldValue = CrudModelRef.value.formData[rowId][column?.name];
|
||||||
|
|
||||||
|
if (column.disable) return;
|
||||||
if (!clickedElement)
|
if (!clickedElement)
|
||||||
clickedElement = document.querySelector(
|
clickedElement = document.querySelector(
|
||||||
`[data-row-index="${rowId}"][data-col-field="${field}"]`,
|
`[data-row-index="${rowId}"][data-col-field="${field}"]`,
|
||||||
|
@ -480,6 +484,7 @@ async function renderInput(rowId, field, clickedElement) {
|
||||||
await destroyInput(rowId, field, clickedElement);
|
await destroyInput(rowId, field, clickedElement);
|
||||||
},
|
},
|
||||||
keydown: async (event) => {
|
keydown: async (event) => {
|
||||||
|
await column?.cellEvent?.['keydown']?.(event, row);
|
||||||
switch (event.key) {
|
switch (event.key) {
|
||||||
case 'Tab':
|
case 'Tab':
|
||||||
await handleTabKey(event, rowId, field);
|
await handleTabKey(event, rowId, field);
|
||||||
|
@ -655,7 +660,9 @@ const rowCtrlClickFunction = computed(() => {
|
||||||
});
|
});
|
||||||
const handleHeaderSelection = (evt, data) => {
|
const handleHeaderSelection = (evt, data) => {
|
||||||
if (evt === 'updateSelected' && selectAll.value) {
|
if (evt === 'updateSelected' && selectAll.value) {
|
||||||
selected.value = tableRef.value.rows;
|
const fn = $props.selectionFn;
|
||||||
|
const rows = tableRef.value.rows;
|
||||||
|
selected.value = fn ? fn(rows) : rows;
|
||||||
} else if (evt === 'selectAll') {
|
} else if (evt === 'selectAll') {
|
||||||
selected.value = data;
|
selected.value = data;
|
||||||
} else {
|
} else {
|
||||||
|
@ -701,7 +708,6 @@ const handleHeaderSelection = (evt, data) => {
|
||||||
:search-url="searchUrl"
|
:search-url="searchUrl"
|
||||||
:disable-infinite-scroll="isTableMode"
|
:disable-infinite-scroll="isTableMode"
|
||||||
:before-save-fn="removeTextValue"
|
:before-save-fn="removeTextValue"
|
||||||
@save-changes="reload"
|
|
||||||
:has-sub-toolbar="$props.hasSubToolbar ?? isEditable"
|
:has-sub-toolbar="$props.hasSubToolbar ?? isEditable"
|
||||||
:auto-load="hasParams || $attrs['auto-load']"
|
:auto-load="hasParams || $attrs['auto-load']"
|
||||||
>
|
>
|
||||||
|
@ -729,7 +735,15 @@ const handleHeaderSelection = (evt, data) => {
|
||||||
:virtual-scroll="isTableMode"
|
:virtual-scroll="isTableMode"
|
||||||
@virtual-scroll="onVirtualScroll"
|
@virtual-scroll="onVirtualScroll"
|
||||||
@row-click="(event, row) => handleRowClick(event, row)"
|
@row-click="(event, row) => handleRowClick(event, row)"
|
||||||
@update:selected="emit('update:selected', $event)"
|
@update:selected="
|
||||||
|
(evt) => {
|
||||||
|
if ($props.selectionFn) selected = $props.selectionFn(evt);
|
||||||
|
emit(
|
||||||
|
'update:selected',
|
||||||
|
selectionFn ? selectionFn(selected) : selected,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
"
|
||||||
@selection="(details) => handleSelection(details, rows)"
|
@selection="(details) => handleSelection(details, rows)"
|
||||||
:hide-selected-banner="true"
|
:hide-selected-banner="true"
|
||||||
:data-cy
|
:data-cy
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
import { describe, expect, it, vi, beforeEach } from 'vitest';
|
||||||
|
import { createWrapper } from 'app/test/vitest/helper';
|
||||||
|
import VnAccountNumber from 'src/components/common/VnAccountNumber.vue';
|
||||||
|
|
||||||
|
describe('VnAccountNumber', () => {
|
||||||
|
let wrapper;
|
||||||
|
let input;
|
||||||
|
let vnInput;
|
||||||
|
let spyShort;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper = createWrapper(VnAccountNumber);
|
||||||
|
wrapper = wrapper.wrapper;
|
||||||
|
input = wrapper.find('input');
|
||||||
|
vnInput = wrapper.findComponent({ name: 'VnInput' });
|
||||||
|
spyShort = vi.spyOn(wrapper.vm, 'useAccountShortToStandard');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filter out non-numeric characters on input event', async () => {
|
||||||
|
await input.setValue('abc123.45!@#');
|
||||||
|
const emitted = wrapper.emitted('update:modelValue');
|
||||||
|
expect(emitted.pop()[0]).toBe('123.45');
|
||||||
|
expect(spyShort).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply conversion on blur when valid short value is provided', async () => {
|
||||||
|
await input.setValue('123.45');
|
||||||
|
await vnInput.trigger('blur');
|
||||||
|
|
||||||
|
const emitted = wrapper.emitted('update:modelValue');
|
||||||
|
expect(emitted.pop()[0]).toBe('1230000045');
|
||||||
|
expect(spyShort).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not change value for invalid input values', async () => {
|
||||||
|
await input.setValue('123');
|
||||||
|
await vnInput.trigger('blur');
|
||||||
|
|
||||||
|
const emitted = wrapper.emitted('update:modelValue');
|
||||||
|
expect(emitted.pop()[0]).toBe('123');
|
||||||
|
expect(spyShort).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
|
@ -36,8 +36,6 @@ const validate = async () => {
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
await props.submitFn(newPassword, oldPassword);
|
await props.submitFn(newPassword, oldPassword);
|
||||||
emit('onSubmit');
|
emit('onSubmit');
|
||||||
} catch (e) {
|
|
||||||
notify('errors.writeRequest', 'negative');
|
|
||||||
} finally {
|
} finally {
|
||||||
changePassDialog.value.hide();
|
changePassDialog.value.hide();
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
|
|
|
@ -6,13 +6,7 @@ import { useRequired } from 'src/composables/useRequired';
|
||||||
const $attrs = useAttrs();
|
const $attrs = useAttrs();
|
||||||
const { isRequired, requiredFieldRule } = useRequired($attrs);
|
const { isRequired, requiredFieldRule } = useRequired($attrs);
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const emit = defineEmits([
|
const emit = defineEmits(['update:modelValue', 'update:options', 'remove']);
|
||||||
'update:modelValue',
|
|
||||||
'update:options',
|
|
||||||
'keyup.enter',
|
|
||||||
'remove',
|
|
||||||
'blur',
|
|
||||||
]);
|
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
|
@ -126,6 +120,14 @@ const handleInsertMode = (e) => {
|
||||||
const handleUppercase = () => {
|
const handleUppercase = () => {
|
||||||
value.value = value.value?.toUpperCase() || '';
|
value.value = value.value?.toUpperCase() || '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const listeners = computed(() =>
|
||||||
|
Object.fromEntries(
|
||||||
|
Object.entries($attrs).filter(
|
||||||
|
([key, val]) => key.startsWith('on') && typeof val === 'function',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -134,10 +136,9 @@ const handleUppercase = () => {
|
||||||
ref="vnInputRef"
|
ref="vnInputRef"
|
||||||
v-model="value"
|
v-model="value"
|
||||||
v-bind="{ ...$attrs, ...styleAttrs }"
|
v-bind="{ ...$attrs, ...styleAttrs }"
|
||||||
|
v-on="listeners"
|
||||||
:type="$attrs.type"
|
:type="$attrs.type"
|
||||||
:class="{ required: isRequired }"
|
:class="{ required: isRequired }"
|
||||||
@keyup.enter="emit('keyup.enter')"
|
|
||||||
@blur="emit('blur')"
|
|
||||||
@keydown="handleKeydown"
|
@keydown="handleKeydown"
|
||||||
:clearable="false"
|
:clearable="false"
|
||||||
:rules="mixinRules"
|
:rules="mixinRules"
|
||||||
|
|
|
@ -445,7 +445,12 @@ function getOptionLabel(property) {
|
||||||
</template>
|
</template>
|
||||||
<template #option="{ opt, itemProps }">
|
<template #option="{ opt, itemProps }">
|
||||||
<QItem v-bind="itemProps">
|
<QItem v-bind="itemProps">
|
||||||
<QItemSection v-if="typeof opt !== 'object'"> {{ opt }}</QItemSection>
|
<QItemSection v-if="typeof optionLabel === 'function'">{{
|
||||||
|
optionLabel(opt)
|
||||||
|
}}</QItemSection>
|
||||||
|
<QItemSection v-else-if="typeof opt !== 'object'">
|
||||||
|
{{ opt }}</QItemSection
|
||||||
|
>
|
||||||
<QItemSection v-else-if="opt[optionValue] == opt[optionLabel]">
|
<QItemSection v-else-if="opt[optionValue] == opt[optionLabel]">
|
||||||
<QItemLabel>{{ opt[optionLabel] }}</QItemLabel>
|
<QItemLabel>{{ opt[optionLabel] }}</QItemLabel>
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
|
|
|
@ -1,22 +1,20 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, useTemplateRef } from 'vue';
|
import { computed, onMounted } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useAccountShortToStandard } from 'src/composables/useAccountShortToStandard';
|
|
||||||
import VnSelectDialog from './VnSelectDialog.vue';
|
import VnSelectDialog from './VnSelectDialog.vue';
|
||||||
import CreateNewExpenseForm from '../CreateNewExpenseForm.vue';
|
import CreateNewExpenseForm from '../CreateNewExpenseForm.vue';
|
||||||
import FetchData from '../FetchData.vue';
|
import { useArrayData } from 'src/composables/useArrayData';
|
||||||
|
|
||||||
const model = defineModel({ type: [String, Number, Object] });
|
const model = defineModel({ type: [String, Number, Object] });
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const expenses = ref([]);
|
const arrayData = useArrayData('expenses', { url: 'Expenses' });
|
||||||
const selectDialogRef = useTemplateRef('selectDialogRef');
|
const expenses = computed(() => arrayData.store.data);
|
||||||
|
|
||||||
async function autocompleteExpense(evt) {
|
onMounted(async () => await arrayData.fetch({}));
|
||||||
const val = evt.target.value;
|
|
||||||
if (!val || isNaN(val)) return;
|
async function updateModel(evt) {
|
||||||
const lookup = expenses.value.find(({ id }) => id == useAccountShortToStandard(val));
|
await arrayData.fetch({});
|
||||||
if (selectDialogRef.value)
|
model.value = evt.id;
|
||||||
selectDialogRef.value.vnSelectDialogRef.vnSelectRef.toggleOption(lookup);
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
|
@ -30,18 +28,11 @@ async function autocompleteExpense(evt) {
|
||||||
:filter-options="['id', 'name']"
|
:filter-options="['id', 'name']"
|
||||||
:tooltip="t('Create a new expense')"
|
:tooltip="t('Create a new expense')"
|
||||||
:acls="[{ model: 'Expense', props: '*', accessType: 'WRITE' }]"
|
:acls="[{ model: 'Expense', props: '*', accessType: 'WRITE' }]"
|
||||||
@keydown.tab.prevent="autocompleteExpense"
|
|
||||||
>
|
>
|
||||||
<template #form>
|
<template #form>
|
||||||
<CreateNewExpenseForm @on-data-saved="$refs.expensesRef.fetch()" />
|
<CreateNewExpenseForm @on-data-saved="updateModel" />
|
||||||
</template>
|
</template>
|
||||||
</VnSelectDialog>
|
</VnSelectDialog>
|
||||||
<FetchData
|
|
||||||
ref="expensesRef"
|
|
||||||
url="Expenses"
|
|
||||||
auto-load
|
|
||||||
@on-fetch="(data) => (expenses = data)"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
<i18n>
|
<i18n>
|
||||||
es:
|
es:
|
||||||
|
|
|
@ -7,6 +7,8 @@ import FetchData from 'components/FetchData.vue';
|
||||||
import VnSelect from 'components/common/VnSelect.vue';
|
import VnSelect from 'components/common/VnSelect.vue';
|
||||||
import { tMobile } from 'composables/tMobile';
|
import { tMobile } from 'composables/tMobile';
|
||||||
import VnSelectWorker from 'src/components/common/VnSelectWorker.vue';
|
import VnSelectWorker from 'src/components/common/VnSelectWorker.vue';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useArrayData } from 'composables/useArrayData';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
|
@ -19,6 +21,7 @@ const claimResponsibles = ref([]);
|
||||||
const claimRedeliveries = ref([]);
|
const claimRedeliveries = ref([]);
|
||||||
const selected = ref([]);
|
const selected = ref([]);
|
||||||
const saveButtonRef = ref();
|
const saveButtonRef = ref();
|
||||||
|
const arrayData = useArrayData('Claim');
|
||||||
|
|
||||||
const developmentsFilter = computed(() => {
|
const developmentsFilter = computed(() => {
|
||||||
return {
|
return {
|
||||||
|
@ -105,6 +108,32 @@ const columns = computed(() => [
|
||||||
align: 'left',
|
align: 'left',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const handleWorker = async (row) => {
|
||||||
|
const { claimResponsibleFk } = row;
|
||||||
|
|
||||||
|
if (!claimResponsibleFk) {
|
||||||
|
row.workerFk = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const commercialResponsible = claimResponsibles?.value?.find(
|
||||||
|
(responsible) => responsible.code === 'com',
|
||||||
|
);
|
||||||
|
|
||||||
|
const claim = arrayData.store.data;
|
||||||
|
|
||||||
|
if (claimResponsibleFk === commercialResponsible?.id) {
|
||||||
|
row.workerFk = claim.workerFk;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = await axios.get(
|
||||||
|
`ClaimDevelopments/${claim.ticketFk}/getResponsible/${claimResponsibleFk}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
row.workerFk = data?.userFk ?? null;
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<FetchData
|
<FetchData
|
||||||
|
@ -166,6 +195,20 @@ const columns = computed(() => [
|
||||||
input-debounce="0"
|
input-debounce="0"
|
||||||
hide-selected
|
hide-selected
|
||||||
/>
|
/>
|
||||||
|
<VnSelect
|
||||||
|
v-else-if="col.name == 'claimResponsible'"
|
||||||
|
v-model="row[col.model]"
|
||||||
|
:url="col.url"
|
||||||
|
:where="col.where"
|
||||||
|
:sort-by="col.sortBy"
|
||||||
|
:options="col.options"
|
||||||
|
:option-value="col.optionValue"
|
||||||
|
:option-label="col.optionLabel"
|
||||||
|
@update:modelValue="handleWorker(row)"
|
||||||
|
:autofocus="col.tabIndex == 1"
|
||||||
|
input-debounce="0"
|
||||||
|
hide-selected
|
||||||
|
/>
|
||||||
<VnSelect
|
<VnSelect
|
||||||
v-else
|
v-else
|
||||||
v-model="row[col.model]"
|
v-model="row[col.model]"
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, markRaw, useTemplateRef, onBeforeMount, watch } from 'vue';
|
import {
|
||||||
|
ref,
|
||||||
|
computed,
|
||||||
|
markRaw,
|
||||||
|
useTemplateRef,
|
||||||
|
onBeforeMount,
|
||||||
|
watch,
|
||||||
|
onUnmounted,
|
||||||
|
} from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { toDate, toCurrency } from 'src/filters';
|
import { toDate, toCurrency } from 'src/filters';
|
||||||
import { useArrayData } from 'src/composables/useArrayData';
|
import { useArrayData } from 'src/composables/useArrayData';
|
||||||
|
@ -19,7 +27,10 @@ import { useQuasar } from 'quasar';
|
||||||
import InvoiceInDescriptorProxy from '../InvoiceIn/Card/InvoiceInDescriptorProxy.vue';
|
import InvoiceInDescriptorProxy from '../InvoiceIn/Card/InvoiceInDescriptorProxy.vue';
|
||||||
import { useStateStore } from 'src/stores/useStateStore';
|
import { useStateStore } from 'src/stores/useStateStore';
|
||||||
import { downloadFile } from 'src/composables/downloadFile';
|
import { downloadFile } from 'src/composables/downloadFile';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useAcl } from 'src/composables/useAcl';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const { notify } = useNotify();
|
const { notify } = useNotify();
|
||||||
|
@ -27,8 +38,6 @@ const user = useState().getUser();
|
||||||
const stateStore = useStateStore();
|
const stateStore = useStateStore();
|
||||||
const updateDialog = ref();
|
const updateDialog = ref();
|
||||||
const uploadDialog = ref();
|
const uploadDialog = ref();
|
||||||
let maxDays;
|
|
||||||
let defaultDays;
|
|
||||||
const dataKey = 'entryPreaccountingFilter';
|
const dataKey = 'entryPreaccountingFilter';
|
||||||
const url = 'Entries/preAccountingFilter';
|
const url = 'Entries/preAccountingFilter';
|
||||||
const arrayData = useArrayData(dataKey);
|
const arrayData = useArrayData(dataKey);
|
||||||
|
@ -45,12 +54,16 @@ const defaultDmsDescription = ref();
|
||||||
const dmsTypeId = ref();
|
const dmsTypeId = ref();
|
||||||
const selectedRows = ref([]);
|
const selectedRows = ref([]);
|
||||||
const totalAmount = ref();
|
const totalAmount = ref();
|
||||||
|
const hasDiferentDms = ref(false);
|
||||||
|
let maxDays;
|
||||||
|
let defaultDays;
|
||||||
|
let supplierRef;
|
||||||
|
let dmsFk;
|
||||||
const totalSelectedAmount = computed(() => {
|
const totalSelectedAmount = computed(() => {
|
||||||
if (!selectedRows.value.length) return 0;
|
if (!selectedRows.value.length) return 0;
|
||||||
return selectedRows.value.reduce((acc, entry) => acc + entry.amount, 0);
|
return selectedRows.value.reduce((acc, entry) => acc + entry.amount, 0);
|
||||||
});
|
});
|
||||||
let supplierRef;
|
|
||||||
let dmsFk;
|
|
||||||
const columns = computed(() => [
|
const columns = computed(() => [
|
||||||
{
|
{
|
||||||
name: 'id',
|
name: 'id',
|
||||||
|
@ -63,6 +76,7 @@ const columns = computed(() => [
|
||||||
{
|
{
|
||||||
name: 'invoiceNumber',
|
name: 'invoiceNumber',
|
||||||
label: t('entry.preAccount.invoiceNumber'),
|
label: t('entry.preAccount.invoiceNumber'),
|
||||||
|
width: 'max-content',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'company',
|
name: 'company',
|
||||||
|
@ -73,7 +87,7 @@ const columns = computed(() => [
|
||||||
optionLabel: 'code',
|
optionLabel: 'code',
|
||||||
options: companies.value,
|
options: companies.value,
|
||||||
},
|
},
|
||||||
orderBy: false,
|
class: 'shrink',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'warehouse',
|
name: 'warehouse',
|
||||||
|
@ -102,6 +116,7 @@ const columns = computed(() => [
|
||||||
{
|
{
|
||||||
name: 'reference',
|
name: 'reference',
|
||||||
label: t('entry.preAccount.reference'),
|
label: t('entry.preAccount.reference'),
|
||||||
|
width: 'max-content',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'shipped',
|
name: 'shipped',
|
||||||
|
@ -136,6 +151,7 @@ const columns = computed(() => [
|
||||||
class: 'fit',
|
class: 'fit',
|
||||||
event: 'update',
|
event: 'update',
|
||||||
},
|
},
|
||||||
|
width: 'max-content',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'country',
|
name: 'country',
|
||||||
|
@ -145,6 +161,7 @@ const columns = computed(() => [
|
||||||
name: 'countryFk',
|
name: 'countryFk',
|
||||||
options: countries.value,
|
options: countries.value,
|
||||||
},
|
},
|
||||||
|
class: 'shrink',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'description',
|
name: 'description',
|
||||||
|
@ -165,11 +182,12 @@ const columns = computed(() => [
|
||||||
component: 'number',
|
component: 'number',
|
||||||
name: 'payDem',
|
name: 'payDem',
|
||||||
},
|
},
|
||||||
|
class: 'shrink',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'fiscalCode',
|
name: 'fiscalCode',
|
||||||
label: t('entry.preAccount.fiscalCode'),
|
label: t('entry.preAccount.fiscalCode'),
|
||||||
format: ({ fiscalCode }) => t(fiscalCode),
|
format: ({ fiscalCode }, dashIfEmpty) => t(dashIfEmpty(fiscalCode)),
|
||||||
columnFilter: {
|
columnFilter: {
|
||||||
component: 'select',
|
component: 'select',
|
||||||
name: 'fiscalCode',
|
name: 'fiscalCode',
|
||||||
|
@ -208,20 +226,21 @@ const columns = computed(() => [
|
||||||
]);
|
]);
|
||||||
|
|
||||||
onBeforeMount(async () => {
|
onBeforeMount(async () => {
|
||||||
|
const filter = JSON.parse(route.query.entryPreaccountingFilter ?? '{}');
|
||||||
const { data } = await axios.get('EntryConfigs/findOne', {
|
const { data } = await axios.get('EntryConfigs/findOne', {
|
||||||
params: { filter: JSON.stringify({ fields: ['maxDays', 'defaultDays'] }) },
|
params: { filter: JSON.stringify({ fields: ['maxDays', 'defaultDays'] }) },
|
||||||
});
|
});
|
||||||
maxDays = data.maxDays;
|
maxDays = data.maxDays;
|
||||||
defaultDays = data.defaultDays;
|
defaultDays = data.defaultDays;
|
||||||
daysAgo.value = arrayData.store.userParams.daysAgo || defaultDays;
|
daysAgo.value = filter.daysAgo || defaultDays;
|
||||||
isBooked.value = arrayData.store.userParams.isBooked || false;
|
isBooked.value = filter.isBooked || false;
|
||||||
stateStore.leftDrawer = false;
|
stateStore.leftDrawer = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(selectedRows, (nVal, oVal) => {
|
onUnmounted(() => (stateStore.leftDrawer = true));
|
||||||
const lastRow = nVal.at(-1);
|
|
||||||
if (lastRow?.isBooked) selectedRows.value.pop();
|
watch(selectedRows, async (nVal, oVal) => {
|
||||||
if (nVal.length > oVal.length && lastRow.invoiceInFk)
|
if (nVal.length > oVal.length && nVal.at(-1).invoiceInFk)
|
||||||
quasar.dialog({
|
quasar.dialog({
|
||||||
component: VnConfirm,
|
component: VnConfirm,
|
||||||
componentProps: { title: t('entry.preAccount.hasInvoice') },
|
componentProps: { title: t('entry.preAccount.hasInvoice') },
|
||||||
|
@ -232,15 +251,22 @@ function filterByDaysAgo(val) {
|
||||||
if (!val) val = defaultDays;
|
if (!val) val = defaultDays;
|
||||||
else if (val > maxDays) val = maxDays;
|
else if (val > maxDays) val = maxDays;
|
||||||
daysAgo.value = val;
|
daysAgo.value = val;
|
||||||
arrayData.store.userParams.daysAgo = daysAgo.value;
|
table.value.reload({
|
||||||
table.value.reload();
|
userParams: { ...arrayData.store.userParams, daysAgo: daysAgo.value },
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function preAccount() {
|
async function preAccount() {
|
||||||
const entries = selectedRows.value;
|
const entries = selectedRows.value;
|
||||||
const { companyFk, isAgricultural, landed } = entries.at(0);
|
const { companyFk, isAgricultural, landed } = entries.at(0);
|
||||||
|
supplierRef = null;
|
||||||
|
dmsFk = undefined;
|
||||||
|
hasDiferentDms.value = false;
|
||||||
try {
|
try {
|
||||||
dmsFk = entries.find(({ gestDocFk }) => gestDocFk)?.gestDocFk;
|
entries.forEach(({ gestDocFk }) => {
|
||||||
|
if (!dmsFk && gestDocFk) dmsFk = gestDocFk;
|
||||||
|
if (dmsFk && gestDocFk && dmsFk != gestDocFk) hasDiferentDms.value = true;
|
||||||
|
});
|
||||||
if (isAgricultural) {
|
if (isAgricultural) {
|
||||||
const year = new Date(landed).getFullYear();
|
const year = new Date(landed).getFullYear();
|
||||||
supplierRef = (
|
supplierRef = (
|
||||||
|
@ -297,8 +323,6 @@ async function createInvoice() {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw e;
|
throw e;
|
||||||
} finally {
|
} finally {
|
||||||
supplierRef = null;
|
|
||||||
dmsFk = undefined;
|
|
||||||
selectedRows.value.length = 0;
|
selectedRows.value.length = 0;
|
||||||
table.value.reload();
|
table.value.reload();
|
||||||
}
|
}
|
||||||
|
@ -359,6 +383,7 @@ async function createInvoice() {
|
||||||
:search-remove-params="false"
|
:search-remove-params="false"
|
||||||
/>
|
/>
|
||||||
<VnTable
|
<VnTable
|
||||||
|
v-if="isBooked !== undefined && daysAgo !== undefined"
|
||||||
v-model:selected="selectedRows"
|
v-model:selected="selectedRows"
|
||||||
:data-key
|
:data-key
|
||||||
:columns
|
:columns
|
||||||
|
@ -377,10 +402,12 @@ async function createInvoice() {
|
||||||
@on-fetch="
|
@on-fetch="
|
||||||
(data) => (totalAmount = data?.reduce((acc, entry) => acc + entry.amount, 0))
|
(data) => (totalAmount = data?.reduce((acc, entry) => acc + entry.amount, 0))
|
||||||
"
|
"
|
||||||
|
:selection-fn="(rows) => rows.filter((row) => !row.isBooked)"
|
||||||
auto-load
|
auto-load
|
||||||
>
|
>
|
||||||
<template #top-left>
|
<template #top-left>
|
||||||
<QBtn
|
<QBtn
|
||||||
|
v-if="useAcl().hasAcl('Entry', 'addInvoiceIn', 'WRITE')"
|
||||||
data-cy="preAccount_btn"
|
data-cy="preAccount_btn"
|
||||||
icon="account_balance"
|
icon="account_balance"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
@ -437,6 +464,11 @@ async function createInvoice() {
|
||||||
:title="t('entry.preAccount.dialog.title')"
|
:title="t('entry.preAccount.dialog.title')"
|
||||||
:message="t('entry.preAccount.dialog.message')"
|
:message="t('entry.preAccount.dialog.message')"
|
||||||
>
|
>
|
||||||
|
<template #customHTML v-if="hasDiferentDms">
|
||||||
|
<p class="text-negative">
|
||||||
|
{{ t('differentGestdoc') }}
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<QBtn
|
<QBtn
|
||||||
data-cy="updateFileYes"
|
data-cy="updateFileYes"
|
||||||
|
@ -471,9 +503,11 @@ en:
|
||||||
IntraCommunity: Intra-community
|
IntraCommunity: Intra-community
|
||||||
NonCommunity: Non-community
|
NonCommunity: Non-community
|
||||||
CanaryIslands: Canary Islands
|
CanaryIslands: Canary Islands
|
||||||
|
differentGestdoc: The entries have different gestdoc
|
||||||
es:
|
es:
|
||||||
IntraCommunity: Intracomunitaria
|
IntraCommunity: Intracomunitaria
|
||||||
NonCommunity: Extracomunitaria
|
NonCommunity: Extracomunitaria
|
||||||
CanaryIslands: Islas Canarias
|
CanaryIslands: Islas Canarias
|
||||||
National: Nacional
|
National: Nacional
|
||||||
|
differentGestdoc: Las entradas tienen diferentes gestdoc
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -130,7 +130,7 @@ entry:
|
||||||
supplierFk: Supplier
|
supplierFk: Supplier
|
||||||
country: Country
|
country: Country
|
||||||
description: Entry type
|
description: Entry type
|
||||||
payDem: Payment term
|
payDem: P. term
|
||||||
isBooked: B
|
isBooked: B
|
||||||
isReceived: R
|
isReceived: R
|
||||||
entryType: Entry type
|
entryType: Entry type
|
||||||
|
@ -138,7 +138,7 @@ entry:
|
||||||
fiscalCode: Account type
|
fiscalCode: Account type
|
||||||
daysAgo: Max 365 days
|
daysAgo: Max 365 days
|
||||||
search: Search
|
search: Search
|
||||||
searchInfo: You can search by supplier name or nickname
|
searchInfo: You can search by supplier name, nickname or tax number
|
||||||
btn: Pre-account
|
btn: Pre-account
|
||||||
hasInvoice: This entry has already an invoice in
|
hasInvoice: This entry has already an invoice in
|
||||||
success: It has been successfully pre-accounted
|
success: It has been successfully pre-accounted
|
||||||
|
|
|
@ -81,7 +81,7 @@ entry:
|
||||||
supplierFk: Proveedor
|
supplierFk: Proveedor
|
||||||
country: País
|
country: País
|
||||||
description: Tipo de Entrada
|
description: Tipo de Entrada
|
||||||
payDem: Plazo de pago
|
payDem: P. pago
|
||||||
isBooked: C
|
isBooked: C
|
||||||
isReceived: R
|
isReceived: R
|
||||||
entryType: Tipo de entrada
|
entryType: Tipo de entrada
|
||||||
|
@ -89,7 +89,7 @@ entry:
|
||||||
fiscalCode: Tipo de cuenta
|
fiscalCode: Tipo de cuenta
|
||||||
daysAgo: Máximo 365 días
|
daysAgo: Máximo 365 días
|
||||||
search: Buscar
|
search: Buscar
|
||||||
searchInfo: Puedes buscar por nombre o alias de proveedor
|
searchInfo: Puedes buscar por nombre, alias o cif de proveedor
|
||||||
btn: Precontabilizar
|
btn: Precontabilizar
|
||||||
hasInvoice: Esta entrada ya tiene una f. recibida
|
hasInvoice: Esta entrada ya tiene una f. recibida
|
||||||
success: Se ha precontabilizado correctamente
|
success: Se ha precontabilizado correctamente
|
||||||
|
|
|
@ -3,15 +3,11 @@ import { ref, computed, onBeforeMount } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { toDate } from 'src/filters';
|
|
||||||
import { useArrayData } from 'src/composables/useArrayData';
|
import { useArrayData } from 'src/composables/useArrayData';
|
||||||
import { getTotal } from 'src/composables/getTotal';
|
import { getTotal } from 'src/composables/getTotal';
|
||||||
import CrudModel from 'src/components/CrudModel.vue';
|
|
||||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
|
||||||
import useNotify from 'src/composables/useNotify.js';
|
import useNotify from 'src/composables/useNotify.js';
|
||||||
import VnInputDate from 'src/components/common/VnInputDate.vue';
|
|
||||||
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
|
|
||||||
import { toCurrency } from 'filters/index';
|
import { toCurrency } from 'filters/index';
|
||||||
|
import VnTable from 'src/components/VnTable/VnTable.vue';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { notify } = useNotify();
|
const { notify } = useNotify();
|
||||||
|
@ -19,256 +15,187 @@ const { t } = useI18n();
|
||||||
const arrayData = useArrayData();
|
const arrayData = useArrayData();
|
||||||
const invoiceIn = computed(() => arrayData.store.data);
|
const invoiceIn = computed(() => arrayData.store.data);
|
||||||
const currency = computed(() => invoiceIn.value?.currency?.code);
|
const currency = computed(() => invoiceIn.value?.currency?.code);
|
||||||
|
|
||||||
const rowsSelected = ref([]);
|
const rowsSelected = ref([]);
|
||||||
const invoiceInFormRef = ref();
|
const invoiceInDueDayTableRef = ref();
|
||||||
const invoiceId = +route.params.id;
|
const invoiceId = +route.params.id;
|
||||||
const filter = { where: { invoiceInFk: invoiceId } };
|
const filter = { where: { invoiceInFk: invoiceId } };
|
||||||
const areRows = ref(false);
|
const areRows = ref(false);
|
||||||
const totalTaxableBase = ref();
|
const totalTaxableBase = ref();
|
||||||
const noMatch = computed(() => totalAmount.value != totalTaxableBase.value);
|
const totalVat = ref();
|
||||||
|
|
||||||
|
const tableRows = computed(
|
||||||
|
() => invoiceInDueDayTableRef.value?.CrudModelRef?.formData || [],
|
||||||
|
);
|
||||||
|
const totalAmount = computed(() => getTotal(tableRows.value, 'amount'));
|
||||||
|
|
||||||
|
const noMatch = computed(
|
||||||
|
() =>
|
||||||
|
totalAmount.value != totalTaxableBase.value &&
|
||||||
|
totalAmount.value != totalVat.value,
|
||||||
|
);
|
||||||
|
|
||||||
const columns = computed(() => [
|
const columns = computed(() => [
|
||||||
{
|
{
|
||||||
name: 'duedate',
|
name: 'dueDated',
|
||||||
label: t('Date'),
|
label: t('Date'),
|
||||||
field: (row) => toDate(row.dueDated),
|
|
||||||
sortable: true,
|
sortable: true,
|
||||||
tabIndex: 1,
|
tabIndex: 1,
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
component: 'date',
|
||||||
|
isEditable: true,
|
||||||
|
create: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'bank',
|
name: 'bankFk',
|
||||||
label: t('Bank'),
|
label: t('Bank'),
|
||||||
field: (row) => row.bankFk,
|
|
||||||
model: 'bankFk',
|
|
||||||
optionLabel: 'bank',
|
|
||||||
url: 'Accountings',
|
|
||||||
sortable: true,
|
sortable: true,
|
||||||
tabIndex: 2,
|
tabIndex: 2,
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
component: 'select',
|
||||||
|
attrs: {
|
||||||
|
url: 'Accountings',
|
||||||
|
optionLabel: 'bank',
|
||||||
|
optionValue: 'id',
|
||||||
|
fields: ['id', 'bank'],
|
||||||
|
'emit-value': true,
|
||||||
|
},
|
||||||
|
format: ({ bank }) => bank?.bank,
|
||||||
|
isEditable: true,
|
||||||
|
create: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'amount',
|
name: 'amount',
|
||||||
label: t('Amount'),
|
label: t('Amount'),
|
||||||
field: (row) => row.amount,
|
|
||||||
sortable: true,
|
sortable: true,
|
||||||
tabIndex: 3,
|
tabIndex: 3,
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
component: 'number',
|
||||||
|
attrs: {
|
||||||
|
clearable: true,
|
||||||
|
'clear-icon': 'close',
|
||||||
|
},
|
||||||
|
isEditable: true,
|
||||||
|
create: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'foreignvalue',
|
name: 'foreignValue',
|
||||||
label: t('Foreign value'),
|
label: t('Foreign value'),
|
||||||
field: (row) => row.foreignValue,
|
|
||||||
sortable: true,
|
sortable: true,
|
||||||
tabIndex: 4,
|
tabIndex: 4,
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
component: 'number',
|
||||||
|
isEditable: isNotEuro(currency.value),
|
||||||
|
create: true,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const totalAmount = computed(() => getTotal(invoiceInFormRef.value.formData, 'amount'));
|
|
||||||
|
|
||||||
const isNotEuro = (code) => code != 'EUR';
|
const isNotEuro = (code) => code != 'EUR';
|
||||||
|
|
||||||
async function insert() {
|
async function insert() {
|
||||||
await axios.post('/InvoiceInDueDays/new', { id: +invoiceId });
|
await axios.post('/InvoiceInDueDays/new', { id: +invoiceId });
|
||||||
await invoiceInFormRef.value.reload();
|
await invoiceInDueDayTableRef.value.reload();
|
||||||
notify(t('globals.dataSaved'), 'positive');
|
notify(t('globals.dataSaved'), 'positive');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setTaxableBase() {
|
async function setTaxableBase() {
|
||||||
|
const ref = invoiceInDueDayTableRef.value;
|
||||||
|
if (ref) ref.create = null;
|
||||||
const { data } = await axios.get(`InvoiceIns/${invoiceId}/getTotals`);
|
const { data } = await axios.get(`InvoiceIns/${invoiceId}/getTotals`);
|
||||||
totalTaxableBase.value = data.totalTaxableBase;
|
totalTaxableBase.value = data.totalTaxableBase;
|
||||||
|
totalVat.value = data.totalVat;
|
||||||
}
|
}
|
||||||
|
|
||||||
onBeforeMount(async () => await setTaxableBase());
|
onBeforeMount(async () => await setTaxableBase());
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<CrudModel
|
<div class="invoice-in-due-day">
|
||||||
|
<VnTable
|
||||||
v-if="invoiceIn"
|
v-if="invoiceIn"
|
||||||
ref="invoiceInFormRef"
|
ref="invoiceInDueDayTableRef"
|
||||||
data-key="InvoiceInDueDays"
|
data-key="InvoiceInDueDays"
|
||||||
url="InvoiceInDueDays"
|
url="InvoiceInDueDays"
|
||||||
|
save-url="InvoiceInDueDays/crud"
|
||||||
:filter="filter"
|
:filter="filter"
|
||||||
|
:user-filter="{
|
||||||
|
include: { relation: 'bank', scope: { fields: ['id', 'bank'] } },
|
||||||
|
}"
|
||||||
auto-load
|
auto-load
|
||||||
:data-required="{ invoiceInFk: invoiceId }"
|
:data-required="{ invoiceInFk: invoiceId }"
|
||||||
v-model:selected="rowsSelected"
|
v-model:selected="rowsSelected"
|
||||||
@on-fetch="(data) => (areRows = !!data.length)"
|
@on-fetch="(data) => (areRows = !!data.length)"
|
||||||
@save-changes="setTaxableBase"
|
@save-changes="setTaxableBase"
|
||||||
|
:columns="columns"
|
||||||
|
:is-editable="true"
|
||||||
|
:table="{ selection: 'multiple', 'row-key': '$index' }"
|
||||||
|
footer
|
||||||
|
:right-search="false"
|
||||||
|
:column-search="false"
|
||||||
|
:disable-option="{ card: true }"
|
||||||
|
class="q-pa-none"
|
||||||
>
|
>
|
||||||
<template #body="{ rows }">
|
<template #column-footer-amount>
|
||||||
<QTable
|
|
||||||
v-model:selected="rowsSelected"
|
|
||||||
selection="multiple"
|
|
||||||
:columns
|
|
||||||
:rows
|
|
||||||
row-key="$index"
|
|
||||||
:grid="$q.screen.lt.sm"
|
|
||||||
>
|
|
||||||
<template #body-cell-duedate="{ row }">
|
|
||||||
<QTd>
|
|
||||||
<VnInputDate v-model="row.dueDated" />
|
|
||||||
</QTd>
|
|
||||||
</template>
|
|
||||||
<template #body-cell-bank="{ row, col }">
|
|
||||||
<QTd>
|
|
||||||
<VnSelect
|
|
||||||
v-model="row[col.model]"
|
|
||||||
:url="col.url"
|
|
||||||
:option-label="col.optionLabel"
|
|
||||||
:option-value="col.optionValue"
|
|
||||||
>
|
|
||||||
<template #option="scope">
|
|
||||||
<QItem v-bind="scope.itemProps">
|
|
||||||
<QItemSection>
|
|
||||||
<QItemLabel>{{
|
|
||||||
`${scope.opt.id}: ${scope.opt.bank}`
|
|
||||||
}}</QItemLabel>
|
|
||||||
</QItemSection>
|
|
||||||
</QItem>
|
|
||||||
</template>
|
|
||||||
</VnSelect>
|
|
||||||
</QTd>
|
|
||||||
</template>
|
|
||||||
<template #body-cell-amount="{ row }">
|
|
||||||
<QTd>
|
|
||||||
<VnInputNumber
|
|
||||||
v-model="row.amount"
|
|
||||||
:is-outlined="false"
|
|
||||||
clearable
|
|
||||||
clear-icon="close"
|
|
||||||
/>
|
|
||||||
</QTd>
|
|
||||||
</template>
|
|
||||||
<template #body-cell-foreignvalue="{ row }">
|
|
||||||
<QTd>
|
|
||||||
<VnInputNumber
|
|
||||||
:class="{
|
|
||||||
'no-pointer-events': !isNotEuro(currency),
|
|
||||||
}"
|
|
||||||
:disable="!isNotEuro(currency)"
|
|
||||||
v-model="row.foreignValue"
|
|
||||||
/>
|
|
||||||
</QTd>
|
|
||||||
</template>
|
|
||||||
<template #bottom-row>
|
|
||||||
<QTr class="bg">
|
|
||||||
<QTd />
|
|
||||||
<QTd />
|
|
||||||
<QTd />
|
|
||||||
<QTd>
|
|
||||||
<QChip
|
<QChip
|
||||||
dense
|
dense
|
||||||
:color="noMatch ? 'negative' : 'transparent'"
|
:color="noMatch ? 'negative' : 'transparent'"
|
||||||
class="q-pa-xs"
|
class="q-pa-xs"
|
||||||
:title="
|
:title="noMatch ? t('invoiceIn.noMatch', { totalTaxableBase }) : ''"
|
||||||
noMatch
|
|
||||||
? t('invoiceIn.noMatch', { totalTaxableBase })
|
|
||||||
: ''
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
{{ toCurrency(totalAmount) }}
|
{{ toCurrency(totalAmount) }}
|
||||||
</QChip>
|
</QChip>
|
||||||
</QTd>
|
</template>
|
||||||
<QTd>
|
<template #column-footer-foreignValue>
|
||||||
<template v-if="isNotEuro(invoiceIn.currency.code)">
|
<template v-if="isNotEuro(currency)">
|
||||||
{{
|
{{
|
||||||
getTotal(rows, 'foreignValue', {
|
getTotal(tableRows, 'foreignValue', {
|
||||||
currency: invoiceIn.currency.code,
|
currency: currency,
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
</template>
|
</template>
|
||||||
</QTd>
|
|
||||||
</QTr>
|
|
||||||
</template>
|
</template>
|
||||||
<template #item="props">
|
</VnTable>
|
||||||
<div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition">
|
<QPageSticky :offset="[20, 20]" style="z-index: 2">
|
||||||
<QCard>
|
|
||||||
<QCardSection>
|
|
||||||
<QCheckbox v-model="props.selected" dense />
|
|
||||||
</QCardSection>
|
|
||||||
<QSeparator />
|
|
||||||
<QList>
|
|
||||||
<QItem>
|
|
||||||
<VnInputDate
|
|
||||||
class="full-width"
|
|
||||||
:label="t('Date')"
|
|
||||||
v-model="props.row.dueDated"
|
|
||||||
/>
|
|
||||||
</QItem>
|
|
||||||
<QItem>
|
|
||||||
<VnSelect
|
|
||||||
:label="t('Bank')"
|
|
||||||
class="full-width"
|
|
||||||
v-model="props.row['bankFk']"
|
|
||||||
url="Accountings"
|
|
||||||
option-label="bank"
|
|
||||||
>
|
|
||||||
<template #option="scope">
|
|
||||||
<QItem v-bind="scope.itemProps">
|
|
||||||
<QItemSection>
|
|
||||||
<QItemLabel>{{
|
|
||||||
`${scope.opt.id}: ${scope.opt.bank}`
|
|
||||||
}}</QItemLabel>
|
|
||||||
</QItemSection>
|
|
||||||
</QItem>
|
|
||||||
</template>
|
|
||||||
</VnSelect>
|
|
||||||
</QItem>
|
|
||||||
<QItem>
|
|
||||||
<VnInputNumber
|
|
||||||
:label="t('Amount')"
|
|
||||||
class="full-width"
|
|
||||||
v-model="props.row.amount"
|
|
||||||
/>
|
|
||||||
</QItem>
|
|
||||||
<QItem>
|
|
||||||
<VnInputNumber
|
|
||||||
:label="t('Foreign value')"
|
|
||||||
class="full-width"
|
|
||||||
:class="{
|
|
||||||
'no-pointer-events': !isNotEuro(currency),
|
|
||||||
}"
|
|
||||||
:disable="!isNotEuro(currency)"
|
|
||||||
v-model="props.row.foreignValue"
|
|
||||||
clearable
|
|
||||||
clear-icon="close"
|
|
||||||
/>
|
|
||||||
</QItem>
|
|
||||||
</QList>
|
|
||||||
</QCard>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</QTable>
|
|
||||||
</template>
|
|
||||||
</CrudModel>
|
|
||||||
<QPageSticky position="bottom-right" :offset="[25, 25]">
|
|
||||||
<QBtn
|
<QBtn
|
||||||
color="primary"
|
|
||||||
icon="add"
|
|
||||||
v-shortcut="'+'"
|
|
||||||
size="lg"
|
|
||||||
round
|
|
||||||
@click="
|
@click="
|
||||||
() => {
|
() => {
|
||||||
if (!areRows) insert();
|
if (!areRows) return insert();
|
||||||
else
|
|
||||||
invoiceInFormRef.insert({
|
invoiceInDueDayTableRef.create = {
|
||||||
amount: (totalTaxableBase - totalAmount).toFixed(2),
|
urlCreate: 'InvoiceInDueDays',
|
||||||
|
onDataSaved: () => invoiceInDueDayTableRef.reload(),
|
||||||
|
title: t('Create due day'),
|
||||||
|
formInitialData: {
|
||||||
invoiceInFk: invoiceId,
|
invoiceInFk: invoiceId,
|
||||||
});
|
dueDated: Date.vnNew(),
|
||||||
|
amount: (totalTaxableBase - totalAmount).toFixed(2),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
invoiceInDueDayTableRef.showForm = true;
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
|
color="primary"
|
||||||
|
fab
|
||||||
|
icon="add"
|
||||||
|
v-shortcut="'+'"
|
||||||
|
data-cy="invoiceInDueDayAdd"
|
||||||
/>
|
/>
|
||||||
|
<QTooltip class="text-no-wrap">
|
||||||
|
{{ t('Create due day') }}
|
||||||
|
</QTooltip>
|
||||||
</QPageSticky>
|
</QPageSticky>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.bg {
|
|
||||||
background-color: var(--vn-light-gray);
|
|
||||||
}
|
|
||||||
|
|
||||||
.q-chip {
|
.q-chip {
|
||||||
color: var(--vn-text-color);
|
color: var(--vn-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.invoice-in-due-day {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
:deep(.full-width) {
|
||||||
|
max-width: 900px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<i18n>
|
<i18n>
|
||||||
es:
|
es:
|
||||||
|
@ -276,4 +203,6 @@ onBeforeMount(async () => await setTaxableBase());
|
||||||
Bank: Caja
|
Bank: Caja
|
||||||
Amount: Importe
|
Amount: Importe
|
||||||
Foreign value: Divisa
|
Foreign value: Divisa
|
||||||
|
invoiceIn.noMatch: El total {totalTaxableBase} no coincide con el importe
|
||||||
|
Create due day: Crear Vencimiento
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -3,71 +3,83 @@ import { computed, ref } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { getTotal } from 'src/composables/getTotal';
|
import { getTotal } from 'src/composables/getTotal';
|
||||||
import CrudModel from 'src/components/CrudModel.vue';
|
|
||||||
import FetchData from 'src/components/FetchData.vue';
|
import FetchData from 'src/components/FetchData.vue';
|
||||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
import VnTable from 'src/components/VnTable/VnTable.vue';
|
||||||
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const invoceInIntrastat = ref([]);
|
|
||||||
const rowsSelected = ref([]);
|
const rowsSelected = ref([]);
|
||||||
const countries = ref([]);
|
const countries = ref([]);
|
||||||
const intrastats = ref([]);
|
const intrastats = ref([]);
|
||||||
const invoiceInFormRef = ref();
|
const invoiceInIntrastatRef = ref();
|
||||||
const invoiceInId = computed(() => +route.params.id);
|
const invoiceInId = computed(() => +route.params.id);
|
||||||
const filter = { where: { invoiceInFk: invoiceInId.value } };
|
const filter = computed(() => ({ where: { invoiceInFk: invoiceInId.value } }));
|
||||||
const columns = computed(() => [
|
const columns = computed(() => [
|
||||||
{
|
{
|
||||||
name: 'code',
|
name: 'intrastatFk',
|
||||||
label: t('Code'),
|
label: t('Code'),
|
||||||
field: (row) => row.intrastatFk,
|
component: 'select',
|
||||||
|
columnFilter: false,
|
||||||
|
attrs: {
|
||||||
options: intrastats.value,
|
options: intrastats.value,
|
||||||
model: 'intrastatFk',
|
|
||||||
optionValue: 'id',
|
optionValue: 'id',
|
||||||
optionLabel: (row) => `${row.id}: ${row.description}`,
|
optionLabel: (row) => `${row.id}: ${row.description}`,
|
||||||
sortable: true,
|
'data-cy': 'intrastat-code',
|
||||||
tabIndex: 1,
|
sortBy: 'id',
|
||||||
align: 'left',
|
fields: ['id', 'description'],
|
||||||
|
filterOptions: ['id', 'description']
|
||||||
|
},
|
||||||
|
format: (row, dashIfEmpty) => dashIfEmpty(getCode(row)),
|
||||||
|
create: true,
|
||||||
|
isEditable: true,
|
||||||
|
width: 'max-content',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'amount',
|
name: 'amount',
|
||||||
label: t('amount'),
|
label: t('amount'),
|
||||||
field: (row) => row.amount,
|
component: 'number',
|
||||||
sortable: true,
|
create: true,
|
||||||
tabIndex: 2,
|
isEditable: true,
|
||||||
align: 'left',
|
columnFilter: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'net',
|
name: 'net',
|
||||||
label: t('net'),
|
label: t('net'),
|
||||||
field: (row) => row.net,
|
component: 'number',
|
||||||
sortable: true,
|
create: true,
|
||||||
tabIndex: 3,
|
isEditable: true,
|
||||||
align: 'left',
|
columnFilter: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'stems',
|
name: 'stems',
|
||||||
label: t('stems'),
|
label: t('stems'),
|
||||||
field: (row) => row.stems,
|
component: 'number',
|
||||||
sortable: true,
|
create: true,
|
||||||
tabIndex: 4,
|
isEditable: true,
|
||||||
align: 'left',
|
columnFilter: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'country',
|
name: 'countryFk',
|
||||||
label: t('country'),
|
label: t('country'),
|
||||||
field: (row) => row.countryFk,
|
component: 'select',
|
||||||
|
attrs: {
|
||||||
options: countries.value,
|
options: countries.value,
|
||||||
model: 'countryFk',
|
|
||||||
optionValue: 'id',
|
|
||||||
optionLabel: 'code',
|
optionLabel: 'code',
|
||||||
sortable: true,
|
},
|
||||||
tabIndex: 5,
|
create: true,
|
||||||
align: 'left',
|
isEditable: true,
|
||||||
|
columnFilter: false,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const tableRows = computed(
|
||||||
|
() => invoiceInIntrastatRef.value?.CrudModelRef?.formData || [],
|
||||||
|
);
|
||||||
|
|
||||||
|
function getCode(row) {
|
||||||
|
const code = intrastats.value.find(({ id }) => id === row.intrastatFk);
|
||||||
|
return code ? `${code.id}: ${code.description}` : null;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<FetchData
|
<FetchData
|
||||||
|
@ -82,165 +94,51 @@ const columns = computed(() => [
|
||||||
auto-load
|
auto-load
|
||||||
@on-fetch="(data) => (intrastats = data)"
|
@on-fetch="(data) => (intrastats = data)"
|
||||||
/>
|
/>
|
||||||
<div class="invoiceIn-intrastat">
|
<div class="invoice-in-intrastat">
|
||||||
<CrudModel
|
<VnTable
|
||||||
ref="invoiceInFormRef"
|
ref="invoiceInIntrastatRef"
|
||||||
data-key="InvoiceInIntrastats"
|
data-key="InvoiceInIntrastats"
|
||||||
url="InvoiceInIntrastats"
|
url="InvoiceInIntrastats"
|
||||||
|
save-url="InvoiceInIntrastats/crud"
|
||||||
|
search-url="InvoiceInIntrastats"
|
||||||
auto-load
|
auto-load
|
||||||
:data-required="{ invoiceInFk: invoiceInId }"
|
:filter
|
||||||
:filter="filter"
|
:user-filter
|
||||||
:insert-on-load="true"
|
|
||||||
v-model:selected="rowsSelected"
|
v-model:selected="rowsSelected"
|
||||||
@on-fetch="(data) => (invoceInIntrastat = data)"
|
:columns
|
||||||
|
:is-editable="true"
|
||||||
|
:table="{ selection: 'multiple', 'row-key': '$index' }"
|
||||||
|
:create="{
|
||||||
|
urlCreate: 'InvoiceInIntrastats',
|
||||||
|
title: t('Create Intrastat'),
|
||||||
|
formInitialData: { invoiceInFk: invoiceInId },
|
||||||
|
onDataSaved: () => invoiceInIntrastatRef.reload(),
|
||||||
|
}"
|
||||||
|
footer
|
||||||
|
data-cy="invoice-in-intrastat-table"
|
||||||
|
:right-search="false"
|
||||||
|
:disable-option="{ card: true }"
|
||||||
>
|
>
|
||||||
<template #body="{ rows }">
|
<template #column-footer-amount>
|
||||||
<QTable
|
{{ getTotal(tableRows, 'amount', { currency: 'default' }) }}
|
||||||
v-model:selected="rowsSelected"
|
|
||||||
selection="multiple"
|
|
||||||
:columns="columns"
|
|
||||||
:rows="rows"
|
|
||||||
row-key="$index"
|
|
||||||
:grid="$q.screen.lt.sm"
|
|
||||||
>
|
|
||||||
<template #body-cell="{ row, col }">
|
|
||||||
<QTd>
|
|
||||||
<VnInputNumber v-model="row[col.name]" />
|
|
||||||
</QTd>
|
|
||||||
</template>
|
</template>
|
||||||
<template #body-cell-code="{ row, col }">
|
<template #column-footer-net>
|
||||||
<QTd>
|
{{ getTotal(tableRows, 'net') }}
|
||||||
<VnSelect
|
|
||||||
v-model="row[col.model]"
|
|
||||||
:options="col.options"
|
|
||||||
:option-value="col.optionValue"
|
|
||||||
:option-label="col.optionLabel"
|
|
||||||
:filter-options="['id', 'description']"
|
|
||||||
data-cy="intrastat-code"
|
|
||||||
>
|
|
||||||
<template #option="scope">
|
|
||||||
<QItem v-bind="scope.itemProps">
|
|
||||||
{{ `${scope.opt.id}: ${scope.opt.description}` }}
|
|
||||||
</QItem>
|
|
||||||
</template>
|
</template>
|
||||||
</VnSelect>
|
<template #column-footer-stems>
|
||||||
</QTd>
|
{{ getTotal(tableRows, 'stems', { decimalPlaces: 0 }) }}
|
||||||
</template>
|
</template>
|
||||||
<template #body-cell-country="{ row, col }">
|
</VnTable>
|
||||||
<QTd>
|
|
||||||
<VnSelect
|
|
||||||
v-model="row[col.model]"
|
|
||||||
:options="col.options"
|
|
||||||
:option-value="col.optionValue"
|
|
||||||
:option-label="col.optionLabel"
|
|
||||||
/>
|
|
||||||
</QTd>
|
|
||||||
</template>
|
|
||||||
<template #bottom-row>
|
|
||||||
<QTr class="bg">
|
|
||||||
<QTd />
|
|
||||||
<QTd />
|
|
||||||
<QTd>
|
|
||||||
{{ getTotal(rows, 'amount', { currency: 'default' }) }}
|
|
||||||
</QTd>
|
|
||||||
<QTd>
|
|
||||||
{{ getTotal(rows, 'net') }}
|
|
||||||
</QTd>
|
|
||||||
<QTd>
|
|
||||||
{{ getTotal(rows, 'stems', { decimalPlaces: 0 }) }}
|
|
||||||
</QTd>
|
|
||||||
<QTd />
|
|
||||||
</QTr>
|
|
||||||
</template>
|
|
||||||
<template #item="props">
|
|
||||||
<div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition">
|
|
||||||
<QCard>
|
|
||||||
<QCardSection>
|
|
||||||
<QCheckbox v-model="props.selected" dense />
|
|
||||||
</QCardSection>
|
|
||||||
<QSeparator />
|
|
||||||
<QList>
|
|
||||||
<QItem>
|
|
||||||
<VnSelect
|
|
||||||
:label="t('Code')"
|
|
||||||
class="full-width"
|
|
||||||
v-model="props.row['intrastatFk']"
|
|
||||||
:options="intrastats"
|
|
||||||
option-value="id"
|
|
||||||
:option-label="
|
|
||||||
(row) => `${row.id}:${row.description}`
|
|
||||||
"
|
|
||||||
:filter-options="['id', 'description']"
|
|
||||||
>
|
|
||||||
<template #option="scope">
|
|
||||||
<QItem v-bind="scope.itemProps">
|
|
||||||
{{
|
|
||||||
`${scope.opt.id}: ${scope.opt.description}`
|
|
||||||
}}
|
|
||||||
</QItem>
|
|
||||||
</template>
|
|
||||||
</VnSelect>
|
|
||||||
</QItem>
|
|
||||||
<QItem
|
|
||||||
v-for="(value, index) of [
|
|
||||||
'amount',
|
|
||||||
'net',
|
|
||||||
'stems',
|
|
||||||
]"
|
|
||||||
:key="index"
|
|
||||||
>
|
|
||||||
<VnInputNumber
|
|
||||||
:label="t(value)"
|
|
||||||
class="full-width"
|
|
||||||
v-model="props.row[value]"
|
|
||||||
clearable
|
|
||||||
clear-icon="close"
|
|
||||||
/>
|
|
||||||
</QItem>
|
|
||||||
<QItem>
|
|
||||||
<VnSelect
|
|
||||||
:label="t('country')"
|
|
||||||
class="full-width"
|
|
||||||
v-model="props.row['countryFk']"
|
|
||||||
:options="countries"
|
|
||||||
option-value="id"
|
|
||||||
option-label="code"
|
|
||||||
/>
|
|
||||||
</QItem>
|
|
||||||
</QList>
|
|
||||||
</QCard>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</QTable>
|
<style lang="scss" scoped>
|
||||||
</template>
|
.invoice-in-intrastat {
|
||||||
</CrudModel>
|
|
||||||
</div>
|
|
||||||
<QPageSticky position="bottom-right" :offset="[25, 25]">
|
|
||||||
<QBtn
|
|
||||||
color="primary"
|
|
||||||
icon="add"
|
|
||||||
v-shortcut="'+'"
|
|
||||||
size="lg"
|
|
||||||
round
|
|
||||||
@click="invoiceInFormRef.insert()"
|
|
||||||
/>
|
|
||||||
</QPageSticky>
|
|
||||||
</template>
|
|
||||||
<style lang="scss">
|
|
||||||
.invoiceIn-intrastat {
|
|
||||||
> .q-card {
|
|
||||||
.vn-label-value {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 1em;
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
.label {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
.value {
|
|
||||||
flex: 0.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
:deep(.full-width) {
|
||||||
|
max-width: 900px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<i18n>
|
<i18n>
|
||||||
|
@ -258,4 +156,5 @@ const columns = computed(() => [
|
||||||
Total amount: Total importe
|
Total amount: Total importe
|
||||||
Total net: Total neto
|
Total net: Total neto
|
||||||
Total stems: Total tallos
|
Total stems: Total tallos
|
||||||
|
Create Intrastat: Crear Intrastat
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -386,6 +386,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
|
||||||
<QTd>{{ toCurrency(entity.totals.totalTaxableBase) }}</QTd>
|
<QTd>{{ toCurrency(entity.totals.totalTaxableBase) }}</QTd>
|
||||||
<QTd></QTd>
|
<QTd></QTd>
|
||||||
<QTd></QTd>
|
<QTd></QTd>
|
||||||
|
<QTd></QTd>
|
||||||
<QTd>{{ toCurrency(getTotalTax(entity.invoiceInTax)) }}</QTd>
|
<QTd>{{ toCurrency(getTotalTax(entity.invoiceInTax)) }}</QTd>
|
||||||
<QTd>{{
|
<QTd>{{
|
||||||
entity.totals.totalTaxableBaseForeignValue &&
|
entity.totals.totalTaxableBaseForeignValue &&
|
||||||
|
|
|
@ -1,123 +1,178 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, nextTick } from 'vue';
|
import { ref, computed, markRaw, useTemplateRef, onBeforeMount } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useArrayData } from 'src/composables/useArrayData';
|
import { useArrayData } from 'src/composables/useArrayData';
|
||||||
import { getTotal } from 'src/composables/getTotal';
|
import { getTotal } from 'src/composables/getTotal';
|
||||||
import { toCurrency } from 'src/filters';
|
import { toCurrency } from 'src/filters';
|
||||||
import FetchData from 'src/components/FetchData.vue';
|
import FetchData from 'src/components/FetchData.vue';
|
||||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
|
||||||
import CrudModel from 'src/components/CrudModel.vue';
|
|
||||||
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
|
|
||||||
import VnSelectDialog from 'src/components/common/VnSelectDialog.vue';
|
|
||||||
import CreateNewExpenseForm from 'src/components/CreateNewExpenseForm.vue';
|
|
||||||
import { getExchange } from 'src/composables/getExchange';
|
import { getExchange } from 'src/composables/getExchange';
|
||||||
|
import VnTable from 'src/components/VnTable/VnTable.vue';
|
||||||
|
import VnSelectExpense from 'src/components/common/VnSelectExpense.vue';
|
||||||
|
import axios from 'axios';
|
||||||
import { useAccountShortToStandard } from 'src/composables/useAccountShortToStandard';
|
import { useAccountShortToStandard } from 'src/composables/useAccountShortToStandard';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const arrayData = useArrayData();
|
const arrayData = useArrayData();
|
||||||
|
const expensesArrayData = useArrayData('expenses', { url: 'Expenses' });
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const invoiceIn = computed(() => arrayData.store.data);
|
const invoiceIn = computed(() => arrayData.store.data);
|
||||||
const currency = computed(() => invoiceIn.value?.currency?.code);
|
const currency = computed(() => invoiceIn.value?.currency?.code);
|
||||||
const expenses = ref([]);
|
const expenses = computed(() => expensesArrayData.store.data);
|
||||||
const sageTaxTypes = ref([]);
|
const sageTaxTypes = ref([]);
|
||||||
const sageTransactionTypes = ref([]);
|
const sageTransactionTypes = ref([]);
|
||||||
const rowsSelected = ref([]);
|
const rowsSelected = ref([]);
|
||||||
const invoiceInFormRef = ref();
|
const invoiceInVatTableRef = useTemplateRef('invoiceInVatTableRef');
|
||||||
|
defineProps({ actionIcon: { type: String, default: 'add' } });
|
||||||
|
|
||||||
defineProps({
|
function taxRate(invoiceInTax) {
|
||||||
actionIcon: {
|
const sageTaxTypeId = invoiceInTax.taxTypeSageFk;
|
||||||
type: String,
|
const taxRateSelection = sageTaxTypes.value.find(
|
||||||
default: 'add',
|
(transaction) => transaction.id == sageTaxTypeId,
|
||||||
},
|
);
|
||||||
});
|
const taxTypeSage = taxRateSelection?.rate ?? 0;
|
||||||
|
const taxableBase = invoiceInTax?.taxableBase ?? 0;
|
||||||
|
|
||||||
|
return (taxTypeSage / 100) * taxableBase;
|
||||||
|
}
|
||||||
|
|
||||||
const columns = computed(() => [
|
const columns = computed(() => [
|
||||||
{
|
{
|
||||||
name: 'expense',
|
name: 'expenseFk',
|
||||||
label: t('Expense'),
|
label: t('Expense'),
|
||||||
field: (row) => row.expenseFk,
|
component: markRaw(VnSelectExpense),
|
||||||
options: expenses.value,
|
format: (row, dashIfEmpty) => {
|
||||||
model: 'expenseFk',
|
const expense = expenses.value?.find((e) => e.id === row.expenseFk);
|
||||||
optionValue: 'id',
|
return expense ? `${expense.id}: ${expense.name}` : dashIfEmpty(null);
|
||||||
optionLabel: (row) => `${row.id}: ${row.name}`,
|
},
|
||||||
sortable: true,
|
sortable: true,
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
isEditable: true,
|
||||||
|
create: true,
|
||||||
|
width: 'max-content',
|
||||||
|
cellEvent: {
|
||||||
|
keydown: async (evt, row) => {
|
||||||
|
if (evt.key !== 'Tab') return;
|
||||||
|
const val = evt.target.value;
|
||||||
|
if (!val || isNaN(val)) return;
|
||||||
|
row.expenseFk = expenses.value.find(
|
||||||
|
(e) => e.id === useAccountShortToStandard(val),
|
||||||
|
)?.id;
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'taxablebase',
|
name: 'taxableBase',
|
||||||
label: t('Taxable base'),
|
label: t('Taxable base'),
|
||||||
field: (row) => row.taxableBase,
|
component: 'number',
|
||||||
model: 'taxableBase',
|
attrs: {
|
||||||
|
clearable: true,
|
||||||
|
'clear-icon': 'close',
|
||||||
|
},
|
||||||
sortable: true,
|
sortable: true,
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
isEditable: true,
|
||||||
|
create: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'isDeductible',
|
name: 'isDeductible',
|
||||||
label: t('invoiceIn.isDeductible'),
|
label: t('invoiceIn.isDeductible'),
|
||||||
field: (row) => row.isDeductible,
|
component: 'checkbox',
|
||||||
model: 'isDeductible',
|
|
||||||
align: 'center',
|
align: 'center',
|
||||||
|
isEditable: true,
|
||||||
|
create: true,
|
||||||
|
createAttrs: {
|
||||||
|
defaultValue: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'sageiva',
|
name: 'taxTypeSageFk',
|
||||||
label: t('Sage iva'),
|
label: t('Sage iva'),
|
||||||
field: (row) => row.taxTypeSageFk,
|
component: 'select',
|
||||||
|
attrs: {
|
||||||
options: sageTaxTypes.value,
|
options: sageTaxTypes.value,
|
||||||
model: 'taxTypeSageFk',
|
|
||||||
optionValue: 'id',
|
optionValue: 'id',
|
||||||
optionLabel: (row) => `${row.id}: ${row.vat}`,
|
optionLabel: (row) => `${row.id}: ${row.vat}`,
|
||||||
|
filterOptions: ['id', 'vat'],
|
||||||
|
'data-cy': 'vat-sageiva',
|
||||||
|
},
|
||||||
|
format: ({ taxTypeSageFk }, dashIfEmpty) => {
|
||||||
|
const taxType = sageTaxTypes.value.find((t) => t.id === taxTypeSageFk);
|
||||||
|
return taxType ? `${taxType.id}: ${taxType.vat}` : dashIfEmpty(taxTypeSageFk);
|
||||||
|
},
|
||||||
sortable: true,
|
sortable: true,
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
isEditable: true,
|
||||||
|
create: true,
|
||||||
|
width: 'max-content',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'sagetransaction',
|
name: 'transactionTypeSageFk',
|
||||||
label: t('Sage transaction'),
|
label: t('Sage transaction'),
|
||||||
field: (row) => row.transactionTypeSageFk,
|
component: 'select',
|
||||||
|
attrs: {
|
||||||
options: sageTransactionTypes.value,
|
options: sageTransactionTypes.value,
|
||||||
model: 'transactionTypeSageFk',
|
|
||||||
optionValue: 'id',
|
optionValue: 'id',
|
||||||
optionLabel: (row) => `${row.id}: ${row.transaction}`,
|
optionLabel: (row) => `${row.id}: ${row.transaction}`,
|
||||||
|
filterOptions: ['id', 'transaction'],
|
||||||
|
},
|
||||||
|
format: ({ transactionTypeSageFk }, dashIfEmpty) => {
|
||||||
|
const transType = sageTransactionTypes.value.find(
|
||||||
|
(t) => t.id === transactionTypeSageFk,
|
||||||
|
);
|
||||||
|
return transType
|
||||||
|
? `${transType.id}: ${transType.transaction}`
|
||||||
|
: dashIfEmpty(transactionTypeSageFk);
|
||||||
|
},
|
||||||
sortable: true,
|
sortable: true,
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
isEditable: true,
|
||||||
|
create: true,
|
||||||
|
width: 'max-content',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'rate',
|
name: 'rate',
|
||||||
label: t('Rate'),
|
label: t('Rate'),
|
||||||
sortable: true,
|
sortable: false,
|
||||||
field: (row) => taxRate(row, row.taxTypeSageFk),
|
format: (row) => taxRate(row).toFixed(2),
|
||||||
align: 'left',
|
align: 'left',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'foreignvalue',
|
name: 'foreignValue',
|
||||||
label: t('Foreign value'),
|
label: t('Foreign value'),
|
||||||
|
component: 'number',
|
||||||
sortable: true,
|
sortable: true,
|
||||||
field: (row) => row.foreignValue,
|
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
create: true,
|
||||||
|
isEditable: isNotEuro(currency.value),
|
||||||
|
format: (row, dashIfEmpty) => dashIfEmpty(row.foreignValue),
|
||||||
|
cellEvent: {
|
||||||
|
'update:modelValue': async (value, oldValue, row) =>
|
||||||
|
await handleForeignValueUpdate(value, row),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'total',
|
name: 'total',
|
||||||
label: 'Total',
|
label: t('Total'),
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
format: (row) => (Number(row.taxableBase || 0) + Number(taxRate(row))).toFixed(2),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const tableRows = computed(
|
||||||
|
() => invoiceInVatTableRef.value?.CrudModelRef?.formData || [],
|
||||||
|
);
|
||||||
const taxableBaseTotal = computed(() => {
|
const taxableBaseTotal = computed(() => {
|
||||||
return getTotal(invoiceInFormRef.value.formData, 'taxableBase');
|
return getTotal(tableRows.value, 'taxableBase');
|
||||||
});
|
});
|
||||||
|
|
||||||
const taxRateTotal = computed(() => {
|
const taxRateTotal = computed(() => {
|
||||||
return getTotal(invoiceInFormRef.value.formData, null, {
|
return tableRows.value.reduce((sum, row) => sum + Number(taxRate(row)), 0);
|
||||||
cb: taxRate,
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
const combinedTotal = computed(() => {
|
const combinedTotal = computed(() => {
|
||||||
return +taxableBaseTotal.value + +taxRateTotal.value;
|
return +taxableBaseTotal.value + +taxRateTotal.value;
|
||||||
});
|
});
|
||||||
|
|
||||||
const filter = {
|
const filter = computed(() => ({
|
||||||
fields: [
|
fields: [
|
||||||
'id',
|
'id',
|
||||||
'invoiceInFk',
|
'invoiceInFk',
|
||||||
|
@ -131,389 +186,75 @@ const filter = {
|
||||||
where: {
|
where: {
|
||||||
invoiceInFk: route.params.id,
|
invoiceInFk: route.params.id,
|
||||||
},
|
},
|
||||||
};
|
}));
|
||||||
|
|
||||||
|
onBeforeMount(async () => await expensesArrayData.fetch({}));
|
||||||
|
|
||||||
const isNotEuro = (code) => code != 'EUR';
|
const isNotEuro = (code) => code != 'EUR';
|
||||||
|
|
||||||
function taxRate(invoiceInTax) {
|
async function handleForeignValueUpdate(val, row) {
|
||||||
const sageTaxTypeId = invoiceInTax.taxTypeSageFk;
|
if (!isNotEuro(currency.value)) return;
|
||||||
const taxRateSelection = sageTaxTypes.value.find(
|
row.taxableBase = await getExchange(
|
||||||
(transaction) => transaction.id == sageTaxTypeId,
|
val,
|
||||||
|
invoiceIn.value?.currencyFk,
|
||||||
|
invoiceIn.value?.issued,
|
||||||
);
|
);
|
||||||
const taxTypeSage = taxRateSelection?.rate ?? 0;
|
|
||||||
const taxableBase = invoiceInTax?.taxableBase ?? 0;
|
|
||||||
|
|
||||||
return ((taxTypeSage / 100) * taxableBase).toFixed(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
function autocompleteExpense(evt, row, col, ref) {
|
|
||||||
const val = evt.target.value;
|
|
||||||
if (!val) return;
|
|
||||||
|
|
||||||
const param = isNaN(val) ? row[col.model] : val;
|
|
||||||
const lookup = expenses.value.find(
|
|
||||||
({ id }) => id == useAccountShortToStandard(param),
|
|
||||||
);
|
|
||||||
|
|
||||||
ref.vnSelectDialogRef.vnSelectRef.toggleOption(lookup);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setCursor(ref) {
|
|
||||||
nextTick(() => {
|
|
||||||
const select = ref.vnSelectDialogRef
|
|
||||||
? ref.vnSelectDialogRef.vnSelectRef
|
|
||||||
: ref.vnSelectRef;
|
|
||||||
select.$el.querySelector('input').setSelectionRange(0, 0);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<FetchData
|
|
||||||
ref="expensesRef"
|
|
||||||
url="Expenses"
|
|
||||||
auto-load
|
|
||||||
@on-fetch="(data) => (expenses = data)"
|
|
||||||
/>
|
|
||||||
<FetchData url="SageTaxTypes" auto-load @on-fetch="(data) => (sageTaxTypes = data)" />
|
<FetchData url="SageTaxTypes" auto-load @on-fetch="(data) => (sageTaxTypes = data)" />
|
||||||
<FetchData
|
<FetchData
|
||||||
url="sageTransactionTypes"
|
url="sageTransactionTypes"
|
||||||
auto-load
|
auto-load
|
||||||
@on-fetch="(data) => (sageTransactionTypes = data)"
|
@on-fetch="(data) => (sageTransactionTypes = data)"
|
||||||
/>
|
/>
|
||||||
<CrudModel
|
<VnTable
|
||||||
ref="invoiceInFormRef"
|
|
||||||
v-if="invoiceIn"
|
v-if="invoiceIn"
|
||||||
|
ref="invoiceInVatTableRef"
|
||||||
data-key="InvoiceInTaxes"
|
data-key="InvoiceInTaxes"
|
||||||
url="InvoiceInTaxes"
|
url="InvoiceInTaxes"
|
||||||
|
save-url="InvoiceInTaxes/crud"
|
||||||
:filter="filter"
|
:filter="filter"
|
||||||
:data-required="{ invoiceInFk: $route.params.id }"
|
:data-required="{ invoiceInFk: $route.params.id }"
|
||||||
:insert-on-load="true"
|
|
||||||
auto-load
|
auto-load
|
||||||
v-model:selected="rowsSelected"
|
v-model:selected="rowsSelected"
|
||||||
|
:columns="columns"
|
||||||
|
:is-editable="true"
|
||||||
|
:table="{ selection: 'multiple', 'row-key': '$index' }"
|
||||||
|
footer
|
||||||
|
:right-search="false"
|
||||||
|
:column-search="false"
|
||||||
|
:disable-option="{ card: true }"
|
||||||
|
class="q-pa-none"
|
||||||
|
:create="{
|
||||||
|
urlCreate: 'InvoiceInTaxes',
|
||||||
|
title: t('Add tax'),
|
||||||
|
formInitialData: { invoiceInFk: $route.params.id, isDeductible: true },
|
||||||
|
onDataSaved: () => invoiceInVatTableRef.reload(),
|
||||||
|
}"
|
||||||
:go-to="`/invoice-in/${$route.params.id}/due-day`"
|
:go-to="`/invoice-in/${$route.params.id}/due-day`"
|
||||||
>
|
>
|
||||||
<template #body="{ rows }">
|
<template #column-footer-taxableBase>
|
||||||
<QTable
|
|
||||||
v-model:selected="rowsSelected"
|
|
||||||
selection="multiple"
|
|
||||||
:columns="columns"
|
|
||||||
:rows="rows"
|
|
||||||
row-key="$index"
|
|
||||||
:grid="$q.screen.lt.sm"
|
|
||||||
>
|
|
||||||
<template #body-cell-expense="{ row, col }">
|
|
||||||
<QTd>
|
|
||||||
<VnSelectDialog
|
|
||||||
:ref="`expenseRef-${row.$index}`"
|
|
||||||
v-model="row[col.model]"
|
|
||||||
:options="col.options"
|
|
||||||
:option-value="col.optionValue"
|
|
||||||
:option-label="col.optionLabel"
|
|
||||||
:filter-options="['id', 'name']"
|
|
||||||
:tooltip="t('Create a new expense')"
|
|
||||||
:acls="[
|
|
||||||
{ model: 'Expense', props: '*', accessType: 'WRITE' },
|
|
||||||
]"
|
|
||||||
@keydown.tab.prevent="
|
|
||||||
autocompleteExpense(
|
|
||||||
$event,
|
|
||||||
row,
|
|
||||||
col,
|
|
||||||
$refs[`expenseRef-${row.$index}`],
|
|
||||||
)
|
|
||||||
"
|
|
||||||
@update:model-value="
|
|
||||||
setCursor($refs[`expenseRef-${row.$index}`])
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<template #option="scope">
|
|
||||||
<QItem v-bind="scope.itemProps">
|
|
||||||
{{ `${scope.opt.id}: ${scope.opt.name}` }}
|
|
||||||
</QItem>
|
|
||||||
</template>
|
|
||||||
<template #form>
|
|
||||||
<CreateNewExpenseForm
|
|
||||||
@on-data-saved="$refs.expensesRef.fetch()"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</VnSelectDialog>
|
|
||||||
</QTd>
|
|
||||||
</template>
|
|
||||||
<template #body-cell-isDeductible="{ row }">
|
|
||||||
<QTd align="center">
|
|
||||||
<QCheckbox
|
|
||||||
v-model="row.isDeductible"
|
|
||||||
data-cy="isDeductible_checkbox"
|
|
||||||
/>
|
|
||||||
</QTd>
|
|
||||||
</template>
|
|
||||||
<template #body-cell-taxablebase="{ row }">
|
|
||||||
<QTd shrink>
|
|
||||||
<VnInputNumber
|
|
||||||
clear-icon="close"
|
|
||||||
v-model="row.taxableBase"
|
|
||||||
clearable
|
|
||||||
/>
|
|
||||||
</QTd>
|
|
||||||
</template>
|
|
||||||
<template #body-cell-sageiva="{ row, col }">
|
|
||||||
<QTd>
|
|
||||||
<VnSelect
|
|
||||||
:ref="`sageivaRef-${row.$index}`"
|
|
||||||
v-model="row[col.model]"
|
|
||||||
:options="col.options"
|
|
||||||
:option-value="col.optionValue"
|
|
||||||
:option-label="col.optionLabel"
|
|
||||||
:filter-options="['id', 'vat']"
|
|
||||||
data-cy="vat-sageiva"
|
|
||||||
@update:model-value="
|
|
||||||
setCursor($refs[`sageivaRef-${row.$index}`])
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<template #option="scope">
|
|
||||||
<QItem v-bind="scope.itemProps">
|
|
||||||
<QItemSection>
|
|
||||||
<QItemLabel>{{ scope.opt.vat }}</QItemLabel>
|
|
||||||
<QItemLabel>
|
|
||||||
{{ `#${scope.opt.id}` }}
|
|
||||||
</QItemLabel>
|
|
||||||
</QItemSection>
|
|
||||||
</QItem>
|
|
||||||
</template>
|
|
||||||
</VnSelect>
|
|
||||||
</QTd>
|
|
||||||
</template>
|
|
||||||
<template #body-cell-sagetransaction="{ row, col }">
|
|
||||||
<QTd>
|
|
||||||
<VnSelect
|
|
||||||
:ref="`sagetransactionRef-${row.$index}`"
|
|
||||||
v-model="row[col.model]"
|
|
||||||
:options="col.options"
|
|
||||||
:option-value="col.optionValue"
|
|
||||||
:option-label="col.optionLabel"
|
|
||||||
:filter-options="['id', 'transaction']"
|
|
||||||
@update:model-value="
|
|
||||||
setCursor($refs[`sagetransactionRef-${row.$index}`])
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<template #option="scope">
|
|
||||||
<QItem v-bind="scope.itemProps">
|
|
||||||
<QItemSection>
|
|
||||||
<QItemLabel>{{
|
|
||||||
scope.opt.transaction
|
|
||||||
}}</QItemLabel>
|
|
||||||
<QItemLabel>
|
|
||||||
{{ `#${scope.opt.id}` }}
|
|
||||||
</QItemLabel>
|
|
||||||
</QItemSection>
|
|
||||||
</QItem>
|
|
||||||
</template>
|
|
||||||
</VnSelect>
|
|
||||||
</QTd>
|
|
||||||
</template>
|
|
||||||
<template #body-cell-foreignvalue="{ row }">
|
|
||||||
<QTd shrink>
|
|
||||||
<VnInputNumber
|
|
||||||
:class="{
|
|
||||||
'no-pointer-events': !isNotEuro(currency),
|
|
||||||
}"
|
|
||||||
:disable="!isNotEuro(currency)"
|
|
||||||
v-model="row.foreignValue"
|
|
||||||
@update:model-value="
|
|
||||||
async (val) => {
|
|
||||||
if (!isNotEuro(currency)) return;
|
|
||||||
row.taxableBase = await getExchange(
|
|
||||||
val,
|
|
||||||
row.currencyFk,
|
|
||||||
invoiceIn.issued,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</QTd>
|
|
||||||
</template>
|
|
||||||
<template #bottom-row>
|
|
||||||
<QTr class="bg">
|
|
||||||
<QTd />
|
|
||||||
<QTd />
|
|
||||||
<QTd>
|
|
||||||
{{ toCurrency(taxableBaseTotal) }}
|
{{ toCurrency(taxableBaseTotal) }}
|
||||||
</QTd>
|
</template>
|
||||||
<QTd />
|
<template #column-footer-rate>
|
||||||
<QTd />
|
|
||||||
<QTd />
|
|
||||||
<QTd>
|
|
||||||
{{ toCurrency(taxRateTotal) }}
|
{{ toCurrency(taxRateTotal) }}
|
||||||
</QTd>
|
</template>
|
||||||
<QTd />
|
<template #column-footer-total>
|
||||||
<QTd>
|
|
||||||
{{ toCurrency(combinedTotal) }}
|
{{ toCurrency(combinedTotal) }}
|
||||||
</QTd>
|
|
||||||
</QTr>
|
|
||||||
</template>
|
</template>
|
||||||
|
</VnTable>
|
||||||
<template #item="props">
|
|
||||||
<div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition">
|
|
||||||
<QCard bordered flat class="q-my-xs">
|
|
||||||
<QCardSection>
|
|
||||||
<QCheckbox v-model="props.selected" dense />
|
|
||||||
</QCardSection>
|
|
||||||
<QSeparator />
|
|
||||||
<QList>
|
|
||||||
<QItem>
|
|
||||||
<VnSelectDialog
|
|
||||||
:label="t('Expense')"
|
|
||||||
class="full-width"
|
|
||||||
v-model="props.row['expenseFk']"
|
|
||||||
:options="expenses"
|
|
||||||
option-value="id"
|
|
||||||
:option-label="(row) => `${row.id}:${row.name}`"
|
|
||||||
:filter-options="['id', 'name']"
|
|
||||||
:tooltip="t('Create a new expense')"
|
|
||||||
>
|
|
||||||
<template #option="scope">
|
|
||||||
<QItem v-bind="scope.itemProps">
|
|
||||||
{{ `${scope.opt.id}: ${scope.opt.name}` }}
|
|
||||||
</QItem>
|
|
||||||
</template>
|
</template>
|
||||||
<template #form>
|
|
||||||
<CreateNewExpenseForm />
|
|
||||||
</template>
|
|
||||||
</VnSelectDialog>
|
|
||||||
</QItem>
|
|
||||||
<QItem>
|
|
||||||
<VnInputNumber
|
|
||||||
:label="t('Taxable base')"
|
|
||||||
:class="{
|
|
||||||
'no-pointer-events': isNotEuro(currency),
|
|
||||||
}"
|
|
||||||
class="full-width"
|
|
||||||
:disable="isNotEuro(currency)"
|
|
||||||
clear-icon="close"
|
|
||||||
v-model="props.row.taxableBase"
|
|
||||||
clearable
|
|
||||||
/>
|
|
||||||
</QItem>
|
|
||||||
<QItem>
|
|
||||||
<VnSelect
|
|
||||||
:label="t('Sage iva')"
|
|
||||||
class="full-width"
|
|
||||||
v-model="props.row['taxTypeSageFk']"
|
|
||||||
:options="sageTaxTypes"
|
|
||||||
option-value="id"
|
|
||||||
:option-label="(row) => `${row.id}:${row.vat}`"
|
|
||||||
:filter-options="['id', 'vat']"
|
|
||||||
>
|
|
||||||
<template #option="scope">
|
|
||||||
<QItem v-bind="scope.itemProps">
|
|
||||||
<QItemSection>
|
|
||||||
<QItemLabel>{{
|
|
||||||
scope.opt.vat
|
|
||||||
}}</QItemLabel>
|
|
||||||
<QItemLabel>
|
|
||||||
{{ `#${scope.opt.id}` }}
|
|
||||||
</QItemLabel>
|
|
||||||
</QItemSection>
|
|
||||||
</QItem>
|
|
||||||
</template>
|
|
||||||
</VnSelect>
|
|
||||||
</QItem>
|
|
||||||
<QItem>
|
|
||||||
<VnSelect
|
|
||||||
class="full-width"
|
|
||||||
v-model="props.row['transactionTypeSageFk']"
|
|
||||||
:options="sageTransactionTypes"
|
|
||||||
option-value="id"
|
|
||||||
:option-label="
|
|
||||||
(row) => `${row.id}:${row.transaction}`
|
|
||||||
"
|
|
||||||
:filter-options="['id', 'transaction']"
|
|
||||||
>
|
|
||||||
<template #option="scope">
|
|
||||||
<QItem v-bind="scope.itemProps">
|
|
||||||
<QItemSection>
|
|
||||||
<QItemLabel>{{
|
|
||||||
scope.opt.transaction
|
|
||||||
}}</QItemLabel>
|
|
||||||
<QItemLabel>
|
|
||||||
{{ `#${scope.opt.id}` }}
|
|
||||||
</QItemLabel>
|
|
||||||
</QItemSection>
|
|
||||||
</QItem>
|
|
||||||
</template>
|
|
||||||
</VnSelect>
|
|
||||||
</QItem>
|
|
||||||
<QItem>
|
|
||||||
{{ toCurrency(taxRate(props.row), currency) }}
|
|
||||||
</QItem>
|
|
||||||
<QItem>
|
|
||||||
<VnInputNumber
|
|
||||||
:label="t('Foreign value')"
|
|
||||||
class="full-width"
|
|
||||||
:class="{
|
|
||||||
'no-pointer-events': !isNotEuro(currency),
|
|
||||||
}"
|
|
||||||
:disable="!isNotEuro(currency)"
|
|
||||||
v-model="props.row.foreignValue"
|
|
||||||
/>
|
|
||||||
</QItem>
|
|
||||||
</QList>
|
|
||||||
</QCard>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</QTable>
|
|
||||||
</template>
|
|
||||||
</CrudModel>
|
|
||||||
<QPageSticky position="bottom-right" :offset="[25, 25]">
|
|
||||||
<QBtn
|
|
||||||
color="primary"
|
|
||||||
icon="add"
|
|
||||||
size="lg"
|
|
||||||
v-shortcut="'+'"
|
|
||||||
round
|
|
||||||
@click="invoiceInFormRef.insert()"
|
|
||||||
>
|
|
||||||
<QTooltip>{{ t('Add tax') }}</QTooltip>
|
|
||||||
</QBtn>
|
|
||||||
</QPageSticky>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.bg {
|
|
||||||
background-color: var(--vn-light-gray);
|
|
||||||
}
|
|
||||||
@media (max-width: $breakpoint-xs) {
|
|
||||||
.q-dialog {
|
|
||||||
.q-card {
|
|
||||||
&__section:not(:first-child) {
|
|
||||||
.q-item {
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.q-checkbox {
|
|
||||||
margin-top: 2rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.q-item {
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
.default-icon {
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 50px;
|
|
||||||
background-color: $primary;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<i18n>
|
<i18n>
|
||||||
es:
|
es:
|
||||||
Expense: Gasto
|
Expense: Gasto
|
||||||
Create a new expense: Crear nuevo gasto
|
Create a new expense: Crear nuevo gasto
|
||||||
Add tax: Crear gasto
|
Add tax: Añadir Gasto/IVA # Changed label slightly
|
||||||
Taxable base: Base imp.
|
Taxable base: Base imp.
|
||||||
Sage tax: Sage iva
|
Sage iva: Sage iva # Kept original label
|
||||||
Sage transaction: Sage transacción
|
Sage transaction: Sage transacción
|
||||||
Rate: Tasa
|
Rate: Cuota # Changed label
|
||||||
Foreign value: Divisa
|
Foreign value: Divisa
|
||||||
|
Total: Total
|
||||||
|
invoiceIn.isDeductible: Deducible
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -41,7 +41,7 @@ async function checkToBook(id) {
|
||||||
params: {
|
params: {
|
||||||
where: JSON.stringify({
|
where: JSON.stringify({
|
||||||
invoiceInFk: id,
|
invoiceInFk: id,
|
||||||
dueDated: { gte: Date.vnNew() },
|
dueDated: { lte: Date.vnNew() },
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -61,6 +61,7 @@ invoiceIn:
|
||||||
isBooked: Is booked
|
isBooked: Is booked
|
||||||
account: Ledger account
|
account: Ledger account
|
||||||
correctingFk: Rectificative
|
correctingFk: Rectificative
|
||||||
|
issued: Issued
|
||||||
noMatch: No match with the vat({totalTaxableBase})
|
noMatch: No match with the vat({totalTaxableBase})
|
||||||
linkVehicleToInvoiceIn: Link vehicle to invoice
|
linkVehicleToInvoiceIn: Link vehicle to invoice
|
||||||
unlinkedVehicle: Unlinked vehicle
|
unlinkedVehicle: Unlinked vehicle
|
||||||
|
|
|
@ -60,6 +60,7 @@ invoiceIn:
|
||||||
isBooked: Contabilizada
|
isBooked: Contabilizada
|
||||||
account: Cuenta contable
|
account: Cuenta contable
|
||||||
correctingFk: Rectificativa
|
correctingFk: Rectificativa
|
||||||
|
issued: Fecha de emisión
|
||||||
noMatch: No cuadra con el iva({totalTaxableBase})
|
noMatch: No cuadra con el iva({totalTaxableBase})
|
||||||
linkVehicleToInvoiceIn: Vincular vehículo a factura
|
linkVehicleToInvoiceIn: Vincular vehículo a factura
|
||||||
unlinkedVehicle: Vehículo desvinculado
|
unlinkedVehicle: Vehículo desvinculado
|
||||||
|
|
|
@ -68,6 +68,19 @@ onMounted(async () => {
|
||||||
<template #menu="{ entity }">
|
<template #menu="{ entity }">
|
||||||
<RouteDescriptorMenu :route="entity" />
|
<RouteDescriptorMenu :route="entity" />
|
||||||
</template>
|
</template>
|
||||||
|
<template #actions="{ entity }">
|
||||||
|
<QCardActions class="flex justify-center" style="padding-inline: 0">
|
||||||
|
<QBtn
|
||||||
|
size="md"
|
||||||
|
icon="vn:delivery"
|
||||||
|
color="primary"
|
||||||
|
:href="`https://grafana.verdnatura.es/d/edkvyi479dbeob/pronostico-de-entregas?orgId=1&var-vRouteFk=${entity.id}`"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ $t('route.deliveryForecast') }}</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
</QCardActions>
|
||||||
|
</template>
|
||||||
</EntityDescriptor>
|
</EntityDescriptor>
|
||||||
</template>
|
</template>
|
||||||
<i18n>
|
<i18n>
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { useRoute } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useStateStore } from 'stores/useStateStore';
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import { QIcon } from 'quasar';
|
import { QIcon } from 'quasar';
|
||||||
import { dashIfEmpty, toCurrency, toDate, toHour } from 'src/filters';
|
import { dashIfEmpty, toCurrency, toDate, toDateHourMinSec, toHour } from 'src/filters';
|
||||||
import { openBuscaman } from 'src/utils/buscaman';
|
import { openBuscaman } from 'src/utils/buscaman';
|
||||||
import CardSummary from 'components/ui/CardSummary.vue';
|
import CardSummary from 'components/ui/CardSummary.vue';
|
||||||
import WorkerDescriptorProxy from 'pages/Worker/Card/WorkerDescriptorProxy.vue';
|
import WorkerDescriptorProxy from 'pages/Worker/Card/WorkerDescriptorProxy.vue';
|
||||||
|
@ -80,6 +80,20 @@ const ticketColumns = ref([
|
||||||
sortable: false,
|
sortable: false,
|
||||||
align: 'left',
|
align: 'left',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'delivered',
|
||||||
|
label: t('route.delivered'),
|
||||||
|
field: (row) => dashIfEmpty(toDateHourMinSec(row?.delivered)),
|
||||||
|
sortable: false,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'estimated',
|
||||||
|
label: t('route.estimated'),
|
||||||
|
field: (row) => dashIfEmpty(toDateHourMinSec(row?.estimated)),
|
||||||
|
sortable: false,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'packages',
|
name: 'packages',
|
||||||
label: t('route.summary.packages'),
|
label: t('route.summary.packages'),
|
||||||
|
@ -89,7 +103,7 @@ const ticketColumns = ref([
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'volume',
|
name: 'volume',
|
||||||
label: t('route.summary.m3'),
|
label: 'm³',
|
||||||
field: (row) => row?.volume,
|
field: (row) => row?.volume,
|
||||||
sortable: false,
|
sortable: false,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
|
@ -267,61 +281,3 @@ const ticketColumns = ref([
|
||||||
</CardSummary>
|
</CardSummary>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<i18n>
|
|
||||||
en:
|
|
||||||
route:
|
|
||||||
summary:
|
|
||||||
date: Date
|
|
||||||
agency: Agency
|
|
||||||
vehicle: Vehicle
|
|
||||||
driver: Driver
|
|
||||||
cost: Cost
|
|
||||||
started: Started time
|
|
||||||
finished: Finished time
|
|
||||||
kmStart: Km start
|
|
||||||
kmEnd: Km end
|
|
||||||
volume: Volume
|
|
||||||
packages: Packages
|
|
||||||
description: Description
|
|
||||||
tickets: Tickets
|
|
||||||
order: Order
|
|
||||||
street: Street
|
|
||||||
city: City
|
|
||||||
pc: PC
|
|
||||||
client: Client
|
|
||||||
state: State
|
|
||||||
m3: m³
|
|
||||||
packaging: Packaging
|
|
||||||
ticket: Ticket
|
|
||||||
closed: Closed
|
|
||||||
open: Open
|
|
||||||
yes: Yes
|
|
||||||
no: No
|
|
||||||
es:
|
|
||||||
route:
|
|
||||||
summary:
|
|
||||||
date: Fecha
|
|
||||||
agency: Agencia
|
|
||||||
vehicle: Vehículo
|
|
||||||
driver: Conductor
|
|
||||||
cost: Costo
|
|
||||||
started: Hora inicio
|
|
||||||
finished: Hora fin
|
|
||||||
kmStart: Km inicio
|
|
||||||
kmEnd: Km fin
|
|
||||||
volume: Volumen
|
|
||||||
packages: Bultos
|
|
||||||
description: Descripción
|
|
||||||
tickets: Tickets
|
|
||||||
order: Orden
|
|
||||||
street: Dirección fiscal
|
|
||||||
city: Población
|
|
||||||
pc: CP
|
|
||||||
client: Cliente
|
|
||||||
state: Estado
|
|
||||||
packaging: Encajado
|
|
||||||
closed: Cerrada
|
|
||||||
open: Abierta
|
|
||||||
yes: Sí
|
|
||||||
no: No
|
|
||||||
</i18n>
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import VnPaginate from 'components/ui/VnPaginate.vue';
|
import VnPaginate from 'components/ui/VnPaginate.vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { dashIfEmpty } from 'src/filters';
|
import { dashIfEmpty, toDateHourMinSec } from 'src/filters';
|
||||||
import VnInputDate from 'components/common/VnInputDate.vue';
|
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||||
import VnInput from 'components/common/VnInput.vue';
|
import VnInput from 'components/common/VnInput.vue';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
@ -24,49 +24,63 @@ const selectedRows = ref([]);
|
||||||
const columns = computed(() => [
|
const columns = computed(() => [
|
||||||
{
|
{
|
||||||
name: 'order',
|
name: 'order',
|
||||||
label: t('Order'),
|
label: t('route.ticket.order'),
|
||||||
field: (row) => dashIfEmpty(row?.priority),
|
field: (row) => dashIfEmpty(row?.priority),
|
||||||
sortable: false,
|
sortable: false,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'client',
|
name: 'client',
|
||||||
label: t('Client'),
|
label: t('route.ticket.client'),
|
||||||
field: (row) => row?.nickname,
|
field: (row) => row?.nickname,
|
||||||
sortable: false,
|
sortable: false,
|
||||||
align: 'left',
|
align: 'left',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'street',
|
name: 'street',
|
||||||
label: t('Street'),
|
label: t('route.ticket.street'),
|
||||||
field: (row) => row?.street,
|
field: (row) => row?.street,
|
||||||
sortable: false,
|
sortable: false,
|
||||||
align: 'left',
|
align: 'left',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'pc',
|
name: 'pc',
|
||||||
label: t('PC'),
|
label: t('route.ticket.PC'),
|
||||||
field: (row) => row?.postalCode,
|
field: (row) => row?.postalCode,
|
||||||
sortable: false,
|
sortable: false,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'city',
|
name: 'city',
|
||||||
label: t('City'),
|
label: t('route.ticket.city'),
|
||||||
field: (row) => row?.city,
|
field: (row) => row?.city,
|
||||||
sortable: false,
|
sortable: false,
|
||||||
align: 'left',
|
align: 'left',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'warehouse',
|
name: 'warehouse',
|
||||||
label: t('Warehouse'),
|
label: t('route.ticket.warehouse'),
|
||||||
field: (row) => row?.warehouseName,
|
field: (row) => row?.warehouseName,
|
||||||
sortable: false,
|
sortable: false,
|
||||||
align: 'left',
|
align: 'left',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'delivered',
|
||||||
|
label: t('route.delivered'),
|
||||||
|
field: (row) => dashIfEmpty(toDateHourMinSec(row?.delivered)),
|
||||||
|
sortable: false,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'estimated',
|
||||||
|
label: t('route.estimated'),
|
||||||
|
field: (row) => dashIfEmpty(toDateHourMinSec(row?.estimated)),
|
||||||
|
sortable: false,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'packages',
|
name: 'packages',
|
||||||
label: t('Packages'),
|
label: t('route.ticket.packages'),
|
||||||
field: (row) => row?.packages,
|
field: (row) => row?.packages,
|
||||||
sortable: false,
|
sortable: false,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
|
@ -80,14 +94,14 @@ const columns = computed(() => [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'packaging',
|
name: 'packaging',
|
||||||
label: t('Packaging'),
|
label: t('route.ticket.packaging'),
|
||||||
field: (row) => row?.ipt,
|
field: (row) => row?.ipt,
|
||||||
sortable: false,
|
sortable: false,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'ticket',
|
name: 'ticket',
|
||||||
label: t('Ticket'),
|
label: t('route.ticket.ticket'),
|
||||||
field: (row) => row?.id,
|
field: (row) => row?.id,
|
||||||
sortable: false,
|
sortable: false,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
|
@ -188,8 +202,8 @@ const confirmRemove = (ticket) => {
|
||||||
.dialog({
|
.dialog({
|
||||||
component: VnConfirm,
|
component: VnConfirm,
|
||||||
componentProps: {
|
componentProps: {
|
||||||
title: t('Confirm removal from route'),
|
title: t('route.ticket.confirmRemoval'),
|
||||||
message: t('Are you sure you want to remove this ticket from the route?'),
|
message: t('route.ticket.confirmRemovalConfirmation'),
|
||||||
promise: () => removeTicket(ticket),
|
promise: () => removeTicket(ticket),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -219,7 +233,7 @@ const openSmsDialog = async () => {
|
||||||
quasar.dialog({
|
quasar.dialog({
|
||||||
component: SendSmsDialog,
|
component: SendSmsDialog,
|
||||||
componentProps: {
|
componentProps: {
|
||||||
title: t('Send SMS to the selected tickets'),
|
title: t('route.ticket.sendSmsTickets'),
|
||||||
url: 'Routes/sendSms',
|
url: 'Routes/sendSms',
|
||||||
destinationFk: clientsId.toString(),
|
destinationFk: clientsId.toString(),
|
||||||
destination: clientsPhone.toString(),
|
destination: clientsPhone.toString(),
|
||||||
|
@ -240,18 +254,25 @@ const openSmsDialog = async () => {
|
||||||
<QDialog v-model="confirmationDialog">
|
<QDialog v-model="confirmationDialog">
|
||||||
<QCard style="min-width: 350px">
|
<QCard style="min-width: 350px">
|
||||||
<QCardSection>
|
<QCardSection>
|
||||||
<p class="text-h6 q-ma-none">{{ t('Select the starting date') }}</p>
|
<p class="text-h6 q-ma-none">
|
||||||
|
{{ t('route.ticket.selectStartingDate') }}
|
||||||
|
</p>
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
|
|
||||||
<QCardSection class="q-pt-none">
|
<QCardSection class="q-pt-none">
|
||||||
<VnInputDate
|
<VnInputDate
|
||||||
:label="t('Stating date')"
|
:label="t('route.ticket.startingDate')"
|
||||||
v-model="startingDate"
|
v-model="startingDate"
|
||||||
autofocus
|
autofocus
|
||||||
/>
|
/>
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
<QCardActions align="right">
|
<QCardActions align="right">
|
||||||
<QBtn flat :label="t('Cancel')" v-close-popup class="text-primary" />
|
<QBtn
|
||||||
|
flat
|
||||||
|
:label="t('globals.cancel')"
|
||||||
|
v-close-popup
|
||||||
|
class="text-primary"
|
||||||
|
/>
|
||||||
<QBtn color="primary" v-close-popup @click="cloneRoutes">
|
<QBtn color="primary" v-close-popup @click="cloneRoutes">
|
||||||
{{ t('globals.clone') }}
|
{{ t('globals.clone') }}
|
||||||
</QBtn>
|
</QBtn>
|
||||||
|
@ -262,7 +283,7 @@ const openSmsDialog = async () => {
|
||||||
<QToolbar class="justify-end">
|
<QToolbar class="justify-end">
|
||||||
<div id="st-actions" class="q-pa-sm">
|
<div id="st-actions" class="q-pa-sm">
|
||||||
<QBtn icon="vn:wand" color="primary" class="q-mr-sm" @click="sortRoutes">
|
<QBtn icon="vn:wand" color="primary" class="q-mr-sm" @click="sortRoutes">
|
||||||
<QTooltip>{{ t('Sort routes') }}</QTooltip>
|
<QTooltip>{{ t('route.ticket.sortRoutes') }}</QTooltip>
|
||||||
</QBtn>
|
</QBtn>
|
||||||
<QBtn
|
<QBtn
|
||||||
icon="vn:buscaman"
|
icon="vn:buscaman"
|
||||||
|
@ -271,7 +292,7 @@ const openSmsDialog = async () => {
|
||||||
:disable="!selectedRows?.length"
|
:disable="!selectedRows?.length"
|
||||||
@click="goToBuscaman()"
|
@click="goToBuscaman()"
|
||||||
>
|
>
|
||||||
<QTooltip>{{ t('Open buscaman') }}</QTooltip>
|
<QTooltip>{{ t('route.ticket.openBuscaman') }}</QTooltip>
|
||||||
</QBtn>
|
</QBtn>
|
||||||
<QBtn
|
<QBtn
|
||||||
icon="filter_alt"
|
icon="filter_alt"
|
||||||
|
@ -280,7 +301,7 @@ const openSmsDialog = async () => {
|
||||||
:disable="!selectedRows?.length"
|
:disable="!selectedRows?.length"
|
||||||
@click="deletePriorities"
|
@click="deletePriorities"
|
||||||
>
|
>
|
||||||
<QTooltip>{{ t('Delete priority') }}</QTooltip>
|
<QTooltip>{{ t('route.ticket.deletePriority') }}</QTooltip>
|
||||||
</QBtn>
|
</QBtn>
|
||||||
<QBtn
|
<QBtn
|
||||||
icon="format_list_numbered"
|
icon="format_list_numbered"
|
||||||
|
@ -288,11 +309,7 @@ const openSmsDialog = async () => {
|
||||||
class="q-mr-sm"
|
class="q-mr-sm"
|
||||||
@click="setOrderedPriority"
|
@click="setOrderedPriority"
|
||||||
>
|
>
|
||||||
<QTooltip
|
<QTooltip>{{ t('route.ticket.renumberAllTickets') }} </QTooltip>
|
||||||
>{{
|
|
||||||
t('Renumber all tickets in the order you see on the screen')
|
|
||||||
}}
|
|
||||||
</QTooltip>
|
|
||||||
</QBtn>
|
</QBtn>
|
||||||
<QBtn
|
<QBtn
|
||||||
icon="sms"
|
icon="sms"
|
||||||
|
@ -301,7 +318,7 @@ const openSmsDialog = async () => {
|
||||||
:disable="!selectedRows?.length"
|
:disable="!selectedRows?.length"
|
||||||
@click="openSmsDialog"
|
@click="openSmsDialog"
|
||||||
>
|
>
|
||||||
<QTooltip>{{ t('Send SMS to all clients') }}</QTooltip>
|
<QTooltip>{{ t('route.ticket.sendSmsClients') }}</QTooltip>
|
||||||
</QBtn>
|
</QBtn>
|
||||||
</div>
|
</div>
|
||||||
</QToolbar>
|
</QToolbar>
|
||||||
|
@ -339,7 +356,11 @@ const openSmsDialog = async () => {
|
||||||
@click="setHighestPriority(row, rows)"
|
@click="setHighestPriority(row, rows)"
|
||||||
>
|
>
|
||||||
<QTooltip>
|
<QTooltip>
|
||||||
{{ t('Assign highest priority') }}
|
{{
|
||||||
|
t(
|
||||||
|
'route.ticket.assignHighestPriority',
|
||||||
|
)
|
||||||
|
}}
|
||||||
</QTooltip>
|
</QTooltip>
|
||||||
</QIcon>
|
</QIcon>
|
||||||
<VnInput
|
<VnInput
|
||||||
|
@ -354,7 +375,9 @@ const openSmsDialog = async () => {
|
||||||
<QTd>
|
<QTd>
|
||||||
<span class="link" @click="goToBuscaman(row)">
|
<span class="link" @click="goToBuscaman(row)">
|
||||||
{{ value }}
|
{{ value }}
|
||||||
<QTooltip>{{ t('Open buscaman') }}</QTooltip>
|
<QTooltip>{{
|
||||||
|
t('route.ticket.openBuscaman')
|
||||||
|
}}</QTooltip>
|
||||||
</span>
|
</span>
|
||||||
</QTd>
|
</QTd>
|
||||||
</template>
|
</template>
|
||||||
|
@ -411,7 +434,7 @@ const openSmsDialog = async () => {
|
||||||
@click="openTicketsDialog"
|
@click="openTicketsDialog"
|
||||||
>
|
>
|
||||||
<QTooltip>
|
<QTooltip>
|
||||||
{{ t('Add ticket') }}
|
{{ t('route.ticket.addTicket') }}
|
||||||
</QTooltip>
|
</QTooltip>
|
||||||
</QBtn>
|
</QBtn>
|
||||||
</QPageSticky>
|
</QPageSticky>
|
||||||
|
@ -432,24 +455,3 @@ const openSmsDialog = async () => {
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<i18n>
|
|
||||||
es:
|
|
||||||
Order: Orden
|
|
||||||
Street: Dirección fiscal
|
|
||||||
City: Población
|
|
||||||
PC: CP
|
|
||||||
Client: Cliente
|
|
||||||
Warehouse: Almacén
|
|
||||||
Packages: Bultos
|
|
||||||
Packaging: Encajado
|
|
||||||
Confirm removal from route: Quitar de la ruta
|
|
||||||
Are you sure you want to remove this ticket from the route?: ¿Seguro que quieres quitar este ticket de la ruta?
|
|
||||||
Sort routes: Ordenar rutas
|
|
||||||
Open buscaman: Abrir buscaman
|
|
||||||
Delete priority: Borrar orden
|
|
||||||
Renumber all tickets in the order you see on the screen: Renumerar todos los tickets con el orden que ves por pantalla
|
|
||||||
Assign highest priority: Asignar máxima prioridad
|
|
||||||
Send SMS to all clients: Mandar sms a todos los clientes de las rutas
|
|
||||||
Send SMS to the selected tickets: Enviar SMS a los tickets seleccionados
|
|
||||||
Add ticket: Añadir ticket
|
|
||||||
</i18n>
|
|
||||||
|
|
|
@ -1,6 +1,33 @@
|
||||||
route:
|
route:
|
||||||
filter:
|
filter:
|
||||||
Served: Served
|
Served: Served
|
||||||
|
summary:
|
||||||
|
date: Date
|
||||||
|
agency: Agency
|
||||||
|
vehicle: Vehicle
|
||||||
|
driver: Driver
|
||||||
|
cost: Cost
|
||||||
|
started: Started time
|
||||||
|
finished: Finished time
|
||||||
|
kmStart: Km start
|
||||||
|
kmEnd: Km end
|
||||||
|
volume: Volume
|
||||||
|
packages: Packages
|
||||||
|
description: Description
|
||||||
|
tickets: Tickets
|
||||||
|
order: Order
|
||||||
|
street: Street
|
||||||
|
city: City
|
||||||
|
pc: PC
|
||||||
|
client: Client
|
||||||
|
state: State
|
||||||
|
m3: m³
|
||||||
|
packaging: Packaging
|
||||||
|
ticket: Ticket
|
||||||
|
closed: Closed
|
||||||
|
open: Open
|
||||||
|
yes: Yes
|
||||||
|
no: No
|
||||||
extendedList:
|
extendedList:
|
||||||
selectStartingDate: Select the starting date
|
selectStartingDate: Select the starting date
|
||||||
startingDate: Starting date
|
startingDate: Starting date
|
||||||
|
@ -75,3 +102,45 @@ route:
|
||||||
searchInfo: You can search by route reference
|
searchInfo: You can search by route reference
|
||||||
dated: Dated
|
dated: Dated
|
||||||
preview: Preview
|
preview: Preview
|
||||||
|
delivered: Delivered
|
||||||
|
estimated: Estimated
|
||||||
|
cmr:
|
||||||
|
search: Search Cmr
|
||||||
|
searchInfo: You can search Cmr by Id
|
||||||
|
params:
|
||||||
|
results: results
|
||||||
|
cmrFk: CMR id
|
||||||
|
hasCmrDms: Attached in gestdoc
|
||||||
|
true: Yes
|
||||||
|
false: No
|
||||||
|
ticketFk: Ticketd id
|
||||||
|
routeFk: Route id
|
||||||
|
countryFk: Country
|
||||||
|
clientFk: Client id
|
||||||
|
warehouseFk: Warehouse
|
||||||
|
shipped: Preparation date
|
||||||
|
viewCmr: View CMR
|
||||||
|
downloadCmrs: Download CMRs
|
||||||
|
search: General search
|
||||||
|
ticket:
|
||||||
|
order: Order
|
||||||
|
street: Street
|
||||||
|
city: City
|
||||||
|
PC: PC
|
||||||
|
client: Client
|
||||||
|
warehouse: Warehouse
|
||||||
|
packages: Packages
|
||||||
|
packaging: Packaging
|
||||||
|
ticket: Ticket
|
||||||
|
confirmRemoval: Confirm removal from route
|
||||||
|
confirmRemovalConfirmation: Are you sure you want to remove this ticket from the route?
|
||||||
|
selectStartingDate: Select the starting date
|
||||||
|
startingDate: Starting date
|
||||||
|
sortRoutes: Sort routes
|
||||||
|
openBuscaman: Open buscaman
|
||||||
|
deletePriority: Delete priority
|
||||||
|
renumberAllTickets: Renumber all tickets in the order you see on the screen
|
||||||
|
assignHighest: Assign highest priority
|
||||||
|
sendSmsTickets: Send SMS to the selected tickets
|
||||||
|
sendSmsClients: Send SMS to all clients
|
||||||
|
addTicket: Add ticket
|
||||||
|
|
|
@ -1,6 +1,31 @@
|
||||||
route:
|
route:
|
||||||
filter:
|
filter:
|
||||||
Served: Servida
|
Served: Servida
|
||||||
|
summary:
|
||||||
|
date: Fecha
|
||||||
|
agency: Agencia
|
||||||
|
vehicle: Vehículo
|
||||||
|
driver: Conductor
|
||||||
|
cost: Costo
|
||||||
|
started: Hora inicio
|
||||||
|
finished: Hora fin
|
||||||
|
kmStart: Km inicio
|
||||||
|
kmEnd: Km fin
|
||||||
|
volume: Volumen
|
||||||
|
packages: Bultos
|
||||||
|
description: Descripción
|
||||||
|
tickets: Tickets
|
||||||
|
order: Orden
|
||||||
|
street: Dirección fiscal
|
||||||
|
city: Población
|
||||||
|
pc: CP
|
||||||
|
client: Cliente
|
||||||
|
state: Estado
|
||||||
|
packaging: Encajado
|
||||||
|
closed: Cerrada
|
||||||
|
open: Abierta
|
||||||
|
yes: Sí
|
||||||
|
no: No
|
||||||
extendedList:
|
extendedList:
|
||||||
selectStartingDate: Seleccione la fecha de inicio
|
selectStartingDate: Seleccione la fecha de inicio
|
||||||
statingDate: Fecha de inicio
|
statingDate: Fecha de inicio
|
||||||
|
@ -76,13 +101,15 @@ route:
|
||||||
searchInfo: Puedes buscar por referencia de la ruta
|
searchInfo: Puedes buscar por referencia de la ruta
|
||||||
dated: Fecha
|
dated: Fecha
|
||||||
preview: Vista previa
|
preview: Vista previa
|
||||||
|
delivered: Entregado
|
||||||
|
estimated: Pronóstico
|
||||||
cmr:
|
cmr:
|
||||||
list:
|
list:
|
||||||
results: resultados
|
results: resultados
|
||||||
cmrFk: Id CMR
|
cmrFk: Id CMR
|
||||||
hasCmrDms: Gestdoc
|
hasCmrDms: Gestdoc
|
||||||
'true': Sí
|
true: Sí
|
||||||
'false': 'No'
|
false: No
|
||||||
ticketFk: Id ticket
|
ticketFk: Id ticket
|
||||||
routeFk: Id ruta
|
routeFk: Id ruta
|
||||||
country: País
|
country: País
|
||||||
|
@ -90,3 +117,25 @@ route:
|
||||||
shipped: Fecha preparación
|
shipped: Fecha preparación
|
||||||
viewCmr: Ver CMR
|
viewCmr: Ver CMR
|
||||||
downloadCmrs: Descargar CMRs
|
downloadCmrs: Descargar CMRs
|
||||||
|
ticket:
|
||||||
|
order: Orden
|
||||||
|
street: Dirección fiscal
|
||||||
|
city: Población
|
||||||
|
PC: CP
|
||||||
|
client: Cliente
|
||||||
|
warehouse: Almacén
|
||||||
|
packages: Bultos
|
||||||
|
packaging: Encajado
|
||||||
|
ticket: Ticket
|
||||||
|
confirmRemoval: Quitar de la ruta
|
||||||
|
confirmRemovalConfirmation: ¿Seguro que quieres quitar este ticket de la ruta?
|
||||||
|
selectStartingDate: Seleccionar fecha de inicio
|
||||||
|
startingDate: F. Inicio
|
||||||
|
sortRoutes: Ordenar rutas
|
||||||
|
openBuscaman: Abrir buscaman
|
||||||
|
deletePriority: Borrar orden
|
||||||
|
renumberAllTickets: Renumerar todos los tickets con el orden que ves por pantalla
|
||||||
|
assignHighest: Asignar máxima prioridad
|
||||||
|
sendSmsTickets: Enviar SMS a los tickets seleccionados
|
||||||
|
sendSmsClients: Mandar sms a todos los clientes de las rutas
|
||||||
|
addTicket: Añadir ticket
|
||||||
|
|
|
@ -13,6 +13,7 @@ export default {
|
||||||
'daysInForward',
|
'daysInForward',
|
||||||
'availabled',
|
'availabled',
|
||||||
'awbFk',
|
'awbFk',
|
||||||
|
'isOrdered',
|
||||||
'isDelivered',
|
'isDelivered',
|
||||||
'isReceived',
|
'isReceived',
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,33 +1,38 @@
|
||||||
/// <reference types="cypress" />
|
/// <reference types="cypress" />
|
||||||
describe('InvoiceInIntrastat', () => {
|
describe('InvoiceInIntrastat', () => {
|
||||||
const firstRow = 'tbody > :nth-child(1)';
|
const firstInstrastat = 'td[data-col-field="intrastatFk"][data-row-index="0"]';
|
||||||
const thirdRow = 'tbody > :nth-child(3)';
|
const firstAmount = 'td[data-col-field="amount"][data-row-index="0"]';
|
||||||
const codes = `[data-cy="intrastat-code"]`;
|
const intrastat = 'Plantas vivas: Esqueje/injerto, Vid';
|
||||||
const firstRowAmount = `${firstRow} > :nth-child(3)`;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
before(() => {
|
||||||
cy.login('administrative');
|
cy.login('administrative');
|
||||||
cy.visit(`/#/invoice-in/1/intrastat`);
|
cy.visit(`/#/invoice-in/1/intrastat`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should edit the first line', () => {
|
it('should edit the first line', () => {
|
||||||
cy.selectOption(`${firstRow} ${codes}`, 'Plantas vivas: Esqueje/injerto, Vid');
|
cy.get(firstInstrastat).click().type(`{selectall}{backspace}${intrastat}{enter}`);
|
||||||
|
cy.get(firstAmount).click().type('10{selectall}{backspace}20{enter}');
|
||||||
cy.saveCard();
|
cy.saveCard();
|
||||||
cy.get(codes).eq(0).contains('6021010: Plantas vivas: Esqueje/injerto, Vid');
|
cy.get(firstInstrastat).should('have.text', `6021010: ${intrastat}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add a new row', () => {
|
it('should add a new row', () => {
|
||||||
cy.addRow();
|
cy.dataCy('vnTableCreateBtn').click();
|
||||||
cy.fillRow(thirdRow, [
|
cy.fillInForm(
|
||||||
false,
|
{
|
||||||
'Plantas vivas: Esqueje/injerto, Vid',
|
'intrastat-code': {
|
||||||
30,
|
val: 'Plantas vivas: Esqueje/injerto, Vid',
|
||||||
10,
|
type: 'select',
|
||||||
5,
|
},
|
||||||
'FR',
|
Amount_input: '30',
|
||||||
]);
|
Net_input: '10',
|
||||||
cy.saveCard();
|
Stems_input: '5',
|
||||||
cy.checkNotification('Data saved');
|
Country_select: { val: 'FR', type: 'select' },
|
||||||
|
},
|
||||||
|
{ attr: 'data-cy' },
|
||||||
|
);
|
||||||
|
cy.dataCy('FormModelPopup_save').click();
|
||||||
|
cy.get('.q-notification__message').should('have.text', 'Data created');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should remove the first line', () => {
|
it('should remove the first line', () => {
|
||||||
|
|
|
@ -1,37 +1,41 @@
|
||||||
/// <reference types="cypress" />
|
/// <reference types="cypress" />
|
||||||
describe('InvoiceInVat', () => {
|
describe('InvoiceInVat', () => {
|
||||||
const thirdRow = 'tbody > :nth-child(3)';
|
|
||||||
const firstLineVat = 'tbody > :nth-child(1) ';
|
|
||||||
const vats = '[data-cy="vat-sageiva"]';
|
|
||||||
const dialogInputs = '.q-dialog label input';
|
const dialogInputs = '.q-dialog label input';
|
||||||
const addBtn = 'tbody tr:nth-child(1) td:nth-child(2) .--add-icon';
|
|
||||||
const randomInt = Math.floor(Math.random() * 100);
|
const randomInt = Math.floor(Math.random() * 100);
|
||||||
|
const firstTaxType = 'td[data-col-field="taxTypeSageFk"][data-row-index="0"]';
|
||||||
|
const firstExpense = 'td[data-col-field="expenseFk"][data-row-index="0"]';
|
||||||
|
const firstDeductible = 'td[data-col-field="isDeductible"][data-row-index="0"]';
|
||||||
|
const taxType = 'H.P. IVA 21% CEE';
|
||||||
|
|
||||||
beforeEach(() => {
|
before(() => {
|
||||||
cy.login('administrative');
|
cy.login('administrative');
|
||||||
cy.visit(`/#/invoice-in/1/vat`);
|
cy.visit(`/#/invoice-in/1/vat`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should edit the sage iva', () => {
|
it('should edit the sage iva', () => {
|
||||||
cy.selectOption(`${firstLineVat} ${vats}`, 'H.P. IVA 21% CEE');
|
cy.get(firstTaxType).click().type(`{selectall}{backspace}${taxType}{enter}`);
|
||||||
cy.saveCard();
|
cy.saveCard();
|
||||||
cy.get(vats).eq(0).contains('8: H.P. IVA 21% CEE');
|
cy.get(firstTaxType).should('have.text', `8: ${taxType}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should mark the line as deductible VAT', () => {
|
it('should mark the line as deductible VAT', () => {
|
||||||
cy.get(`${firstLineVat} [data-cy="isDeductible_checkbox"]`).click();
|
cy.get(firstDeductible).click().click();
|
||||||
|
|
||||||
cy.saveCard();
|
|
||||||
|
|
||||||
cy.get(`${firstLineVat} [data-cy="isDeductible_checkbox"]`).click();
|
|
||||||
cy.saveCard();
|
cy.saveCard();
|
||||||
|
cy.get('.q-notification__message').should('have.text', 'Data saved');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add a new row', () => {
|
it('should add a new row', () => {
|
||||||
cy.addRow();
|
cy.dataCy('vnTableCreateBtn').click();
|
||||||
cy.fillRow(thirdRow, [true, 2000000001, 30, 'H.P. IVA 10']);
|
cy.fillInForm(
|
||||||
cy.saveCard();
|
{
|
||||||
cy.get('.q-notification__message').should('have.text', 'Data saved');
|
Expense_select: { val: '2000000001', type: 'select' },
|
||||||
|
'Taxable base_input': '30',
|
||||||
|
'vat-sageiva': { val: 'H.P. IVA 10', type: 'select' },
|
||||||
|
},
|
||||||
|
{ attr: 'data-cy' },
|
||||||
|
);
|
||||||
|
cy.dataCy('FormModelPopup_save').click();
|
||||||
|
cy.get('.q-notification__message').should('have.text', 'Data created');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should remove the first line', () => {
|
it('should remove the first line', () => {
|
||||||
|
@ -39,10 +43,10 @@ describe('InvoiceInVat', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly handle expense addition', () => {
|
it('should correctly handle expense addition', () => {
|
||||||
cy.get(addBtn).click();
|
cy.get(firstExpense).click();
|
||||||
|
cy.get('.--add-icon').click();
|
||||||
cy.get(dialogInputs).eq(0).type(randomInt);
|
cy.get(dialogInputs).eq(0).type(randomInt);
|
||||||
cy.get(dialogInputs).eq(1).type('This is a dummy expense');
|
cy.get(dialogInputs).eq(1).type('This is a dummy expense');
|
||||||
|
|
||||||
cy.get('[data-cy="FormModelPopup_save"]').click();
|
cy.get('[data-cy="FormModelPopup_save"]').click();
|
||||||
cy.get('.q-notification__message').should('have.text', 'Data created');
|
cy.get('.q-notification__message').should('have.text', 'Data created');
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue