Merge branch 'test' of https://gitea.verdnatura.es/verdnatura/salix-front into dev
gitea/salix-front/pipeline/head This commit looks good
Details
gitea/salix-front/pipeline/head This commit looks good
Details
This commit is contained in:
commit
749fb7a4a8
|
@ -0,0 +1,184 @@
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, watch, computed, ref, useAttrs } from 'vue';
|
||||||
|
import { date } from 'quasar';
|
||||||
|
import VnDate from './VnDate.vue';
|
||||||
|
import { useRequired } from 'src/composables/useRequired';
|
||||||
|
|
||||||
|
const $attrs = useAttrs();
|
||||||
|
const { isRequired, requiredFieldRule } = useRequired($attrs);
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
isOutlined: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
isPopupOpen: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
multiple: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const model = defineModel({
|
||||||
|
type: [String, Date, Array],
|
||||||
|
default: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
const vnInputDateRef = ref(null);
|
||||||
|
|
||||||
|
const dateFormat = 'DD/MM/YYYY';
|
||||||
|
const isPopupOpen = ref();
|
||||||
|
const hover = ref();
|
||||||
|
const mask = ref();
|
||||||
|
|
||||||
|
const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])];
|
||||||
|
|
||||||
|
const formattedDate = computed({
|
||||||
|
get() {
|
||||||
|
if (!model.value) return model.value;
|
||||||
|
if ($props.multiple) {
|
||||||
|
return model.value
|
||||||
|
.map((d) => date.formatDate(new Date(d), dateFormat))
|
||||||
|
.join(', ');
|
||||||
|
}
|
||||||
|
return date.formatDate(new Date(model.value), dateFormat);
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
if (value == model.value) return;
|
||||||
|
if ($props.multiple) return; // No permitir edición manual en modo múltiple
|
||||||
|
|
||||||
|
let newDate;
|
||||||
|
if (value) {
|
||||||
|
// parse input
|
||||||
|
if (value.includes('/') && value.length >= 10) {
|
||||||
|
if (value.at(2) == '/') value = value.split('/').reverse().join('/');
|
||||||
|
value = date.formatDate(
|
||||||
|
new Date(value).toISOString(),
|
||||||
|
'YYYY-MM-DDTHH:mm:ss.SSSZ',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const [year, month, day] = value.split('-').map((e) => parseInt(e));
|
||||||
|
newDate = new Date(year, month - 1, day);
|
||||||
|
if (model.value && !$props.multiple) {
|
||||||
|
const orgDate =
|
||||||
|
model.value instanceof Date ? model.value : new Date(model.value);
|
||||||
|
|
||||||
|
newDate.setHours(
|
||||||
|
orgDate.getHours(),
|
||||||
|
orgDate.getMinutes(),
|
||||||
|
orgDate.getSeconds(),
|
||||||
|
orgDate.getMilliseconds(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isNaN(newDate)) model.value = newDate.toISOString();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const popupDate = computed(() => {
|
||||||
|
if (!model.value) return model.value;
|
||||||
|
if ($props.multiple) {
|
||||||
|
return model.value.map((d) => date.formatDate(new Date(d), 'YYYY/MM/DD'));
|
||||||
|
}
|
||||||
|
return date.formatDate(new Date(model.value), 'YYYY/MM/DD');
|
||||||
|
});
|
||||||
|
onMounted(() => {
|
||||||
|
// fix quasar bug
|
||||||
|
mask.value = '##/##/####';
|
||||||
|
if ($props.multiple && !model.value) {
|
||||||
|
model.value = [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
watch(
|
||||||
|
() => model.value,
|
||||||
|
(val) => (formattedDate.value = val),
|
||||||
|
{ immediate: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
const styleAttrs = computed(() => {
|
||||||
|
return $props.isOutlined
|
||||||
|
? {
|
||||||
|
dense: true,
|
||||||
|
outlined: true,
|
||||||
|
rounded: true,
|
||||||
|
}
|
||||||
|
: {};
|
||||||
|
});
|
||||||
|
|
||||||
|
const manageDate = (dates) => {
|
||||||
|
if ($props.multiple) {
|
||||||
|
model.value = dates.map((d) => new Date(d).toISOString());
|
||||||
|
} else {
|
||||||
|
formattedDate.value = dates;
|
||||||
|
}
|
||||||
|
if ($props.isPopupOpen) isPopupOpen.value = false;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div @mouseover="hover = true" @mouseleave="hover = false">
|
||||||
|
<QInput
|
||||||
|
ref="vnInputDateRef"
|
||||||
|
v-model="formattedDate"
|
||||||
|
class="vn-input-date"
|
||||||
|
:mask="$props.multiple ? undefined : mask"
|
||||||
|
placeholder="dd/mm/aaaa"
|
||||||
|
v-bind="{ ...$attrs, ...styleAttrs }"
|
||||||
|
:class="{ required: isRequired }"
|
||||||
|
:rules="mixinRules"
|
||||||
|
:clearable="false"
|
||||||
|
@click="isPopupOpen = !isPopupOpen"
|
||||||
|
@keydown="isPopupOpen = false"
|
||||||
|
hide-bottom-space
|
||||||
|
:data-cy="($attrs['data-cy'] ?? $attrs.label) + '_inputDate'"
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<QIcon
|
||||||
|
name="close"
|
||||||
|
size="xs"
|
||||||
|
v-if="
|
||||||
|
($attrs.clearable == undefined || $attrs.clearable) &&
|
||||||
|
hover &&
|
||||||
|
model &&
|
||||||
|
!$attrs.disable
|
||||||
|
"
|
||||||
|
@click="
|
||||||
|
vnInputDateRef.focus();
|
||||||
|
model = null;
|
||||||
|
isPopupOpen = false;
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<QMenu
|
||||||
|
v-if="$q.screen.gt.xs"
|
||||||
|
transition-show="scale"
|
||||||
|
transition-hide="scale"
|
||||||
|
v-model="isPopupOpen"
|
||||||
|
anchor="bottom left"
|
||||||
|
self="top start"
|
||||||
|
:no-focus="true"
|
||||||
|
:no-parent-event="true"
|
||||||
|
>
|
||||||
|
<VnDate
|
||||||
|
v-model="popupDate"
|
||||||
|
@update:model-value="manageDate"
|
||||||
|
:multiple="multiple"
|
||||||
|
/>
|
||||||
|
</QMenu>
|
||||||
|
<QDialog v-else v-model="isPopupOpen">
|
||||||
|
<VnDate
|
||||||
|
v-model="popupDate"
|
||||||
|
@update:model-value="manageDate"
|
||||||
|
:multiple="multiple"
|
||||||
|
/>
|
||||||
|
</QDialog>
|
||||||
|
</QInput>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Open date: Abrir fecha
|
||||||
|
</i18n>
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { toPercentage } from 'filters/index';
|
import { toCurrency, toPercentage } from 'filters/index';
|
||||||
|
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
@ -8,6 +8,10 @@ const props = defineProps({
|
||||||
type: Number,
|
type: Number,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
format: {
|
||||||
|
type: String,
|
||||||
|
default: 'percentage', // 'currency'
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const valueClass = computed(() =>
|
const valueClass = computed(() =>
|
||||||
|
@ -21,7 +25,10 @@ const formattedValue = computed(() => props.value);
|
||||||
<template>
|
<template>
|
||||||
<span :class="valueClass">
|
<span :class="valueClass">
|
||||||
<QIcon :name="iconName" size="sm" class="value-icon" />
|
<QIcon :name="iconName" size="sm" class="value-icon" />
|
||||||
{{ toPercentage(formattedValue) }}
|
<span v-if="$props.format === 'percentage'">{{
|
||||||
|
toPercentage(formattedValue)
|
||||||
|
}}</span>
|
||||||
|
<span v-if="$props.format === 'currency'">{{ toCurrency(formattedValue) }}</span>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -897,6 +897,7 @@ components:
|
||||||
minPrice: Min. Price
|
minPrice: Min. Price
|
||||||
itemFk: Item id
|
itemFk: Item id
|
||||||
dated: Date
|
dated: Date
|
||||||
|
date: Date
|
||||||
userPanel:
|
userPanel:
|
||||||
copyToken: Token copied to clipboard
|
copyToken: Token copied to clipboard
|
||||||
settings: Settings
|
settings: Settings
|
||||||
|
|
|
@ -981,6 +981,7 @@ components:
|
||||||
minPrice: Precio mínimo
|
minPrice: Precio mínimo
|
||||||
itemFk: Id item
|
itemFk: Id item
|
||||||
dated: Fecha
|
dated: Fecha
|
||||||
|
date: Fecha
|
||||||
userPanel:
|
userPanel:
|
||||||
copyToken: Token copiado al portapapeles
|
copyToken: Token copiado al portapapeles
|
||||||
settings: Configuración
|
settings: Configuración
|
||||||
|
|
|
@ -55,7 +55,6 @@ const filterBanks = {
|
||||||
fields: ['id', 'bank', 'accountingTypeFk'],
|
fields: ['id', 'bank', 'accountingTypeFk'],
|
||||||
include: { relation: 'accountingType' },
|
include: { relation: 'accountingType' },
|
||||||
order: 'id',
|
order: 'id',
|
||||||
limit: 30,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const filterClientFindOne = {
|
const filterClientFindOne = {
|
||||||
|
@ -200,7 +199,6 @@ async function getAmountPaid() {
|
||||||
option-label="bank"
|
option-label="bank"
|
||||||
:include="{ relation: 'accountingType' }"
|
:include="{ relation: 'accountingType' }"
|
||||||
sort-by="id"
|
sort-by="id"
|
||||||
:limit="0"
|
|
||||||
@update:model-value="
|
@update:model-value="
|
||||||
(value, options) => setPaymentType(data, value, options)
|
(value, options) => setPaymentType(data, value, options)
|
||||||
"
|
"
|
||||||
|
|
|
@ -8,10 +8,11 @@ import VnTable from 'src/components/VnTable/VnTable.vue';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { displayResults } from 'src/pages/Ticket/Negative/composables/notifyResults';
|
import { displayResults } from 'src/pages/Ticket/Negative/composables/notifyResults';
|
||||||
import FetchData from 'components/FetchData.vue';
|
import FetchData from 'components/FetchData.vue';
|
||||||
import { useState } from 'src/composables/useState';
|
import useNotify from 'src/composables/useNotify.js';
|
||||||
|
|
||||||
const MATCH = 'match';
|
const MATCH = 'match';
|
||||||
const { notifyResults } = displayResults();
|
const { notifyResults } = displayResults();
|
||||||
|
const { notify } = useNotify();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
|
@ -42,7 +43,7 @@ const ticketConfig = ref({});
|
||||||
const proposalTableRef = ref(null);
|
const proposalTableRef = ref(null);
|
||||||
|
|
||||||
const sale = computed(() => $props.sales[0]);
|
const sale = computed(() => $props.sales[0]);
|
||||||
const saleFk = computed(() => sale.value.saleFk);
|
const saleFk = computed(() => sale.value?.saleFk);
|
||||||
const filter = computed(() => ({
|
const filter = computed(() => ({
|
||||||
where: $props.filter,
|
where: $props.filter,
|
||||||
|
|
||||||
|
@ -56,8 +57,24 @@ const defaultColumnAttrs = {
|
||||||
};
|
};
|
||||||
const emit = defineEmits(['onDialogClosed', 'itemReplaced']);
|
const emit = defineEmits(['onDialogClosed', 'itemReplaced']);
|
||||||
|
|
||||||
const conditionalValuePrice = (price) =>
|
const priceStatusClass = (proposalPrice) => {
|
||||||
price > 1 + ticketConfig.value.lackAlertPrice / 100 ? 'match' : 'not-match';
|
const originalPrice = sale.value?.price;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!originalPrice ||
|
||||||
|
!ticketConfig.value ||
|
||||||
|
typeof ticketConfig.value.lackAlertPrice !== 'number'
|
||||||
|
) {
|
||||||
|
return 'price-ok';
|
||||||
|
}
|
||||||
|
|
||||||
|
const priceIncreasePercentage =
|
||||||
|
((proposalPrice - originalPrice) / originalPrice) * 100;
|
||||||
|
|
||||||
|
return priceIncreasePercentage > ticketConfig.value.lackAlertPrice
|
||||||
|
? 'price-alert'
|
||||||
|
: 'price-ok';
|
||||||
|
};
|
||||||
|
|
||||||
const columns = computed(() => [
|
const columns = computed(() => [
|
||||||
{
|
{
|
||||||
|
@ -97,7 +114,15 @@ const columns = computed(() => [
|
||||||
{
|
{
|
||||||
align: 'left',
|
align: 'left',
|
||||||
sortable: true,
|
sortable: true,
|
||||||
label: t('item.list.color'),
|
label: t('item.list.producer'),
|
||||||
|
name: 'subName',
|
||||||
|
field: 'subName',
|
||||||
|
columnClass: 'expand',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'left',
|
||||||
|
sortable: true,
|
||||||
|
label: t('proposal.tag5'),
|
||||||
name: 'tag5',
|
name: 'tag5',
|
||||||
field: 'value5',
|
field: 'value5',
|
||||||
columnClass: 'expand',
|
columnClass: 'expand',
|
||||||
|
@ -105,7 +130,7 @@ const columns = computed(() => [
|
||||||
{
|
{
|
||||||
align: 'left',
|
align: 'left',
|
||||||
sortable: true,
|
sortable: true,
|
||||||
label: t('item.list.stems'),
|
label: t('proposal.tag6'),
|
||||||
name: 'tag6',
|
name: 'tag6',
|
||||||
field: 'value6',
|
field: 'value6',
|
||||||
columnClass: 'expand',
|
columnClass: 'expand',
|
||||||
|
@ -113,12 +138,27 @@ const columns = computed(() => [
|
||||||
{
|
{
|
||||||
align: 'left',
|
align: 'left',
|
||||||
sortable: true,
|
sortable: true,
|
||||||
label: t('item.list.producer'),
|
label: t('proposal.tag7'),
|
||||||
name: 'tag7',
|
name: 'tag7',
|
||||||
field: 'value7',
|
field: 'value7',
|
||||||
columnClass: 'expand',
|
columnClass: 'expand',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
align: 'left',
|
||||||
|
sortable: true,
|
||||||
|
label: t('proposal.tag8'),
|
||||||
|
name: 'tag8',
|
||||||
|
field: 'value8',
|
||||||
|
columnClass: 'expand',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'left',
|
||||||
|
sortable: true,
|
||||||
|
label: t('proposal.advanceable'),
|
||||||
|
name: 'advanceable',
|
||||||
|
field: 'advanceable',
|
||||||
|
columnClass: 'expand',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
...defaultColumnAttrs,
|
...defaultColumnAttrs,
|
||||||
label: t('proposal.price2'),
|
label: t('proposal.price2'),
|
||||||
|
@ -169,14 +209,14 @@ function extractMatchValues(obj) {
|
||||||
.filter((key) => key.startsWith(MATCH))
|
.filter((key) => key.startsWith(MATCH))
|
||||||
.map((key) => parseInt(key.replace(MATCH, ''), 10));
|
.map((key) => parseInt(key.replace(MATCH, ''), 10));
|
||||||
}
|
}
|
||||||
const gradientStyle = (value) => {
|
const gradientStyleClass = (row) => {
|
||||||
let color = 'white';
|
let color = 'white';
|
||||||
const perc = parseFloat(value);
|
const value = parseFloat(row);
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case perc >= 0 && perc < 33:
|
case value >= 0 && value < 33:
|
||||||
color = 'primary';
|
color = 'primary';
|
||||||
break;
|
break;
|
||||||
case perc >= 33 && perc < 66:
|
case value >= 33 && value < 66:
|
||||||
color = 'warning';
|
color = 'warning';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -193,52 +233,63 @@ const statusConditionalValue = (row) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const isSelectionAvailable = (itemProposal) => {
|
const isSelectionAvailable = (itemProposal) => {
|
||||||
const { price2 } = itemProposal;
|
const { price2, available } = itemProposal;
|
||||||
const salePrice = sale.value.price;
|
const salePrice = sale.value.price;
|
||||||
const byPrice = (100 * price2) / salePrice > ticketConfig.value.lackAlertPrice;
|
const { lackAlertPrice } = ticketConfig.value;
|
||||||
if (byPrice) {
|
const isPriceTooHigh = (100 * price2) / salePrice > lackAlertPrice;
|
||||||
return byPrice;
|
if (isPriceTooHigh) {
|
||||||
|
return isPriceTooHigh;
|
||||||
}
|
}
|
||||||
const byQuantity =
|
const hasEnoughQuantity =
|
||||||
(100 * itemProposal.available) / Math.abs($props.itemLack.lack) <
|
(100 * available) / Math.abs($props.itemLack.lack) < lackAlertPrice;
|
||||||
ticketConfig.value.lackAlertPrice;
|
return hasEnoughQuantity;
|
||||||
return byQuantity;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
async function change({ itemFk: substitutionFk }) {
|
async function change(itemSubstitution) {
|
||||||
try {
|
if (!isSelectionAvailable(itemSubstitution)) {
|
||||||
const promises = $props.sales.map(({ saleFk, quantity }) => {
|
notify(t('notAvailable'), 'warning');
|
||||||
const params = {
|
return;
|
||||||
|
}
|
||||||
|
const { itemFk: substitutionFk } = itemSubstitution;
|
||||||
|
let body;
|
||||||
|
const promises = $props.sales.map(({ saleFk, quantity, ticketFk }) => {
|
||||||
|
body = {
|
||||||
saleFk,
|
saleFk,
|
||||||
substitutionFk,
|
substitutionFk,
|
||||||
quantity,
|
quantity,
|
||||||
|
ticketFk,
|
||||||
};
|
};
|
||||||
return axios.post('Sales/replaceItem', params);
|
return axios.post('Sales/replaceItem', body);
|
||||||
});
|
});
|
||||||
const results = await Promise.allSettled(promises);
|
const results = await Promise.allSettled(promises);
|
||||||
|
|
||||||
notifyResults(results, 'saleFk');
|
notifyResults(results, 'ticketFk');
|
||||||
emit('itemReplaced', {
|
emit('itemReplaced', {
|
||||||
|
...body,
|
||||||
type: 'refresh',
|
type: 'refresh',
|
||||||
quantity: quantity.value,
|
|
||||||
itemProposal: proposalSelected.value[0],
|
itemProposal: proposalSelected.value[0],
|
||||||
});
|
});
|
||||||
proposalSelected.value = [];
|
proposalSelected.value = [];
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleTicketConfig(data) {
|
async function handleTicketConfig(data) {
|
||||||
ticketConfig.value = data[0];
|
ticketConfig.value = data[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function filterRows(data) {
|
||||||
|
const filteredRows = data.sort(
|
||||||
|
(a, b) => isSelectionAvailable(b) - isSelectionAvailable(a),
|
||||||
|
);
|
||||||
|
proposalTableRef.value.CrudModelRef.formData = filteredRows;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<FetchData
|
<FetchData
|
||||||
url="TicketConfigs"
|
url="TicketConfigs"
|
||||||
:filter="{ fields: ['lackAlertPrice'] }"
|
:filter="{ fields: ['lackAlertPrice'] }"
|
||||||
@on-fetch="handleTicketConfig"
|
@on-fetch="handleTicketConfig"
|
||||||
></FetchData>
|
auto-load
|
||||||
|
/>
|
||||||
<QInnerLoading
|
<QInnerLoading
|
||||||
:showing="isLoading"
|
:showing="isLoading"
|
||||||
:label="t && t('globals.pleaseWait')"
|
:label="t && t('globals.pleaseWait')"
|
||||||
|
@ -255,13 +306,22 @@ async function handleTicketConfig(data) {
|
||||||
:user-filter="filter"
|
:user-filter="filter"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
class="full-width q-mt-md"
|
class="full-width q-mt-md"
|
||||||
|
@on-fetch="filterRows"
|
||||||
row-key="id"
|
row-key="id"
|
||||||
:row-click="change"
|
:row-click="change"
|
||||||
:is-editable="false"
|
:is-editable="false"
|
||||||
:right-search="false"
|
:right-search="false"
|
||||||
:without-header="true"
|
|
||||||
:disable-option="{ card: true, table: true }"
|
:disable-option="{ card: true, table: true }"
|
||||||
>
|
>
|
||||||
|
<template #top-right>
|
||||||
|
<QBtn
|
||||||
|
flat
|
||||||
|
class="q-mr-sm"
|
||||||
|
color="primary"
|
||||||
|
icon="refresh"
|
||||||
|
@click="proposalTableRef.reload()"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
<template #column-longName="{ row }">
|
<template #column-longName="{ row }">
|
||||||
<QTd
|
<QTd
|
||||||
class="flex"
|
class="flex"
|
||||||
|
@ -269,15 +329,17 @@ async function handleTicketConfig(data) {
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="middle full-width"
|
class="middle full-width"
|
||||||
:class="[`proposal-${gradientStyle(statusConditionalValue(row))}`]"
|
:class="[
|
||||||
|
`proposal-${gradientStyleClass(statusConditionalValue(row))}`,
|
||||||
|
]"
|
||||||
>
|
>
|
||||||
<QTooltip> {{ statusConditionalValue(row) }}% </QTooltip>
|
<QTooltip> {{ statusConditionalValue(row) }}% </QTooltip>
|
||||||
</div>
|
</div>
|
||||||
<div style="flex: 2 0 100%; align-content: center">
|
<div style="flex: 2 0 100%; align-content: center">
|
||||||
<div>
|
<span class="link" @click.stop>
|
||||||
<span class="link">{{ row.longName }}</span>
|
{{ row.longName }}
|
||||||
<ItemDescriptorProxy :id="row.id" />
|
<ItemDescriptorProxy :id="row.id" />
|
||||||
</div>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</QTd>
|
</QTd>
|
||||||
</template>
|
</template>
|
||||||
|
@ -290,6 +352,9 @@ async function handleTicketConfig(data) {
|
||||||
<template #column-tag7="{ row }">
|
<template #column-tag7="{ row }">
|
||||||
<span :class="{ match: !row.match7 }">{{ row.value7 }}</span>
|
<span :class="{ match: !row.match7 }">{{ row.value7 }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
<template #column-tag8="{ row }">
|
||||||
|
<span :class="{ match: !row.match8 }">{{ row.value8 }}</span>
|
||||||
|
</template>
|
||||||
<template #column-counter="{ row }">
|
<template #column-counter="{ row }">
|
||||||
<span
|
<span
|
||||||
:class="{
|
:class="{
|
||||||
|
@ -304,8 +369,17 @@ async function handleTicketConfig(data) {
|
||||||
</template>
|
</template>
|
||||||
<template #column-price2="{ row }">
|
<template #column-price2="{ row }">
|
||||||
<div class="flex column items-center content-center">
|
<div class="flex column items-center content-center">
|
||||||
<VnStockValueDisplay :value="(sales[0].price - row.price2) / 100" />
|
<!-- Use class binding for tooltip background -->
|
||||||
<span :class="[conditionalValuePrice(row.price2)]">{{
|
<QTooltip :offset="[0, 5]" anchor="top middle" self="bottom middle">
|
||||||
|
<div>{{ $t('proposal.price2') }}: {{ toCurrency(row.price2) }}</div>
|
||||||
|
<div>
|
||||||
|
{{ $t('proposal.itemOldPrice') }}:
|
||||||
|
{{ toCurrency(sales[0]?.price) }}
|
||||||
|
</div>
|
||||||
|
</QTooltip>
|
||||||
|
<VnStockValueDisplay :format="'currency'" :value="-row.price2 / 100" />
|
||||||
|
<!-- Use class binding for text color -->
|
||||||
|
<span :class="[priceStatusClass(row.price2)]">{{
|
||||||
toCurrency(row.price2)
|
toCurrency(row.price2)
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -319,12 +393,26 @@ async function handleTicketConfig(data) {
|
||||||
margin-right: 2px;
|
margin-right: 2px;
|
||||||
flex: 2 0 5px;
|
flex: 2 0 5px;
|
||||||
}
|
}
|
||||||
|
.price-alert {
|
||||||
|
color: $negative;
|
||||||
|
&.q-tooltip {
|
||||||
|
background-color: $negative;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-ok {
|
||||||
|
color: inherit;
|
||||||
|
&.q-tooltip {
|
||||||
|
background-color: $positive;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.match {
|
.match {
|
||||||
color: $negative;
|
color: $negative;
|
||||||
}
|
}
|
||||||
.not-match {
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
.proposal-warning {
|
.proposal-warning {
|
||||||
background-color: $warning;
|
background-color: $warning;
|
||||||
}
|
}
|
||||||
|
@ -344,3 +432,9 @@ async function handleTicketConfig(data) {
|
||||||
font-size: smaller;
|
font-size: smaller;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<i18n>
|
||||||
|
en:
|
||||||
|
notAvailable: 'Not available for replacement'
|
||||||
|
es:
|
||||||
|
notAvailable: 'No disponible para reemplazo'
|
||||||
|
</i18n>
|
||||||
|
|
|
@ -23,33 +23,32 @@ const $props = defineProps({
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const { dialogRef } = useDialogPluginComponent();
|
|
||||||
|
const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
|
||||||
|
useDialogPluginComponent();
|
||||||
const emit = defineEmits([
|
const emit = defineEmits([
|
||||||
'onDialogClosed',
|
'onDialogClosed',
|
||||||
|
'onDialogOk',
|
||||||
'itemReplaced',
|
'itemReplaced',
|
||||||
...useDialogPluginComponent.emits,
|
...useDialogPluginComponent.emits,
|
||||||
]);
|
]);
|
||||||
defineExpose({ show: () => dialogRef.value.show(), hide: () => dialogRef.value.hide() });
|
defineExpose({ show: () => dialogRef.value.show(), hide: () => dialogRef.value.hide() });
|
||||||
|
const itemReplaced = (data) => {
|
||||||
|
onDialogOK(data);
|
||||||
|
dialogRef.value.hide();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<QDialog ref="dialogRef" transition-show="scale" transition-hide="scale">
|
<QDialog ref="dialogRef" transition-show="scale" transition-hide="scale">
|
||||||
<QCard class="dialog-width">
|
<QCard class="dialog-width">
|
||||||
<QCardSection class="row items-center q-pb-none">
|
<QCardSection class="row items-center q-pb-none">
|
||||||
<span class="text-h6 text-grey">{{ $t('itemProposal') }}</span>
|
<span class="text-h6 text-grey" v-text="$t('itemProposal')" />
|
||||||
<QSpace />
|
<QSpace />
|
||||||
<QBtn icon="close" flat round dense v-close-popup />
|
<QBtn icon="close" flat round dense v-close-popup />
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
<QCardSection>
|
<QCardSection>
|
||||||
<ItemProposal
|
<ItemProposal v-bind="$props" @item-replaced="itemReplaced"
|
||||||
v-bind="$props"
|
/></QCardSection>
|
||||||
@item-replaced="
|
|
||||||
(data) => {
|
|
||||||
emit('itemReplaced', data);
|
|
||||||
dialogRef.hide();
|
|
||||||
}
|
|
||||||
"
|
|
||||||
></ItemProposal
|
|
||||||
></QCardSection>
|
|
||||||
</QCard>
|
</QCard>
|
||||||
</QDialog>
|
</QDialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -231,6 +231,11 @@ proposal:
|
||||||
value6: value6
|
value6: value6
|
||||||
value7: value7
|
value7: value7
|
||||||
value8: value8
|
value8: value8
|
||||||
|
tag5: Tag5
|
||||||
|
tag6: Tag6
|
||||||
|
tag7: Tag7
|
||||||
|
tag8: Tag8
|
||||||
|
advanceable: Advanceable
|
||||||
available: Available
|
available: Available
|
||||||
minQuantity: minQuantity
|
minQuantity: minQuantity
|
||||||
price2: Price
|
price2: Price
|
||||||
|
|
|
@ -237,11 +237,16 @@ proposal:
|
||||||
value6: value6
|
value6: value6
|
||||||
value7: value7
|
value7: value7
|
||||||
value8: value8
|
value8: value8
|
||||||
|
tag5: Tag5
|
||||||
|
tag6: Tag6
|
||||||
|
tag7: Tag7
|
||||||
|
tag8: Tag8
|
||||||
available: Disponible
|
available: Disponible
|
||||||
minQuantity: Min. cantidad
|
minQuantity: Min. cantidad
|
||||||
price2: Precio
|
price2: Precio
|
||||||
located: Ubicado
|
located: Ubicado
|
||||||
counter: Contador
|
counter: Contador
|
||||||
|
advanceable: Adelantable
|
||||||
difference: Diferencial
|
difference: Diferencial
|
||||||
groupingPrice: Precio Grouping
|
groupingPrice: Precio Grouping
|
||||||
itemOldPrice: Precio itemOld
|
itemOldPrice: Precio itemOld
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
export default async function (data, date) {
|
export default async function (data, landed) {
|
||||||
const reducedData = data.reduce((acc, item) => {
|
const reducedData = data.reduce((acc, item) => {
|
||||||
const existing = acc.find(({ ticketFk }) => ticketFk === item.id);
|
const existing = acc.find(({ ticketFk }) => ticketFk === item.id);
|
||||||
if (existing) {
|
if (existing) {
|
||||||
existing.sales.push(item.saleFk);
|
existing.sales.push(item.saleFk);
|
||||||
} else {
|
} else {
|
||||||
acc.push({ ticketFk: item.ticketFk, sales: [item.saleFk], date });
|
acc.push({ ticketFk: item.ticketFk, sales: [item.saleFk], landed });
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
|
@ -17,7 +17,6 @@ import ItemProposalProxy from 'src/pages/Item/components/ItemProposalProxy.vue';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
const quasar = useQuasar();
|
const quasar = useQuasar();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const editableStates = ref([]);
|
|
||||||
const stateStore = useStateStore();
|
const stateStore = useStateStore();
|
||||||
const tableRef = ref();
|
const tableRef = ref();
|
||||||
const changeItemDialogRef = ref(null);
|
const changeItemDialogRef = ref(null);
|
||||||
|
@ -70,14 +69,11 @@ const showItemProposal = () => {
|
||||||
})
|
})
|
||||||
.onOk(itemProposalEvt);
|
.onOk(itemProposalEvt);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isButtonDisabled = computed(() => selectedRows.value.length !== 1);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<FetchData
|
|
||||||
url="States/editableStates"
|
|
||||||
@on-fetch="(data) => (editableStates = data)"
|
|
||||||
auto-load
|
|
||||||
/>
|
|
||||||
<FetchData
|
<FetchData
|
||||||
:url="`Items/${entityId}/getCard`"
|
:url="`Items/${entityId}/getCard`"
|
||||||
:fields="['longName']"
|
:fields="['longName']"
|
||||||
|
@ -99,11 +95,7 @@ const showItemProposal = () => {
|
||||||
>
|
>
|
||||||
<template #top-right>
|
<template #top-right>
|
||||||
<QBtnGroup push class="q-mr-lg" style="column-gap: 1px">
|
<QBtnGroup push class="q-mr-lg" style="column-gap: 1px">
|
||||||
<QBtn
|
<QBtn data-cy="transferLines" color="primary" :disable="isButtonDisabled">
|
||||||
data-cy="transferLines"
|
|
||||||
color="primary"
|
|
||||||
:disable="!(selectedRows.length === 1)"
|
|
||||||
>
|
|
||||||
<template #default>
|
<template #default>
|
||||||
<QIcon name="vn:splitline" />
|
<QIcon name="vn:splitline" />
|
||||||
<QIcon name="vn:ticket" />
|
<QIcon name="vn:ticket" />
|
||||||
|
@ -124,7 +116,7 @@ const showItemProposal = () => {
|
||||||
<QBtn
|
<QBtn
|
||||||
color="primary"
|
color="primary"
|
||||||
@click="showItemProposal"
|
@click="showItemProposal"
|
||||||
:disable="!(selectedRows.length === 1)"
|
:disable="isButtonDisabled"
|
||||||
data-cy="itemProposal"
|
data-cy="itemProposal"
|
||||||
>
|
>
|
||||||
<QIcon name="import_export" class="rotate-90" />
|
<QIcon name="import_export" class="rotate-90" />
|
||||||
|
@ -135,7 +127,7 @@ const showItemProposal = () => {
|
||||||
<VnPopupProxy
|
<VnPopupProxy
|
||||||
data-cy="changeItem"
|
data-cy="changeItem"
|
||||||
icon="sync"
|
icon="sync"
|
||||||
:disable="!(selectedRows.length === 1)"
|
:disable="isButtonDisabled"
|
||||||
:tooltip="t('negative.detail.modal.changeItem.title')"
|
:tooltip="t('negative.detail.modal.changeItem.title')"
|
||||||
>
|
>
|
||||||
<template #extraIcon> <QIcon name="vn:item" /> </template>
|
<template #extraIcon> <QIcon name="vn:item" /> </template>
|
||||||
|
@ -149,7 +141,7 @@ const showItemProposal = () => {
|
||||||
<VnPopupProxy
|
<VnPopupProxy
|
||||||
data-cy="changeState"
|
data-cy="changeState"
|
||||||
icon="sync"
|
icon="sync"
|
||||||
:disable="!(selectedRows.length === 1)"
|
:disable="isButtonDisabled"
|
||||||
:tooltip="t('negative.detail.modal.changeState.title')"
|
:tooltip="t('negative.detail.modal.changeState.title')"
|
||||||
>
|
>
|
||||||
<template #extraIcon> <QIcon name="vn:eye" /> </template>
|
<template #extraIcon> <QIcon name="vn:eye" /> </template>
|
||||||
|
@ -163,7 +155,7 @@ const showItemProposal = () => {
|
||||||
<VnPopupProxy
|
<VnPopupProxy
|
||||||
data-cy="changeQuantity"
|
data-cy="changeQuantity"
|
||||||
icon="sync"
|
icon="sync"
|
||||||
:disable="!(selectedRows.length === 1)"
|
:disable="isButtonDisabled"
|
||||||
:tooltip="t('negative.detail.modal.changeQuantity.title')"
|
:tooltip="t('negative.detail.modal.changeQuantity.title')"
|
||||||
@click="showChangeQuantityDialog = true"
|
@click="showChangeQuantityDialog = true"
|
||||||
>
|
>
|
||||||
|
|
|
@ -7,6 +7,8 @@ import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
||||||
import VnInput from 'src/components/common/VnInput.vue';
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
import VnInputDateTime from 'src/components/common/VnInputDateTime.vue';
|
import VnInputDateTime from 'src/components/common/VnInputDateTime.vue';
|
||||||
|
import VnInputDates from 'src/components/common/VnInputDates.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
dataKey: {
|
dataKey: {
|
||||||
|
@ -73,8 +75,8 @@ const setUserParams = (params) => {
|
||||||
<VnFilterPanel
|
<VnFilterPanel
|
||||||
:data-key="props.dataKey"
|
:data-key="props.dataKey"
|
||||||
:search-button="true"
|
:search-button="true"
|
||||||
|
:hidden-tags="['excludedDates']"
|
||||||
@set-user-params="setUserParams"
|
@set-user-params="setUserParams"
|
||||||
:unremovable-params="['warehouseFk']"
|
|
||||||
>
|
>
|
||||||
<template #tags="{ tag, formatFn }">
|
<template #tags="{ tag, formatFn }">
|
||||||
<div class="q-gutter-x-xs">
|
<div class="q-gutter-x-xs">
|
||||||
|
@ -92,7 +94,7 @@ const setUserParams = (params) => {
|
||||||
dense
|
dense
|
||||||
filled
|
filled
|
||||||
@update:model-value="
|
@update:model-value="
|
||||||
(value) => {
|
() => {
|
||||||
setUserParams(params);
|
setUserParams(params);
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
|
@ -127,8 +129,19 @@ const setUserParams = (params) => {
|
||||||
dense
|
dense
|
||||||
filled
|
filled
|
||||||
/>
|
/>
|
||||||
</QItemSection> </QItem
|
</QItemSection>
|
||||||
><QItem>
|
</QItem>
|
||||||
|
<QItem>
|
||||||
|
<QItemSection>
|
||||||
|
<VnInputDates
|
||||||
|
v-model="params.excludedDates"
|
||||||
|
filled
|
||||||
|
:label="t('negative.excludedDates')"
|
||||||
|
>
|
||||||
|
</VnInputDates>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem>
|
||||||
<QItemSection v-if="categoriesOptions">
|
<QItemSection v-if="categoriesOptions">
|
||||||
<VnSelect
|
<VnSelect
|
||||||
:label="t('negative.categoryFk')"
|
:label="t('negative.categoryFk')"
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { onBeforeMount } from 'vue';
|
||||||
import { dashIfEmpty, toDate, toHour } from 'src/filters';
|
import { dashIfEmpty, toDate, toHour } from 'src/filters';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { useState } from 'src/composables/useState';
|
import { useState } from 'src/composables/useState';
|
||||||
|
import EntryDescriptorProxy from 'src/pages/Entry/Card/EntryDescriptorProxy.vue';
|
||||||
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
|
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
|
||||||
import RightMenu from 'src/components/common/RightMenu.vue';
|
import RightMenu from 'src/components/common/RightMenu.vue';
|
||||||
import TicketLackFilter from './TicketLackFilter.vue';
|
import TicketLackFilter from './TicketLackFilter.vue';
|
||||||
|
@ -45,10 +46,10 @@ const columns = computed(() => [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
columnClass: 'shrink',
|
columnClass: 'shrink',
|
||||||
name: 'timed',
|
name: 'minTimed',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
label: t('negative.timed'),
|
label: t('negative.timed'),
|
||||||
format: ({ timed }) => toHour(timed),
|
format: ({ minTimed }) => toHour(minTimed),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
cardVisible: true,
|
cardVisible: true,
|
||||||
columnFilter: {
|
columnFilter: {
|
||||||
|
@ -64,9 +65,25 @@ const columns = computed(() => [
|
||||||
columnFilter: {
|
columnFilter: {
|
||||||
component: 'input',
|
component: 'input',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
inWhere: false,
|
columnClass: 'shrink',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'nextEntryFk',
|
||||||
|
align: 'center',
|
||||||
|
label: t('negative.nextEntryFk'),
|
||||||
|
format: ({ nextEntryFk }) => nextEntryFk,
|
||||||
|
sortable: false,
|
||||||
|
columnFilter: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'nextEntryLanded',
|
||||||
|
align: 'center',
|
||||||
|
label: t('negative.nextEntryLanded'),
|
||||||
|
format: ({ nextEntryLanded }) => toDate(nextEntryLanded),
|
||||||
|
sortable: false,
|
||||||
|
columnFilter: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'longName',
|
name: 'longName',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
@ -195,6 +212,12 @@ const setUserParams = (params) => {
|
||||||
<span @click.stop>{{ row.itemFk }}</span>
|
<span @click.stop>{{ row.itemFk }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<template #column-nextEntryFk="{ row }">
|
||||||
|
<span class="link" @click.stop>
|
||||||
|
{{ row.nextEntryFk }}
|
||||||
|
<EntryDescriptorProxy :id="row.nextEntryFk" />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
<template #column-longName="{ row }">
|
<template #column-longName="{ row }">
|
||||||
<span class="link" @click.stop>
|
<span class="link" @click.stop>
|
||||||
{{ row.longName }}
|
{{ row.longName }}
|
||||||
|
|
|
@ -35,6 +35,7 @@ const filterLack = ref({
|
||||||
order: 'ts.alertLevelCode ASC',
|
order: 'ts.alertLevelCode ASC',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const editableStates = ref([]);
|
||||||
const selectedRows = ref([]);
|
const selectedRows = ref([]);
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { notify } = useNotify();
|
const { notify } = useNotify();
|
||||||
|
@ -135,9 +136,12 @@ const saveChange = async (field, { row }) => {
|
||||||
try {
|
try {
|
||||||
switch (field) {
|
switch (field) {
|
||||||
case 'alertLevelCode':
|
case 'alertLevelCode':
|
||||||
|
const { id: code } = editableStates.value.find(
|
||||||
|
({ name }) => name === row.code,
|
||||||
|
);
|
||||||
await axios.post(`Tickets/state`, {
|
await axios.post(`Tickets/state`, {
|
||||||
ticketFk: row.ticketFk,
|
ticketFk: row.ticketFk,
|
||||||
code: row[field],
|
code,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -160,6 +164,11 @@ function onBuysFetched(data) {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="States/editableStates"
|
||||||
|
@on-fetch="(data) => (editableStates = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
<FetchData
|
<FetchData
|
||||||
ref="fetchItemLack"
|
ref="fetchItemLack"
|
||||||
:url="`Tickets/itemLack`"
|
:url="`Tickets/itemLack`"
|
||||||
|
@ -309,12 +318,12 @@ function onBuysFetched(data) {
|
||||||
</template>
|
</template>
|
||||||
<template #column-alertLevelCode="props">
|
<template #column-alertLevelCode="props">
|
||||||
<VnSelect
|
<VnSelect
|
||||||
url="States/editableStates"
|
:options="editableStates"
|
||||||
auto-load
|
auto-load
|
||||||
hide-selected
|
hide-selected
|
||||||
option-value="id"
|
option-value="name"
|
||||||
option-label="name"
|
option-label="name"
|
||||||
v-model="props.row.alertLevelCode"
|
v-model="props.row.code"
|
||||||
v-on="getInputEvents(props)"
|
v-on="getInputEvents(props)"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -19,18 +19,18 @@ const $props = defineProps({
|
||||||
const updateItem = async () => {
|
const updateItem = async () => {
|
||||||
try {
|
try {
|
||||||
showChangeItemDialog.value = true;
|
showChangeItemDialog.value = true;
|
||||||
const rowsToUpdate = $props.selectedRows.map(({ saleFk, quantity }) =>
|
const rowsToUpdate = $props.selectedRows.map(({ saleFk, ticketFk, quantity }) =>
|
||||||
axios.post(`Sales/replaceItem`, {
|
axios.post(`Sales/replaceItem`, {
|
||||||
saleFk,
|
saleFk,
|
||||||
|
ticketFk,
|
||||||
substitutionFk: newItem.value,
|
substitutionFk: newItem.value,
|
||||||
quantity,
|
quantity,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
const result = await Promise.allSettled(rowsToUpdate);
|
const result = await Promise.allSettled(rowsToUpdate);
|
||||||
notifyResults(result, 'saleFk');
|
notifyResults(result, 'ticketFk');
|
||||||
emit('update-item', newItem.value);
|
emit('update-item', newItem.value);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error updating item:', err);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -41,6 +41,7 @@ const updateItem = async () => {
|
||||||
<QCardSection class="row items-center justify-center column items-stretch">
|
<QCardSection class="row items-center justify-center column items-stretch">
|
||||||
<span>{{ $t('negative.detail.modal.changeItem.title') }}</span>
|
<span>{{ $t('negative.detail.modal.changeItem.title') }}</span>
|
||||||
<VnSelect
|
<VnSelect
|
||||||
|
data-cy="New item_select"
|
||||||
url="Items/WithName"
|
url="Items/WithName"
|
||||||
:fields="['id', 'name']"
|
:fields="['id', 'name']"
|
||||||
:sort-by="['id DESC']"
|
:sort-by="['id DESC']"
|
||||||
|
|
|
@ -19,9 +19,9 @@ const $props = defineProps({
|
||||||
const updateState = async () => {
|
const updateState = async () => {
|
||||||
try {
|
try {
|
||||||
showChangeStateDialog.value = true;
|
showChangeStateDialog.value = true;
|
||||||
const rowsToUpdate = $props.selectedRows.map(({ id }) =>
|
const rowsToUpdate = $props.selectedRows.map(({ ticketFk }) =>
|
||||||
axios.post(`Tickets/state`, {
|
axios.post(`Tickets/state`, {
|
||||||
ticketFk: id,
|
ticketFk,
|
||||||
code: newState.value,
|
code: newState.value,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -49,8 +49,9 @@ const updateState = async () => {
|
||||||
v-model="newState"
|
v-model="newState"
|
||||||
:options="editableStates"
|
:options="editableStates"
|
||||||
option-label="name"
|
option-label="name"
|
||||||
option-value="code"
|
option-value="id"
|
||||||
autofocus
|
autofocus
|
||||||
|
data-cy="New state_select"
|
||||||
/>
|
/>
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
<QCardActions align="right">
|
<QCardActions align="right">
|
||||||
|
|
|
@ -14,8 +14,6 @@ import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
import VnInputDate from 'src/components/common/VnInputDate.vue';
|
import VnInputDate from 'src/components/common/VnInputDate.vue';
|
||||||
import VnRow from 'src/components/ui/VnRow.vue';
|
import VnRow from 'src/components/ui/VnRow.vue';
|
||||||
import TicketFilter from './TicketFilter.vue';
|
import TicketFilter from './TicketFilter.vue';
|
||||||
import VnInput from 'src/components/common/VnInput.vue';
|
|
||||||
import FetchData from 'src/components/FetchData.vue';
|
|
||||||
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
|
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
|
||||||
import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue';
|
import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue';
|
||||||
import ZoneDescriptorProxy from 'src/pages/Zone/Card/ZoneDescriptorProxy.vue';
|
import ZoneDescriptorProxy from 'src/pages/Zone/Card/ZoneDescriptorProxy.vue';
|
||||||
|
@ -25,6 +23,7 @@ import TicketProblems from 'src/components/TicketProblems.vue';
|
||||||
import VnSection from 'src/components/common/VnSection.vue';
|
import VnSection from 'src/components/common/VnSection.vue';
|
||||||
import { getAddresses } from 'src/pages/Customer/composables/getAddresses';
|
import { getAddresses } from 'src/pages/Customer/composables/getAddresses';
|
||||||
import { getAgencies } from 'src/pages/Route/Agency/composables/getAgencies';
|
import { getAgencies } from 'src/pages/Route/Agency/composables/getAgencies';
|
||||||
|
import TicketNewPayment from './components/TicketNewPayment.vue';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
@ -73,11 +72,6 @@ const initializeFromQuery = () => {
|
||||||
|
|
||||||
const selectedRows = ref([]);
|
const selectedRows = ref([]);
|
||||||
const hasSelectedRows = computed(() => selectedRows.value.length > 0);
|
const hasSelectedRows = computed(() => selectedRows.value.length > 0);
|
||||||
const showForm = ref(false);
|
|
||||||
const dialogData = ref();
|
|
||||||
const companiesOptions = ref([]);
|
|
||||||
const accountingOptions = ref([]);
|
|
||||||
const amountToReturn = ref();
|
|
||||||
const dataKey = 'TicketList';
|
const dataKey = 'TicketList';
|
||||||
const formInitialData = ref({});
|
const formInitialData = ref({});
|
||||||
|
|
||||||
|
@ -381,87 +375,18 @@ function openBalanceDialog(ticket) {
|
||||||
description.value.push(ticketData.id);
|
description.value.push(ticketData.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
const balanceCreateDialog = ref({
|
const dialogData = ref({
|
||||||
amountPaid: amountPaid.value,
|
amountPaid: amountPaid.value,
|
||||||
clientFk: clientFk.value,
|
clientFk: clientFk.value,
|
||||||
description: `Albaran: ${description.value.join(', ')}`,
|
description: `Albaran: ${description.value.join(', ')}`,
|
||||||
});
|
});
|
||||||
dialogData.value = balanceCreateDialog;
|
quasar.dialog({
|
||||||
showForm.value = true;
|
component: TicketNewPayment,
|
||||||
}
|
componentProps: {
|
||||||
|
clientId: clientFk.value,
|
||||||
async function onSubmit() {
|
formData: dialogData.value,
|
||||||
const { data: email } = await axios.get('Clients', {
|
|
||||||
params: {
|
|
||||||
filter: JSON.stringify({ where: { id: dialogData.value.value.clientFk } }),
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data } = await axios.post(
|
|
||||||
`Clients/${dialogData.value.value.clientFk}/createReceipt`,
|
|
||||||
{
|
|
||||||
payed: dialogData.value.payed,
|
|
||||||
companyFk: dialogData.value.companyFk,
|
|
||||||
bankFk: dialogData.value.bankFk,
|
|
||||||
amountPaid: dialogData.value.value.amountPaid,
|
|
||||||
description: dialogData.value.value.description,
|
|
||||||
clientFk: dialogData.value.value.clientFk,
|
|
||||||
email: email[0].email,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (data) notify('globals.dataSaved', 'positive');
|
|
||||||
showForm.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const setAmountToReturn = (newAmountGiven) => {
|
|
||||||
const amountPaid = dialogData.value.value.amountPaid;
|
|
||||||
|
|
||||||
amountToReturn.value = newAmountGiven - amountPaid;
|
|
||||||
};
|
|
||||||
|
|
||||||
function setReference(data) {
|
|
||||||
let newDescription = '';
|
|
||||||
|
|
||||||
switch (data) {
|
|
||||||
case 1:
|
|
||||||
newDescription = `${t(
|
|
||||||
'ticketList.creditCard',
|
|
||||||
)}, ${dialogData.value.value.description.replace(
|
|
||||||
/^(Credit Card, |Cash, |Transfers, )/,
|
|
||||||
'',
|
|
||||||
)}`;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
newDescription = `${t(
|
|
||||||
'ticketList.cash',
|
|
||||||
)}, ${dialogData.value.value.description.replace(
|
|
||||||
/^(Credit Card, |Cash, |Transfers, )/,
|
|
||||||
'',
|
|
||||||
)}`;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
newDescription = `${newDescription.replace(
|
|
||||||
/^(Credit Card, |Cash, |Transfers, )/,
|
|
||||||
'',
|
|
||||||
)}`;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
newDescription = `${t(
|
|
||||||
'ticketList.transfers',
|
|
||||||
)}, ${dialogData.value.value.description.replace(
|
|
||||||
/^(Credit Card, |Cash, |Transfers, )/,
|
|
||||||
'',
|
|
||||||
)}`;
|
|
||||||
break;
|
|
||||||
case 3317:
|
|
||||||
newDescription = '';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
dialogData.value.value.description = newDescription;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function exprBuilder(param, value) {
|
function exprBuilder(param, value) {
|
||||||
|
@ -492,16 +417,6 @@ function exprBuilder(param, value) {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<FetchData
|
|
||||||
url="Companies"
|
|
||||||
@on-fetch="(data) => (companiesOptions = data)"
|
|
||||||
auto-load
|
|
||||||
/>
|
|
||||||
<FetchData
|
|
||||||
url="Accountings"
|
|
||||||
@on-fetch="(data) => (accountingOptions = data)"
|
|
||||||
auto-load
|
|
||||||
/>
|
|
||||||
<VnSection
|
<VnSection
|
||||||
:data-key="dataKey"
|
:data-key="dataKey"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
|
@ -742,99 +657,6 @@ function exprBuilder(param, value) {
|
||||||
{{ t('ticketList.accountPayment') }}
|
{{ t('ticketList.accountPayment') }}
|
||||||
</QTooltip>
|
</QTooltip>
|
||||||
</QPageSticky>
|
</QPageSticky>
|
||||||
<QDialog ref="dialogRef" v-model="showForm">
|
|
||||||
<QCard class="q-pa-md q-mb-md">
|
|
||||||
<QForm @submit="onSubmit()" class="q-pa-sm">
|
|
||||||
{{ t('ticketList.addPayment') }}
|
|
||||||
<VnRow>
|
|
||||||
<VnInputDate
|
|
||||||
:label="t('ticketList.date')"
|
|
||||||
v-model="dialogData.payed"
|
|
||||||
/>
|
|
||||||
<VnSelect
|
|
||||||
:label="t('ticketList.company')"
|
|
||||||
v-model="dialogData.companyFk"
|
|
||||||
:options="companiesOptions"
|
|
||||||
option-label="code"
|
|
||||||
hide-selected
|
|
||||||
>
|
|
||||||
</VnSelect>
|
|
||||||
</VnRow>
|
|
||||||
<VnRow>
|
|
||||||
<VnSelect
|
|
||||||
:label="t('ticketList.bank')"
|
|
||||||
v-model="dialogData.bankFk"
|
|
||||||
:options="accountingOptions"
|
|
||||||
option-label="bank"
|
|
||||||
hide-selected
|
|
||||||
@update:model-value="setReference"
|
|
||||||
/>
|
|
||||||
<VnInput
|
|
||||||
:label="t('ticketList.amount')"
|
|
||||||
v-model="dialogData.value.amountPaid"
|
|
||||||
/>
|
|
||||||
</VnRow>
|
|
||||||
<VnRow v-if="dialogData.bankFk === 2">
|
|
||||||
<span>
|
|
||||||
{{ t('ticketList.cash') }}
|
|
||||||
</span>
|
|
||||||
</VnRow>
|
|
||||||
<VnRow v-if="dialogData.bankFk === 2">
|
|
||||||
<VnInput
|
|
||||||
:label="t('ticketList.deliveredAmount')"
|
|
||||||
v-model="dialogData.value.amountGiven"
|
|
||||||
@update:model-value="setAmountToReturn"
|
|
||||||
type="number"
|
|
||||||
/>
|
|
||||||
<VnInput
|
|
||||||
:label="t('ticketList.amountToReturn')"
|
|
||||||
:model-value="amountToReturn"
|
|
||||||
type="number"
|
|
||||||
readonly
|
|
||||||
/>
|
|
||||||
</VnRow>
|
|
||||||
<VnRow v-if="dialogData.bankFk === 3 || dialogData.bankFk === 3117">
|
|
||||||
<VnInput
|
|
||||||
:label="t('ticketList.compensation')"
|
|
||||||
v-model="dialogData.value.compensation"
|
|
||||||
type="text"
|
|
||||||
/>
|
|
||||||
</VnRow>
|
|
||||||
<VnRow>
|
|
||||||
<VnInput
|
|
||||||
:label="t('ticketList.reference')"
|
|
||||||
v-model="dialogData.value.description"
|
|
||||||
type="text"
|
|
||||||
/>
|
|
||||||
</VnRow>
|
|
||||||
<VnRow v-if="dialogData.bankFk === 2">
|
|
||||||
<QCheckbox
|
|
||||||
:label="t('ticketList.viewReceipt')"
|
|
||||||
v-model="dialogData.value.viewReceipt"
|
|
||||||
:toggle-indeterminate="false"
|
|
||||||
/>
|
|
||||||
<QCheckbox
|
|
||||||
:label="t('ticketList.sendEmail')"
|
|
||||||
v-model="dialogData.value.senEmail"
|
|
||||||
:toggle-indeterminate="false"
|
|
||||||
/>
|
|
||||||
</VnRow>
|
|
||||||
<div class="q-mt-lg row justify-end">
|
|
||||||
<QBtn
|
|
||||||
:label="t('globals.save')"
|
|
||||||
color="primary"
|
|
||||||
@click="onSubmit()"
|
|
||||||
/>
|
|
||||||
<QBtn
|
|
||||||
flat
|
|
||||||
:label="t('globals.close')"
|
|
||||||
color="primary"
|
|
||||||
v-close-popup
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</QForm>
|
|
||||||
</QCard>
|
|
||||||
</QDialog>
|
|
||||||
<QPageSticky v-if="hasSelectedRows" :offset="[20, 200]" style="z-index: 2">
|
<QPageSticky v-if="hasSelectedRows" :offset="[20, 200]" style="z-index: 2">
|
||||||
<QBtn
|
<QBtn
|
||||||
@click="sendDocuware(selectedRows)"
|
@click="sendDocuware(selectedRows)"
|
||||||
|
|
|
@ -0,0 +1,304 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useDialogPluginComponent } from 'quasar';
|
||||||
|
|
||||||
|
import { usePrintService } from 'src/composables/usePrintService';
|
||||||
|
import useNotify from 'src/composables/useNotify.js';
|
||||||
|
|
||||||
|
import FormModelPopup from 'src/components/FormModelPopup.vue';
|
||||||
|
import VnRow from 'src/components/ui/VnRow.vue';
|
||||||
|
import VnInputDate from 'src/components/common/VnInputDate.vue';
|
||||||
|
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
|
||||||
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import VnAccountNumber from 'src/components/common/VnAccountNumber.vue';
|
||||||
|
import { useState } from 'src/composables/useState';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { notify } = useNotify();
|
||||||
|
const { sendEmail, openReport } = usePrintService();
|
||||||
|
const { dialogRef } = useDialogPluginComponent();
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
formData: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
clientId: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
promise: {
|
||||||
|
type: Function,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const closeButton = ref(null);
|
||||||
|
const viewReceipt = ref();
|
||||||
|
const shouldSendEmail = ref(false);
|
||||||
|
const maxAmount = ref();
|
||||||
|
const accountingType = ref({});
|
||||||
|
const isCash = ref(false);
|
||||||
|
const formModelRef = ref(false);
|
||||||
|
const amountToReturn = ref();
|
||||||
|
const filterBanks = {
|
||||||
|
fields: ['id', 'bank', 'accountingTypeFk'],
|
||||||
|
include: { relation: 'accountingType' },
|
||||||
|
order: 'id',
|
||||||
|
};
|
||||||
|
|
||||||
|
const state = useState();
|
||||||
|
const user = state.getUser();
|
||||||
|
const initialData = ref({
|
||||||
|
...$props.formData,
|
||||||
|
companyFk: user.value.companyFk,
|
||||||
|
payed: Date.vnNew(),
|
||||||
|
});
|
||||||
|
|
||||||
|
function setPaymentType(data, accounting) {
|
||||||
|
data.bankFk = accounting.id;
|
||||||
|
if (!accounting) return;
|
||||||
|
accountingType.value = accounting.accountingType;
|
||||||
|
data.description = [];
|
||||||
|
data.payed = Date.vnNew();
|
||||||
|
isCash.value = accountingType.value.code == 'cash';
|
||||||
|
viewReceipt.value = isCash.value;
|
||||||
|
if (accountingType.value.daysInFuture)
|
||||||
|
data.payed.setDate(data.payed.getDate() + accountingType.value.daysInFuture);
|
||||||
|
maxAmount.value = accountingType.value && accountingType.value.maxAmount;
|
||||||
|
if (accountingType.value.code == 'compensation') return (data.description = '');
|
||||||
|
|
||||||
|
let descriptions = [];
|
||||||
|
if (accountingType.value.receiptDescription)
|
||||||
|
descriptions.push(accountingType.value.receiptDescription);
|
||||||
|
if (data.description > 0) descriptions.push(data.description);
|
||||||
|
data.description = descriptions.join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
|
const calculateFromAmount = (event) => {
|
||||||
|
initialData.value.amountToReturn = Number(
|
||||||
|
(parseFloat(initialData.value.deliveredAmount) + parseFloat(event) * -1).toFixed(
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const calculateFromDeliveredAmount = (event) => {
|
||||||
|
amountToReturn.value = Number((event - initialData.value.amountPaid).toFixed(2));
|
||||||
|
};
|
||||||
|
|
||||||
|
function onBeforeSave(data) {
|
||||||
|
const exceededAmount = data.amountPaid > maxAmount.value;
|
||||||
|
if (isCash.value && exceededAmount)
|
||||||
|
return notify(t('Amount exceeded', { maxAmount: maxAmount.value }), 'negative');
|
||||||
|
|
||||||
|
if (isCash.value && shouldSendEmail.value && !data.email)
|
||||||
|
return notify(t('There is no assigned email for this client'), 'negative');
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onDataSaved({ email, id }) {
|
||||||
|
try {
|
||||||
|
if (shouldSendEmail.value && isCash.value)
|
||||||
|
await sendEmail(`Receipts/${id}/receipt-email`, {
|
||||||
|
recipient: email,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (viewReceipt.value) openReport(`Receipts/${id}/receipt-pdf`, {}, '_blank');
|
||||||
|
} finally {
|
||||||
|
if ($props.promise) $props.promise();
|
||||||
|
if (closeButton.value) closeButton.value.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getSupplierClientReferences(data) {
|
||||||
|
if (!data) return (initialData.value.description = '');
|
||||||
|
const params = { bankAccount: data.compensationAccount };
|
||||||
|
const { data: reference } = await axios(`Clients/getClientOrSupplierReference`, {
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
if (reference.supplierId) {
|
||||||
|
data.description = t('Supplier Compensation Reference', {
|
||||||
|
supplierId: reference.supplierId,
|
||||||
|
supplierName: reference.supplierName,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data.description = t('Client Compensation Reference', {
|
||||||
|
clientId: reference.clientId,
|
||||||
|
clientName: reference.clientName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getAmountPaid() {
|
||||||
|
const filter = {
|
||||||
|
where: {
|
||||||
|
clientFk: $props.clientId,
|
||||||
|
companyFk: initialData.value.companyFk,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const { data } = await getClientRisk(filter);
|
||||||
|
initialData.value.amountPaid = (data?.length && data[0].amount) || undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onSubmit(formData) {
|
||||||
|
const clientFk = $props.clientId;
|
||||||
|
const {
|
||||||
|
data: [{ email }],
|
||||||
|
} = await axios.get('Clients', {
|
||||||
|
params: {
|
||||||
|
filter: JSON.stringify({ where: { id: clientFk } }),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data } = await axios.post(`Clients/${clientFk}/createReceipt`, {
|
||||||
|
payed: formData.payed,
|
||||||
|
companyFk: formData.companyFk,
|
||||||
|
bankFk: formData.bankFk,
|
||||||
|
amountPaid: formData.amountPaid,
|
||||||
|
description: formData.description,
|
||||||
|
clientFk,
|
||||||
|
email,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data) notify('globals.dataSaved', 'positive');
|
||||||
|
await onDataSaved(data);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<QDialog ref="dialogRef" persistent>
|
||||||
|
<FormModelPopup
|
||||||
|
ref="formModelRef"
|
||||||
|
:form-initial-data="initialData"
|
||||||
|
:save-fn="onSubmit"
|
||||||
|
:prevent-submit="true"
|
||||||
|
:mapper="onBeforeSave"
|
||||||
|
>
|
||||||
|
<template #form-inputs="{ data, validate }">
|
||||||
|
<h5 class="q-mt-none">{{ t('New payment') }}</h5>
|
||||||
|
<VnRow>
|
||||||
|
<VnSelect
|
||||||
|
autofocus
|
||||||
|
:label="t('Bank')"
|
||||||
|
v-model="data.bankFk"
|
||||||
|
url="Accountings"
|
||||||
|
:filter="filterBanks"
|
||||||
|
option-label="bank"
|
||||||
|
:include="{ relation: 'accountingType' }"
|
||||||
|
sort-by="id"
|
||||||
|
@update:model-value="
|
||||||
|
(value, options) => setPaymentType(data, value, options)
|
||||||
|
"
|
||||||
|
:emit-value="false"
|
||||||
|
data-cy="paymentBank"
|
||||||
|
>
|
||||||
|
<template #option="scope">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>
|
||||||
|
{{ scope.opt.id }}: {{ scope.opt.bank }}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelect>
|
||||||
|
<VnInputNumber
|
||||||
|
:label="t('Amount')"
|
||||||
|
:required="true"
|
||||||
|
@update:model-value="calculateFromAmount($event)"
|
||||||
|
clearable
|
||||||
|
v-model.number="data.amountPaid"
|
||||||
|
data-cy="paymentAmount"
|
||||||
|
:positive="false"
|
||||||
|
/>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow>
|
||||||
|
<VnInputDate
|
||||||
|
:label="t('Date')"
|
||||||
|
v-model="data.payed"
|
||||||
|
:required="true"
|
||||||
|
/>
|
||||||
|
<VnSelect
|
||||||
|
url="Companies"
|
||||||
|
:label="t('Company')"
|
||||||
|
:required="true"
|
||||||
|
:rules="validate('entry.companyFk')"
|
||||||
|
hide-selected
|
||||||
|
option-label="code"
|
||||||
|
v-model="data.companyFk"
|
||||||
|
@update:model-value="getAmountPaid()"
|
||||||
|
/>
|
||||||
|
</VnRow>
|
||||||
|
<div v-if="accountingType.code == 'compensation'">
|
||||||
|
<div class="text-h6">
|
||||||
|
{{ t('Compensation') }}
|
||||||
|
</div>
|
||||||
|
<VnRow>
|
||||||
|
<VnAccountNumber
|
||||||
|
:label="t('Compensation account')"
|
||||||
|
clearable
|
||||||
|
v-model="data.compensationAccount"
|
||||||
|
@blur="getSupplierClientReferences(data)"
|
||||||
|
/>
|
||||||
|
</VnRow>
|
||||||
|
</div>
|
||||||
|
<VnInput
|
||||||
|
:label="t('Reference')"
|
||||||
|
:required="true"
|
||||||
|
clearable
|
||||||
|
v-model="data.description"
|
||||||
|
/>
|
||||||
|
<div v-if="accountingType.code == 'cash'">
|
||||||
|
<div class="text-h6">{{ t('Cash') }}</div>
|
||||||
|
<VnRow>
|
||||||
|
<VnInputNumber
|
||||||
|
:label="t('Delivered amount')"
|
||||||
|
@update:model-value="calculateFromDeliveredAmount($event)"
|
||||||
|
clearable
|
||||||
|
v-model="data.deliveredAmount"
|
||||||
|
/>
|
||||||
|
<VnInputNumber
|
||||||
|
:label="t('Amount to return')"
|
||||||
|
disable
|
||||||
|
v-model="amountToReturn"
|
||||||
|
/>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow>
|
||||||
|
<QCheckbox v-model="viewReceipt" :label="t('View recipt')" />
|
||||||
|
<QCheckbox v-model="shouldSendEmail" :label="t('Send email')" />
|
||||||
|
</VnRow>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</FormModelPopup>
|
||||||
|
</QDialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
en:
|
||||||
|
Supplier Compensation Reference: ({supplierId}) Ntro Proveedor {supplierName}
|
||||||
|
Client Compensation Reference: ({clientId}) Ntro Cliente {clientName}
|
||||||
|
es:
|
||||||
|
New payment: Añadir pago
|
||||||
|
Date: Fecha
|
||||||
|
Company: Empresa
|
||||||
|
Bank: Caja
|
||||||
|
Amount: Importe
|
||||||
|
Reference: Referencia
|
||||||
|
Cash: Efectivo
|
||||||
|
Delivered amount: Cantidad entregada
|
||||||
|
Amount to return: Cantidad a devolver
|
||||||
|
View recipt: Ver recibido
|
||||||
|
Send email: Enviar correo
|
||||||
|
Compensation: Compensación
|
||||||
|
Compensation account: Cuenta para compensar
|
||||||
|
Supplier Compensation Reference: ({supplierId}) Ntro Proveedor {supplierName}
|
||||||
|
Client Compensation Reference: ({clientId}) Ntro Cliente {clientName}
|
||||||
|
There is no assigned email for this client: No hay correo asignado para este cliente
|
||||||
|
Amount exceeded: Según ley contra el fraude no se puede recibir cobros por importe igual o superior a {maxAmount}
|
||||||
|
</i18n>
|
|
@ -206,7 +206,6 @@ ticketList:
|
||||||
toLines: Go to lines
|
toLines: Go to lines
|
||||||
addressNickname: Address nickname
|
addressNickname: Address nickname
|
||||||
ref: Reference
|
ref: Reference
|
||||||
hour: Hour
|
|
||||||
rounding: Rounding
|
rounding: Rounding
|
||||||
noVerifiedData: No verified data
|
noVerifiedData: No verified data
|
||||||
warehouse: Warehouse
|
warehouse: Warehouse
|
||||||
|
@ -215,6 +214,8 @@ ticketList:
|
||||||
clientFrozen: Client frozen
|
clientFrozen: Client frozen
|
||||||
componentLack: Component lack
|
componentLack: Component lack
|
||||||
negative:
|
negative:
|
||||||
|
nextEntryFk: Next entry
|
||||||
|
nextEntryLanded: Next entry landed
|
||||||
hour: Hour
|
hour: Hour
|
||||||
id: Id Article
|
id: Id Article
|
||||||
longName: Article
|
longName: Article
|
||||||
|
@ -225,6 +226,7 @@ negative:
|
||||||
value: Negative
|
value: Negative
|
||||||
itemFk: Article
|
itemFk: Article
|
||||||
producer: Producer
|
producer: Producer
|
||||||
|
excludedDates: Excluded dates
|
||||||
warehouse: Warehouse
|
warehouse: Warehouse
|
||||||
warehouseFk: Warehouse
|
warehouseFk: Warehouse
|
||||||
category: Category
|
category: Category
|
||||||
|
|
|
@ -215,6 +215,8 @@ ticketList:
|
||||||
addressNickname: Alias consignatario
|
addressNickname: Alias consignatario
|
||||||
ref: Referencia
|
ref: Referencia
|
||||||
negative:
|
negative:
|
||||||
|
nextEntryLanded: F. Entrada
|
||||||
|
nextEntryFk: Entrada
|
||||||
hour: Hora
|
hour: Hora
|
||||||
id: Id Articulo
|
id: Id Articulo
|
||||||
longName: Artículo
|
longName: Artículo
|
||||||
|
@ -225,7 +227,8 @@ negative:
|
||||||
origen: Origen
|
origen: Origen
|
||||||
value: Negativo
|
value: Negativo
|
||||||
warehouseFk: Almacen
|
warehouseFk: Almacen
|
||||||
producer: Producer
|
producer: Productor
|
||||||
|
excludedDates: Fechas excluidas
|
||||||
category: Categoría
|
category: Categoría
|
||||||
categoryFk: Familia
|
categoryFk: Familia
|
||||||
typeFk: Familia
|
typeFk: Familia
|
||||||
|
|
|
@ -45,7 +45,7 @@ describe('OrderCatalog', { testIsolation: true }, () => {
|
||||||
).type('{enter}');
|
).type('{enter}');
|
||||||
cy.get(':nth-child(1) > [data-cy="catalogFilterCategory"]').click();
|
cy.get(':nth-child(1) > [data-cy="catalogFilterCategory"]').click();
|
||||||
cy.dataCy('catalogFilterValueDialogBtn').last().click();
|
cy.dataCy('catalogFilterValueDialogBtn').last().click();
|
||||||
cy.selectOption("[data-cy='catalogFilterValueDialogTagSelect']", 'Tallos');
|
cy.selectOption('[data-cy="catalogFilterValueDialogTagSelect"]', 'Tallos');
|
||||||
cy.dataCy('catalogFilterValueDialogValueInput').find('input').focus();
|
cy.dataCy('catalogFilterValueDialogValueInput').find('input').focus();
|
||||||
cy.dataCy('catalogFilterValueDialogValueInput').find('input').type('2');
|
cy.dataCy('catalogFilterValueDialogValueInput').find('input').type('2');
|
||||||
cy.dataCy('catalogFilterValueDialogValueInput').find('input').type('{enter}');
|
cy.dataCy('catalogFilterValueDialogValueInput').find('input').type('{enter}');
|
||||||
|
|
|
@ -1,146 +1,161 @@
|
||||||
/// <reference types="cypress" />
|
/// <reference types="cypress" />
|
||||||
describe.skip('Ticket Lack detail', () => {
|
const firstRow = 'tr.cursor-pointer > :nth-child(1)';
|
||||||
|
const ticketId = 1000000;
|
||||||
|
const clickNotificationAction = () => {
|
||||||
|
const notification = '.q-notification';
|
||||||
|
cy.waitForElement(notification);
|
||||||
|
cy.get(notification).should('be.visible');
|
||||||
|
cy.get('.q-notification__actions > .q-btn').click();
|
||||||
|
cy.get('@open').should((openStub) => {
|
||||||
|
expect(openStub).to.be.called;
|
||||||
|
const firstArg = openStub.args[0][0];
|
||||||
|
expect(firstArg).to.match(/\/ticket\/\d+\/sale/);
|
||||||
|
expect(firstArg).to.include(`/ticket/${ticketId}/sale`);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
describe('Ticket Lack detail', { testIsolation: true }, () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
cy.viewport(1980, 1020);
|
||||||
cy.login('developer');
|
cy.login('developer');
|
||||||
cy.intercept('GET', /\/api\/Tickets\/itemLack\/5.*$/, {
|
cy.intercept('GET', /\/api\/Tickets\/itemLack\/88.*$/).as('getItemLack');
|
||||||
statusCode: 200,
|
cy.visit('/#/ticket/negative/88');
|
||||||
body: [
|
cy.window().then((win) => {
|
||||||
{
|
cy.stub(win, 'open').as('open');
|
||||||
saleFk: 33,
|
});
|
||||||
code: 'OK',
|
cy.wait('@getItemLack').then((interception) => {
|
||||||
ticketFk: 142,
|
const { query } = interception.request;
|
||||||
nickname: 'Malibu Point',
|
const filter = JSON.parse(query.filter);
|
||||||
shipped: '2000-12-31T23:00:00.000Z',
|
expect(filter).to.have.property('where');
|
||||||
hour: 0,
|
expect(filter.where).to.have.property('alertLevelCode', 'FREE');
|
||||||
quantity: 50,
|
});
|
||||||
agName: 'Super-Man delivery',
|
});
|
||||||
alertLevel: 0,
|
describe('Table detail', () => {
|
||||||
stateName: 'OK',
|
it('should open descriptors', () => {
|
||||||
stateId: 3,
|
cy.get('.q-table').should('be.visible');
|
||||||
itemFk: 5,
|
cy.colField('zoneName').click();
|
||||||
price: 1.79,
|
cy.dataCy('ZoneDescriptor').should('be.visible');
|
||||||
alertLevelCode: 'FREE',
|
cy.get('.q-item > .q-item__label').should('have.text', ' #1');
|
||||||
zoneFk: 9,
|
cy.colField('ticketFk').click();
|
||||||
zoneName: 'Zone superMan',
|
cy.dataCy('TicketDescriptor').should('be.visible');
|
||||||
theoreticalhour: '2011-11-01T22:59:00.000Z',
|
cy.get('.q-item > .q-item__label').should('have.text', ` #${ticketId}`);
|
||||||
isRookie: 1,
|
cy.colField('nickname').find('.link').click();
|
||||||
turno: 1,
|
cy.waitForElement('[data-cy="CustomerDescriptor"]');
|
||||||
peticionCompra: 1,
|
cy.dataCy('CustomerDescriptor').should('be.visible');
|
||||||
hasObservation: 1,
|
cy.get('.q-item > .q-item__label').should('have.text', ' #1');
|
||||||
hasToIgnore: 1,
|
|
||||||
isBasket: 1,
|
|
||||||
minTimed: 0,
|
|
||||||
customerId: 1104,
|
|
||||||
customerName: 'Tony Stark',
|
|
||||||
observationTypeCode: 'administrative',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}).as('getItemLack');
|
|
||||||
|
|
||||||
cy.visit('/#/ticket/negative/5', false);
|
|
||||||
cy.wait('@getItemLack');
|
|
||||||
});
|
});
|
||||||
describe('Table actions', () => {
|
|
||||||
it('should display only one row in the lack list', () => {
|
it('should display only one row in the lack list', () => {
|
||||||
cy.location('href').should('contain', '#/ticket/negative/5');
|
cy.dataCy('changeItem').should('be.disabled');
|
||||||
|
cy.dataCy('changeState').should('be.disabled');
|
||||||
cy.get('[data-cy="changeItem"]').should('be.disabled');
|
cy.dataCy('changeQuantity').should('be.disabled');
|
||||||
cy.get('[data-cy="changeState"]').should('be.disabled');
|
cy.dataCy('itemProposal').should('be.disabled');
|
||||||
cy.get('[data-cy="changeQuantity"]').should('be.disabled');
|
cy.dataCy('transferLines').should('be.disabled');
|
||||||
cy.get('[data-cy="itemProposal"]').should('be.disabled');
|
|
||||||
cy.get('[data-cy="transferLines"]').should('be.disabled');
|
|
||||||
cy.get('tr.cursor-pointer > :nth-child(1)').click();
|
cy.get('tr.cursor-pointer > :nth-child(1)').click();
|
||||||
cy.get('[data-cy="changeItem"]').should('be.enabled');
|
cy.dataCy('changeItem').should('be.enabled');
|
||||||
cy.get('[data-cy="changeState"]').should('be.enabled');
|
cy.dataCy('changeState').should('be.enabled');
|
||||||
cy.get('[data-cy="changeQuantity"]').should('be.enabled');
|
cy.dataCy('changeQuantity').should('be.enabled');
|
||||||
cy.get('[data-cy="itemProposal"]').should('be.enabled');
|
cy.dataCy('itemProposal').should('be.enabled');
|
||||||
cy.get('[data-cy="transferLines"]').should('be.enabled');
|
cy.dataCy('transferLines').should('be.enabled');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Split', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.get(firstRow).click();
|
||||||
|
cy.dataCy('transferLines').click();
|
||||||
|
});
|
||||||
|
it('Split', () => {
|
||||||
|
cy.dataCy('ticketTransferPopup').find('.flex > .q-btn').click();
|
||||||
|
|
||||||
|
cy.checkNotification(`Ticket ${ticketId}: No split`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('change quantity', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.get(firstRow).click();
|
||||||
|
|
||||||
|
cy.dataCy('changeQuantity').click();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('by popup', () => {
|
||||||
|
cy.dataCy('New quantity_input').type(10);
|
||||||
|
cy.get('.q-btn--unelevated > .q-btn__content > .block').click();
|
||||||
|
|
||||||
|
clickNotificationAction();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('Change state', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.get(firstRow).click();
|
||||||
|
cy.dataCy('changeState').click();
|
||||||
|
});
|
||||||
|
it('by popup', () => {
|
||||||
|
cy.dataCy('New state_select').should('be.visible');
|
||||||
|
cy.selectOption('[data-cy="New state_select"]', 'OK');
|
||||||
|
cy.get('.q-btn--unelevated > .q-btn__content > .block').click();
|
||||||
|
|
||||||
|
clickNotificationAction();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('Change Item', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.get(firstRow).click();
|
||||||
|
cy.dataCy('changeItem').click();
|
||||||
|
});
|
||||||
|
it('by popup', () => {
|
||||||
|
cy.dataCy('New item_select').should('be.visible');
|
||||||
|
cy.selectOption('[data-cy="New item_select"]', 'Palito rojo');
|
||||||
|
cy.get('.q-btn--unelevated > .q-btn__content > .block').click();
|
||||||
|
cy.checkNotification('Ticket 1000000: price retrieval failed');
|
||||||
|
cy.dataCy('changeItem').click();
|
||||||
|
cy.selectOption('[data-cy="New item_select"]', 'Ranged weapon longbow 200cm');
|
||||||
|
cy.get('.q-btn--unelevated > .q-btn__content > .block').click();
|
||||||
|
|
||||||
|
clickNotificationAction();
|
||||||
|
});
|
||||||
|
after(() => {
|
||||||
|
cy.visit(`/#/ticket/${ticketId}/sale`);
|
||||||
|
const quantity = Math.floor(Math.random() * 100) + 1;
|
||||||
|
const rowIndex = 1;
|
||||||
|
|
||||||
|
cy.dataCy('ticketSaleQuantityInput')
|
||||||
|
.find('input')
|
||||||
|
.eq(rowIndex)
|
||||||
|
.clear()
|
||||||
|
.type(`${quantity}{enter}`);
|
||||||
|
|
||||||
|
cy.dataCy('ticketSaleQuantityInput')
|
||||||
|
.find('input')
|
||||||
|
.eq(rowIndex)
|
||||||
|
.should('have.value', `${quantity}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Item proposal', () => {
|
describe('Item proposal', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.get('tr.cursor-pointer > :nth-child(1)').click();
|
cy.get(firstRow).click();
|
||||||
|
cy.dataCy('itemProposal').click();
|
||||||
cy.intercept('GET', /\/api\/Items\/getSimilar\?.*$/, {
|
|
||||||
statusCode: 200,
|
|
||||||
body: [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
longName: 'Ranged weapon longbow 50cm',
|
|
||||||
subName: 'Stark Industries',
|
|
||||||
tag5: 'Color',
|
|
||||||
value5: 'Brown',
|
|
||||||
match5: 0,
|
|
||||||
match6: 0,
|
|
||||||
match7: 0,
|
|
||||||
match8: 1,
|
|
||||||
tag6: 'Categoria',
|
|
||||||
value6: '+1 precission',
|
|
||||||
tag7: 'Tallos',
|
|
||||||
value7: '1',
|
|
||||||
tag8: null,
|
|
||||||
value8: null,
|
|
||||||
available: 20,
|
|
||||||
calc_id: 6,
|
|
||||||
counter: 0,
|
|
||||||
minQuantity: 1,
|
|
||||||
visible: null,
|
|
||||||
price2: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
longName: 'Ranged weapon longbow 100cm',
|
|
||||||
subName: 'Stark Industries',
|
|
||||||
tag5: 'Color',
|
|
||||||
value5: 'Brown',
|
|
||||||
match5: 0,
|
|
||||||
match6: 1,
|
|
||||||
match7: 0,
|
|
||||||
match8: 1,
|
|
||||||
tag6: 'Categoria',
|
|
||||||
value6: '+1 precission',
|
|
||||||
tag7: 'Tallos',
|
|
||||||
value7: '1',
|
|
||||||
tag8: null,
|
|
||||||
value8: null,
|
|
||||||
available: 50,
|
|
||||||
calc_id: 6,
|
|
||||||
counter: 1,
|
|
||||||
minQuantity: 5,
|
|
||||||
visible: null,
|
|
||||||
price2: 10,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
longName: 'Ranged weapon longbow 200cm',
|
|
||||||
subName: 'Stark Industries',
|
|
||||||
tag5: 'Color',
|
|
||||||
value5: 'Brown',
|
|
||||||
match5: 1,
|
|
||||||
match6: 1,
|
|
||||||
match7: 1,
|
|
||||||
match8: 1,
|
|
||||||
tag6: 'Categoria',
|
|
||||||
value6: '+1 precission',
|
|
||||||
tag7: 'Tallos',
|
|
||||||
value7: '1',
|
|
||||||
tag8: null,
|
|
||||||
value8: null,
|
|
||||||
available: 185,
|
|
||||||
calc_id: 6,
|
|
||||||
counter: 10,
|
|
||||||
minQuantity: 10,
|
|
||||||
visible: null,
|
|
||||||
price2: 100,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}).as('getItemGetSimilar');
|
|
||||||
cy.get('[data-cy="itemProposal"]').click();
|
|
||||||
cy.wait('@getItemGetSimilar');
|
|
||||||
});
|
});
|
||||||
describe.skip('Replace item if', () => {
|
describe('Replace item if', () => {
|
||||||
it('Quantity is less than available', () => {
|
it('Quantity is less than available', () => {
|
||||||
cy.get(':nth-child(1) > .text-right > .q-btn').click();
|
const index = 2;
|
||||||
|
cy.colField('tag7', index).click();
|
||||||
|
cy.checkNotification('Not available for replacement');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('item proposal cells', () => {
|
||||||
|
const index = 1;
|
||||||
|
cy.colField('longName', index)
|
||||||
|
.find('.no-padding > .q-td > .middle')
|
||||||
|
.should('have.class', 'proposal-primary');
|
||||||
|
cy.colField('tag5', index)
|
||||||
|
.find('.no-padding > .match')
|
||||||
|
.should('have.class', 'match');
|
||||||
|
cy.colField('tag6', index)
|
||||||
|
.find('.no-padding > .match')
|
||||||
|
.should('have.class', 'match');
|
||||||
|
cy.colField('tag7', index).click();
|
||||||
|
|
||||||
|
clickNotificationAction();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,34 +1,16 @@
|
||||||
/// <reference types="cypress" />
|
/// <reference types="cypress" />
|
||||||
describe('Ticket Lack list', () => {
|
describe('Ticket Lack list', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.login('developer');
|
cy.viewport(1980, 1020);
|
||||||
cy.intercept('GET', /Tickets\/itemLack\?.*$/, {
|
|
||||||
statusCode: 200,
|
|
||||||
body: [
|
|
||||||
{
|
|
||||||
itemFk: 5,
|
|
||||||
longName: 'Ranged weapon pistol 9mm',
|
|
||||||
warehouseFk: 1,
|
|
||||||
producer: null,
|
|
||||||
size: 15,
|
|
||||||
category: null,
|
|
||||||
warehouse: 'Warehouse One',
|
|
||||||
lack: -50,
|
|
||||||
inkFk: 'SLV',
|
|
||||||
timed: '2025-01-25T22:59:00.000Z',
|
|
||||||
minTimed: '23:59',
|
|
||||||
originFk: 'Holand',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}).as('getLack');
|
|
||||||
|
|
||||||
|
cy.login('developer');
|
||||||
cy.visit('/#/ticket/negative');
|
cy.visit('/#/ticket/negative');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Table actions', () => {
|
describe('Table actions', () => {
|
||||||
it('should display only one row in the lack list', () => {
|
it('should display only one row in the lack list', () => {
|
||||||
cy.wait('@getLack', { timeout: 10000 });
|
cy.get('[data-col-field="longName"]').first().click();
|
||||||
|
cy.dataCy('ItemDescriptor').should('be.visible');
|
||||||
cy.get('.q-virtual-scroll__content > :nth-child(1) > .sticky').click();
|
cy.get('.q-virtual-scroll__content > :nth-child(1) > .sticky').click();
|
||||||
cy.location('href').should('contain', '#/ticket/negative/5');
|
cy.location('href').should('contain', '#/ticket/negative/5');
|
||||||
});
|
});
|
||||||
|
|
|
@ -22,7 +22,7 @@ describe('TicketSale', { testIsolation: true }, () => {
|
||||||
cy.intercept('POST', /\/api\/Sales\/\d+\/updatePrice/).as('updatePrice');
|
cy.intercept('POST', /\/api\/Sales\/\d+\/updatePrice/).as('updatePrice');
|
||||||
|
|
||||||
cy.dataCy('saveManaBtn').click();
|
cy.dataCy('saveManaBtn').click();
|
||||||
handleVnConfirm();
|
cy.handleVnConfirm();
|
||||||
cy.wait('@updatePrice').its('response.statusCode').should('eq', 200);
|
cy.wait('@updatePrice').its('response.statusCode').should('eq', 200);
|
||||||
|
|
||||||
cy.get('[data-col-field="price"]')
|
cy.get('[data-col-field="price"]')
|
||||||
|
@ -43,7 +43,7 @@ describe('TicketSale', { testIsolation: true }, () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
cy.dataCy('saveManaBtn').click();
|
cy.dataCy('saveManaBtn').click();
|
||||||
handleVnConfirm();
|
cy.handleVnConfirm();
|
||||||
cy.wait('@updateDiscount').its('response.statusCode').should('eq', 204);
|
cy.wait('@updateDiscount').its('response.statusCode').should('eq', 204);
|
||||||
|
|
||||||
cy.get('[data-col-field="discount"]')
|
cy.get('[data-col-field="discount"]')
|
||||||
|
@ -61,7 +61,7 @@ describe('TicketSale', { testIsolation: true }, () => {
|
||||||
.find('[data-cy="undefined_input"]')
|
.find('[data-cy="undefined_input"]')
|
||||||
.type(concept)
|
.type(concept)
|
||||||
.type('{enter}');
|
.type('{enter}');
|
||||||
handleVnConfirm();
|
cy.handleVnConfirm();
|
||||||
|
|
||||||
cy.get('[data-col-field="item"]').should('contain.text', `${concept}`);
|
cy.get('[data-col-field="item"]').should('contain.text', `${concept}`);
|
||||||
});
|
});
|
||||||
|
@ -71,13 +71,9 @@ describe('TicketSale', { testIsolation: true }, () => {
|
||||||
cy.dataCy('ticketSaleQuantityInput').find('input').clear();
|
cy.dataCy('ticketSaleQuantityInput').find('input').clear();
|
||||||
cy.intercept('POST', '**/api').as('postRequest');
|
cy.intercept('POST', '**/api').as('postRequest');
|
||||||
|
|
||||||
cy.dataCy('ticketSaleQuantityInput')
|
cy.dataCy('ticketSaleQuantityInput').find('input').type(`${quantity}{enter}`);
|
||||||
.find('input')
|
|
||||||
.type(quantity)
|
|
||||||
.trigger('tab');
|
|
||||||
cy.get('.q-page > :nth-child(6)').click();
|
|
||||||
|
|
||||||
handleVnConfirm();
|
cy.handleVnConfirm();
|
||||||
|
|
||||||
cy.get('[data-cy="ticketSaleQuantityInput"]')
|
cy.get('[data-cy="ticketSaleQuantityInput"]')
|
||||||
.find('input')
|
.find('input')
|
||||||
|
@ -210,8 +206,3 @@ function selectFirstRow() {
|
||||||
cy.waitForElement(firstRow);
|
cy.waitForElement(firstRow);
|
||||||
cy.get(firstRow).find('.q-checkbox__inner').click();
|
cy.get(firstRow).find('.q-checkbox__inner').click();
|
||||||
}
|
}
|
||||||
function handleVnConfirm() {
|
|
||||||
cy.confirmVnConfirm();
|
|
||||||
|
|
||||||
cy.checkNotification('Data saved');
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
Cypress.Commands.add('handleVnConfirm', () => {
|
||||||
|
cy.confirmVnConfirm();
|
||||||
|
|
||||||
|
cy.checkNotification('Data saved');
|
||||||
|
});
|
||||||
|
|
||||||
Cypress.Commands.add('confirmVnConfirm', () =>
|
Cypress.Commands.add('confirmVnConfirm', () =>
|
||||||
cy.dataCy('VnConfirm_confirm').should('exist').click(),
|
cy.dataCy('VnConfirm_confirm').should('exist').click(),
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,3 +18,37 @@ Cypress.Commands.add('tableActions', (n = 0, child = 1) =>
|
||||||
`:nth-child(${child}) > .q-table--col-auto-width > [data-cy="tableAction-${n}"] > .q-btn__content > .q-icon`,
|
`:nth-child(${child}) > .q-table--col-auto-width > [data-cy="tableAction-${n}"] > .q-btn__content > .q-icon`,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Cypress.Commands.add('validateVnTableRows', (opts = {}) => {
|
||||||
|
let { cols = [] } = opts;
|
||||||
|
const { rows = [] } = opts;
|
||||||
|
if (!Array.isArray(cols)) cols = [cols];
|
||||||
|
const rowSelector = rows.length
|
||||||
|
? rows.map((row) => `> :nth-child(${row})`).join(', ')
|
||||||
|
: '> *';
|
||||||
|
cy.get(`[data-cy="vnTable"] .q-virtual-scroll__content`).within(() => {
|
||||||
|
cy.get(`${rowSelector}`).each(($el) => {
|
||||||
|
for (const { name, type = 'string', val, operation = 'equal' } of cols) {
|
||||||
|
cy.wrap($el)
|
||||||
|
.find(`[data-cy="vnTableCell_${name}"]`)
|
||||||
|
.invoke('text')
|
||||||
|
.then((text) => {
|
||||||
|
if (type === 'string')
|
||||||
|
expect(text.trim().toLowerCase()).to[operation](
|
||||||
|
val.toLowerCase(),
|
||||||
|
);
|
||||||
|
if (type === 'number') cy.checkNumber(text, val, operation);
|
||||||
|
if (type === 'date') cy.checkDate(text, val, operation);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Cypress.Commands.add('colField', (name, index = null, key = 'data-col-field') => {
|
||||||
|
if (index) {
|
||||||
|
cy.get(`:nth-child(${index}) > [${key}="${name}"]`);
|
||||||
|
} else {
|
||||||
|
cy.get(`[${key}="${name}"]`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in New Issue