#7553 modified TicketExpedition & changes in ticket section #571

Merged
jon merged 79 commits from 7553_FixTicketExpedition into dev 2024-09-25 05:51:16 +00:00
37 changed files with 2609 additions and 1180 deletions

View File

@ -234,6 +234,8 @@ async function remove(data) {
newData = newData.filter((form) => !ids.some((id) => id == form[pk])); newData = newData.filter((form) => !ids.some((id) => id == form[pk]));
fetch(newData); fetch(newData);
}); });
} else {
reset();
} }
emit('update:selected', []); emit('update:selected', []);
} }

View File

@ -53,6 +53,10 @@ const $props = defineProps({
type: Boolean, type: Boolean,
default: true, default: true,
}, },
bottom: {
type: Object,
default: null,
},
cardClass: { cardClass: {
type: String, type: String,
default: 'flex-one', default: 'flex-one',
@ -102,6 +106,10 @@ const $props = defineProps({
type: Boolean, type: Boolean,
default: false, default: false,
}, },
disabledAttr: {
type: Boolean,
default: false,
},
}); });
const { t } = useI18n(); const { t } = useI18n();
const stateStore = useStateStore(); const stateStore = useStateStore();
@ -520,6 +528,29 @@ function handleOnDataSaved(_) {
/> />
</QTd> </QTd>
</template> </template>
<template #bottom v-if="bottom">
<slot name="bottom-table">
<QBtn
@click="
() =>
createAsDialog
? (showForm = !showForm)
: handleOnDataSaved(create)
"
class="cursor-pointer fill-icon"
color="primary"
icon="add_circle"
size="md"
round
flat
shortcut="+"
:disabled="!disabledAttr"
/>
<QTooltip>
{{ createForm.title }}
</QTooltip>
</slot>
</template>
<template #item="{ row, colsMap }"> <template #item="{ row, colsMap }">
<component <component
:is="$props.redirect ? 'router-link' : 'span'" :is="$props.redirect ? 'router-link' : 'span'"
@ -853,4 +884,13 @@ es:
cursor: text; cursor: text;
user-select: all; user-select: all;
} }
.full-width-slot {
width: 100%;
display: flex;
text-align: center;
color: var(--vn-text-color);
margin-bottom: -1%;
background-color: var(--vn-header-color);
}
</style> </style>

View File

@ -47,6 +47,7 @@ let store;
let entity; let entity;
const isLoading = ref(false); const isLoading = ref(false);
const isSameDataKey = computed(() => $props.dataKey === route.meta.moduleName); const isSameDataKey = computed(() => $props.dataKey === route.meta.moduleName);
const menuRef = ref();
defineExpose({ getData }); defineExpose({ getData });
onBeforeMount(async () => { onBeforeMount(async () => {
@ -170,7 +171,7 @@ const toModule = computed(() =>
<QTooltip> <QTooltip>
{{ t('components.cardDescriptor.moreOptions') }} {{ t('components.cardDescriptor.moreOptions') }}
</QTooltip> </QTooltip>
<QMenu ref="menuRef"> <QMenu :ref="menuRef">
<QList> <QList>
<slot name="menu" :entity="entity" :menu-ref="menuRef" /> <slot name="menu" :entity="entity" :menu-ref="menuRef" />
</QList> </QList>

View File

@ -495,6 +495,8 @@ ticket:
warehouse: Warehouse warehouse: Warehouse
customerCard: Customer card customerCard: Customer card
alias: Alias alias: Alias
ticketList: Ticket List
newOrder: New Order
boxing: boxing:
expedition: Expedition expedition: Expedition
item: Item item: Item
@ -516,6 +518,7 @@ ticket:
landed: Landed landed: Landed
consigneePhone: Consignee phone consigneePhone: Consignee phone
consigneeMobile: Consignee mobile consigneeMobile: Consignee mobile
consigneeAddress: Consignee address
clientPhone: Client phone clientPhone: Client phone
clientMobile: Client mobile clientMobile: Client mobile
consignee: Consignee consignee: Consignee
@ -545,6 +548,11 @@ ticket:
weight: Weight weight: Weight
goTo: Go to goTo: Go to
summaryAmount: Summary summaryAmount: Summary
purchaseRequest: Purchase request
service: Service
description: Description
attender: Attender
ok: Ok
create: create:
client: Client client: Client
address: Address address: Address
@ -568,7 +576,6 @@ invoiceOut:
client: Client client: Client
company: Company company: Company
customerCard: Customer card customerCard: Customer card
ticketList: Ticket List
summary: summary:
issued: Issued issued: Issued
created: Created created: Created

View File

@ -63,7 +63,7 @@ globals:
shipped: F. envío shipped: F. envío
totalEntries: Ent. totales totalEntries: Ent. totales
amount: Importe amount: Importe
packages: Bultos packages: Embalajes
download: Descargar download: Descargar
downloadPdf: Descargar PDF downloadPdf: Descargar PDF
selectRows: 'Seleccionar las { numberRows } filas(s)' selectRows: 'Seleccionar las { numberRows } filas(s)'
@ -267,7 +267,7 @@ globals:
tracking: Estados tracking: Estados
components: Componentes components: Componentes
pictures: Fotos pictures: Fotos
packages: Bultos packages: Embalajes
ldap: LDAP ldap: LDAP
samba: Samba samba: Samba
twoFactor: Doble factor twoFactor: Doble factor
@ -486,7 +486,7 @@ ticket:
tracking: Estados tracking: Estados
components: Componentes components: Componentes
pictures: Fotos pictures: Fotos
packages: Bultos packages: Embalajes
list: list:
nickname: Alias nickname: Alias
state: Estado state: Estado
@ -504,6 +504,8 @@ ticket:
warehouse: Almacén warehouse: Almacén
customerCard: Ficha del cliente customerCard: Ficha del cliente
alias: Alias alias: Alias
ticketList: Listado de tickets
newOrder: Nuevo pedido
boxing: boxing:
expedition: Expedición expedition: Expedición
item: Artículo item: Artículo
@ -525,6 +527,7 @@ ticket:
landed: Entregado landed: Entregado
consigneePhone: Tel. consignatario consigneePhone: Tel. consignatario
consigneeMobile: Móv. consignatario consigneeMobile: Móv. consignatario
consigneeAddress: Dir. consignatario
clientPhone: Tel. cliente clientPhone: Tel. cliente
clientMobile: Móv. cliente clientMobile: Móv. cliente
consignee: Consignatario consignee: Consignatario
@ -554,6 +557,10 @@ ticket:
weight: Peso weight: Peso
goTo: Ir a goTo: Ir a
summaryAmount: Resumen summaryAmount: Resumen
purchaseRequest: Petición de compra
service: Servicio
description: Descripción
attender: Consignatario
create: create:
client: Cliente client: Cliente
jon marked this conversation as resolved
Review

Consignatario

Consignatario
address: Dirección address: Dirección

View File

@ -0,0 +1,177 @@
<script setup>
import { useI18n } from 'vue-i18n';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnInput from 'src/components/common/VnInput.vue';
import { QItem } from 'quasar';
import VnSelect from 'src/components/common/VnSelect.vue';
import { QItemSection } from 'quasar';
import VnInputDate from 'src/components/common/VnInputDate.vue';
import { toDate } from 'src/filters';
const { t } = useI18n();
defineProps({ dataKey: { type: String, required: true } });
</script>
<template>
<VnFilterPanel :data-key="dataKey" :search-button="true">
<template #tags="{ tag, formatFn }">
<div class="q-gutter-x-xs">
<strong>{{ t(`params.${tag.label}`) }}: </strong>
<span>{{ formatFn(tag.value) }}</span>
</div>
</template>
<template #body="{ params }">
<QItem>
<QItemSection>
<VnInput
:label="t('params.item')"
v-model="params.itemId"
is-outlined
lazy-rules
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnSelect
v-model="params.buyerId"
url="TicketRequests/getItemTypeWorker"
:fields="['id', 'nickname']"
sort-by="nickname ASC"
:label="t('params.buyer')"
option-value="id"
option-label="nickname"
dense
outlined
rounded
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
jon marked this conversation as resolved
Review

este com,entario sobra

este com,entario sobra
<VnSelect
v-model="params.typeId"
url="ItemTypes"
:include="['category']"
:fields="['id', 'name', 'categoryFk']"
sort-by="name ASC"
:label="t('params.typeId')"
option-label="name"
option-value="id"
dense
outlined
rounded
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>{{ scope.opt?.name }}</QItemLabel>
<QItemLabel caption>{{
scope.opt?.category?.name
}}</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnSelect
v-model="params.categoryId"
url="ItemCategories"
:fields="['id', 'name']"
sort-by="name ASC"
:label="t('params.categoryId')"
option-label="name"
option-value="id"
dense
outlined
rounded
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnSelect
v-model="params.campaignId"
url="Campaigns/latest"
sort-by="dated DESC"
:label="t('params.campaignId')"
option-label="code"
option-value="id"
dense
outlined
rounded
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>{{
t(`params.${scope.opt?.code}`)
}}</QItemLabel>
<QItemLabel caption>{{
toDate(scope.opt.dated)
}}</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInputDate
:label="t('params.from')"
v-model="params.from"
@update:model-value="searchFn()"
is-outlined
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<VnInputDate
:label="t('params.to')"
v-model="params.to"
@update:model-value="searchFn()"
is-outlined
/>
</QItemSection>
</QItem>
</template>
</VnFilterPanel>
</template>
<i18n>
en:
params:
item: Item id
buyer: Buyer
type: Type
category: Category
itemId: Item id
buyerId: Buyer
typeId: Type
categoryId: Category
from: From
to: To
campaignId: Campaña
valentinesDay: Valentine's Day
mothersDay: Mother's Day
allSaints: All Saints' Day
es:
params:
item: Id artículo
buyer: Comprador
type: Tipo
category: Categoría
itemId: Id Artículo
buyerId: Comprador
typeId: Tipo
categoryId: Reino
from: Desde
to: Hasta
campaignId: Campaña
valentinesDay: Día de San Valentín
mothersDay: Día de la Madre
allSaints: Día de Todos los Santos
</i18n>

View File

@ -1,5 +1,6 @@
<script setup> <script setup>
import InvoiceOutDescriptor from './InvoiceOutDescriptor.vue'; import InvoiceOutDescriptor from './InvoiceOutDescriptor.vue';
import InvoiceOutSummary from './InvoiceOutSummary.vue';
const $props = defineProps({ const $props = defineProps({
id: { id: {
@ -10,6 +11,10 @@ const $props = defineProps({
</script> </script>
<template> <template>
<QPopupProxy> <QPopupProxy>
<InvoiceOutDescriptor v-if="$props.id" :id="$props.id" /> <InvoiceOutDescriptor
v-if="$props.id"
:id="$props.id"
:summary="InvoiceOutSummary"
/>
</QPopupProxy> </QPopupProxy>
</template> </template>

View File

@ -249,7 +249,7 @@ watch(
@on-fetch="(data) => (orderSummary.vat = data)" @on-fetch="(data) => (orderSummary.vat = data)"
auto-load auto-load
/> />
<QDrawer side="right" :width="270" v-model="stateStore.rightDrawer"> <QDrawer side="right" :width="265" v-model="stateStore.rightDrawer">
<QCard <QCard
class="order-lines-summary q-pa-lg" class="order-lines-summary q-pa-lg"
v-if="orderSummary.vat && orderSummary.total" v-if="orderSummary.vat && orderSummary.total"

View File

@ -14,14 +14,15 @@ import OrderFilter from './Card/OrderFilter.vue';
import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vue'; import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vue';
import WorkerDescriptorProxy from '../Worker/Card/WorkerDescriptorProxy.vue'; import WorkerDescriptorProxy from '../Worker/Card/WorkerDescriptorProxy.vue';
import { toDateTimeFormat } from 'src/filters/date'; import { toDateTimeFormat } from 'src/filters/date';
import { onMounted } from 'vue';
import { useRoute } from 'vue-router';
const { t } = useI18n(); const { t } = useI18n();
const { viewSummary } = useSummaryDialog(); const { viewSummary } = useSummaryDialog();
const tableRef = ref(); const tableRef = ref();
const agencyList = ref([]); const agencyList = ref([]);
const addressesList = ref([]); const addressesList = ref([]);
const clientId = ref(); const route = useRoute();
const columns = computed(() => [ const columns = computed(() => [
{ {
align: 'left', align: 'left',
@ -169,6 +170,13 @@ const getDateColor = (date) => {
if (comparation == 0) return 'bg-warning'; if (comparation == 0) return 'bg-warning';
if (comparation < 0) return 'bg-success'; if (comparation < 0) return 'bg-success';
}; };
onMounted(() => {
if (!route.query.createForm) return;
const clientId = route.query.createForm;
const id = JSON.parse(clientId);
fetchClientAddress(id.clientFk, id);
});
</script> </script>
<template> <template>
<OrderSearchbar /> <OrderSearchbar />
@ -184,13 +192,14 @@ const getDateColor = (date) => {
:order="['landed DESC', 'clientFk ASC', 'id DESC']" :order="['landed DESC', 'clientFk ASC', 'id DESC']"
:create="{ :create="{
urlCreate: 'Orders/new', urlCreate: 'Orders/new',
title: 'Create Order', title: t('module.cerateOrder'),
onDataSaved: (url) => { onDataSaved: (url) => {
tableRef.redirect(url); tableRef.redirect(url);
}, },
formInitialData: { formInitialData: {
jon marked this conversation as resolved
Review

En la linea 195 el titulo del create no está traducido

En la linea 195 el titulo del create no está traducido
active: true, active: true,
addressId: null, addressId: null,
clientFk: null,
}, },
}" }"
:user-params="{ showEmpty: false }" :user-params="{ showEmpty: false }"
@ -221,7 +230,7 @@ const getDateColor = (date) => {
<VnSelect <VnSelect
url="Clients" url="Clients"
:include="{ relation: 'addresses' }" :include="{ relation: 'addresses' }"
v-model="clientId" v-model="data.clientFk"
:label="t('module.customer')" :label="t('module.customer')"
@update:model-value="(id) => fetchClientAddress(id, data)" @update:model-value="(id) => fetchClientAddress(id, data)"
/> />

View File

@ -10,6 +10,7 @@ module:
total: Total total: Total
salesPerson: Sales Person salesPerson: Sales Person
address: Address address: Address
cerateOrder: Create order
lines: lines:
item: Item item: Item
warehouse: Warehouse warehouse: Warehouse

View File

@ -10,6 +10,7 @@ module:
total: Total total: Total
salesPerson: Comercial salesPerson: Comercial
address: Dirección address: Dirección
cerateOrder: Crear cesta
lines: lines:
item: Artículo item: Artículo
warehouse: Almacén warehouse: Almacén

View File

@ -4,7 +4,6 @@ import { useI18n } from 'vue-i18n';
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue'; import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
import FetchedTags from 'components/ui/FetchedTags.vue'; import FetchedTags from 'components/ui/FetchedTags.vue';
import RightMenu from 'src/components/common/RightMenu.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
@ -115,7 +114,7 @@ const totalNewPrice = computed(() => {
const totalDifference = computed(() => { const totalDifference = computed(() => {
return rows.value.reduce((acc, item) => acc + item.component?.difference || 0, 0); return rows.value.reduce((acc, item) => acc + item.component?.difference || 0, 0);
}); });
const showMovablecolumn = computed(() => (haveDifferences.value > 0 ? ['movable'] : [])); const showMovableColumn = computed(() => (haveDifferences.value > 0 ? ['movable'] : []));
const haveDifferences = computed(() => _ticketData.value.sale?.haveDifferences); const haveDifferences = computed(() => _ticketData.value.sale?.haveDifferences);
const ticketHaveNegatives = () => { const ticketHaveNegatives = () => {
let _haveNegatives = false; let _haveNegatives = false;
@ -145,85 +144,83 @@ onUnmounted(() => (stateStore.rightDrawer = false));
@on-fetch="(data) => (ticketUpdateActions = data)" @on-fetch="(data) => (ticketUpdateActions = data)"
auto-load auto-load
/> />
<RightMenu> <QDrawer side="right" :width="265" v-model="stateStore.rightDrawer">
<template #right-panel> <QCard
<QCard class="q-pa-md q-mb-md q-ma-md color-vn-text"
class="q-pa-md q-mb-md q-ma-md color-vn-text" bordered
bordered flat
flat style="border-color: black"
style="border-color: black" >
<QCardSection horizontal>
<span class="text-weight-bold text-subtitle1 text-center full-width">
{{ t('basicData.total') }}
</span>
</QCardSection>
<QCardSection class="column items-left" horizontal>
<span>
{{ t('basicData.price') }}:
{{ toCurrency(totalPrice) }}
</span>
</QCardSection>
<QCardSection class="column items-left" horizontal>
<span>
{{ t('basicData.newPrice') }}: {{ toCurrency(totalNewPrice) }}
</span>
</QCardSection>
<QCardSection class="column items-left" horizontal>
<span>
{{ t('basicData.difference') }}: {{ toCurrency(totalDifference) }}
</span>
</QCardSection>
</QCard>
<QCard
v-if="totalDifference"
class="q-pa-md q-mb-md q-ma-md color-vn-text"
bordered
flat
style="border-color: black"
>
<QCardSection horizontal>
<span class="text-weight-bold text-subtitle1 text-center full-width">
{{ t('basicData.chargeDifference') }}
</span>
</QCardSection>
<QCardSection
v-for="(action, index) in ticketUpdateActions"
:key="index"
horizontal
> >
<QCardSection horizontal> <QRadio
<span class="text-weight-bold text-subtitle1 text-center full-width"> v-model="_ticketData.option"
{{ t('basicData.total') }} :val="action.code"
</span> :label="action.description"
</QCardSection> dense
<QCardSection class="column items-center" horizontal> />
<span> </QCardSection>
{{ t('basicData.price') }}: </QCard>
{{ toCurrency(totalPrice) }} <QCard
</span> v-if="haveNegatives"
</QCardSection> class="q-pa-md q-mb-md q-ma-md color-vn-text"
<QCardSection class="column items-center" horizontal> bordered
<span> flat
{{ t('basicData.newPrice') }}: {{ toCurrency(totalNewPrice) }} style="border-color: black"
</span> >
</QCardSection> <QCardSection horizontal class="flex row items-center">
<QCardSection class="column items-center" horizontal> <QCheckbox
<span> :label="t('basicData.withoutNegatives')"
{{ t('basicData.difference') }}: {{ toCurrency(totalDifference) }} v-model="_ticketData.withoutNegatives"
</span> :toggle-indeterminate="false"
</QCardSection> />
</QCard> <QIcon name="info" size="xs" class="q-ml-sm">
<QCard <QTooltip max-width="350px">
v-if="totalDifference" {{ t('basicData.withoutNegativesInfo') }}
class="q-pa-md q-mb-md q-ma-md color-vn-text" </QTooltip>
bordered </QIcon>
flat </QCardSection>
style="border-color: black" </QCard>
> </QDrawer>
<QCardSection horizontal>
<span class="text-weight-bold text-subtitle1 text-center full-width">
{{ t('basicData.chargeDifference') }}
</span>
</QCardSection>
<QCardSection
v-for="(action, index) in ticketUpdateActions"
:key="index"
horizontal
>
<QRadio
v-model="_ticketData.option"
:val="action.code"
:label="action.description"
dense
/>
</QCardSection>
</QCard>
<QCard
v-if="haveNegatives"
class="q-pa-md q-mb-md q-ma-md color-vn-text"
bordered
flat
style="border-color: black"
>
<QCardSection horizontal class="flex row items-center">
<QCheckbox
:label="t('basicData.withoutNegatives')"
v-model="_ticketData.withoutNegatives"
:toggle-indeterminate="false"
/>
<QIcon name="info" size="xs" class="q-ml-sm">
<QTooltip max-width="350px">
{{ t('basicData.withoutNegativesInfo') }}
</QTooltip>
</QIcon>
</QCardSection>
</QCard>
</template>
</RightMenu>
<QTable <QTable
:visible-columns="showMovablecolumn" :visible-columns="showMovableColumn"
:rows="rows" :rows="rows"
:columns="columns" :columns="columns"
row-key="id" row-key="id"
@ -233,15 +230,15 @@ onUnmounted(() => (stateStore.rightDrawer = false));
flat flat
> >
<template #body-cell-item="{ row }"> <template #body-cell-item="{ row }">
<QTd> <QTd @click.stop class="link">
<QBtn flat color="primary"> <QBtn flat>
{{ row.itemFk }} {{ row.itemFk }}
<ItemDescriptorProxy :id="row.itemFk" /> <ItemDescriptorProxy :id="row.itemFk" />
</QBtn> </QBtn>
</QTd> </QTd>
</template> </template>
<template #body-cell-description="{ row }"> <template #body-cell-description="{ row }">
<QTd> <QTd style="display: contents">
<div class="column"> <div class="column">
<span>{{ row.item.name }}</span> <span>{{ row.item.name }}</span>
<span class="color-vn-label">{{ row.item.subName }}</span> <span class="color-vn-label">{{ row.item.subName }}</span>

View File

@ -12,6 +12,7 @@ import VnInputTime from 'components/common/VnInputTime.vue';
import axios from 'axios'; import axios from 'axios';
import useNotify from 'src/composables/useNotify.js'; import useNotify from 'src/composables/useNotify.js';
import { useValidator } from 'src/composables/useValidator';
import { toTimeFormat } from 'filters/date.js'; import { toTimeFormat } from 'filters/date.js';
const $props = defineProps({ const $props = defineProps({
@ -23,7 +24,7 @@ const $props = defineProps({
}); });
const emit = defineEmits(['updateForm']); const emit = defineEmits(['updateForm']);
const { validate } = useValidator();
const { notify } = useNotify(); const { notify } = useNotify();
const router = useRouter(); const router = useRouter();
const { t } = useI18n(); const { t } = useI18n();
@ -51,18 +52,18 @@ const agencyByWarehouseFilter = computed(() => ({
}, },
})); }));
const zonesFilter = computed(() => ({ function zoneWhere() {
fields: ['id', 'name'], if (formData?.value?.agencyModeFk) {
order: 'name ASC', return formData.value?.agencyModeFk
where: formData.value?.agencyModeFk ? {
? { shipped: formData.value?.shipped,
shipped: formData.value?.shipped, addressFk: formData.value?.addressFk,
addressFk: formData.value?.addressFk, agencyModeFk: formData.value?.agencyModeFk,
agencyModeFk: formData.value?.agencyModeFk, warehouseFk: formData.value?.warehouseFk,
warehouseFk: formData.value?.warehouseFk, }
} : {};
: {}, }
})); }
const getLanded = async (params) => { const getLanded = async (params) => {
try { try {
@ -293,13 +294,6 @@ onMounted(() => onFormModelInit());
@on-fetch="(data) => (agenciesOptions = data)" @on-fetch="(data) => (agenciesOptions = data)"
auto-load auto-load
/> />
<FetchData
ref="zonesFetchRef"
url="Zones/includingExpired"
:filter="zonesFilter"
@on-fetch="(data) => (zonesOptions = data)"
auto-load
/>
<QForm> <QForm>
<VnRow> <VnRow>
<VnSelect <VnSelect
@ -313,6 +307,7 @@ onMounted(() => onFormModelInit());
hide-selected hide-selected
map-options map-options
:required="true" :required="true"
:rules="validate('basicData.client')"
> >
<template #option="scope"> <template #option="scope">
<QItem v-bind="scope.itemProps"> <QItem v-bind="scope.itemProps">
@ -333,6 +328,7 @@ onMounted(() => onFormModelInit());
hide-selected hide-selected
map-options map-options
:required="true" :required="true"
:rules="validate('basicData.warehouse')"
/> />
</VnRow> </VnRow>
<VnRow> <VnRow>
@ -345,6 +341,7 @@ onMounted(() => onFormModelInit());
hide-selected hide-selected
map-options map-options
:required="true" :required="true"
:rules="validate('basicData.address')"
> >
<template #option="scope"> <template #option="scope">
<QItem v-bind="scope.itemProps"> <QItem v-bind="scope.itemProps">
@ -392,6 +389,7 @@ onMounted(() => onFormModelInit());
:label="t('basicData.alias')" :label="t('basicData.alias')"
v-model="formData.nickname" v-model="formData.nickname"
:required="true" :required="true"
:rules="validate('basicData.alias')"
/> />
</VnRow> </VnRow>
<VnRow class="row q-gutter-md q-mb-md no-wrap"> <VnRow class="row q-gutter-md q-mb-md no-wrap">
@ -404,6 +402,7 @@ onMounted(() => onFormModelInit());
hide-selected hide-selected
map-options map-options
:required="true" :required="true"
:rules="validate('basicData.company')"
/> />
<VnSelect <VnSelect
:label="t('basicData.agency')" :label="t('basicData.agency')"
@ -414,17 +413,22 @@ onMounted(() => onFormModelInit());
hide-selected hide-selected
map-options map-options
@focus="agencyFetchRef.fetch()" @focus="agencyFetchRef.fetch()"
:rules="validate('basicData.agency')"
/> />
<VnSelect <VnSelect
:label="t('basicData.zone')" :label="t('basicData.zone')"
v-model="zoneId" v-model="zoneId"
option-value="id" option-value="id"
option-label="name" option-label="name"
:options="zonesOptions" url="Zones/includingExpired"
:fields="['id', 'name']"
sort-by="id"
:where="zoneWhere"
hide-selected hide-selected
map-options map-options
:required="true" :required="true"
@focus="zonesFetchRef.fetch()" @focus="zonesFetchRef.fetch()"
:rules="validate('basicData.zone')"
> >
<template #option="scope"> <template #option="scope">
<QItem v-bind="scope.itemProps"> <QItem v-bind="scope.itemProps">
@ -444,16 +448,19 @@ onMounted(() => onFormModelInit());
:label="t('basicData.shipped')" :label="t('basicData.shipped')"
v-model="formData.shipped" v-model="formData.shipped"
:required="true" :required="true"
:rules="validate('basicData.shipped')"
/> />
<VnInputTime <VnInputTime
:label="t('basicData.shippedHour')" :label="t('basicData.shippedHour')"
v-model="formData.shipped" v-model="formData.shipped"
:required="true" :required="true"
:rules="validate('basicData.shippedHour')"
/> />
<VnInputDate <VnInputDate
:label="t('basicData.landed')" :label="t('basicData.landed')"
v-model="formData.landed" v-model="formData.landed"
:required="true" :required="true"
:rules="validate('basicData.landed')"
/> />
</VnRow> </VnRow>
</QForm> </QForm>

View File

@ -3,7 +3,7 @@ import { ref, onBeforeMount } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import BasicDataTable from './BasicDataTable.vue'; import TicketBasicData from './TicketBasicData.vue';
import TicketBasicDataForm from './TicketBasicDataForm.vue'; import TicketBasicDataForm from './TicketBasicDataForm.vue';
import { useVnConfirm } from 'composables/useVnConfirm'; import { useVnConfirm } from 'composables/useVnConfirm';
@ -158,7 +158,10 @@ onBeforeMount(async () => await getTicketData());
color="primary" color="primary"
animated animated
keep-alive keep-alive
style="max-width: 800px; margin: auto" style="margin: auto"
:style="{
'max-width': step > 1 ? 'none' : '800px',
}"
> >
<QStep :name="1" :title="t('globals.pageTitles.basicData')" :done="step > 1"> <QStep :name="1" :title="t('globals.pageTitles.basicData')" :done="step > 1">
<TicketBasicDataForm <TicketBasicDataForm
@ -168,7 +171,7 @@ onBeforeMount(async () => await getTicketData());
/> />
</QStep> </QStep>
<QStep :name="2" :title="t('basicData.priceDifference')"> <QStep :name="2" :title="t('basicData.priceDifference')">
<BasicDataTable <TicketBasicData
:form-data="formData" :form-data="formData"
v-model:haveNegatives="haveNegatives" v-model:haveNegatives="haveNegatives"
@update-form="($event) => (formData = $event)" @update-form="($event) => (formData = $event)"

View File

@ -31,6 +31,7 @@ const router = useRouter();
const { notify } = useNotify(); const { notify } = useNotify();
const newTicketFormData = reactive({}); const newTicketFormData = reactive({});
const date = new Date();
jon marked this conversation as resolved
Review

He puesto esta fecha por defecto, no sé como lo verás

He puesto esta fecha por defecto, no sé como lo verás
const createTicket = async () => { const createTicket = async () => {
try { try {
@ -64,7 +65,11 @@ const createTicket = async () => {
> >
<template #form-inputs="{ data }"> <template #form-inputs="{ data }">
<VnRow> <VnRow>
<VnInputDate :label="t('expedition.landed')" v-model="data.landed" /> <VnInputDate
:label="t('expedition.landed')"
v-model="data.landed"
:model-value="date"
/>
</VnRow> </VnRow>
<VnRow> <VnRow>
<VnInput <VnInput

View File

@ -5,15 +5,16 @@ import { useRoute } from 'vue-router';
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue'; import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
import FetchedTags from 'components/ui/FetchedTags.vue'; import FetchedTags from 'components/ui/FetchedTags.vue';
import RightMenu from 'src/components/common/RightMenu.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import ZoneDescriptorProxy from 'src/pages/Zone/Card/ZoneDescriptorProxy.vue'; import ZoneDescriptorProxy from 'src/pages/Zone/Card/ZoneDescriptorProxy.vue';
import VnImg from 'src/components/ui/VnImg.vue';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import { dashIfEmpty } from 'src/filters'; import { dashIfEmpty } from 'src/filters';
import { useArrayData } from 'composables/useArrayData'; import { useArrayData } from 'composables/useArrayData';
import { toCurrency } from 'filters/index'; import { toCurrency } from 'filters/index';
import axios from 'axios'; import axios from 'axios';
import VnTable from 'src/components/VnTable/VnTable.vue';
const route = useRoute(); const route = useRoute();
const stateStore = useStateStore(); const stateStore = useStateStore();
@ -75,22 +76,39 @@ const columns = computed(() => [
name: 'item', name: 'item',
align: 'left', align: 'left',
}, },
{
align: 'left',
label: t('lines.image'),
name: 'image',
columnField: {
component: VnImg,
attrs: (id) => {
return {
id,
width: '50px',
};
},
},
columnFilter: false,
},
{ {
label: t('ticketComponents.description'), label: t('ticketComponents.description'),
name: 'description', name: 'description',
align: 'left', align: 'left',
columnClass: 'expand',
}, },
{ {
label: t('ticketComponents.quantity'), label: t('ticketComponents.quantity'),
name: 'quantity', name: 'quantity',
field: 'quantity', field: 'quantity',
align: 'left', align: 'left',
format: (val) => dashIfEmpty(val), format: (row) => dashIfEmpty(row.quantity),
}, },
{ {
label: t('ticketComponents.serie'), label: t('ticketComponents.serie'),
name: 'serie', name: 'serie',
align: 'left', align: 'left',
format: (row) => dashIfEmpty(row.serie),
}, },
{ {
label: t('ticketComponents.components'), label: t('ticketComponents.components'),
@ -174,181 +192,166 @@ onUnmounted(() => (stateStore.rightDrawer = false));
@on-fetch="(data) => (components = data)" @on-fetch="(data) => (components = data)"
auto-load auto-load
/> />
<RightMenu> <QDrawer side="right" :width="265" v-model="stateStore.rightDrawer">
<template #right-panel> <QCard class="q-pa-sm color-vn-text" bordered flat style="border-color: black">
<QCard <QCardSection horizontal>
class="q-pa-sm color-vn-text" <span class="text-weight-bold text-subtitle1 text-center full-width">
bordered {{ t('ticketComponents.total') }}
flat </span>
style="border-color: black" </QCardSection>
<QCardSection horizontal>
<span class="q-mr-xs color-vn-label"
>{{ t('ticketComponents.baseToCommission') }}:
</span>
<span>{{ toCurrency(getBase) }}</span>
</QCardSection>
<QCardSection horizontal>
<span class="q-mr-xs color-vn-label"
>{{ t('ticketComponents.totalWithoutVat') }}:
</span>
<span>{{ toCurrency(getTotal) }}</span>
</QCardSection>
</QCard>
<QCard class="q-pa-sm color-vn-text" bordered flat style="border-color: black">
<QCardSection horizontal>
<span class="text-weight-bold text-subtitle1 text-center full-width">
{{ t('ticketComponents.components') }}
</span>
</QCardSection>
<QCardSection
v-for="(component, index) in componentsList"
:key="index"
horizontal
> >
<QCardSection horizontal> <span v-if="component.name" class="q-mr-xs color-vn-label">
<span class="text-weight-bold text-subtitle1 text-center full-width"> {{ component.name }}:
{{ t('ticketComponents.total') }} </span>
</span> <span v-if="component.value">{{
</QCardSection> toCurrency(component.value, 'EUR', 3)
<QCardSection horizontal> }}</span>
<span class="q-mr-xs color-vn-label" </QCardSection>
>{{ t('ticketComponents.baseToCommission') }}: </QCard>
</span> <QCard class="q-pa-sm color-vn-text" bordered flat style="border-color: black">
<span>{{ toCurrency(getBase) }}</span> <QCardSection horizontal>
</QCardSection> <span class="text-weight-bold text-subtitle1 text-center full-width">
<QCardSection horizontal> {{ t('ticketComponents.zoneBreakdown') }}
<span class="q-mr-xs color-vn-label" </span>
>{{ t('ticketComponents.totalWithoutVat') }}: </QCardSection>
</span> <QCardSection horizontal>
<span>{{ toCurrency(getTotal) }}</span> <span class="q-mr-xs color-vn-label">
</QCardSection> {{ t('ticketComponents.price') }}:
</QCard> </span>
<QCard <span>{{ toCurrency(ticketData?.zonePrice, 'EUR', 2) }}</span>
class="q-pa-sm color-vn-text" </QCardSection>
bordered <QCardSection horizontal>
flat <span class="q-mr-xs color-vn-label">
style="border-color: black" {{ t('ticketComponents.bonus') }}:
> </span>
<QCardSection horizontal> <span>{{ toCurrency(ticketData?.zoneBonus, 'EUR', 2) }}</span>
<span class="text-weight-bold text-subtitle1 text-center full-width"> </QCardSection>
{{ t('ticketComponents.components') }} <QCardSection horizontal>
</span> <span class="q-mr-xs color-vn-label">
</QCardSection> {{ t('ticketComponents.zone') }}:
<QCardSection </span>
v-for="(component, index) in componentsList" <span class="link">
:key="index" {{ dashIfEmpty(ticketData?.zone?.name) }}
horizontal <ZoneDescriptorProxy :id="ticketData?.zone?.id" />
> </span>
<span v-if="component.name" class="q-mr-xs color-vn-label"> </QCardSection>
{{ component.name }}: <QCardSection v-if="ticketData?.zone?.isVolumetric" horizontal>
</span> <span class="q-mr-xs color-vn-label">
<span v-if="component.value">{{ {{ t('ticketComponents.volume') }}:
toCurrency(component.value, 'EUR', 3) </span>
}}</span> <span>{{ ticketVolume }}</span>
</QCardSection> </QCardSection>
</QCard> <QCardSection horizontal>
<QCard <span class="q-mr-xs color-vn-label">
class="q-pa-sm color-vn-text" {{ t('ticketComponents.packages') }}:
bordered </span>
flat <span>{{ dashIfEmpty(ticketData?.packages) }}</span>
style="border-color: black" </QCardSection>
> </QCard>
<QCardSection horizontal> <QCard class="q-pa-sm color-vn-text" bordered flat style="border-color: black">
<span class="text-weight-bold text-subtitle1 text-center full-width"> <QCardSection horizontal>
{{ t('ticketComponents.zoneBreakdown') }} <span class="text-weight-bold text-subtitle1 text-center full-width">
</span> {{ t('ticketComponents.theoricalCost') }}
</QCardSection> </span>
<QCardSection horizontal> </QCardSection>
<span class="q-mr-xs color-vn-label"> <QCardSection horizontal>
{{ t('ticketComponents.price') }}: <span class="q-mr-xs color-vn-label">
</span> {{ t('ticketComponents.totalPrice') }}:
<span>{{ toCurrency(ticketData?.zonePrice, 'EUR', 2) }}</span> </span>
</QCardSection> <span>{{ toCurrency(theoricalCost, 'EUR', 2) }}</span>
<QCardSection horizontal> </QCardSection>
<span class="q-mr-xs color-vn-label"> </QCard>
{{ t('ticketComponents.bonus') }}: </QDrawer>
</span> <VnTable
<span>{{ toCurrency(ticketData?.zoneBonus, 'EUR', 2) }}</span> ref="tableRef"
</QCardSection> data-key="TicketComponents"
<QCardSection horizontal> url="Sales"
<span class="q-mr-xs color-vn-label"> :user-filter="salesFilter"
{{ t('ticketComponents.zone') }}:
</span>
<span class="link">
{{ dashIfEmpty(ticketData?.zone?.name) }}
<ZoneDescriptorProxy :id="ticketData?.zone?.id" />
</span>
</QCardSection>
<QCardSection v-if="ticketData?.zone?.isVolumetric" horizontal>
<span class="q-mr-xs color-vn-label">
{{ t('ticketComponents.volume') }}:
</span>
<span>{{ ticketVolume }}</span>
</QCardSection>
<QCardSection horizontal>
<span class="q-mr-xs color-vn-label">
{{ t('ticketComponents.packages') }}:
</span>
<span>{{ dashIfEmpty(ticketData?.packages) }}</span>
</QCardSection>
</QCard>
<QCard
class="q-pa-sm color-vn-text"
bordered
flat
style="border-color: black"
>
<QCardSection horizontal>
<span class="text-weight-bold text-subtitle1 text-center full-width">
{{ t('ticketComponents.theoricalCost') }}
</span>
</QCardSection>
<QCardSection horizontal>
<span class="q-mr-xs color-vn-label">
{{ t('ticketComponents.totalPrice') }}:
</span>
<span>{{ toCurrency(theoricalCost, 'EUR', 2) }}</span>
</QCardSection>
</QCard>
</template>
</RightMenu>
<QTable
:rows="components"
:columns="columns" :columns="columns"
row-key="id" :right-search="false"
:pagination="{ rowsPerPage: 0 }" auto-load
class="full-width q-mt-md" :disable-option="{ card: true }"
:no-data-label="t('globals.noResults')" :column-search="false"
> >
<template #body-cell-item="{ row }"> <template #column-item="{ row }">
<QTd> <span @click.stop flat class="link">
<QBtn flat color="primary"> {{ row.itemFk }}
<span class="link">{{ row.itemFk }}</span> <ItemDescriptorProxy :id="row.itemFk" />
<ItemDescriptorProxy :id="row.itemFk" /> </span>
</QBtn>
</QTd>
</template> </template>
<template #body-cell-description="{ row }"> <template #column-image="{ row }">
<QTd> <div class="image-wrapper">
<div class="column"> <VnImg :id="parseInt(row?.item?.id)" class="rounded" />
<span>{{ row.item.name }}</span> </div>
<span class="color-vn-label">{{ row.item.subName }}</span>
<FetchedTags :item="row.item" />
</div>
</QTd>
</template> </template>
<template #body-cell-serie="{ row }"> <template #column-description="{ row }">
<QTd> <div class="column">
<div class="column"> <span>{{ row.item.name }}</span>
<span v-for="(saleComponent, index) in row.components" :key="index"> <span class="color-vn-label">{{ row.item.subName }}</span>
{{ saleComponent.component?.componentType?.name }} <FetchedTags :item="row.item" />
</span> </div>
</div>
</QTd>
</template> </template>
<template #body-cell-components="{ row }"> <template #column-serie="{ row }">
<QTd> <div class="column">
<div class="column"> <span v-for="(saleComponent, index) in row.components" :key="index">
<span v-for="(saleComponent, index) in row.components" :key="index"> {{ saleComponent.component?.componentType?.name }}
{{ saleComponent.component?.name }} </span>
</span> </div>
</div>
</QTd>
</template> </template>
<template #body-cell-import="{ row }"> <template #column-components="{ row }">
<QTd> <div class="column">
<div class="column text-right"> <span v-for="(saleComponent, index) in row.components" :key="index">
<span v-for="(saleComponent, index) in row.components" :key="index"> {{ saleComponent.component?.name }}
</span>
</div>
</template>
<template #column-import="{ row }">
<div class="column text-left">
<span v-for="(saleComponent, index) in row.components" :key="index">
{{ toCurrency(saleComponent.value, 'EUR', 3) }}
</span>
</div>
</template>
<template #column-total="{ row }">
<div class="column">
<span v-for="(saleComponent, index) in row.components" :key="index">
{{ toCurrency(saleComponent.value * row.quantity, 'EUR', 3) }}
<!-- <QTooltip>
{{ saleComponent.component?.name }}:
{{ toCurrency(saleComponent.value, 'EUR', 3) }} {{ toCurrency(saleComponent.value, 'EUR', 3) }}
</span> </QTooltip> -->
</div> </span>
</QTd> </div>
</template> </template>
<template #body-cell-total="{ row }"> </VnTable>
<QTd>
<div class="column text-right">
<span v-for="(saleComponent, index) in row.components" :key="index">
{{ toCurrency(saleComponent.value * row.quantity, 'EUR', 3) }}
</span>
</div>
</QTd>
</template>
</QTable>
</template> </template>
<style lang="scss" scoped>
.image-wrapper {
width: 40px;
height: 40px;
}
</style>

View File

@ -2,13 +2,13 @@
import { ref, computed } from 'vue'; import { ref, computed } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { toDate } from 'src/filters';
import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue'; import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
import CardDescriptor from 'components/ui/CardDescriptor.vue'; import CardDescriptor from 'components/ui/CardDescriptor.vue';
import TicketDescriptorMenu from './TicketDescriptorMenu.vue'; import TicketDescriptorMenu from './TicketDescriptorMenu.vue';
import VnLv from 'src/components/ui/VnLv.vue'; import VnLv from 'src/components/ui/VnLv.vue';
import useCardDescription from 'src/composables/useCardDescription'; import useCardDescription from 'src/composables/useCardDescription';
import VnUserLink from 'src/components/ui/VnUserLink.vue'; import VnUserLink from 'src/components/ui/VnUserLink.vue';
import { toDateTimeFormat } from 'src/filters/date';
const $props = defineProps({ const $props = defineProps({
id: { id: {
@ -30,13 +30,24 @@ const filter = {
{ {
relation: 'address', relation: 'address',
scope: { scope: {
fields: ['id', 'name', 'mobile', 'phone'], fields: ['id', 'name', 'mobile', 'phone', 'incotermsFk'],
}, },
}, },
{ {
relation: 'client', relation: 'client',
scope: { scope: {
fields: ['id', 'name', 'salesPersonFk', 'phone', 'mobile', 'email'], fields: [
'id',
'name',
'salesPersonFk',
'phone',
'mobile',
'email',
'isActive',
'isFreezed',
'isTaxDataChecked',
'hasElectronicInvoice',
],
include: [ include: [
{ {
relation: 'user', relation: 'user',
@ -87,6 +98,10 @@ const filter = {
}; };
const data = ref(useCardDescription()); const data = ref(useCardDescription());
function ticketFilter(ticket) {
return JSON.stringify({ clientFk: ticket.clientFk });
Review

Creo que no se está ejecutando

Creo que no se está ejecutando
}
</script> </script>
<template> <template>
@ -128,7 +143,10 @@ const data = ref(useCardDescription());
/> />
</template> </template>
</VnLv> </VnLv>
<VnLv :label="t('ticket.card.shipped')" :value="toDate(entity.shipped)" /> <VnLv
:label="t('ticket.card.shipped')"
:value="toDateTimeFormat(entity.shipped)"
/>
<VnLv <VnLv
v-if="entity.agencyMode" v-if="entity.agencyMode"
:label="t('ticket.card.agency')" :label="t('ticket.card.agency')"
@ -138,7 +156,39 @@ const data = ref(useCardDescription());
<VnLv :label="t('ticket.card.alias')" :value="entity.nickname" /> <VnLv :label="t('ticket.card.alias')" :value="entity.nickname" />
</template> </template>
<template #icons="{ entity }"> <template #icons="{ entity }">
<QCardActions> <QCardActions class="q-gutter-x-xs">
<QIcon
v-if="entity.client.isActive == false"
name="vn:disabled"
size="xs"
color="primary"
>
<QTooltip>{{ t('Client inactive') }}</QTooltip>
</QIcon>
<QIcon
v-if="entity.client.isFreezed == true"
name="vn:frozen"
size="xs"
color="primary"
>
<QTooltip>{{ t('Client Frozen') }}</QTooltip>
</QIcon>
<QIcon
v-if="entity.problem.includes('hasRisk')"
name="vn:risk"
size="xs"
color="primary"
>
<QTooltip>{{ t('Client has debt') }}</QTooltip>
</QIcon>
<QIcon
v-if="entity.client.isTaxDataChecked == false"
name="vn:no036"
size="xs"
color="primary"
>
<QTooltip>{{ t('Client not checked') }}</QTooltip>
</QIcon>
<QIcon <QIcon
v-if="entity.isDeleted == true" v-if="entity.isDeleted == true"
name="vn:deletedTicket" name="vn:deletedTicket"
@ -159,6 +209,27 @@ const data = ref(useCardDescription());
> >
<QTooltip>{{ t('ticket.card.customerCard') }}</QTooltip> <QTooltip>{{ t('ticket.card.customerCard') }}</QTooltip>
</QBtn> </QBtn>
<QBtn
size="md"
icon="vn:ticket"
color="primary"
:to="{ name: 'TicketList', query: { table: ticketFilter(entity) } }"
>
<QTooltip>{{ t('ticket.card.ticketList') }}</QTooltip>
</QBtn>
<QBtn
size="md"
icon="vn:basketadd"
color="primary"
:to="{
name: 'OrderList',
query: {
createForm: JSON.stringify({ clientFk: entity.clientFk }),
},
}"
>
<QTooltip>{{ t('ticket.card.newOrder') }}</QTooltip>
</QBtn>
</QCardActions> </QCardActions>
</template> </template>
</CardDescriptor> </CardDescriptor>
@ -168,4 +239,8 @@ const data = ref(useCardDescription());
es: es:
This ticket is deleted: Este ticket está eliminado This ticket is deleted: Este ticket está eliminado
Go to module index: Ir al índice del modulo Go to module index: Ir al índice del modulo
Client inactive: Cliente inactivo
Client not checked: Cliente no verificado
Client has debt: Cliente con deuda
Client Frozen: Cliente congelado
</i18n> </i18n>

View File

@ -1,6 +1,6 @@
<script setup> <script setup>
import axios from 'axios'; import axios from 'axios';
import { ref } from 'vue'; import { ref, toRefs } from 'vue';
jon marked this conversation as resolved
Review

Revisate la traduccion "¡Se eliminará el ticket de la ruta actual! ¿Continuar de todas formas?" en salix, porque parece que en Lilium no existe. Fallo mio y de william

Revisate la traduccion "¡Se eliminará el ticket de la ruta actual! ¿Continuar de todas formas?" en salix, porque parece que en Lilium no existe. Fallo mio y de william
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
@ -9,6 +9,11 @@ import SendEmailDialog from 'components/common/SendEmailDialog.vue';
import VnConfirm from 'components/ui/VnConfirm.vue'; import VnConfirm from 'components/ui/VnConfirm.vue';
import VnSmsDialog from 'components/common/VnSmsDialog.vue'; import VnSmsDialog from 'components/common/VnSmsDialog.vue';
import toDate from 'filters/toDate'; import toDate from 'filters/toDate';
import FormPopup from 'components/FormPopup.vue';
import VnSelect from 'components/common/VnSelect.vue';
import FetchData from 'components/FetchData.vue';
import VnInputTime from 'src/components/common/VnInputTime.vue';
import { useAcl } from 'src/composables/useAcl';
import VnInputNumber from 'src/components/common/VnInputNumber.vue'; import VnInputNumber from 'src/components/common/VnInputNumber.vue';
import { useArrayData } from 'src/composables/useArrayData'; import { useArrayData } from 'src/composables/useArrayData';
@ -24,8 +29,16 @@ const { dialog, notify } = useQuasar();
const { t } = useI18n(); const { t } = useI18n();
const { openReport, sendEmail } = usePrintService(); const { openReport, sendEmail } = usePrintService();
const ticketSummary = useArrayData('TicketSummary'); const ticketSummary = useArrayData('TicketSummary');
const ticket = ref(props.ticket); const { ticket } = toRefs(props);
const ticketId = currentRoute.value.params.id; const ticketId = currentRoute.value.params.id;
const client = ref();
const showTransferDialog = ref(false);
const showTurnDialog = ref(false);
const showChangeTimeDialog = ref(false);
const dialogRef = ref();
const isEditable = ref();
const hasInvoicing = useAcl('invoicing');
const hasPdf = ref();
const weight = ref(); const weight = ref();
const actions = { const actions = {
clone: async () => { clone: async () => {
@ -76,6 +89,7 @@ const actions = {
notify({ notify({
message: t('You can undo this action within the first hour'), message: t('You can undo this action within the first hour'),
icon: 'info', icon: 'info',
type: 'warning',
}); });
push({ name: 'TicketList' }); push({ name: 'TicketList' });
@ -87,10 +101,14 @@ const actions = {
function openDeliveryNote(type = 'deliveryNote', documentType = 'pdf') { function openDeliveryNote(type = 'deliveryNote', documentType = 'pdf') {
const path = `Tickets/${ticket.value.id}/delivery-note-${documentType}`; const path = `Tickets/${ticket.value.id}/delivery-note-${documentType}`;
openReport(path, { openReport(
recipientId: ticket.value.clientFk, path,
type: type, {
}); recipientId: ticket.value.clientFk,
type: type,
},
'_blank'
);
} }
function sendDeliveryNoteConfirmation(type = 'deliveryNote', documentType = 'pdf') { function sendDeliveryNoteConfirmation(type = 'deliveryNote', documentType = 'pdf') {
@ -173,34 +191,280 @@ function openConfirmDialog(callback) {
dialog({ dialog({
component: VnConfirm, component: VnConfirm,
componentProps: { componentProps: {
title: t('This ticket will be removed from current route! Continue anyway?'),
message: t('You are going to delete this ticket'),
promise: actions[callback], promise: actions[callback],
}, },
}); });
} }
function generatePdfDialog() {
dialog({
component: VnConfirm,
componentProps: {
promise: generatePdfInvoice,
},
});
}
async function generatePdfInvoice() {
const { data } = await axios.get('invoiceOuts', {
params: {
filter: JSON.stringify({
where: { ref: ticket.value.refFk },
}),
},
});
const invoiceId = data[0].id;
const { response } = await axios.post(`InvoiceOuts/${invoiceId}/createPdf`);
if (!response) {
notify({
message: 'The invoice PDF document has been regenerated',
type: 'positive',
});
}
}
function makeInvoiceDialog() {
dialog({
component: VnConfirm,
componentProps: {
title: t('Are you sure you want to invoice this ticket?'),
message: t('You are going to invoice this ticket'),
promise: makeInvoice,
},
});
}
async function makeInvoice() {
const params = {
ticketsIds: [parseInt(ticketId)],
};
await axios.post(`Tickets/invoiceTicketsAndPdf`, params);
notify({
message: t('Ticket invoiced'),
type: 'positive',
});
window.location.reload();
}
async function transferClient(client) {
const params = {
clientFk: client,
};
const { data } = await axios.patch(`Tickets/${ticketId}/transferClient`, params);
if (data) window.location.reload();
}
async function addTurn(day) {
const params = {
ticketFk: parseInt(ticketId),
weekDay: day,
agencyModeFk: ticket.value.agencyModeFk,
};
await axios.patch(`TicketWeeklies`, params);
notify({
message: t('Current ticket deleted and added to shift'),
type: 'positive',
});
window.location.reload();
}
async function createRefund(withWarehouse) {
const params = {
ticketsIds: [parseInt(ticketId)],
withWarehouse: withWarehouse,
};
const { data } = await axios.post(`Tickets/refund`, params);
if (data) {
const refundTicket = data;
push({ name: 'TicketSale', params: { id: refundTicket[0].id } });
}
}
async function changeShippedHour(time) {
const params = {
shipped: time,
};
const { data } = await axios.post(`Tickets/${ticketId}/updateEditableTicket`, params);
if (data) window.location.reload();
}
function openRecalculateDialog() {
dialog({
component: VnConfirm,
componentProps: {
title: t('Recalculate components'),
message: t('Are you sure you want to recalculate components?'),
promise: recalculateComponents,
},
});
}
async function recalculateComponents() {
await axios.post(`Tickets/${ticketId}/recalculateComponents`);
notify({
message: t('Data saved'),
type: 'positive',
});
window.location.reload();
}
const handleFetchData = (data) => {
isEditable.value = data;
handleInvoiceOutData();
};
async function handleInvoiceOutData() {
const { data } = await axios.get(`InvoiceOuts`, {
params: {
filter: JSON.stringify({ where: { ref: ticket.value.refFk } }),
},
});
hasPdf.value = data[0]?.hasPdf;
}
</script> </script>
<template> <template>
<FetchData
:url="`Tickets/${ticketId}/isEditable`"
auto-load
@on-fetch="handleFetchData"
/>
<QItem @click="showTransferDialog = !showTransferDialog" v-ripple clickable>
<QItemSection avatar>
<QIcon name="content_paste" />
</QItemSection>
<QItemSection>{{ t('Transfer client') }}</QItemSection>
</QItem>
<QDialog ref="dialogRef" v-model="showTransferDialog">
<FormPopup
@on-submit="transferClient(client)"
:title="t('Transfer client')"
:custom-submit-button-label="t('Transfer client')"
:default-cancel-button="false"
>
<template #form-inputs>
<VnSelect
url="Clients"
:fields="['id', 'name']"
option-label="name"
option-value="id"
v-model="client"
:label="t('Client')"
auto-load
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>
{{ `#${scope.opt.id} - ` }}
{{ scope.opt.name }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
</template>
</FormPopup>
</QDialog>
<QItem @click="showTurnDialog = !showTurnDialog" v-ripple clickable>
<QItemSection avatar>
<QIcon name="vn:calendar" />
</QItemSection>
<QItemSection>{{ t('addTurn') }}</QItemSection>
</QItem>
<QDialog ref="dialogRef" v-model="showTurnDialog">
<FormPopup
@on-submit="addTurn"
:title="t('What is the day of receipt of the ticket?')"
:default-submit-button="false"
:default-cancel-button="false"
style="text-align: center"
>
<template #form-inputs>
<QBtnGroup spread>
<QBtn
:label="t('weekdays.mon')"
color="primary"
@click="addTurn(0)"
v-ripple
class="weekdaysBtn"
/>
<QBtn
:label="t('weekdays.tue')"
color="primary"
@click="addTurn(1)"
v-ripple
class="weekdaysBtn"
/>
<QBtn
:label="t('weekdays.wed')"
color="primary"
@click="addTurn(2)"
v-ripple
class="weekdaysBtn"
/>
<QBtn
:label="t('weekdays.thu')"
color="primary"
@click="addTurn(3)"
v-ripple
class="weekdaysBtn"
/>
<QBtn
:label="t('weekdays.fri')"
color="primary"
@click="addTurn(4)"
v-ripple
class="weekdaysBtn"
/>
<QBtn
:label="t('weekdays.sat')"
color="primary"
@click="addTurn(5)"
v-ripple
class="weekdaysBtn"
/>
<QBtn
:label="t('weekdays.sun')"
color="primary"
@click="addTurn(6)"
v-ripple
class="weekdaysBtn"
/>
</QBtnGroup>
</template>
</FormPopup>
</QDialog>
<QItem v-ripple clickable> <QItem v-ripple clickable>
<QItemSection avatar> <QItemSection avatar>
<QIcon name="picture_as_pdf" /> <QIcon name="picture_as_pdf" />
</QItemSection> </QItemSection>
<QItemSection>{{ t('Open Delivery Note...') }}</QItemSection> <QItemSection>{{ t('Show Delivery Note...') }}</QItemSection>
<QItemSection side> <QItemSection side>
<QIcon name="keyboard_arrow_right" /> <QIcon name="keyboard_arrow_right" />
</QItemSection> </QItemSection>
<QMenu anchor="top end" self="top start" auto-close bordered> <QMenu anchor="top end" self="top start" auto-close bordered>
<QList> <QList>
<QItem @click="openDeliveryNote('deliveryNote')" v-ripple clickable> <QItem @click="openDeliveryNote('deliveryNote')" v-ripple clickable>
<QItemSection>{{ t('With prices') }}</QItemSection> <QItemSection>{{ t('as PDF') }}</QItemSection>
</QItem> </QItem>
<QItem @click="openDeliveryNote('withoutPrices')" v-ripple clickable> <QItem @click="openDeliveryNote('withoutPrices')" v-ripple clickable>
<QItemSection>{{ t('Without prices') }}</QItemSection> <QItemSection>{{ t('as PDF without prices') }}</QItemSection>
</QItem> </QItem>
<QItem <QItem
@click="openDeliveryNote('deliveryNote', 'csv')" @click="openDeliveryNote('deliveryNote', 'csv')"
v-ripple v-ripple
clickable clickable
> >
<QItemSection>{{ t('As CSV') }}</QItemSection> <QItemSection>{{ t('as CSV') }}</QItemSection>
</QItem> </QItem>
</QList> </QList>
</QMenu> </QMenu>
@ -220,21 +484,21 @@ function openConfirmDialog(callback) {
v-ripple v-ripple
clickable clickable
> >
<QItemSection>{{ t('With prices') }}</QItemSection> <QItemSection>{{ t('Send PDF') }}</QItemSection>
</QItem> </QItem>
<QItem <QItem
@click="sendDeliveryNoteConfirmation('withoutPrices')" @click="sendDeliveryNoteConfirmation('withoutPrices')"
v-ripple v-ripple
clickable clickable
> >
<QItemSection>{{ t('Without prices') }}</QItemSection> <QItemSection>{{ t('Send PDF to tablet') }}</QItemSection>
</QItem> </QItem>
<QItem <QItem
@click="sendDeliveryNoteConfirmation('deliveryNote', 'csv')" @click="sendDeliveryNoteConfirmation('deliveryNote', 'csv')"
v-ripple v-ripple
clickable clickable
> >
<QItemSection>{{ t('As CSV') }}</QItemSection> <QItemSection>{{ t('Send CSV') }}</QItemSection>
</QItem> </QItem>
</QList> </QList>
</QMenu> </QMenu>
@ -243,8 +507,26 @@ function openConfirmDialog(callback) {
<QItemSection avatar> <QItemSection avatar>
<QIcon name="receipt" /> <QIcon name="receipt" />
</QItemSection> </QItemSection>
<QItemSection>{{ t('Open Proforma Invoice') }}</QItemSection> <QItemSection>{{ t('Show Proforma') }}</QItemSection>
</QItem> </QItem>
<QItem
v-if="isEditable"
@click="showChangeTimeDialog = !showChangeTimeDialog"
v-ripple
clickable
>
<QItemSection avatar>
<QIcon name="schedule" />
</QItemSection>
<QItemSection>{{ t('Change shipped hour') }}</QItemSection>
</QItem>
<QDialog ref="dialogRef" v-model="showChangeTimeDialog">
<FormPopup @on-submit="changeShippedHour(time)" :title="t('Change shipped hour')">
<template #form-inputs>
<VnInputTime v-model="time" :label="t('Shipped hour')" clearable />
</template>
</FormPopup>
</QDialog>
<QItem v-ripple clickable> <QItem v-ripple clickable>
<QItemSection avatar> <QItemSection avatar>
<QIcon name="sms" /> <QIcon name="sms" />
@ -259,24 +541,72 @@ function openConfirmDialog(callback) {
<QItemSection>{{ t('Pending payment') }}</QItemSection> <QItemSection>{{ t('Pending payment') }}</QItemSection>
</QItem> </QItem>
<QItem @click="showSmsDialog('minAmount')" v-ripple clickable> <QItem @click="showSmsDialog('minAmount')" v-ripple clickable>
<QItemSection>{{ t('Minimum amount') }}</QItemSection> <QItemSection>{{ t('Minimum import') }}</QItemSection>
</QItem> </QItem>
<QItem <QItem
@click="showSmsDialogWithChanges('orderChanges')" @click="showSmsDialogWithChanges('orderChanges')"
v-ripple v-ripple
clickable clickable
> >
<QItemSection>{{ t('Order changes') }}</QItemSection> <QItemSection>{{ t('Notify changes') }}</QItemSection>
</QItem> </QItem>
</QList> </QList>
</QMenu> </QMenu>
</QItem> </QItem>
<QItem @click="makeInvoiceDialog()" v-ripple clickable v-if="isEditable">
<QItemSection avatar>
<QIcon name="picture_as_pdf" />
</QItemSection>
<QItemSection>{{ t('Make invoice') }}</QItemSection>
</QItem>
<QItem
@click="generatePdfDialog()"
v-ripple
clickable
v-if="ticket.refFk !== null && (hasInvoicing || hasPdf)"
>
<QItemSection avatar>
<QIcon name="picture_as_pdf" />
</QItemSection>
<QItemSection>{{
hasPdf ? t('Regenerate PDF invoice') : t('Generate PDF invoice')
}}</QItemSection>
</QItem>
<QItem @click="openConfirmDialog('clone')" v-ripple clickable> <QItem @click="openConfirmDialog('clone')" v-ripple clickable>
<QItemSection avatar> <QItemSection avatar>
<QIcon name="content_copy" /> <QIcon name="content_copy" />
</QItemSection> </QItemSection>
<QItemSection>{{ t('To clone ticket') }}</QItemSection> <QItemSection>{{ t('To clone ticket') }}</QItemSection>
</QItem> </QItem>
<QItem v-if="isEditable" @click="openRecalculateDialog()" v-ripple clickable>
<QItemSection avatar>
<QIcon name="refresh" />
</QItemSection>
<QItemSection>{{ t('Recalculate components') }}</QItemSection>
</QItem>
<QItem v-ripple clickable>
<QItemSection avatar>
<QIcon name="monetization_on" />
</QItemSection>
<QItemSection>{{ t('Refund all...') }}</QItemSection>
<QItemSection side>
<QIcon name="keyboard_arrow_right" />
</QItemSection>
<QMenu anchor="top end" self="top start" auto-close bordered>
<QList>
<QItem v-ripple clickable @click="createRefund(true)">
<QItemSection>
{{ t('with warehouse') }}
</QItemSection>
</QItem>
<QItem v-ripple clickable @click="createRefund(false)">
<QItemSection>
{{ t('without warehouse') }}
</QItemSection>
</QItem>
</QList>
</QMenu>
</QItem>
<QItem @click="$refs.weightDialog.show()" v-ripple clickable> <QItem @click="$refs.weightDialog.show()" v-ripple clickable>
<QItemSection avatar> <QItemSection avatar>
<QIcon name="weight" /> <QIcon name="weight" />
@ -307,29 +637,61 @@ function openConfirmDialog(callback) {
</template> </template>
</VnConfirm> </VnConfirm>
</template> </template>
<style lang="scss" scoped>
.weekdaysBtn {
margin: 1%;
}
</style>
<i18n> <i18n>
en: en:
addTurn: Add turn
invoiceIds: "Invoices have been generated with the following ids: {invoiceIds}" invoiceIds: "Invoices have been generated with the following ids: {invoiceIds}"
es: es:
Open Delivery Note...: Abrir albarán... Show Delivery Note...: Ver albarán...
Send Delivery Note...: Enviar albarán... Send Delivery Note...: Enviar albarán...
With prices: Con precios as PDF: como PDF
Without prices: Sin precios as PDF without prices: como PDF sin precios
As CSV: Como CSV as CSV: Como CSV
Open Proforma Invoice: Abrir factura proforma Send PDF: Enviar PDF
Send PDF to tablet: Enviar PDF a tablet
Send CSV: Enviar CSV
Show Proforma: Ver proforma
Delete ticket: Eliminar ticket Delete ticket: Eliminar ticket
Send SMS...: Enviar SMS Send SMS...: Enviar SMS...
Pending payment: Pago pendiente Pending payment: Pago pendiente
Minimum amount: Importe mínimo Minimum import: Importe mínimo
Order changes: Cambios del pedido Notify changes: Notificar cambios
Ticket deleted: Ticket eliminado Ticket deleted: Ticket eliminado
You can undo this action within the first hour: Puedes deshacer esta acción dentro de la primera hora You can undo this action within the first hour: Puedes deshacer esta acción dentro de la primera hora
To clone ticket: Clonar ticket To clone ticket: Clonar ticket
Ticket cloned: Ticked clonado Ticket cloned: Ticked clonado
It was not able to clone the ticket: No se pudo clonar el ticket It was not able to clone the ticket: No se pudo clonar el ticket
Generate PDF invoice: Generar PDF factura
Regenerate PDF invoice: Regenerar PDF factura
The invoice PDF document has been regenerated: El documento PDF de la factura ha sido regenerado
Transfer client: Transferir cliente
Client: Cliente
addTurn: Añadir a turno
What is the day of receipt of the ticket?: ¿Cuál es el día de preparación del pedido?
Current ticket deleted and added to shift: Ticket actual eliminado y añadido al turno
Refund all...: Abonar todo...
with warehouse: con almacén
without warehouse: sin almacén
Make invoice: Crear factura
Change shipped hour: Cambiar hora de envío
Shipped hour: Hora de envío
Recalculate components: Recalcular componentes
Are you sure you want to recalculate components?: ¿Seguro que quieres recalcular los componentes?
Data saved: Datos guardados
Are you sure you want to invoice this ticket?: ¿Seguro que quieres facturar este ticket?
You are going to invoice this ticket: Vas a facturar este ticket
Ticket invoiced: Ticket facturado
Set weight: Establecer peso Set weight: Establecer peso
Weight set: Peso establecido Weight set: Peso establecido
This ticket may be invoiced, do you want to continue?: Es posible que se facture este ticket, desea continuar? This ticket may be invoiced, do you want to continue?: Es posible que se facture este ticket, desea continuar?
invoiceIds: "Se han generado las facturas con los siguientes ids: {invoiceIds}" invoiceIds: "Se han generado las facturas con los siguientes ids: {invoiceIds}"
This ticket will be removed from current route! Continue anyway?: ¡Se eliminará el ticket de la ruta actual! ¿Continuar de todas formas?
You are going to delete this ticket: Vas a eliminar este ticket
</i18n> </i18n>

View File

@ -1,39 +1,35 @@
<script setup> <script setup>
import { onMounted, ref, computed, onUnmounted, reactive, watch } from 'vue'; import { onMounted, ref, computed, onUnmounted, watch } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import VnInput from 'src/components/common/VnInput.vue';
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue'; import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
import TicketEditManaProxy from './TicketEditMana.vue';
import TableVisibleColumns from 'src/components/common/TableVisibleColumns.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import ExpeditionNewTicket from './ExpeditionNewTicket.vue'; import ExpeditionNewTicket from './ExpeditionNewTicket.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import { toCurrency, toPercentage } from 'src/filters';
import { useArrayData } from 'composables/useArrayData'; import { useArrayData } from 'composables/useArrayData';
import { useVnConfirm } from 'composables/useVnConfirm'; import { useVnConfirm } from 'composables/useVnConfirm';
import useNotify from 'src/composables/useNotify.js'; import useNotify from 'src/composables/useNotify.js';
import { toDateTimeFormat } from 'src/filters/date'; import { toDateTimeFormat } from 'src/filters/date';
import axios from 'axios'; import axios from 'axios';
import VnTable from 'src/components/VnTable/VnTable.vue';
const route = useRoute(); const route = useRoute();
const stateStore = useStateStore(); const stateStore = useStateStore();
const { t } = useI18n(); const { t } = useI18n();
const { notify } = useNotify(); const { notify } = useNotify();
const { openConfirmationModal } = useVnConfirm(); const { openConfirmationModal } = useVnConfirm();
const editPriceProxyRef = ref(null);
const newTicketDialogRef = ref(null); const newTicketDialogRef = ref(null);
const logsTableDialogRef = ref(null); const logsTableDialogRef = ref(null);
const tableRef = ref();
const expeditionsLogsData = ref([]); const expeditionsLogsData = ref([]);
const selectedExpeditions = ref([]); const selectedExpeditions = ref([]);
const allColumnNames = ref([]); const allColumnNames = ref([]);
const visibleColumns = ref([]);
const newTicketWithRoute = ref(false); const newTicketWithRoute = ref(false);
const selectedRows = ref([]);
const hasSelectedRows = computed(() => selectedRows.value.length > 0);
const exprBuilder = (param, value) => { const exprBuilder = (param, value) => {
switch (param) { switch (param) {
@ -54,8 +50,6 @@ const expeditionsArrayData = useArrayData('ticketExpeditions', {
filter: expeditionsFilter.value, filter: expeditionsFilter.value,
exprBuilder: exprBuilder, exprBuilder: exprBuilder,
}); });
const expeditionsStore = expeditionsArrayData.store;
const ticketExpeditions = computed(() => expeditionsStore.data);
const ticketArrayData = useArrayData('ticketData'); const ticketArrayData = useArrayData('ticketData');
const ticketStore = ticketArrayData.store; const ticketStore = ticketArrayData.store;
@ -73,129 +67,87 @@ watch(
{ immediate: true } { immediate: true }
); );
const params = reactive({});
const applyColumnFilter = async (col) => {
try {
const paramKey = col.columnFilter?.filterParamKey || col.field;
params[paramKey] = col.columnFilter.filterValue;
await expeditionsArrayData.addFilter({ filter: expeditionsFilter.value, params });
} catch (err) {
console.error('Error applying column filter', err);
}
};
const getInputEvents = (col) => {
return col.columnFilter.type === 'select'
? { 'update:modelValue': () => applyColumnFilter(col) }
: {
'keyup.enter': () => applyColumnFilter(col),
};
};
const columns = computed(() => [ const columns = computed(() => [
{ {
align: 'left',
label: t('expedition.id'), label: t('expedition.id'),
name: 'id', name: 'id',
field: 'id', chip: {
align: 'left', condition: () => true,
sortable: true, },
isId: true,
columnFilter: { columnFilter: {
component: VnInput, inWhere: true,
type: 'text',
filterParamKey: 'expeditionFk',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
}, },
}, },
{ {
label: t('expedition.item'), label: t('expedition.item'),
name: 'item', name: 'packagingItemFk',
align: 'left', align: 'left',
cardVisible: true,
columnFilter: { columnFilter: {
component: VnInput, inWhere: true,
type: 'text',
filterParamKey: 'packageItemName',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
}, },
}, },
{ {
label: t('expedition.name'), label: t('expedition.name'),
name: 'name', name: 'packageItemName',
field: 'packageItemName',
align: 'left', align: 'left',
isTitle: true,
columnFilter: { columnFilter: {
component: VnSelect, inWhere: true,
type: 'select',
filterValue: null,
event: getInputEvents,
attrs: {
url: 'Items',
fields: ['id', 'name'],
'sort-by': 'name ASC',
'option-value': 'id',
'option-label': 'name',
dense: true,
},
}, },
}, },
{ {
label: t('expedition.packageType'), label: t('expedition.packageType'),
name: 'packageType', name: 'freightItemName',
field: 'freightItemName',
align: 'left', align: 'left',
columnFilter: { columnFilter: {
component: VnInput, inWhere: true,
type: 'text',
// filterParamKey: 'expeditionFk',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
}, },
}, },
{ {
label: t('expedition.counter'), label: t('expedition.counter'),
name: 'counter', name: 'counter',
field: 'counter',
align: 'left', align: 'left',
columnFilter: null, columnFilter: {
inWhere: true,
},
}, },
{ {
label: t('expedition.externalId'), label: t('expedition.externalId'),
name: 'externalId', name: 'externalId',
field: 'externalId',
align: 'left', align: 'left',
columnFilter: null, cardVisible: true,
columnFilter: {
inWhere: true,
},
}, },
{ {
label: t('expedition.created'), label: t('expedition.created'),
name: 'created', name: 'created',
field: 'created',
align: 'left', align: 'left',
columnFilter: null, cardVisible: true,
format: (value) => toDateTimeFormat(value), format: (row) => toDateTimeFormat(row.created),
}, },
{ {
label: t('expedition.state'), label: t('expedition.state'),
name: 'state', name: 'state',
field: 'state',
align: 'left', align: 'left',
columnFilter: null, cardVisible: true,
columnFilter: { inWhere: true },
}, },
{ {
label: '', align: 'right',
name: 'history', name: 'tableActions',
align: 'left', actions: [
columnFilter: null, {
title: t('expedition.historyAction'),
icon: 'history',
isPrimary: true,
action: (row) => showLog(row),
},
],
}, },
]); ]);
@ -204,23 +156,29 @@ const logTableColumns = computed(() => [
label: t('expedition.state'), label: t('expedition.state'),
name: 'state', name: 'state',
field: 'state', field: 'state',
align: 'left', align: 'center',
sortable: true, sortable: true,
}, },
{ {
label: t('expedition.name'), label: t('expedition.name'),
name: 'name', name: 'name',
align: 'name', field: 'name',
align: 'center',
columnFilter: null, columnFilter: null,
}, },
{ {
label: t('expedition.created'), label: t('expedition.created'),
name: 'created', name: 'created',
field: 'created', field: 'created',
align: 'left', align: 'center',
columnFilter: null,
format: (value) => toDateTimeFormat(value), format: (value) => toDateTimeFormat(value),
}, },
{
label: t('expedition.isScanned'),
name: 'isScanned',
field: 'isScanned',
align: 'center',
},
]); ]);
const showNewTicketDialog = (withRoute = false) => { const showNewTicketDialog = (withRoute = false) => {
@ -255,10 +213,20 @@ const getExpeditionState = async (expedition) => {
order: ['created DESC'], order: ['created DESC'],
}; };
const { data } = await axios.get(`ExpeditionStates/filter`, { const { data: expeditionStates } = await axios.get(`ExpeditionStates/filter`, {
params: { filter: JSON.stringify(filter) }, params: { filter: JSON.stringify(filter) },
}); });
expeditionsLogsData.value = data; const { data: scannedStates } = await axios.get(`ExpeditionStates`, {
params: { filter: JSON.stringify(filter), fields: ['id', 'isScanned'] },
});
expeditionsLogsData.value = expeditionStates.map((state) => {
const scannedState = scannedStates.find((s) => s.id === state.id);
return {
...state,
isScanned: scannedState ? scannedState.isScanned : false,
};
});
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
@ -275,21 +243,13 @@ onUnmounted(() => (stateStore.rightDrawer = false));
<template> <template>
<VnSubToolbar> <VnSubToolbar>
<template #st-data>
<TableVisibleColumns
:all-columns="allColumnNames"
table-code="expeditionIndex"
labels-traductions-path="expedition"
@on-config-saved="visibleColumns = [...$event, 'history']"
/>
</template>
<template #st-actions> <template #st-actions>
<QBtnGroup push class="q-gutter-x-sm" flat> <QBtnGroup push class="q-gutter-x-sm" flat>
<QBtnDropdown <QBtnDropdown
ref="btnDropdownRef" ref="btnDropdownRef"
color="primary" color="primary"
:label="t('expedition.move')" :label="t('expedition.move')"
:disable="!selectedExpeditions.length" :disable="!hasSelectedRows"
> >
<template #label> <template #label>
<QTooltip>{{ t('Select lines to see the options') }}</QTooltip> <QTooltip>{{ t('Select lines to see the options') }}</QTooltip>
@ -322,7 +282,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
</QList> </QList>
</QBtnDropdown> </QBtnDropdown>
<QBtn <QBtn
:disable="!selectedExpeditions.length" :disable="!hasSelectedRows"
icon="delete" icon="delete"
color="primary" color="primary"
@click=" @click="
@ -332,115 +292,34 @@ onUnmounted(() => (stateStore.rightDrawer = false));
deleteExpedition deleteExpedition
) )
" "
/> >
<QTooltip>{{ t('expedition.removeExpedition') }}</QTooltip>
</QBtn>
</QBtnGroup> </QBtnGroup>
</template> </template>
</VnSubToolbar> </VnSubToolbar>
<QTable <VnTable
:rows="ticketExpeditions" ref="tableRef"
data-key="TicketExpedition"
url="Expeditions/filter"
:columns="columns" :columns="columns"
row-key="id" :filter="expeditionsFilter"
:pagination="{ rowsPerPage: 0 }" v-model:selected="selectedRows"
class="full-width q-mt-md" :table="{
selection="multiple" 'row-key': 'id',
v-model:selected="selectedExpeditions" selection: 'multiple',
:visible-columns="visibleColumns" }"
:no-data-label="t('globals.noResults')" auto-load
order="created DESC"
> >
<template #top-row="{ cols }"> <template #column-packagingItemFk="{ row }">
<QTr> <span class="link" @click.stop>
<QTd /> {{ row.packagingItemFk }}
<QTd v-for="(col, index) in cols" :key="index" style="max-width: 100px">
<component
:is="col.columnFilter.component"
v-if="col.columnFilter"
v-model="col.columnFilter.filterValue"
v-bind="col.columnFilter.attrs"
v-on="col.columnFilter.event(col)"
dense
/>
</QTd>
</QTr>
</template>
<template #body-cell-item="{ row }">
<QTd auto-width @click.stop>
<QBtn flat color="primary">{{ row.packagingItemFk }}</QBtn>
<ItemDescriptorProxy :id="row.packagingItemFk" /> <ItemDescriptorProxy :id="row.packagingItemFk" />
</QTd> </span>
</template> </template>
<template #body-cell-available="{ row }"> </VnTable>
<QTd @click.stop>
<QBadge :color="row.available < 0 ? 'alert' : 'transparent'" dense>
{{ row.available }}
</QBadge>
</QTd>
</template>
<template #body-cell-price="{ row }">
<QTd>
<template v-if="isTicketEditable && row.id">
<QBtn flat color="primary" dense @click="onOpenEditPricePopover(row)">
{{ toCurrency(row.price) }}
</QBtn>
<TicketEditManaProxy
ref="editPriceProxyRef"
:mana="mana"
:new-price="getNewPrice"
@save="updatePrice(row)"
>
<VnInput
v-model.number="edit.price"
:label="t('ticketSale.price')"
type="number"
/>
</TicketEditManaProxy>
</template>
<span v-else>{{ toCurrency(row.price) }}</span>
</QTd>
</template>
<template #body-cell-discount="{ row }">
<QTd>
<template v-if="!isLocked && row.id">
<QBtn
flat
color="primary"
dense
@click="onOpenEditDiscountPopover(row)"
>
{{ toPercentage(row.discount / 100) }}
</QBtn>
<TicketEditManaProxy
:mana="mana"
:new-price="getNewPrice"
@save="changeDiscount(row)"
>
<VnInput
v-model.number="edit.discount"
:label="t('ticketSale.discount')"
type="number"
/>
</TicketEditManaProxy>
</template>
<span v-else>{{ toPercentage(row.discount / 100) }}</span>
</QTd>
</template>
<template #body-cell-history="{ row }">
<QTd>
<QBtn
@click.stop="showLog(row)"
color="primary"
icon="history"
size="md"
flat
>
<QTooltip class="text-no-wrap">
{{ t('expedition.historyAction') }}
</QTooltip>
</QBtn>
</QTd>
</template>
</QTable>
<QDialog ref="newTicketDialogRef" transition-show="scale" transition-hide="scale"> <QDialog ref="newTicketDialogRef" transition-show="scale" transition-hide="scale">
<ExpeditionNewTicket <ExpeditionNewTicket
:ticket="ticketData" :ticket="ticketData"
@ -454,12 +333,23 @@ onUnmounted(() => (stateStore.rightDrawer = false));
data-key="TicketExpeditionLog" data-key="TicketExpeditionLog"
:rows="expeditionsLogsData" :rows="expeditionsLogsData"
:columns="logTableColumns" :columns="logTableColumns"
class="q-pa-sm" class="q-pa-md full-width"
> >
<template #body-cell-name="{ row }"> <template #body-cell-name="{ row }">
<QTd auto-width> <QTd style="text-align: center">
<QBtn flat dense color="primary">{{ row.name }}</QBtn> <span class="link" @click.stop>
<WorkerDescriptorProxy :id="row.workerFk" /> <QBtn flat dense>{{ row.name }}</QBtn>
<WorkerDescriptorProxy :id="row.workerFk" />
</span>
</QTd>
</template>
<template #body-cell-isScanned="{ row }">
<QTd style="text-align: center">
<QCheckbox disable v-model="row.isScanned">
{{
row.isScanned === 1 ? t('expedition.yes') : t('expedition.no')
}}
</QCheckbox>
</QTd> </QTd>
</template> </template>
</QTable> </QTable>

View File

@ -17,6 +17,7 @@ const ticketNotesCrudRef = ref(null);
const observationTypes = ref([]); const observationTypes = ref([]);
const arrayData = useArrayData('TicketNotes'); const arrayData = useArrayData('TicketNotes');
const { store } = arrayData; const { store } = arrayData;
const isSaving = ref(false);
const crudModelFilter = reactive({ const crudModelFilter = reactive({
where: { ticketFk: route.params.id }, where: { ticketFk: route.params.id },
@ -33,6 +34,17 @@ watch(
await ticketNotesCrudRef.value.reload(); await ticketNotesCrudRef.value.reload();
} }
); );
function handleDelete(row) {
ticketNotesCrudRef.value.remove([row]);
}
async function handleSave() {
if (!isSaving.value) {
isSaving.value = true;
await ticketNotesCrudRef.value?.saveChanges();
isSaving.value = false;
}
}
</script> </script>
<template> <template>
@ -73,13 +85,14 @@ watch(
:label="t('ticketNotes.description')" :label="t('ticketNotes.description')"
v-model="row.description" v-model="row.description"
class="col" class="col"
@keyup.enter="handleSave"
/> />
<QIcon <QIcon
name="delete" name="delete"
size="sm" size="sm"
class="cursor-pointer" class="cursor-pointer"
color="primary" color="primary"
@click="ticketNotesCrudRef.remove([row])" @click="handleDelete(row)"
> >
<QTooltip> <QTooltip>
{{ t('ticketNotes.removeNote') }} {{ t('ticketNotes.removeNote') }}

View File

@ -10,6 +10,7 @@ import FetchData from 'components/FetchData.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue';
import { useArrayData } from 'src/composables/useArrayData'; import { useArrayData } from 'src/composables/useArrayData';
import VnRow from 'src/components/ui/VnRow.vue';
const route = useRoute(); const route = useRoute();
const { t } = useI18n(); const { t } = useI18n();

View File

@ -1,27 +1,35 @@
<script setup> <script setup>
import { ref, computed, watch, reactive } from 'vue'; import { ref, computed, watch, reactive } from 'vue';
import axios from 'axios';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import VnInput from 'src/components/common/VnInput.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue'; import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
import CrudModel from 'src/components/CrudModel.vue';
import TicketCreateRequest from './TicketCreateRequest.vue';
import { dashIfEmpty } from 'src/filters'; import { dashIfEmpty } from 'src/filters';
import { toDateFormat } from 'src/filters/date.js'; import { toDateFormat } from 'src/filters/date.js';
import VnTable from 'src/components/VnTable/VnTable.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import FetchData from 'src/components/FetchData.vue';
import { useVnConfirm } from 'composables/useVnConfirm';
import useNotify from 'src/composables/useNotify.js';
import { onMounted } from 'vue';
import { useStateStore } from 'src/stores/useStateStore';
const route = useRoute(); const route = useRoute();
const stateStore = useStateStore();
const { t } = useI18n(); const { t } = useI18n();
const createTicketRequestDialogRef = ref(null); const tableRef = ref();
const crudModelRef = ref(null); const attendersOptions = ref([]);
const { openConfirmationModal } = useVnConfirm();
const { notify } = useNotify();
watch( watch(
() => route.params.id, () => route.params.id,
async (val) => { async (val) => {
crudModelFilter.where.ticketFk = val; crudModelFilter.where.ticketFk = val;
crudModelRef.value.reload(); tableRef.value.reload();
} }
); );
@ -72,64 +80,81 @@ const crudModelFilter = reactive({
const columns = computed(() => [ const columns = computed(() => [
{ {
align: 'left',
label: t('purchaseRequest.id'), label: t('purchaseRequest.id'),
name: 'id', name: 'id',
field: 'id', chip: {
align: 'left', condition: () => true,
columnFilter: null, },
isId: true,
}, },
{ {
align: 'left',
label: t('purchaseRequest.description'), label: t('purchaseRequest.description'),
name: 'description', name: 'description',
field: 'description', columnClass: 'expand',
align: 'left',
format: (val) => dashIfEmpty(val),
}, },
{ {
align: 'left',
label: t('purchaseRequest.created'), label: t('purchaseRequest.created'),
name: 'created', name: 'created',
field: 'created', format: (row) => toDateFormat(row.created),
align: 'left', cardVisible: true,
format: (val) => toDateFormat(val),
}, },
{ {
align: 'left',
label: t('purchaseRequest.requester'), label: t('purchaseRequest.requester'),
name: 'requester', name: 'requesterFk',
align: 'left', cardVisible: true,
sortable: true, format: (row) => dashIfEmpty(row.requester?.user?.nickname),
}, },
{ {
align: 'left',
label: t('purchaseRequest.atender'), label: t('purchaseRequest.atender'),
name: 'atender', name: 'attenderFk',
align: 'left', cardVisible: true,
format: (row) => dashIfEmpty(row.atender?.user?.nickname),
}, },
{ {
align: 'left',
label: t('purchaseRequest.quantity'), label: t('purchaseRequest.quantity'),
name: 'quantity', name: 'quantity',
align: 'left',
}, },
{ {
align: 'left',
label: t('purchaseRequest.price'), label: t('purchaseRequest.price'),
name: 'price', name: 'price',
align: 'left',
}, },
{ {
align: 'left',
label: t('purchaseRequest.saleFk'), label: t('purchaseRequest.saleFk'),
name: 'saleFk', name: 'saleFk',
align: 'left', cardVisible: true,
}, },
{ {
align: 'left',
label: t('purchaseRequest.state'), label: t('purchaseRequest.state'),
name: 'state', name: 'isOk',
field: 'isOk', cardVisible: true,
align: 'left',
format: (val) => t(getRequestState(val)),
}, },
{ {
label: '', align: 'right',
name: 'actions', name: 'tableActions',
align: 'left', actions: [
columnFilter: null, {
title: t('globals.delete'),
icon: 'delete',
isPrimary: true,
action: (row) =>
openConfirmationModal(
t('You are going to delete this ticket purchase request'),
t(
'This ticket will be removed from ticket purchase requests! Continue anyway?'
),
() => removeLine(row.id)
),
},
],
}, },
]); ]);
@ -140,131 +165,117 @@ const getRequestState = (state) => {
case false: case false:
return 'Denied'; return 'Denied';
case true: case true:
return 'Acepted'; return 'Accepted';
} }
}; };
const isEditable = (isOk) => isOk !== null; const isEditable = (isOk) => isOk !== null;
const removeLine = async (row) => crudModelRef.value.remove([row]); async function removeLine(id) {
try {
await axios.delete(`TicketRequests/${id}`);
notify(t('globals.dataSaved'), 'positive');
location.reload();
} catch (err) {
console.error('Error ', err);
}
}
const openCreateModal = () => createTicketRequestDialogRef.value.show(); onMounted(() => (stateStore.rightDrawer = false));
</script> </script>
<template> <template>
<QPage class="column items-center q-pa-md"> <FetchData
<CrudModel url="TicketRequests/getItemTypeWorker"
data-key="PurchaseRequests" :filter="{ fields: ['id', 'nickname'], order: 'nickname ASC' }"
url="TicketRequests" auto-load
ref="crudModelRef" @on-fetch="(data) => (attendersOptions = data)"
:filter="crudModelFilter" />
:order="['created ASC']" <VnTable
:default-remove="false" ref="tableRef"
:default-save="false" data-key="PurchaseRequests"
:default-reset="false" url="TicketRequests"
:limit="0" :create="{
auto-load urlCreate: 'TicketRequests',
> title: t('Create request'),
<template #body="{ rows }"> onDataSaved: ({ id }) => tableRef.reload(id),
<QTable formInitialData: {
:rows="rows" ticketFk: route.params.id,
:columns="columns" },
row-key="id" }"
:pagination="{ rowsPerPage: 0 }" save-url="TicketRequests/crud"
class="full-width q-mt-md" :filter="crudModelFilter"
:no-data-label="t('globals.noResults')" :columns="columns"
@row-click="(_, row) => redirectToTicketSummary(row.ticketFk)" :is-editable="true"
> :right-search="false"
<template #body-cell-description="{ row }"> :column-search="false"
<QTd @click.stop> auto-load
<VnInput >
v-model="row.description" <template #column-description="{ row }">
@blur="crudModelRef.saveChanges()" <VnInput v-model="row.description" :disable="isEditable(row.isOk)" />
:disable="isEditable(row.isOk)" </template>
/> <template #column-requesterFk="{ row }">
</QTd> <span class="link" @click.stop>
</template> {{ row.requester?.user?.nickname }}
<template #body-cell-requester="{ row }"> <WorkerDescriptorProxy :id="row.requesterFk" />
<QTd @click.stop> </span>
<QBtn flat color="primary"> </template>
{{ row.requester?.user?.nickname }} <template #column-attenderFk="{ row }">
<WorkerDescriptorProxy :id="row.requesterFk" /> <span class="link" @click.stop>
</QBtn> {{ row.atender?.user?.nickname }}
</QTd> <WorkerDescriptorProxy :id="row.attenderFk" />
</template> </span>
<template #body-cell-atender="{ row }"> </template>
<QTd @click.stop> <template #column-quantity="{ row }">
<QBtn flat color="primary"> <VnInput v-model="row.quantity" :disable="isEditable(row.isOk)" />
{{ row.atender?.user?.nickname }} </template>
<WorkerDescriptorProxy :id="row.attenderFk" /> <template #column-price="{ row }">
</QBtn> <span @click.stop>
</QTd> <VnInput v-model="row.price" :disable="isEditable(row.isOk)">
</template> {{ row.price }}
<template #body-cell-quantity="{ row }"> </VnInput>
<QTd @click.stop> </span>
<VnInput </template>
v-model="row.quantity" <template #column-saleFk="{ row }">
@blur="crudModelRef.saveChanges()" <QTd style="width: 3%">
:disable="isEditable(row.isOk)" <span class="link" @click.stop>
/> {{ dashIfEmpty(row.sale?.itemFk) }}
</QTd> <ItemDescriptorProxy :id="row.sale?.itemFk" /> </span
</template> ></QTd>
<template #body-cell-price="{ row }"> </template>
<QTd @click.stop> <template #column-isOk="{ row }">
<VnInput {{ t(getRequestState(row.isOk)) }}
v-model="row.price" </template>
@blur="crudModelRef.saveChanges()"
:disable="isEditable(row.isOk)" <template #more-create-dialog="{ data }">
/> <VnInput
</QTd> v-model="data.description"
</template> :label="t('purchaseRequest.description')"
<template #body-cell-saleFk="{ row }">
<QTd @click.stop>
<QBtn v-if="row.saleFk" flat color="primary">
{{ row.sale.itemFk }}
<ItemDescriptorProxy :id="row.sale.itemFk" />
</QBtn>
</QTd>
</template>
<template #body-cell-actions="{ row }">
<QTd>
<QIcon
@click.stop="removeLine(row)"
class="q-ml-sm cursor-pointer"
color="primary"
name="delete"
size="sm"
>
<QTooltip>
{{ t('globals.delete') }}
</QTooltip>
</QIcon>
</QTd>
</template>
</QTable>
</template>
</CrudModel>
<QDialog
ref="createTicketRequestDialogRef"
transition-show="scale"
transition-hide="scale"
>
<TicketCreateRequest @on-request-created="crudModelRef.reload()" />
</QDialog>
<QPageSticky :offset="[20, 20]">
<QBtn
@click="openCreateModal()"
color="primary"
fab
icon="add"
shortcut="+"
/> />
<QTooltip class="text-no-wrap"> <VnSelect
{{ t('purchaseRequest.newRequest') }} :label="t('purchaseRequest.atender')"
</QTooltip> v-model="data.attenderFk"
</QPageSticky> :options="attendersOptions"
</QPage> hide-selected
option-label="nickname"
option-value="id"
/>
<VnInput
v-model="data.quantity"
:label="t('purchaseRequest.quantity')"
type="number"
min="1"
/>
<VnInput
v-model="data.price"
:label="t('purchaseRequest.price')"
type="number"
min="0"
/>
</template>
</VnTable>
</template> </template>
<i18n> <i18n>
es: es:
New: Nueva New: Nueva

View File

@ -16,11 +16,12 @@ import TicketSaleMoreActions from './TicketSaleMoreActions.vue';
import TicketTransfer from './TicketTransfer.vue'; import TicketTransfer from './TicketTransfer.vue';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import { toCurrency, toPercentage, dashIfEmpty } from 'src/filters'; import { toCurrency, toPercentage } from 'src/filters';
import { useArrayData } from 'composables/useArrayData'; import { useArrayData } from 'composables/useArrayData';
import { useVnConfirm } from 'composables/useVnConfirm'; import { useVnConfirm } from 'composables/useVnConfirm';
import useNotify from 'src/composables/useNotify.js'; import useNotify from 'src/composables/useNotify.js';
import axios from 'axios'; import axios from 'axios';
import VnTable from 'src/components/VnTable/VnTable.vue';
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
@ -33,7 +34,8 @@ const stateBtnDropdownRef = ref(null);
const arrayData = useArrayData('ticketData'); const arrayData = useArrayData('ticketData');
const { store } = arrayData; const { store } = arrayData;
const selectedRows = ref([]);
const hasSelectedRows = computed(() => selectedRows.value.length > 0);
const ticketConfig = ref(null); const ticketConfig = ref(null);
const isLocked = ref(false); const isLocked = ref(false);
const isTicketEditable = ref(false); const isTicketEditable = ref(false);
@ -47,6 +49,7 @@ const transfer = ref({
lastActiveTickets: [], lastActiveTickets: [],
sales: [], sales: [],
}); });
const tableRef = ref([]);
watch( watch(
() => route.params.id, () => route.params.id,
@ -55,86 +58,89 @@ watch(
const columns = computed(() => [ const columns = computed(() => [
{ {
label: '', align: 'left',
name: 'statusIcons', name: 'statusIcons',
align: 'left',
}, },
{ {
label: '', align: 'center',
name: 'picture', label: t('lines.image'),
align: 'left', name: 'image',
columnField: {
component: VnImg,
attrs: (id) => {
return {
id,
width: '50px',
};
},
},
columnFilter: false,
}, },
{ {
align: 'left',
label: t('ticketSale.visible'), label: t('ticketSale.visible'),
name: 'visible', name: 'visible',
field: 'visible', format: (row, dashIfEmpty) => dashIfEmpty(row.visible),
align: 'left',
sortable: true,
}, },
{ {
align: 'left',
label: t('ticketSale.available'), label: t('ticketSale.available'),
name: 'available', name: 'available',
field: 'available', format: (row, dashIfEmpty) => dashIfEmpty(row.available),
align: 'left',
sortable: true,
}, },
{ {
align: 'left',
label: t('ticketSale.id'), label: t('ticketSale.id'),
name: 'itemFk', name: 'itemFk',
field: 'itemFk',
align: 'left',
sortable: true,
}, },
{ {
align: 'left',
label: t('ticketSale.quantity'), label: t('ticketSale.quantity'),
name: 'quantity', name: 'quantity',
field: 'quantity', format: (row) => toCurrency(row.quantity),
align: 'left',
sortable: true,
}, },
{ {
align: 'left',
label: t('ticketSale.item'), label: t('ticketSale.item'),
name: 'item', name: 'item',
field: 'item', format: (row) => row?.item?.name,
align: 'left', columnClass: 'expand',
sortable: true,
}, },
{ {
align: 'left',
label: t('ticketSale.price'), label: t('ticketSale.price'),
name: 'price', name: 'price',
field: 'price', format: (row) => toCurrency(row.price),
align: 'left',
sortable: true,
format: (val) => toCurrency(val),
}, },
{ {
align: 'left',
label: t('ticketSale.discount'), label: t('ticketSale.discount'),
name: 'discount', name: 'discount',
field: 'discount', format: (row) => toPercentage(row.discount),
align: 'left',
sortable: true,
}, },
{ {
align: 'left',
label: t('ticketSale.amount'), label: t('ticketSale.amount'),
name: 'amount', name: 'amount',
field: 'amount', format: (row) => parseInt(row.amount * row.quantity),
align: 'left',
sortable: true,
format: (val) => toCurrency(val),
}, },
{ {
align: 'left',
label: t('ticketSale.packaging'), label: t('ticketSale.packaging'),
name: 'itemPackingTypeFk', name: 'itemPackingTypeFk',
field: 'item', format: (row, dashIfEmpty) => dashIfEmpty(row?.item?.itemPackingTypeFk),
align: 'left',
sortable: true,
format: (val) => dashIfEmpty(val?.itemPackingTypeFk),
}, },
{ {
label: '', align: 'right',
name: 'history', name: 'tableActions',
align: 'left', actions: [
columnFilter: null, {
title: t('ticketSale.history'),
icon: 'history',
isPrimary: true,
action: (row) => goToLog(row.id),
},
],
}, },
]); ]);
@ -210,6 +216,7 @@ const addSale = async (sale) => {
sale.item = newSale.item; sale.item = newSale.item;
notify('globals.dataSaved', 'positive'); notify('globals.dataSaved', 'positive');
window.location.reload();
} catch (err) { } catch (err) {
console.error('Error adding sale', err); console.error('Error adding sale', err);
} }
@ -259,7 +266,7 @@ const getMana = async () => {
const selectedValidSales = computed(() => { const selectedValidSales = computed(() => {
if (!sales.value) return; if (!sales.value) return;
return selectedSales.value.filter((sale) => sale.id != undefined); return [...selectedRows.value];
}); });
const onOpenEditPricePopover = async (sale) => { const onOpenEditPricePopover = async (sale) => {
@ -374,7 +381,7 @@ const changeTicketState = async (val) => {
}; };
const removeSelectedSales = () => { const removeSelectedSales = () => {
selectedSales.value.forEach((sale) => { selectedRows.value.forEach((sale) => {
const index = sales.value.indexOf(sale); const index = sales.value.indexOf(sale);
sales.value.splice(index, 1); sales.value.splice(index, 1);
}); });
@ -382,19 +389,29 @@ const removeSelectedSales = () => {
const removeSales = async () => { const removeSales = async () => {
try { try {
const params = { sales: selectedValidSales.value, ticketId: store.data.id }; const params = {
sales: selectedRows.value.filter((sale) => sale.id),
ticketId: store.data.id,
};
selectedRows.value
.filter((sale) => !sale.id)
.forEach((sale) =>
tableRef.value.CrudModelRef.formData.splice(sale.$index, 1)
);
if (params.sales.length == 0) return;
await axios.post('Sales/deleteSales', params); await axios.post('Sales/deleteSales', params);
removeSelectedSales(); removeSelectedSales();
notify('globals.dataSaved', 'positive'); notify('globals.dataSaved', 'positive');
window.location.reload();
} catch (err) { } catch (err) {
console.error('Error deleting sales', err); console.error('Error deleting sales', err);
} }
}; };
const insertRow = () => sales.value.push({ ...DEFAULT_EDIT });
const setTransferParams = async () => { const setTransferParams = async () => {
try { try {
selectedSales.value = selectedValidSales.value;
const checkedSales = JSON.parse(JSON.stringify(selectedSales.value)); const checkedSales = JSON.parse(JSON.stringify(selectedSales.value));
transfer.value = { transfer.value = {
lastActiveTickets: [], lastActiveTickets: [],
@ -418,9 +435,70 @@ onMounted(async () => {
stateStore.rightDrawer = true; stateStore.rightDrawer = true;
getConfig(); getConfig();
getSales(); getSales();
getItems();
}); });
onUnmounted(() => (stateStore.rightDrawer = false)); onUnmounted(() => (stateStore.rightDrawer = false));
const items = ref([]);
const newRow = ref({});
async function getItems() {
const { data } = await axios.get(`Items/withName`);
items.value = data;
}
const updateItem = (row) => {
const selectedItem = items.value.find((item) => item.id === row.itemFk);
if (selectedItem) {
row.item = selectedItem;
row.itemFk = selectedItem.id;
row.price = selectedItem.price;
row.discount = 0;
row.quantity = 0;
row.amount = row.price * row.quantity;
}
endNewRow(selectedItem);
};
function handleOnDataSave({ CrudModelRef }) {
const { copy } = addRow(CrudModelRef.formData);
CrudModelRef.insert(copy);
}
const addRow = (original = null) => {
let copy = null;
if (!original) {
copy = { isNew: true };
} else {
copy = {
itemFk: original.itemFk,
item: original.item,
quantity: original.quantity,
price: original.price,
discount: original.discount,
amount: original.amount,
isNew: true,
};
}
newRow.value = copy;
return { original, copy };
};
const endNewRow = (row) => {
if (row.itemFk && row.quantity) {
row.isNew = false;
}
};
watch(
() => newRow.value.itemFk,
(newItemFk) => {
if (newItemFk) {
updateItem(newRow.value);
}
}
);
</script> </script>
<template> <template>
@ -471,7 +549,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
:ticket="store.data" :ticket="store.data"
:is-ticket-editable="isTicketEditable" :is-ticket-editable="isTicketEditable"
:sales="selectedValidSales" :sales="selectedValidSales"
:disable="!selectedSales.length" :disable="!hasSelectedRows"
:mana="mana" :mana="mana"
:ticket-config="ticketConfig" :ticket-config="ticketConfig"
@get-mana="getMana()" @get-mana="getMana()"
@ -480,7 +558,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
<QBtn <QBtn
color="primary" color="primary"
icon="delete" icon="delete"
:disable="!isTicketEditable || !selectedSales.length" :disable="!isTicketEditable || !hasSelectedRows"
@click=" @click="
openConfirmationModal( openConfirmationModal(
t('Continue anyway?'), t('Continue anyway?'),
@ -494,7 +572,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
<QBtn <QBtn
color="primary" color="primary"
icon="vn:splitline" icon="vn:splitline"
:disable="!isTicketEditable || !selectedSales.length" :disable="!isTicketEditable || !hasSelectedRows"
@click="setTransferParams()" @click="setTransferParams()"
> >
<QTooltip>{{ t('Transfer lines') }}</QTooltip> <QTooltip>{{ t('Transfer lines') }}</QTooltip>
@ -507,246 +585,214 @@ onUnmounted(() => (stateStore.rightDrawer = false));
</QBtnGroup> </QBtnGroup>
</template> </template>
</VnSubToolbar> </VnSubToolbar>
<QDrawer side="right" :width="270" v-model="stateStore.rightDrawer"> <QDrawer side="right" :width="265" v-model="stateStore.rightDrawer">
<div <div
class="q-pa-md q-mb-md q-ma-md color-vn-text" class="q-pa-md q-mb-md q-ma-md color-vn-text"
style="border: 2px solid black" style="border: 2px solid black"
> >
<QCardSection class="justify-center text-subtitle1" horizontal> <QCardSection class="justify-end text-subtitle1" horizontal>
<span class="q-mr-xs color-vn-label" <span class="q-mr-xs color-vn-label"
>{{ t('ticketSale.subtotal') }}: >{{ t('ticketSale.subtotal') }}:
</span> </span>
<span>{{ toCurrency(store.data?.totalWithoutVat) }}</span> <span>{{ toCurrency(store.data?.totalWithoutVat) }}</span>
</QCardSection> </QCardSection>
<QCardSection class="justify-center text-subtitle1" horizontal> <QCardSection class="justify-end text-subtitle1" horizontal>
<span class="q-mr-xs color-vn-label"> {{ t('ticketSale.tax') }}: </span> <span class="q-mr-xs color-vn-label"> {{ t('ticketSale.tax') }}: </span>
<span>{{ <span>{{
toCurrency(store.data?.totalWithVat - store.data?.totalWithoutVat) toCurrency(store.data?.totalWithVat - store.data?.totalWithoutVat)
}}</span> }}</span>
</QCardSection> </QCardSection>
<QCardSection <QCardSection class="justify-end text-weight-bold text-subtitle1" horizontal>
class="justify-center text-weight-bold text-subtitle1"
horizontal
>
<span class="q-mr-xs color-vn-label"> {{ t('ticketSale.total') }}: </span> <span class="q-mr-xs color-vn-label"> {{ t('ticketSale.total') }}: </span>
<span>{{ toCurrency(store.data?.totalWithVat) }}</span> <span>{{ toCurrency(store.data?.totalWithVat) }}</span>
</QCardSection> </QCardSection>
</div></QDrawer </div></QDrawer
> >
<QTable <VnTable
:rows="sales" ref="tableRef"
data-key="TicketSales"
:url="`Tickets/${route.params.id}/getSales`"
:columns="columns" :columns="columns"
row-key="id" v-model:selected="selectedRows"
:pagination="{ rowsPerPage: 0 }" :bottom="true"
class="full-width q-mt-md" :table="{
selection="multiple" 'row-key': 'id',
v-model:selected="selectedSales" selection: 'multiple',
:no-data-label="t('globals.noResults')" }"
:right-search="false"
:column-search="false"
:disable-option="{ card: true }"
auto-load
:create="{
onDataSaved: handleOnDataSave,
}"
:create-as-dialog="false"
:crud-model="{
paginate: false,
}"
:default-remove="false"
:default-reset="false"
:default-save="false"
:disabled-attr="isTicketEditable"
> >
<template #body-cell-statusIcons="{ row }"> <template #column-statusIcons="{ row }">
<QTd class="q-gutter-x-xs"> <router-link
<router-link v-if="row.claim?.claimFk"
v-if="row.claim?.claimFk" :to="{ name: 'ClaimBasicData', params: { id: row.claim?.claimFk } }"
:to="{ name: 'ClaimBasicData', params: { id: row.claim?.claimFk } }" >
> <QIcon color="primary" name="vn:claims" size="xs">
<QIcon color="primary" name="vn:claims" size="xs">
<QTooltip>
{{ t('ticketSale.claim') }}:
{{ row.claim?.claimFk }}
</QTooltip>
</QIcon>
</router-link>
<QIcon v-if="row.visible < 0" color="primary" name="warning" size="xs">
<QTooltip> <QTooltip>
{{ t('ticketSale.visible') }}: {{ row.visible || 0 }} {{ t('ticketSale.claim') }}:
{{ row.claim?.claimFk }}
</QTooltip> </QTooltip>
</QIcon> </QIcon>
<QIcon v-if="row.reserved" color="primary" name="vn:reserva" size="xs"> </router-link>
<QTooltip> <QIcon v-if="row.visible < 0" color="primary" name="warning" size="xs">
{{ t('ticketSale.reserved') }} <QTooltip>
</QTooltip> {{ t('ticketSale.visible') }}: {{ row.visible || 0 }}
</QIcon> </QTooltip>
<QIcon </QIcon>
v-if="row.itemShortage" <QIcon v-if="row.reserved" color="primary" name="vn:reserva" size="xs">
color="primary" <QTooltip>
name="vn:unavailable" {{ t('ticketSale.reserved') }}
size="xs" </QTooltip>
> </QIcon>
<QTooltip> <QIcon
{{ t('ticketSale.noVisible') }} v-if="row.itemShortage"
</QTooltip>
</QIcon>
<QIcon
v-if="row.hasComponentLack"
color="primary"
name="vn:components"
size="xs"
>
<QTooltip>
{{ t('ticketSale.hasComponentLack') }}
</QTooltip>
</QIcon>
</QTd>
</template>
<template #body-cell-picture="{ row }">
<QTd>
<div class="image-wrapper">
<VnImg :id="row.itemFk" class="rounded" />
</div>
</QTd>
</template>
<template #body-cell-visible="{ row }">
<QTd @click.stop>
<QBadge :color="row.visible < 0 ? 'alert' : 'transparent'" dense>
{{ row.visible }}
</QBadge>
</QTd>
</template>
<template #body-cell-available="{ row }">
<QTd @click.stop>
<QBadge :color="row.available < 0 ? 'alert' : 'transparent'" dense>
{{ row.available }}
</QBadge>
</QTd>
</template>
<template #body-cell-itemFk="{ row }">
<QTd @click.stop>
<div v-if="row.id">
<QBtn flat color="primary" dense>
{{ row.itemFk }}
</QBtn>
<ItemDescriptorProxy :id="row.itemFk" />
</div>
<VnSelect
v-else
hide-selected
option-label="name"
option-value="id"
url="Items/withName"
:fields="['id', 'name']"
sort-by="id DESC"
@update:model-value="changeQuantity(row)"
v-model="row.itemFk"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel> #{{ scope.opt?.id }} </QItemLabel>
<QItemLabel caption>{{ scope.opt?.name }}</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
</QTd>
</template>
<template #body-cell-quantity="{ row }">
<QTd @click.stop>
<VnInput
v-if="isTicketEditable"
v-model.number="row.quantity"
@keyup.enter="changeQuantity(row)"
@blur="changeQuantity(row)"
@focus="edit.oldQuantity = row.quantity"
/>
<span v-else>{{ row.quantity }}</span>
</QTd>
</template>
<template #body-cell-item="{ row }">
<QTd class="col">
<div class="column">
<span>{{ row.concept }}</span>
<span class="color-vn-label">{{ row.item?.subName }}</span>
<FetchedTags v-if="row.item" :item="row.item" />
<QPopupProxy v-if="row.id && isTicketEditable">
<VnInput v-model="row.concept" @change="updateConcept(row)" />
</QPopupProxy>
</div>
</QTd>
</template>
<template #body-cell-price="{ row }">
<QTd>
<template v-if="isTicketEditable && row.id">
<QBtn flat color="primary" dense @click="onOpenEditPricePopover(row)">
{{ toCurrency(row.price) }}
</QBtn>
<TicketEditManaProxy
ref="editPriceProxyRef"
:mana="mana"
:new-price="getNewPrice"
@save="updatePrice(row)"
>
<VnInput
v-model.number="edit.price"
:label="t('ticketSale.price')"
type="number"
/>
</TicketEditManaProxy>
</template>
<span v-else>{{ toCurrency(row.price) }}</span>
</QTd>
</template>
<template #body-cell-discount="{ row }">
<QTd>
<template v-if="!isLocked && row.id">
<QBtn
flat
color="primary"
dense
@click="onOpenEditDiscountPopover(row)"
>
{{ toPercentage(row.discount / 100) }}
</QBtn>
<TicketEditManaProxy
:mana="mana"
:new-price="getNewPrice"
@save="changeDiscount(row)"
>
<VnInput
v-model.number="edit.discount"
:label="t('ticketSale.discount')"
type="number"
/>
</TicketEditManaProxy>
</template>
<span v-else>{{ toPercentage(row.discount / 100) }}</span>
</QTd>
</template>
<template #body-cell-history="{ row }">
<QTd>
<QBtn
v-if="row.$hasLogs"
@click.stop="goToLog(row.id)"
color="primary"
icon="history"
size="md"
flat
>
<QTooltip class="text-no-wrap">
{{ t('ticketSale.history') }}
</QTooltip>
</QBtn>
</QTd>
</template>
<template #bottom-row>
<QBtn
class="cursor-pointer fill-icon q-ml-md q-my-lg"
color="primary" color="primary"
icon="add_circle" name="vn:unavailable"
size="md" size="xs"
round
flat
shortcut="+"
:disable="!isTicketEditable"
@click="insertRow()"
> >
<QTooltip> <QTooltip>
{{ t('Add item') }} {{ t('ticketSale.noVisible') }}
</QTooltip> </QTooltip>
</QBtn> </QIcon>
<QIcon
v-if="row.hasComponentLack"
color="primary"
name="vn:components"
size="xs"
>
<QTooltip>
{{ t('ticketSale.hasComponentLack') }}
</QTooltip>
</QIcon>
</template> </template>
</QTable> <template #column-image="{ row }">
<div class="image-wrapper">
<VnImg :id="parseInt(row?.item?.id)" class="rounded" />
</div>
</template>
<template #column-visible="{ row }">
<QBadge :color="row.visible < 0 ? 'alert' : 'transparent'" dense>
{{ row.visible }}
</QBadge>
</template>
<template #column-available="{ row }">
<QBadge :color="row.available < 0 ? 'alert' : 'transparent'" dense>
{{ row.available }}
</QBadge>
</template>
<template #column-itemFk="{ row }">
<VnSelect
v-if="row.isNew"
url="Items/WithName"
:fields="['id', 'name']"
:sort-by="['id DESC']"
:options="items"
option-label="name"
option-value="id"
v-model="row.itemFk"
@update:model-value="updateItem(row)"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>
{{ scope.opt?.id }} - {{ scope.opt?.name }}</QItemLabel
>
</QItemSection>
</QItem>
</template>
</VnSelect>
<span v-else class="link" @click.stop>
{{ row?.itemFk }}
<ItemDescriptorProxy :id="row?.itemFk" />
</span>
</template>
<template #column-item="{ row }">
<div class="row column full-width justify-between items-start">
{{ row?.item?.name }}
<div v-if="row?.item?.subName" class="subName">
{{ row?.item?.subName.toUpperCase() }}
</div>
</div>
<FetchedTags :item="row" :max-length="6" />
<QPopupProxy v-if="row.id && isTicketEditable">
<VnInput v-model="row.concept" @change="updateConcept(row)" />
</QPopupProxy>
</template>
<template #column-quantity="{ row }">
<VnInput
v-if="row.isNew"
v-model.number="row.quantity"
type="number"
@blur="changeQuantity(row)"
@focus="edit.oldQuantity = row.quantity"
/>
<VnInput
v-else-if="isTicketEditable"
v-model.number="row.quantity"
@blur="changeQuantity(row)"
@focus="edit.oldQuantity = row.quantity"
/>
<span v-else>{{ row.quantity }}</span>
</template>
<template #column-price="{ row }">
<template v-if="isTicketEditable && row.id">
<QBtn flat class="link" dense @click="onOpenEditPricePopover(row)">
{{ toCurrency(row.price) }}
</QBtn>
<TicketEditManaProxy
ref="editPriceProxyRef"
:mana="mana"
:new-price="getNewPrice"
@save="updatePrice(row)"
>
<VnInput
v-model.number="edit.price"
:label="t('ticketSale.price')"
type="number"
/>
</TicketEditManaProxy>
</template>
<span v-else>{{ toCurrency(row.price) }}</span>
</template>
<template #column-discount="{ row }">
<template v-if="!isLocked && row.id">
<QBtn flat class="link" dense @click="onOpenEditDiscountPopover(row)">
{{ toPercentage(row.discount / 100) }}
</QBtn>
<TicketEditManaProxy
:mana="mana"
:new-price="getNewPrice"
@save="changeDiscount(row)"
>
<VnInput
v-model.number="edit.discount"
:label="t('ticketSale.discount')"
type="number"
/>
</TicketEditManaProxy>
</template>
<span v-else>{{ toPercentage(row.discount / 100) }}</span>
</template>
<template #column-amount="{ row }">
{{ toCurrency(row.quantity * row.price) }}
</template>
</VnTable>
<QPageSticky :offset="[20, 20]"> <QPageSticky :offset="[20, 20]" style="z-index: 2">
<QBtn @click="newOrderFromTicket()" color="primary" fab icon="add" shortcut="+" /> <QBtn @click="newOrderFromTicket()" color="primary" fab icon="add" shortcut="+" />
<QTooltip class="text-no-wrap"> <QTooltip class="text-no-wrap">
{{ t('Add item to basket') }} {{ t('Add item to basket') }}
@ -754,6 +800,18 @@ onUnmounted(() => (stateStore.rightDrawer = false));
</QPageSticky> </QPageSticky>
</template> </template>
<style lang="scss" scoped>
.image-wrapper {
height: 50px;
width: 50px;
}
.subName {
text-transform: uppercase;
color: var(--vn-label-color);
}
</style>
<i18n> <i18n>
es: es:
New item: Nuevo artículo New item: Nuevo artículo

View File

@ -25,7 +25,7 @@ const saleTrackingFetchDataRef = ref(null);
const sales = ref([]); const sales = ref([]);
const saleTrackings = ref([]); const saleTrackings = ref([]);
const itemShelvignsSales = ref([]); const itemShelvingsSales = ref([]);
const saleTrackingUrl = computed(() => `SaleTrackings/${route.params.id}/filter`); const saleTrackingUrl = computed(() => `SaleTrackings/${route.params.id}/filter`);
const oldQuantity = ref(null); const oldQuantity = ref(null);
@ -88,7 +88,7 @@ const logTableColumns = computed(() => [
label: t('ticketSaleTracking.original'), label: t('ticketSaleTracking.original'),
name: 'original', name: 'original',
field: 'originalQuantity', field: 'originalQuantity',
align: 'original', align: 'left',
sortable: true, sortable: true,
}, },
{ {
@ -177,7 +177,7 @@ const getItemShelvingSales = async (sale) => {
const { data } = await axios.get(`ItemShelvingSales/filter`, { const { data } = await axios.get(`ItemShelvingSales/filter`, {
params: { filter: JSON.stringify(filter) }, params: { filter: JSON.stringify(filter) },
}); });
itemShelvignsSales.value = data; itemShelvingsSales.value = data;
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
@ -337,7 +337,7 @@ const qCheckBoxController = (sale, action) => {
:no-data-label="t('globals.noResults')" :no-data-label="t('globals.noResults')"
> >
<template #body-cell-isChecked="{ row }"> <template #body-cell-isChecked="{ row }">
<QTd @click.stop> <QTd @click.stop style="width: 20%">
<QCheckbox <QCheckbox
:model-value="!!row.hasSaleGroupDetail" :model-value="!!row.hasSaleGroupDetail"
color="pink" color="pink"
@ -396,12 +396,14 @@ const qCheckBoxController = (sale, action) => {
</QTd> </QTd>
</template> </template>
<template #body-cell-item="{ row }"> <template #body-cell-item="{ row }">
<QTd @click.stop> <QTd @click.stop style="width: 20%">
<div> <div>
<QBtn flat color="primary"> <span class="link">
{{ row.itemFk }} <QBtn flat>
</QBtn> {{ row.itemFk }}
<ItemDescriptorProxy :id="row.itemFk" /> </QBtn>
<ItemDescriptorProxy :id="row.itemFk" />
</span>
</div> </div>
</QTd> </QTd>
</template> </template>
@ -416,8 +418,18 @@ const qCheckBoxController = (sale, action) => {
</div> </div>
</QTd> </QTd>
</template> </template>
<template #body-cell-quantity="{ row }">
<QTd style="width: 10%">
{{ row.quantity }}
</QTd>
</template>
<template #body-cell-parking="{ row }">
<QTd style="width: 10%">
{{ dashIfEmpty(row.parkingFk) }}
</QTd>
</template>
<template #body-cell-actions="{ row }"> <template #body-cell-actions="{ row }">
<QTd> <QTd style="width: 20%">
<QBtn <QBtn
@click.stop="showLog(row)" @click.stop="showLog(row)"
color="primary" color="primary"
@ -452,12 +464,14 @@ const qCheckBoxController = (sale, action) => {
data-key="saleTrackingLog" data-key="saleTrackingLog"
:rows="saleTrackings" :rows="saleTrackings"
:columns="logTableColumns" :columns="logTableColumns"
class="q-pa-sm" class="q-pa-sm full-width"
> >
<template #body-cell-worker="{ row }"> <template #body-cell-worker="{ row }">
<QTd auto-width> <QTd>
<QBtn flat dense color="primary">{{ row.name }}</QBtn> <QBtn flat class="link">
<WorkerDescriptorProxy :id="row.workerFk" /> {{ row.name }}
<WorkerDescriptorProxy :id="row.workerFk" />
</QBtn>
</QTd> </QTd>
</template> </template>
</QTable> </QTable>
@ -469,12 +483,12 @@ const qCheckBoxController = (sale, action) => {
> >
<QTable <QTable
data-key="itemShelvingsSales" data-key="itemShelvingsSales"
:rows="itemShelvignsSales" :rows="itemShelvingsSales"
:columns="shelvingsTableColumns" :columns="shelvingsTableColumns"
class="q-pa-sm" class="q-pa-sm full-width"
> >
<template #body-cell-quantity="{ row }"> <template #body-cell-quantity="{ row }">
<QTd auto-width> <QTd>
<VnInput <VnInput
v-model.number="row.quantity" v-model.number="row.quantity"
@keyup.enter="updateQuantity(row)" @keyup.enter="updateQuantity(row)"
@ -484,13 +498,15 @@ const qCheckBoxController = (sale, action) => {
</QTd> </QTd>
</template> </template>
<template #body-cell-worker="{ row }"> <template #body-cell-worker="{ row }">
<QTd auto-width> <QTd>
<QBtn flat dense color="primary">{{ row.name }}</QBtn> <QBtn flat class="link">
<WorkerDescriptorProxy :id="row.userFk" /> {{ row.name }}
<WorkerDescriptorProxy :id="row.userFk" />
</QBtn>
</QTd> </QTd>
</template> </template>
<template #body-cell-shelving="{ row }"> <template #body-cell-shelving="{ row }">
<QTd auto-width> <QTd>
<VnSelect <VnSelect
url="Shelvings" url="Shelvings"
hide-selected hide-selected
@ -503,7 +519,7 @@ const qCheckBoxController = (sale, action) => {
</QTd> </QTd>
</template> </template>
<template #body-cell-parking="{ row }"> <template #body-cell-parking="{ row }">
<QTd auto-width> <QTd>
<VnSelect <VnSelect
url="Parkings" url="Parkings"
hide-selected hide-selected
@ -538,4 +554,10 @@ $estados: (
} }
} }
} }
@media (min-width: 560px) {
.q-dialog__inner--minimized > div {
max-width: 900px;
}
}
</style> </style>

View File

@ -25,7 +25,7 @@ const { notify } = useNotify();
const selected = ref([]); const selected = ref([]);
const defaultTaxClass = ref(null); const defaultTaxClass = ref(null);
const isSaving = ref(false);
const crudModelFilter = computed(() => ({ const crudModelFilter = computed(() => ({
where: { ticketFk: route.params.id }, where: { ticketFk: route.params.id },
})); }));
@ -50,7 +50,7 @@ const createRefund = async () => {
if (!selected.value.length) return; if (!selected.value.length) return;
const params = { const params = {
servicesIds: selected.value.map((s) => +s.ticketFk), servicesIds: selected.value.map((s) => +s.id),
withWarehouse: false, withWarehouse: false,
negative: true, negative: true,
}; };
@ -104,7 +104,31 @@ const columns = computed(() => [
sortable: true, sortable: true,
align: 'left', align: 'left',
}, },
{
label: '',
name: 'actions',
align: 'left',
columnFilter: null,
},
]); ]);
async function deleteService(row) {
const serviceId = row.id;
jon marked this conversation as resolved
Review

Mmm..si le doy al mas, pero luego le doy al delete no tengo id.
Salta error porque ejecuta peticion de axios cuando deberia eliminar de la tabla, no?

Mmm..si le doy al mas, pero luego le doy al delete no tengo id. Salta error porque ejecuta peticion de axios cuando deberia eliminar de la tabla, no?
if (!row.id) ticketServiceCrudRef.value.reset();
else {
const { data } = await axios.delete(`TicketServices/${serviceId}`);
if (data) notify('Service deleted successfully', 'positive');
ticketServiceCrudRef.value.reload();
}
}
async function handleSave() {
if (!isSaving.value) {
isSaving.value = true;
await ticketServiceCrudRef.value?.saveChanges();
isSaving.value = false;
}
}
</script> </script>
<template> <template>
@ -123,6 +147,8 @@ const columns = computed(() => [
:data-required="crudModelRequiredData" :data-required="crudModelRequiredData"
auto-load auto-load
v-model:selected="selected" v-model:selected="selected"
:order="['description ASC']"
:default-remove="false"
> >
<template #moreBeforeActions> <template #moreBeforeActions>
<QBtn <QBtn
@ -177,9 +203,25 @@ const columns = computed(() => [
v-model.number="row.price" v-model.number="row.price"
type="number" type="number"
min="0" min="0"
@keyup.enter="handleSave"
/> />
</QTd> </QTd>
</template> </template>
<template #body-cell-actions="{ row }">
<QTd auto-width>
<QIcon
color="primary"
name="delete"
class="cursor-pointer"
size="sm"
@click.stop="deleteService(row)"
>
<QTooltip class="text-no-wrap">
{{ t('globals.delete') }}
</QTooltip>
</QIcon>
</QTd>
</template>
</QTable> </QTable>
</template> </template>
</CrudModel> </CrudModel>
@ -193,3 +235,4 @@ const columns = computed(() => [
/> />
</QPageSticky> </QPageSticky>
</template> </template>
ñ

View File

@ -1,4 +1,5 @@
<script setup> <script setup>
import RouteDescriptorProxy from 'pages/Route/Card/RouteDescriptorProxy.vue';
import { onMounted, ref, computed } from 'vue'; import { onMounted, ref, computed } from 'vue';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
@ -14,6 +15,8 @@ import { getUrl } from 'src/composables/getUrl';
import VnUserLink from 'src/components/ui/VnUserLink.vue'; import VnUserLink from 'src/components/ui/VnUserLink.vue';
import VnTitle from 'src/components/common/VnTitle.vue'; import VnTitle from 'src/components/common/VnTitle.vue';
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue'; import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
import ZoneDescriptorProxy from 'src/pages/Zone/Card/ZoneDescriptorProxy.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
@ -71,13 +74,41 @@ async function changeState(value) {
await axios.post(`Tickets/state`, formData); await axios.post(`Tickets/state`, formData);
router.go(route.fullPath); router.go(route.fullPath);
} }
function getNoteValue(description) {
switch (description) {
case 'ItemPicker':
return t('ItemPicker');
case 'Packager':
return t('Packager');
case 'Delivery':
return t('Delivery');
case 'SalesPerson':
return t('SalesPerson');
case 'Administrative':
return t('Administrative');
case 'Weight':
return t('Weight');
case 'InvoiceOut':
return t('InvoiceOut');
case 'DropOff':
return t('DropOff');
case 'Sustitución':
return t('Sustitución');
}
}
function toTicketUrl(section) {
return '#/ticket/' + entityId.value + '/' + section;
}
</script> </script>
<template> <template>
<FetchData <FetchData
url="States/editableStates" url="States/editableStates"
@on-fetch="(data) => (editableStates = data)" :filter="{ fields: ['code', 'name', 'id', 'alertLevel'], order: 'name ASC' }"
auto-load auto-load
@on-fetch="(data) => (editableStates = data)"
/> />
<CardSummary <CardSummary
ref="summaryRef" ref="summaryRef"
@ -94,46 +125,36 @@ async function changeState(value) {
</template> </template>
<template #header-right> <template #header-right>
<QBtnDropdown <QBtnDropdown
side
top
color="black" color="black"
text-color="white" text-color="white"
:label="t('ticket.summary.changeState')" :label="t('ticket.summary.changeState')"
:disable="!isEditable()" :disable="!isEditable()"
> >
<QList> <VnSelect
<QVirtualScroll :options="editableStates"
style="max-height: 300px" hide-selected
:items="editableStates" option-label="name"
separator option-value="code"
v-slot="{ item, index }" hide-dropdown-icon
> focus-on-mount
<QItem @update:model-value="changeState(item.code)"
:key="index" />
dense
clickable
v-close-popup
@click="changeState(item.code)"
>
<QItemSection>
<QItemLabel>{{ item.name }}</QItemLabel>
</QItemSection>
</QItem>
</QVirtualScroll>
</QList>
</QBtnDropdown> </QBtnDropdown>
</template> </template>
<template #body="{ entity }"> <template #body="{ entity }">
<QCard class="vn-one"> <QCard class="vn-one">
<VnTitle <VnTitle
:url="ticketUrl + 'basic-data/step-one'" :url="toTicketUrl('basic-data')"
:text="t('globals.summary.basicData')" :text="t('globals.summary.basicData')"
/> />
<VnLv :label="t('ticket.summary.state')"> <VnLv v-if="entity.ticketState" :label="t('ticket.summary.state')">
<template #value> <template #value>
<QChip :color="entity.ticketState?.state?.classColor ?? 'dark'"> <QBadge
{{ entity.ticketState?.state?.name }} text-color="black"
</QChip> :color="entity.ticketState.state.classColor"
>
{{ entity.ticketState.state.name }}
</QBadge>
</template> </template>
</VnLv> </VnLv>
<VnLv :label="t('ticket.summary.salesPerson')"> <VnLv :label="t('ticket.summary.salesPerson')">
@ -148,7 +169,14 @@ async function changeState(value) {
:label="t('ticket.summary.agency')" :label="t('ticket.summary.agency')"
:value="entity.agencyMode?.name" :value="entity.agencyMode?.name"
/> />
<VnLv :label="t('ticket.summary.zone')" :value="entity?.zone?.name" /> <VnLv :label="t('ticket.summary.zone')">
<template #value>
<span class="link" @click.stop>
{{ entity?.zone?.name }}
<ZoneDescriptorProxy :id="entity.zoneFk" />
</span>
</template>
</VnLv>
<VnLv <VnLv
:label="t('ticket.summary.warehouse')" :label="t('ticket.summary.warehouse')"
:value="entity.warehouse?.name" :value="entity.warehouse?.name"
@ -168,7 +196,14 @@ async function changeState(value) {
</a> </a>
</template> </template>
</VnLv> </VnLv>
<VnLv :label="t('ticket.summary.route')" :value="entity.routeFk" /> <VnLv :label="t('ticket.summary.route')">
<template #value>
<span class="link">
{{ entity.routeFk }}
<RouteDescriptorProxy :id="entity.routeFk" />
</span>
</template>
</VnLv>
<VnLv :label="t('ticket.summary.invoice')"> <VnLv :label="t('ticket.summary.invoice')">
<template #value> <template #value>
<span :class="{ link: entity.refFk }"> <span :class="{ link: entity.refFk }">
@ -185,9 +220,9 @@ async function changeState(value) {
:value="dashIfEmpty(entity.weight)" :value="dashIfEmpty(entity.weight)"
/> />
</QCard> </QCard>
<QCard class="vn-one"> <QCard class="vn-one" style="flex: 2 1">
<VnTitle <VnTitle
:url="ticketUrl + 'basic-data/step-one'" :url="toTicketUrl('basic-data')"
:text="t('globals.summary.basicData')" :text="t('globals.summary.basicData')"
/> />
<VnLv <VnLv
@ -230,13 +265,13 @@ async function changeState(value) {
</QCard> </QCard>
<QCard class="vn-one" v-if="entity.notes.length"> <QCard class="vn-one" v-if="entity.notes.length">
<VnTitle <VnTitle
:url="ticketUrl + 'observation'" :url="toTicketUrl('observation')"
:text="t('ticket.pageTitles.notes')" :text="t('ticket.pageTitles.notes')"
/> />
<VnLv <VnLv
v-for="note in entity.notes" v-for="note in entity.notes"
:key="note.id" :key="note.id"
:label="note.observationType.description" :label="getNoteValue(note.observationType.description)"
:value="note.description" :value="note.description"
> >
<template #value> <template #value>
@ -246,6 +281,7 @@ async function changeState(value) {
type="textarea" type="textarea"
class="notes" class="notes"
readonly readonly
autogrow
/> />
</template> </template>
</VnLv> </VnLv>
@ -263,13 +299,14 @@ async function changeState(value) {
/> />
<VnLv <VnLv
:label="t('ticket.summary.total')" :label="t('ticket.summary.total')"
:value="toCurrency(entity.totalWithVat)" :value="toCurrency(ticket.totalWithVat)"
style="font-weight: bold"
/> />
</div> </div>
</QCard> </QCard>
<QCard class="vn-max"> <QCard class="vn-max">
<VnTitle <VnTitle
:url="ticketUrl + 'sale'" :url="toTicketUrl('sale')"
:text="t('ticket.summary.saleLines')" :text="t('ticket.summary.saleLines')"
/> />
<QTable :rows="entity.sales" style="text-align: center"> <QTable :rows="entity.sales" style="text-align: center">
@ -292,11 +329,10 @@ async function changeState(value) {
</template> </template>
<template #body="props"> <template #body="props">
<QTr :props="props"> <QTr :props="props">
<QTd> <QTd class="q-gutter-x-xs">
<QBtn <QBtn
flat flat
round round
size="xs"
icon="vn:claims" icon="vn:claims"
v-if="props.row.claim" v-if="props.row.claim"
color="primary" color="primary"
@ -307,15 +343,14 @@ async function changeState(value) {
}, },
}" }"
> >
<QTooltip <QTooltip>
>{{ t('ticket.summary.claim') }}: {{ t('ticket.summary.claim') }}:
{{ props.row.claim.claimFk }}</QTooltip {{ props.row.claim.claimFk }}
> </QTooltip>
</QBtn> </QBtn>
<QBtn <QBtn
flat flat
round round
size="xs"
icon="vn:claims" icon="vn:claims"
v-if="props.row.claimBeginning" v-if="props.row.claimBeginning"
color="primary" color="primary"
@ -326,27 +361,27 @@ async function changeState(value) {
}, },
}" }"
> >
<QTooltip <QTooltip>
>{{ t('ticket.summary.claim') }}: {{ t('ticket.summary.claim') }}:
{{ props.row.claimBeginning.claimFk }}</QTooltip {{ props.row.claimBeginning.claimFk }}
> </QTooltip>
</QBtn> </QBtn>
<QIcon <QIcon
name="warning" name="warning"
v-show="props.row.visible < 0" v-show="props.row.visible < 0"
size="xs"
color="primary" color="primary"
size="xs"
> >
<QTooltip <QTooltip>
>{{ t('ticket.summary.visible') }}: {{ t('ticket.summary.visible') }}:
{{ props.row.visible }}</QTooltip {{ props.row.visible }}
> </QTooltip>
</QIcon> </QIcon>
<QIcon <QIcon
name="vn:reserva" name="vn:reserved"
v-show="props.row.reserved" v-show="props.row.reserved"
size="xs"
color="primary" color="primary"
size="xs"
> >
<QTooltip> <QTooltip>
{{ t('ticket.summary.reserved') }} {{ t('ticket.summary.reserved') }}
@ -355,8 +390,8 @@ async function changeState(value) {
<QIcon <QIcon
name="vn:unavailable" name="vn:unavailable"
v-show="props.row.itemShortage" v-show="props.row.itemShortage"
size="xs"
color="primary" color="primary"
size="xs"
> >
<QTooltip> <QTooltip>
{{ t('ticket.summary.itemShortage') }} {{ t('ticket.summary.itemShortage') }}
@ -365,8 +400,8 @@ async function changeState(value) {
<QIcon <QIcon
name="vn:components" name="vn:components"
v-show="props.row.hasComponentLack" v-show="props.row.hasComponentLack"
size="xs"
color="primary" color="primary"
size="xs"
> >
<QTooltip> <QTooltip>
{{ t('ticket.summary.hasComponentLack') }} {{ t('ticket.summary.hasComponentLack') }}
@ -383,8 +418,34 @@ async function changeState(value) {
/> />
</QBtn> </QBtn>
</QTd> </QTd>
<QTd>{{ props.row.visible }}</QTd> <QTd>
<QTd>{{ props.row.available }}</QTd> <QChip
v-if="props.row.visible < 0"
dense
rounded
:color="'negative'"
text-color="white"
>
{{ props.row.visible }}
</QChip>
<span v-else>
{{ props.row.visible }}
</span>
</QTd>
<QTd>
<QChip
v-if="props.row.available < 0"
dense
rounded
:color="'negative'"
text-color="white"
>
{{ props.row.available }}
</QChip>
<span v-else>
{{ props.row.available }}
</span>
</QTd>
<QTd>{{ props.row.quantity }}</QTd> <QTd>{{ props.row.quantity }}</QTd>
<QTd class="description-cell"> <QTd class="description-cell">
<div class="row full-width justify-between"> <div class="row full-width justify-between">
@ -414,14 +475,11 @@ async function changeState(value) {
</template> </template>
</QTable> </QTable>
</QCard> </QCard>
<QCard <QCard class="vn-max" v-if="ticket.packagings.length != 0">
class="vn-max" <VnTitle :url="toTicketUrl('package')" :text="t('globals.packages')" />
v-if="entity.packagings.length || entity.services.length" <QTable :rows="ticket.packagings" flat style="text-align: center">
>
<VnTitle :url="ticketUrl + 'package'" :text="t('globals.packages')" />
<QTable :rows="entity.packagings" flat>
<template #header="props"> <template #header="props">
<QTr :props="props"> <QTr class="tr-header" :props="props">
<QTh auto-width>{{ t('ticket.summary.created') }}</QTh> <QTh auto-width>{{ t('ticket.summary.created') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.package') }}</QTh> <QTh auto-width>{{ t('ticket.summary.package') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.quantity') }}</QTh> <QTh auto-width>{{ t('ticket.summary.quantity') }}</QTh>
@ -435,13 +493,15 @@ async function changeState(value) {
</QTr> </QTr>
</template> </template>
</QTable> </QTable>
</QCard>
<QCard class="vn-max" v-if="ticket.services.length != 0">
<VnTitle <VnTitle
:url="ticketUrl + 'service'" :url="toTicketUrl('service')"
:text="t('ticket.summary.service')" :text="t('ticket.summary.service')"
/> />
<QTable :rows="entity.services" flat> <QTable :rows="ticket.services" flat style="text-align: center">
<template #header="props"> <template #header="props">
<QTr :props="props"> <QTr class="tr-header" :props="props">
<QTh auto-width>{{ t('ticket.summary.quantity') }}</QTh> <QTh auto-width>{{ t('ticket.summary.quantity') }}</QTh>
<QTh auto-width>{{ t('globals.description') }}</QTh> <QTh auto-width>{{ t('globals.description') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.price') }}</QTh> <QTh auto-width>{{ t('ticket.summary.price') }}</QTh>
@ -462,12 +522,62 @@ async function changeState(value) {
</template> </template>
</QTable> </QTable>
</QCard> </QCard>
<QCard class="vn-max" v-if="ticket.requests.length != 0">
<VnTitle
:url="toTicketUrl('request')"
:text="t('ticket.summary.purchaseRequest')"
/>
<QTable :rows="ticket.requests" flat style="text-align: center">
<template #header="props">
<QTr class="tr-header" :props="props">
<QTh auto-width>{{ t('ticket.summary.description') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.created') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.requester') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.attender') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.quantity') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.price') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.item') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.ok') }}</QTh>
</QTr>
</template>
<template #body="props">
<QTr :props="props">
<QTd>{{ props.row.description }}</QTd>
<QTd>{{ toDate(props.row.created) }}</QTd>
<QTd>{{ props.row.requester?.user?.username }}</QTd>
<QTd>{{ props.row.atender?.user?.username }}</QTd>
<QTd>{{ props.row.quantity }}</QTd>
<QTd>{{ toCurrency(props.row.price) }}</QTd>
<QTd>
<span class="link" v-if="props.row.isOk">
{{ props.row.itemFk }}
<ItemDescriptorProxy :id="props.row.itemFk" />
</span>
</QTd>
<QTd>
<QCheckbox
v-model="props.row.isOk"
disable
:toggle-indeterminate="false"
>
<QTooltip v-if="props.row.isOk">
{{ t('Accepted') }}
</QTooltip>
<QTooltip v-else>
{{ t('Denied') }}
</QTooltip>
</QCheckbox>
</QTd>
</QTr>
</template>
</QTable>
</QCard>
</template> </template>
</CardSummary> </CardSummary>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.notes { .notes {
width: max-content; width: fit-content;
} }
.q-card.q-card--dark.q-dark.vn-one { .q-card.q-card--dark.q-dark.vn-one {
@ -503,3 +613,28 @@ async function changeState(value) {
color: $primary-light; color: $primary-light;
} }
</style> </style>
<i18n>
en:
ItemPicker: Item Picker
Packager: Packager
Delivery: Delivery
SalesPerson: Sales Person
Administrative: Administrative
Weight: Weight
InvoiceOut: Invoice Out
DropOff: Drop Off
Sustitución: Sustitution
es:
ItemPicker: Items
Packager: Encajador
Delivery: Envío
SalesPerson: Comercial
Administrative: Administrativa
Weight: Peso
InvoiceOut: Facturas
DropOff: Despacho
Sustitución: Sustitución
Accepted: Aceptado
Denied: Denegado
</i18n>

View File

@ -96,8 +96,8 @@ const openCreateModal = () => createTrackingDialogRef.value.show();
:no-data-label="t('globals.noResults')" :no-data-label="t('globals.noResults')"
> >
<template #body-cell-worker="{ row }"> <template #body-cell-worker="{ row }">
<QTd @click.stop> <QTd>
Review

Aqui habia un cambio con respecto al orden de los filtros. Seguro que jorge sabe donde se hace porque lo hizo para InvoiceInSummary

Aqui habia un cambio con respecto al orden de los filtros. Seguro que jorge sabe donde se hace porque lo hizo para InvoiceInSummary
<QBtn flat color="primary"> <QBtn flat class="link" @click.stop>
{{ row.user?.name }} {{ row.user?.name }}
<WorkerDescriptorProxy :id="row.user?.worker?.id" /> <WorkerDescriptorProxy :id="row.user?.worker?.id" />
</QBtn> </QBtn>

View File

@ -1,13 +1,11 @@
<script setup> <script setup>
import { ref, computed, onMounted } from 'vue'; import { ref, computed, onMounted } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import TicketTransferForm from './TicketTransferForm.vue'; import TicketTransferForm from './TicketTransferForm.vue';
import { toDateFormat } from 'src/filters/date.js'; import { toDateFormat } from 'src/filters/date.js';
import axios from 'axios';
const $props = defineProps({ const $props = defineProps({
mana: { mana: {
@ -28,13 +26,10 @@ const $props = defineProps({
}, },
}); });
const emit = defineEmits(['refreshData']);
const router = useRouter();
const { t } = useI18n(); const { t } = useI18n();
const QPopupProxyRef = ref(null); const QPopupProxyRef = ref(null);
const transferFormRef = ref(null);
const _transfer = ref(null); const _transfer = ref();
const transferLinesColumns = computed(() => [ const transferLinesColumns = computed(() => [
{ {
@ -85,19 +80,11 @@ const destinationTicketColumns = computed(() => [
}, },
]); ]);
const transferSales = async (ticketId) => { const handleRowClick = (row) => {
const params = { const ticketId = row.id;
ticketId: ticketId, if (transferFormRef.value) {
sales: $props.transfer.sales, transferFormRef.value.transferSales(ticketId);
}; }
const { data } = await axios.post(
`tickets/${$props.ticket.id}/transferSales`,
params
);
if (data && data.id === $props.ticket.id) emit('refreshData');
else router.push({ name: 'TicketSale', params: { id: data.id } });
}; };
onMounted(() => (_transfer.value = $props.transfer)); onMounted(() => (_transfer.value = $props.transfer));
@ -105,9 +92,8 @@ onMounted(() => (_transfer.value = $props.transfer));
<template> <template>
<QPopupProxy ref="QPopupProxyRef"> <QPopupProxy ref="QPopupProxyRef">
<QCard class="q-px-md" style="display: flex"> <QCard class="q-px-md" style="display: flex; width: 80vw">
<QTable <QTable
v-if="transfer.sales"
:rows="transfer.sales" :rows="transfer.sales"
:columns="transferLinesColumns" :columns="transferLinesColumns"
:title="t('Sales to transfer')" :title="t('Sales to transfer')"
@ -121,9 +107,6 @@ onMounted(() => (_transfer.value = $props.transfer));
<VnInput <VnInput
v-model.number="row.quantity" v-model.number="row.quantity"
:clearable="false" :clearable="false"
@keyup.enter="changeQuantity(row)"
@blur="changeQuantity(row)"
@focus="edit.oldQuantity = row.quantity"
style="max-width: 60px" style="max-width: 60px"
/> />
</QTd> </QTd>
@ -137,6 +120,7 @@ onMounted(() => (_transfer.value = $props.transfer));
:title="t('Destination ticket')" :title="t('Destination ticket')"
row-key="id" row-key="id"
class="full-width q-mt-md" class="full-width q-mt-md"
@row-click="(_, row) => handleRowClick(row)"
> >
<template #body-cell-address="{ row }"> <template #body-cell-address="{ row }">
<QTd @click.stop> <QTd @click.stop>
@ -158,10 +142,10 @@ onMounted(() => (_transfer.value = $props.transfer));
</template> </template>
<template #no-data> <template #no-data>
<TicketTransferForm v-bind="$props" /> <TicketTransferForm ref="transferFormRef" v-bind="$props" />
</template> </template>
<template #bottom> <template #bottom>
<TicketTransferForm v-bind="$props" /> <TicketTransferForm ref="transferFormRef" v-bind="$props" />
</template> </template>
</QTable> </QTable>
</QCard> </QCard>

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { ref, onMounted } from 'vue'; import { ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
@ -31,7 +31,7 @@ const emit = defineEmits(['refreshData']);
const router = useRouter(); const router = useRouter();
const { t } = useI18n(); const { t } = useI18n();
const _transfer = ref(null); const _transfer = ref($props.transfer);
const transferSales = async (ticketId) => { const transferSales = async (ticketId) => {
const params = { const params = {
@ -48,11 +48,10 @@ const transferSales = async (ticketId) => {
else router.push({ name: 'TicketSale', params: { id: data.id } }); else router.push({ name: 'TicketSale', params: { id: data.id } });
}; };
onMounted(() => (_transfer.value = $props.transfer)); defineExpose({ transferSales });
</script> </script>
<template> <template>
{{ _transfer }}
<QForm class="q-mt-lg full-width"> <QForm class="q-mt-lg full-width">
<VnInput <VnInput
v-model.number="_transfer.ticketId" v-model.number="_transfer.ticketId"

View File

@ -5,17 +5,18 @@ import { useRoute } from 'vue-router';
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue'; import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
import FetchedTags from 'components/ui/FetchedTags.vue'; import FetchedTags from 'components/ui/FetchedTags.vue';
import RightMenu from 'src/components/common/RightMenu.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import { dashIfEmpty } from 'src/filters'; import { dashIfEmpty } from 'src/filters';
import axios from 'axios'; import axios from 'axios';
import VnTable from 'src/components/VnTable/VnTable.vue';
const route = useRoute(); const route = useRoute();
const stateStore = useStateStore(); const stateStore = useStateStore();
const { t } = useI18n(); const { t } = useI18n();
const salesRef = ref(null); const salesRef = ref(null);
const tableRef = ref();
watch( watch(
() => route.params.id, () => route.params.id,
@ -34,37 +35,42 @@ const salesFilter = computed(() => ({
const sales = ref([]); const sales = ref([]);
const packingTypeVolume = ref([]); const packingTypeVolume = ref([]);
const rows = computed(() => sales.value);
const columns = computed(() => [ const columns = computed(() => [
{ {
align: 'left',
label: t('volume.item'), label: t('volume.item'),
name: 'item', name: 'itemFk',
align: 'left', chip: {
condition: () => true,
},
isId: true,
}, },
{ {
align: 'left',
label: t('volume.description'), label: t('volume.description'),
name: 'description', name: 'concept',
align: 'left', columnClass: 'expand',
cardVisible: true,
}, },
{ {
align: 'left',
label: t('volume.packingType'), label: t('volume.packingType'),
name: 'quantity', name: 'itemPackingTypeFk',
field: (row) => row.item.itemPackingTypeFk, cardVisible: true,
align: 'left', format: (row, dashIfEmpty) => dashIfEmpty(row.item.itemPackingTypeFk),
format: (val) => dashIfEmpty(val),
}, },
{ {
align: 'left',
label: t('volume.quantity'), label: t('volume.quantity'),
name: 'quantity', name: 'quantity',
field: 'quantity', cardVisible: true,
align: 'left',
}, },
{ {
label: t('volume.volumeQuantity'),
name: 'quantity',
field: (row) => row.saleVolume?.volume,
align: 'left', align: 'left',
label: t('volume.volumeQuantity'),
name: 'volume',
cardVisible: true,
}, },
]); ]);
@ -87,9 +93,7 @@ const applyVolumes = async (salesData) => {
} }
}; };
onMounted(() => { onMounted(() => (stateStore.rightDrawer = true));
stateStore.rightDrawer = true;
});
onUnmounted(() => (stateStore.rightDrawer = false)); onUnmounted(() => (stateStore.rightDrawer = false));
</script> </script>
@ -102,52 +106,55 @@ onUnmounted(() => (stateStore.rightDrawer = false));
@on-fetch="(data) => applyVolumes(data)" @on-fetch="(data) => applyVolumes(data)"
auto-load auto-load
/> />
<RightMenu v-if="packingTypeVolume.length"> <QDrawer
<template #right-panel> v-if="packingTypeVolume.length"
<QCard side="right"
v-for="(packingType, index) in packingTypeVolume" :width="265"
:key="index" v-model="stateStore.rightDrawer"
class="q-pa-md q-mb-md q-ma-md color-vn-text"
bordered
flat
style="border-color: black"
>
<QCardSection class="column items-center" horizontal>
<span>
{{ t('volume.type') }}:
{{ dashIfEmpty(packingType.description) }}
</span>
</QCardSection>
<QCardSection class="column items-center" horizontal>
<span> {{ t('volume.volume') }}: {{ packingType.volume }} </span>
</QCardSection>
</QCard>
</template>
</RightMenu>
<QTable
:rows="rows"
:columns="columns"
row-key="id"
:pagination="{ rowsPerPage: 0 }"
class="full-width q-mt-md"
:no-data-label="t('globals.noResults')"
> >
<template #body-cell-item="{ row }"> <QCard
<QTd> v-for="(packingType, index) in packingTypeVolume"
<QBtn flat color="primary"> :key="index"
{{ row.itemFk }} class="q-pa-md q-mb-md q-ma-md color-vn-text"
<ItemDescriptorProxy :id="row.itemFk" /> bordered
</QBtn> flat
</QTd> style="border-color: black"
>
<QCardSection class="column items-center" horizontal>
<span>
{{ t('volume.type') }}:
{{ dashIfEmpty(packingType.description) }}
</span>
</QCardSection>
<QCardSection class="column items-center" horizontal>
<span> {{ t('volume.volume') }}: {{ packingType.volume }} </span>
</QCardSection>
</QCard>
</QDrawer>
<VnTable
ref="tableRef"
data-key="TicketVolume"
:url="`Tickets/${route.params.id}/getSales`"
:columns="columns"
:right-search="false"
:column-search="false"
:order="['itemPackingTypeFk']"
auto-load
>
<template #column-itemFk="{ row }">
<span class="link">
{{ row.itemFk }}
<ItemDescriptorProxy :id="row.itemFk" />
</span>
</template> </template>
<template #body-cell-description="{ row }"> <template #column-concept="{ row }">
<QTd> <span>{{ row.item.name }}</span>
<div class="column"> <span class="color-vn-label q-pl-md">{{ row.item.subName }}</span>
<span>{{ row.item.name }}</span> <FetchedTags :item="row.item" />
<span class="color-vn-label">{{ row.item.subName }}</span>
<FetchedTags :item="row.item" />
</div>
</QTd>
</template> </template>
</QTable> <template #column-volume="{ rowIndex }">
<span>{{ packingTypeVolume?.[rowIndex]?.volume }}</span>
</template>
</VnTable>
</template> </template>

View File

@ -456,11 +456,12 @@ const handleCloseProgressDialog = () => {
const handleCancelProgress = () => (cancelProgress.value = true); const handleCancelProgress = () => (cancelProgress.value = true);
onMounted(async () => { onMounted(async () => {
let today = Date.vnNew().toISOString(); let today = Date.vnNew();
const tomorrow = new Date(today); const tomorrow = new Date(today);
tomorrow.setDate(tomorrow.getDate() + 1); tomorrow.setDate(tomorrow.getDate() + 1);
userParams.dateFuture = tomorrow; userParams.dateFuture = tomorrow;
userParams.dateToAdvance = today; userParams.dateToAdvance = today;
userParams.scopeDays = 1;
userParams.warehouseFk = user.value.warehouseFk; userParams.warehouseFk = user.value.warehouseFk;
const filter = { limit: 0 }; const filter = { limit: 0 };
await arrayData.addFilter({ filter, userParams }); await arrayData.addFilter({ filter, userParams });
@ -550,6 +551,7 @@ onMounted(async () => {
style="max-width: 99%" style="max-width: 99%"
> >
<template #header="props"> <template #header="props">
{{ userParams.scopeDays }}
<QTr :props="props"> <QTr :props="props">
<QTh <QTh
class="horizontal-separator text-uppercase color-vn-label" class="horizontal-separator text-uppercase color-vn-label"

View File

@ -9,6 +9,7 @@ import VnInputDate from 'src/components/common/VnInputDate.vue';
import axios from 'axios'; import axios from 'axios';
import { onMounted } from 'vue'; import { onMounted } from 'vue';
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
const { t } = useI18n(); const { t } = useI18n();
const props = defineProps({ const props = defineProps({
@ -116,6 +117,15 @@ onMounted(async () => await getItemPackingTypes());
</VnSelect> </VnSelect>
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem>
<QItemSection>
<VnInputNumber
v-model="params.scopeDays"
:label="t('Days onward')"
is-outlined
/>
</QItemSection>
</QItem>
<QItem> <QItem>
<QItemSection> <QItemSection>
<QCheckbox <QCheckbox

View File

@ -115,6 +115,15 @@ const warehouses = ref([]);
/> />
</QItemSection> </QItemSection>
</QItem> </QItem>
<QItem>
<QItemSection>
<VnInput
v-model="params.scopeDays"
:label="t('Days onward')"
is-outlined
/>
</QItemSection>
</QItem>
<QItem> <QItem>
<QItemSection> <QItemSection>
<QCheckbox <QCheckbox
@ -278,4 +287,5 @@ es:
Warehouse: Almacén Warehouse: Almacén
Yes: Si Yes: Si
No: No No: No
Days onward: Días adelante
</i18n> </i18n>

View File

@ -4,7 +4,9 @@ import { computed, ref, onMounted } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { toDate, toCurrency } from 'src/filters/index'; import { useQuasar } from 'quasar';
import { toDate, toCurrency, dashIfEmpty } from 'src/filters/index';
import useNotify from 'src/composables/useNotify';
import TicketSummary from './Card/TicketSummary.vue'; import TicketSummary from './Card/TicketSummary.vue';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import { useSummaryDialog } from 'src/composables/useSummaryDialog'; import { useSummaryDialog } from 'src/composables/useSummaryDialog';
@ -14,11 +16,19 @@ 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 RightMenu from 'src/components/common/RightMenu.vue'; import RightMenu from 'src/components/common/RightMenu.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 '../Customer/Card/CustomerDescriptorProxy.vue';
import ZoneDescriptorProxy from '../Zone/Card/ZoneDescriptorProxy.vue';
import { toTimeFormat } from 'src/filters/date';
import InvoiceOutDescriptorProxy from '../InvoiceOut/Card/InvoiceOutDescriptorProxy.vue';
const route = useRoute(); const route = useRoute();
const { t } = useI18n(); const { t } = useI18n();
const { viewSummary } = useSummaryDialog(); const { viewSummary } = useSummaryDialog();
const tableRef = ref(); const tableRef = ref();
const quasar = useQuasar();
const { notify } = useNotify();
const clientsOptions = ref([]); const clientsOptions = ref([]);
const addressesOptions = ref([]); const addressesOptions = ref([]);
const agenciesOptions = ref([]); const agenciesOptions = ref([]);
@ -26,6 +36,7 @@ const selectedClient = ref();
const stateStore = useStateStore(); const stateStore = useStateStore();
const from = Date.vnNew(); const from = Date.vnNew();
from.setHours(0, 0, 0, 0); from.setHours(0, 0, 0, 0);
from.setDate(from.getDate() - 7);
const to = Date.vnNew(); const to = Date.vnNew();
to.setHours(23, 59, 0, 0); to.setHours(23, 59, 0, 0);
to.setDate(to.getDate() + 1); to.setDate(to.getDate() + 1);
@ -44,19 +55,21 @@ const initializeFromQuery = () => {
Object.assign(userParams, { from, to }); Object.assign(userParams, { from, to });
}; };
const selectedRows = ref([]);
const hasSelectedRows = computed(() => selectedRows.value.length > 0);
const showForm = ref(false);
const dialogData = ref();
const companiesOptions = ref([]);
const accountingOptions = ref([]);
const amountToReturn = ref();
const columns = computed(() => [ const columns = computed(() => [
{ {
align: 'left', align: 'left',
name: 'stateFk', name: 'statusIcons',
label: t('ticketList.state'), hidden: true,
columnFilter: { format: () => '',
name: 'stateFk', columnClass: 'expand',
component: 'select',
attrs: {
url: 'States',
fields: ['id', 'name'],
},
},
}, },
{ {
align: 'left', align: 'left',
@ -67,40 +80,6 @@ const columns = computed(() => [
}, },
isId: true, isId: true,
}, },
{
align: 'left',
name: 'nickname',
label: t('ticketList.nickname'),
isTitle: true,
},
{
align: 'left',
name: 'shipped',
cardVisible: true,
label: t('ticketList.shipped'),
columnFilter: {
component: 'date',
alias: 't',
inWhere: true,
},
format: ({ shipped }) => toDate(shipped),
},
{
align: 'left',
name: 'zoneFk',
label: t('ticketList.zone'),
columnFilter: {
component: 'select',
attrs: {
url: 'Zones',
fields: ['id', 'name'],
},
alias: 't',
inWhere: true,
},
format: (row, dashIfEmpty) => dashIfEmpty(row.zoneName),
},
{ {
align: 'left', align: 'left',
label: t('ticketList.salesPerson'), label: t('ticketList.salesPerson'),
@ -118,6 +97,89 @@ const columns = computed(() => [
}, },
format: (row, dashIfEmpty) => dashIfEmpty(row.salesPerson), format: (row, dashIfEmpty) => dashIfEmpty(row.salesPerson),
}, },
{
align: 'left',
name: 'shippedDate',
cardVisible: true,
label: t('ticketList.shipped'),
columnFilter: {
component: 'date',
alias: 't',
inWhere: true,
},
format: ({ shippedDate }) => toDate(shippedDate),
},
{
align: 'left',
name: 'shipped',
label: t('ticketList.hour'),
format: (row) => toTimeFormat(row.shipped),
},
{
align: 'left',
name: 'zoneLanding',
label: t('ticketList.closure'),
format: (row, dashIfEmpty) => dashIfEmpty(toTimeFormat(row.zoneLanding)),
},
{
align: 'left',
name: 'nickname',
label: t('ticketList.nickname'),
columnClass: 'expand',
},
{
align: 'left',
name: 'addressNickname',
label: t('ticketList.addressNickname'),
columnClass: 'expand',
},
{
align: 'left',
name: 'province',
label: t('ticketList.province'),
columnClass: 'expand',
},
{
align: 'left',
name: 'stateFk',
label: t('ticketList.state'),
columnFilter: {
name: 'stateFk',
component: 'select',
attrs: {
url: 'States',
fields: ['id', 'name'],
},
},
columnClass: 'expand',
},
{
align: 'left',
name: 'refFk',
label: t('ticketList.ref'),
},
{
align: 'left',
name: 'zoneFk',
label: t('ticketList.zone'),
columnFilter: {
component: 'select',
attrs: {
url: 'Zones',
fields: ['id', 'name'],
},
alias: 't',
inWhere: true,
},
columnClass: 'expand',
format: (row, dashIfEmpty) => dashIfEmpty(row.zoneName),
},
{
align: 'left',
name: 'warehouse',
label: t('ticketList.warehouse'),
columnClass: 'expand',
},
{ {
align: 'left', align: 'left',
name: 'totalWithVat', name: 'totalWithVat',
@ -133,15 +195,27 @@ const columns = computed(() => [
align: 'right', align: 'right',
name: 'tableActions', name: 'tableActions',
actions: [ actions: [
{
title: t('ticketList.toLines'),
icon: 'list',
isPrimary: true,
action: (row) => redirectToLines(row.id),
},
{ {
title: t('ticketList.summary'), title: t('ticketList.summary'),
icon: 'preview', icon: 'preview',
isPrimary: true,
action: (row) => viewSummary(row.id, TicketSummary), action: (row) => viewSummary(row.id, TicketSummary),
}, },
], ],
}, },
]); ]);
function redirectToLines(id) {
const url = `#/ticket/${id}/sale`;
window.open(url, '_blank');
}
const onClientSelected = async (formData) => { const onClientSelected = async (formData) => {
await fetchClient(formData); await fetchClient(formData);
await fetchAddresses(formData); await fetchAddresses(formData);
@ -210,16 +284,174 @@ const fetchAddresses = async (formData) => {
} }
}; };
const getColor = (row) => { const getColor = (row) => {
return row?.classColor ? `bg-${row.classColor}` : 'bg-orange'; if (row.alertLevelCode === 'OK') return 'bg-success';
else if (row.alertLevelCode === 'FREE') return 'bg-notice';
else if (row.alertLevel === 1) return 'bg-warning';
else if (row.alertLevel === 0) return 'bg-alert';
};
const getDateColor = (date) => {
const today = Date.vnNew();
today.setHours(0, 0, 0, 0);
const timeTicket = new Date(date);
timeTicket.setHours(0, 0, 0, 0);
const comparation = today - timeTicket;
if (comparation == 0) return 'bg-warning';
if (comparation < 0) return 'bg-success';
}; };
onMounted(() => { onMounted(() => {
initializeFromQuery(); initializeFromQuery();
stateStore.rightDrawer = true; stateStore.rightDrawer = true;
}); });
async function makeInvoice(ticket) {
const ticketsIds = ticket.map((item) => item.id);
const { data } = await axios.post(`Tickets/invoiceTicketsAndPdf`, { ticketsIds });
const response = data;
if (response)
quasar.notify({
message: t('globals.dataSaved'),
type: 'positive',
});
}
async function sendDocuware(ticket) {
try {
let ticketIds = ticket.map((item) => item.id);
const { data } = await axios.post(`Docuwares/upload`, {
fileCabinet: 'deliveryNote',
ticketIds,
});
for (let ticket of ticketIds) {
ticket.stateFk = data.id;
ticket.state = data.name;
ticket.alertLevel = data.alertLevel;
ticket.alertLevelCode = data.code;
}
notify('globals.dataSaved', 'positive');
} catch (err) {
console.err('err: ', err);
}
}
function openBalanceDialog(ticket) {
const checkedTickets = ticket;
const amountPaid = ref(0);
const clientFk = ref(null);
const description = ref([]);
const firstTicketClientId = checkedTickets[0].clientFk;
const isSameClient = checkedTickets.every(
(ticket) => ticket.clientFk === firstTicketClientId
);
if (!isSameClient) {
throw new Error('You cannot make a payment on account from multiple clients');
}
for (let ticketData of checkedTickets) {
amountPaid.value += ticketData.totalWithVat;
clientFk.value = ticketData.clientFk;
description.value.push(ticketData.id);
}
const balanceCreateDialog = ref({
amountPaid: amountPaid.value,
clientFk: clientFk.value,
description: `Albaran: ${description.value.join(', ')}`,
});
dialogData.value = balanceCreateDialog;
showForm.value = true;
}
async function onSubmit() {
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;
}
</script> </script>
<template> <template>
<FetchData
url="Companies"
@on-fetch="(data) => (companiesOptions = data)"
auto-load
/>
<FetchData
url="Accountings"
@on-fetch="(data) => (accountingOptions = data)"
auto-load
/>
<VnSearchbar <VnSearchbar
data-key="Ticket" data-key="Ticket"
:label="t('Search ticket')" :label="t('Search ticket')"
@ -246,12 +478,136 @@ onMounted(() => {
:user-params="userParams" :user-params="userParams"
:right-search="false" :right-search="false"
redirect="ticket" redirect="ticket"
auto-load v-model:selected="selectedRows"
:table="{
'row-key': 'id',
selection: 'multiple',
}"
> >
<template #column-statusIcons="{ row }">
<div class="q-gutter-x-xs">
<QIcon
v-if="row.isTaxDataChecked === 0"
color="primary"
name="vn:no036"
size="xs"
>
<QTooltip>
{{ t('No verified data') }}
</QTooltip>
</QIcon>
<QIcon
v-if="row.hasTicketRequest"
color="primary"
name="vn:buyrequest"
size="xs"
>
<QTooltip>
{{ t('Purchase request') }}
</QTooltip>
</QIcon>
<QIcon
v-if="row.itemShortage"
color="primary"
name="vn:unavailable"
size="xs"
>
<QTooltip>
{{ t('Not visible') }}
</QTooltip>
</QIcon>
<QIcon v-if="row.isFreezed" color="primary" name="vn:frozen" size="xs">
<QTooltip>
{{ t('Client frozen') }}
</QTooltip>
</QIcon>
<QIcon v-if="row.risk" color="primary" name="vn:risk" size="xs">
<QTooltip> {{ t('Risk') }}: {{ row.risk }} </QTooltip>
</QIcon>
<QIcon
v-if="row.hasComponentLack"
color="primary"
name="vn:components"
size="xs"
>
<QTooltip>
{{ t('Component lack') }}
</QTooltip>
</QIcon>
<QIcon
v-if="row.hasRounding"
color="primary"
name="sync_problem"
size="xs"
>
<QTooltip>
{{ t('Rounding') }}
</QTooltip>
</QIcon>
</div>
</template>
<template #column-salesPersonFk="{ row }">
<span class="link" @click.stop>
{{ row.salesPerson }}
<CustomerDescriptorProxy :id="row.salesPersonFk" />
</span>
</template>
<template #column-shippedDate="{ row }">
<span v-if="getDateColor(row.shipped)">
<QChip :class="getDateColor(row.shipped)" dense square>
{{ toDate(row.shippedDate) }}
</QChip>
</span>
</template>
<template #column-nickname="{ row }">
<span class="link" @click.stop>
{{ row.nickname }}
<CustomerDescriptorProxy :id="row.clientFk" />
</span>
</template>
<template #column-addressNickname="{ row }">
<span class="link" @click.stop>
{{ row.addressNickname }}
<CustomerDescriptorProxy :id="row.clientFk" />
</span>
</template>
<template #column-stateFk="{ row }">
<span v-if="getColor(row)">
<QChip :class="getColor(row)" dense square>
{{ row.state }}
</QChip>
</span>
<span v-else>
{{ row.state }}
</span>
</template>
<template #column-refFk="{ row }">
<span class="link" @click.stop>
{{ dashIfEmpty(row.refFk) }}
<InvoiceOutDescriptorProxy :id="row.invoiceOutId" />
</span>
</template>
<template #column-zoneFk="{ row }">
<span class="link" @click.stop>
{{ dashIfEmpty(row.zoneName) }}
<ZoneDescriptorProxy :id="row.zoneFk" />
</span>
</template>
<template #column-totalWithVat="{ row }">
<QChip
v-if="row.totalWithVat > 0 && row.totalWithVat < 50"
class="bg-warning"
dense
square
>
{{ row.totalWithVat }}
</QChip>
</template>
<template #more-create-dialog="{ data }"> <template #more-create-dialog="{ data }">
<VnRow> <VnRow>
<VnSelect <VnSelect
url="Clients" url="Clients"
:fields="['id', 'name']"
jon marked this conversation as resolved
Review

añadir order:id

añadir order:id
:label="t('ticketList.client')" :label="t('ticketList.client')"
v-model="data.clientId" v-model="data.clientId"
:options="clientsOptions" :options="clientsOptions"
@ -259,6 +615,7 @@ onMounted(() => {
option-label="name" option-label="name"
hide-selected hide-selected
@update:model-value="(client) => onClientSelected(data)" @update:model-value="(client) => onClientSelected(data)"
:sort-by="'id ASC'"
> >
<template #option="scope"> <template #option="scope">
<QItem v-bind="scope.itemProps"> <QItem v-bind="scope.itemProps">
@ -339,12 +696,136 @@ onMounted(() => {
</div> </div>
</VnRow> </VnRow>
</template> </template>
<template #column-stateFk="{ row }">
<QChip :class="getColor(row)" dense square>
{{ row.state }}
</QChip>
</template>
</VnTable> </VnTable>
<QPageSticky :offset="[20, 80]" style="z-index: 2">
<QBtn
v-if="hasSelectedRows"
@click="makeInvoice(selectedRows)"
color="primary"
fab
icon="vn:invoice-in"
/>
<QTooltip>
{{ t('ticketList.createInvoice') }}
</QTooltip>
</QPageSticky>
<QPageSticky v-if="hasSelectedRows" :offset="[20, 140]" style="z-index: 2">
<QBtn
@click.stop="openBalanceDialog(selectedRows)"
color="primary"
fab
icon="vn:recovery"
/>
<QTooltip>
{{ t('ticketList.accountPayment') }}
</QTooltip>
</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-value="id"
option-label="code"
hide-selected
>
</VnSelect>
</VnRow>
<VnRow>
<VnSelect
:label="t('ticketList.bank')"
v-model="dialogData.bankFk"
:options="accountingOptions"
option-value="id"
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">
<QBtn
@click="sendDocuware(selectedRows)"
color="primary"
fab
icon="install_mobile"
/>
<QTooltip>
{{ t('ticketList.sendDocuware') }}
</QTooltip>
</QPageSticky>
</template> </template>
<i18n> <i18n>
@ -353,4 +834,5 @@ es:
You can search by ticket id or alias: Puedes buscar por id o alias del ticket You can search by ticket id or alias: Puedes buscar por id o alias del ticket
Zone: Zona Zone: Zona
New ticket: Nuevo ticket New ticket: Nuevo ticket
Component lack: Faltan componentes
</i18n> </i18n>

View File

@ -67,6 +67,8 @@ advanceTickets:
advanceWithoutNegativeTitle: Advance tickets (without negatives) advanceWithoutNegativeTitle: Advance tickets (without negatives)
advanceWithoutNegativeSubtitle: Advance {selectedTickets} tickets confirmation advanceWithoutNegativeSubtitle: Advance {selectedTickets} tickets confirmation
errorsList: Errors list errorsList: Errors list
search: Search advance tickets
searchInfo: Search advance tickets by ID or client ID
futureTickets: futureTickets:
problems: Problems problems: Problems
ticketId: ID ticketId: ID
@ -105,7 +107,7 @@ expedition:
name: Name name: Name
packageType: Package type packageType: Package type
counter: Counter counter: Counter
externalId: externalId externalId: external Id
created: Created created: Created
state: State state: State
historyAction: Status log historyAction: Status log
@ -118,6 +120,10 @@ expedition:
removeExpeditionSubtitle: Are you sure you want to delete this expedition? removeExpeditionSubtitle: Are you sure you want to delete this expedition?
worker: Worker worker: Worker
move: Move move: Move
isScanned: Is scanned
yes: Yes
no: No
removeExpedition: Delete expedition
basicData: basicData:
next: Next next: Next
back: Back back: Back
@ -233,7 +239,7 @@ package:
removePackage: Remove package removePackage: Remove package
ticketList: ticketList:
id: Id id: Id
nickname: Nickname nickname: Ticket nickname
state: State state: State
shipped: Shipped shipped: Shipped
zone: Zone zone: Zone
@ -242,3 +248,27 @@ ticketList:
summary: Summary summary: Summary
client: Customer client: Customer
createTicket: Create ticket createTicket: Create ticket
createInvoice: Create invoice
accountPayment: Account payment
sendDocuware: Set delivered and send delivery note(s) to the tablet
addPayment: Add payment
date: Date
company: Company
amount: Amount
reference: Reference
bank: Bank
cash: Cash
deliveredAmount: Delivered amount
amountToReturn: Amount to return
viewReceipt: View receipt
sendEmail: Send email
compensation: Compensation
creditCard: Credit card
transfers: Transfers
province: Province
warehouse: Warehouse
hour: Hour
closure: Closure
toLines: Go to lines
addressNickname: Address nickname
ref: Reference

View File

@ -114,6 +114,8 @@ advanceTickets:
advanceWithoutNegativeTitle: Adelantar tickets (sin negativos) advanceWithoutNegativeTitle: Adelantar tickets (sin negativos)
advanceWithoutNegativeSubtitle: '¿Desea adelantar {selectedTickets} tickets?' advanceWithoutNegativeSubtitle: '¿Desea adelantar {selectedTickets} tickets?'
errorsList: Lista de errores errorsList: Lista de errores
search: Buscar por tickets adelantados
searchInfo: Buscar tickets adelantados por el identificador o el identificador del cliente
futureTickets: futureTickets:
problems: Problemas problems: Problemas
ticketId: ID ticketId: ID
@ -192,9 +194,9 @@ expedition:
id: Expedición id: Expedición
item: Artículo item: Artículo
name: Nombre name: Nombre
packageType: Package type packageType: Embalaje
counter: Contador counter: Contador
externalId: externalId externalId: ID externo
created: Fecha creación created: Fecha creación
state: Estado state: Estado
historyAction: Historial de estados historyAction: Historial de estados
@ -207,6 +209,10 @@ expedition:
removeExpeditionSubtitle: ¿Está seguro de eliminar esta expedición? removeExpeditionSubtitle: ¿Está seguro de eliminar esta expedición?
worker: Trabajador worker: Trabajador
move: Mover move: Mover
isScanned: Escaneado
yes:
no: No
removeExpedition: Eliminar expedición
package: package:
package: Embalaje package: Embalaje
quantity: Cantidad quantity: Cantidad
@ -236,7 +242,7 @@ You can search by ticket id or alias: Puedes buscar por id o alias del ticket
Select lines to see the options: Selecciona líneas para ver las opciones Select lines to see the options: Selecciona líneas para ver las opciones
ticketList: ticketList:
id: Id id: Id
nickname: Alias nickname: Alias ticket
state: Estado state: Estado
shipped: F. Envío shipped: F. Envío
zone: Zona zone: Zona
@ -245,3 +251,27 @@ ticketList:
summary: Resumen summary: Resumen
client: Cliente client: Cliente
createTicket: Crear ticket createTicket: Crear ticket
createInvoice: Crear factura
accountPayment: Pago a cuenta...
sendDocuware: Marcar como servido/s y enviar albarán/es a la tablet
addPayment: Añadir pago
date: Fecha
company: Empresa
amount: Importe
reference: Referencia
bank: Caja
cash: Efectivo
deliveredAmount: Cantidad entregada
amountToReturn: Cantidad a devolver
viewReceipt: Ver recibido
sendEmail: Enviar correo
compensation: Compensación
creditCard: Tarjeta de crédito
transfers: Transferencias
province: Provincia
warehouse: Almacén
hour: Hora
closure: Cierre
toLines: Ir a lineas
addressNickname: Alias consignatario
ref: Referencia

View File

@ -198,7 +198,7 @@ export default {
name: 'TicketPackage', name: 'TicketPackage',
meta: { meta: {
title: 'packages', title: 'packages',
icon: 'vn:bin', icon: 'vn:bucket',
}, },
component: () => import('src/pages/Ticket/Card/TicketPackage.vue'), component: () => import('src/pages/Ticket/Card/TicketPackage.vue'),
}, },