Vistas sección pedidos #77
|
@ -24,7 +24,7 @@ module.exports = configure(function (ctx) {
|
|||
// app boot file (/src/boot)
|
||||
// --> boot files are part of "main.js"
|
||||
// https://v2.quasar.dev/quasar-cli-webpack/boot-files
|
||||
boot: ['i18n', 'axios', 'error-handler', 'app'],
|
||||
boot: ['i18n', 'axios', 'vnDate', 'error-handler', 'app'],
|
||||
|
||||
// https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js#Property%3A-css
|
||||
css: ['app.scss', 'width.scss', 'responsive.scss'],
|
||||
|
|
16
src/App.vue
|
@ -1,11 +1,11 @@
|
|||
<script setup>
|
||||
import { useAppStore } from 'stores/app';
|
||||
jsegarra marked this conversation as resolved
Outdated
|
||||
import { onBeforeMount } from 'vue';
|
||||
const appStore = useAppStore();
|
||||
|
||||
onBeforeMount(() => appStore.init());
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<router-view />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'App'
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { boot } from 'quasar/wrappers'
|
||||
import { appStore } from 'stores/app'
|
||||
import { userStore } from 'stores/user'
|
||||
import { boot } from 'quasar/wrappers';
|
||||
import { useAppStore } from 'stores/app';
|
||||
import { userStore } from 'stores/user';
|
||||
|
||||
export default boot(({ app }) => {
|
||||
const props = app.config.globalProperties
|
||||
props.$app = appStore()
|
||||
props.$user = userStore()
|
||||
props.$actions = document.createElement('div')
|
||||
})
|
||||
const props = app.config.globalProperties;
|
||||
props.$app = useAppStore();
|
||||
props.$user = userStore();
|
||||
props.$actions = document.createElement('div');
|
||||
});
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import { boot } from 'quasar/wrappers';
|
||||
|
||||
export default boot(() => {
|
||||
Date.vnUTC = () => {
|
||||
const env = process.env.NODE_ENV;
|
||||
if (!env || env === 'development') {
|
||||
return new Date(Date.UTC(2001, 0, 1, 11));
|
||||
}
|
||||
|
||||
return new Date();
|
||||
};
|
||||
|
||||
Date.vnNew = () => {
|
||||
return new Date(Date.vnUTC());
|
||||
};
|
||||
|
||||
Date.vnNow = () => {
|
||||
return new Date(Date.vnUTC()).getTime();
|
||||
};
|
||||
});
|
|
@ -184,7 +184,6 @@ defineExpose({
|
|||
v-if="!loading"
|
||||
ref="addressFormRef"
|
||||
class="column full-width q-gutter-y-xs"
|
||||
@submit="submit()"
|
||||
>
|
||||
<span class="text-h6 text-bold">
|
||||
{{ title }}
|
||||
|
@ -197,7 +196,7 @@ defineExpose({
|
|||
:class="{ 'q-mt-md': showBottomActions }"
|
||||
>
|
||||
<QBtn
|
||||
v-if="defaultActions"
|
||||
v-if="defaultActions && showBottomActions"
|
||||
:label="t('cancel')"
|
||||
:icon="showBottomActions ? undefined : 'check'"
|
||||
rounded
|
||||
|
@ -208,12 +207,12 @@ defineExpose({
|
|||
<QBtn
|
||||
v-if="defaultActions"
|
||||
:label="t('save')"
|
||||
type="submit"
|
||||
:icon="showBottomActions ? undefined : 'check'"
|
||||
rounded
|
||||
no-caps
|
||||
flat
|
||||
:disabled="!showBottomActions && !updatedColumns.length"
|
||||
@click="submit()"
|
||||
/>
|
||||
<slot name="actions" />
|
||||
</component>
|
||||
|
|
|
@ -9,7 +9,7 @@ const emit = defineEmits([
|
|||
'remove'
|
||||
]);
|
||||
|
||||
const $props = defineProps({
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: [String, Number],
|
||||
default: null
|
||||
|
@ -33,7 +33,7 @@ const requiredFieldRule = val => !!val || t('globals.fieldRequired');
|
|||
const vnInputRef = ref(null);
|
||||
const value = computed({
|
||||
get() {
|
||||
return $props.modelValue;
|
||||
return props.modelValue;
|
||||
},
|
||||
set(value) {
|
||||
emit('update:modelValue', value);
|
||||
|
@ -41,7 +41,7 @@ const value = computed({
|
|||
});
|
||||
const hover = ref(false);
|
||||
const styleAttrs = computed(() => {
|
||||
return $props.isOutlined
|
||||
return props.isOutlined
|
||||
? { dense: true, outlined: true, rounded: true }
|
||||
: {};
|
||||
});
|
||||
|
@ -88,9 +88,7 @@ const inputRules = [
|
|||
<template #append>
|
||||
<slot v-if="$slots.append && !$attrs.disabled" name="append" />
|
||||
<QIcon
|
||||
v-if="
|
||||
hover && value && !$attrs.disabled && $props.clearable
|
||||
"
|
||||
v-if="hover && value && !$attrs.disabled && props.clearable"
|
||||
name="close"
|
||||
size="xs"
|
||||
@click="
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<script setup>
|
||||
const props = defineProps({
|
||||
clickable: { type: Boolean, default: true },
|
||||
rounded: { type: Boolean, default: true }
|
||||
});
|
||||
|
||||
const emit = defineEmits(['click']);
|
||||
|
||||
const handleClick = () => {
|
||||
if (props.clickable) {
|
||||
emit('click');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QItem
|
||||
v-bind="$attrs"
|
||||
v-ripple="clickable"
|
||||
:clickable="clickable"
|
||||
class="full-width row items-center justify-between card no-border-radius bg-white"
|
||||
:class="{ 'cursor-pointer': clickable, 'no-radius': !rounded }"
|
||||
@click="handleClick()"
|
||||
>
|
||||
<QItemSection class="no-padding">
|
||||
<div class="row no-wrap">
|
||||
<slot name="prepend" />
|
||||
<div class="column full-width">
|
||||
<slot name="content" />
|
||||
</div>
|
||||
</div>
|
||||
</QItemSection>
|
||||
<QItemSection class="no-padding" side>
|
||||
<slot name="actions" />
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card {
|
||||
border-bottom: 1px solid $gray-light;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,152 @@
|
|||
<script setup>
|
||||
jsegarra
commented
La ruta de este archivo debe estar en pages/Ecommerce porque sólo se usa en TicketView La ruta de este archivo debe estar en pages/Ecommerce porque sólo se usa en TicketView
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { currency, formatDateTitle } from 'src/lib/filters.js';
|
||||
import VnImg from 'src/components/ui/VnImg.vue';
|
||||
|
||||
defineProps({
|
||||
ticket: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
rows: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const lineDiscountSubtotal = line => {
|
||||
return line.quantity * line.price;
|
||||
};
|
||||
|
||||
const lineSubtotal = line =>
|
||||
lineDiscountSubtotal(line) * ((100 - line.discount) / 100);
|
||||
</script>
|
||||
jsegarra marked this conversation as resolved
jsegarra
commented
porque no haces line.discount en vez de crear una variable? porque no haces line.discount en vez de crear una variable?
wbuezas
commented
Modificado. Commit: Modificado.
Commit: https://gitea.verdnatura.es/verdnatura/hedera-web/commit/a0fc1cfc070cd790923b7067580aabb77be7a0fd
|
||||
|
||||
<template>
|
||||
<QCard class="vn-w-sm" style="padding: 32px">
|
||||
<QCardSection class="no-padding q-mb-md">
|
||||
<div class="text-h6">#{{ ticket.id }}</div>
|
||||
</QCardSection>
|
||||
<QCardSection class="no-padding q-mb-md q-gutter-y-xs">
|
||||
<div class="text-subtitle1 text-bold">
|
||||
{{ t('shippingInformation') }}
|
||||
</div>
|
||||
<div>
|
||||
{{ t('preparation') }}
|
||||
{{ formatDateTitle(ticket.shipped) }}
|
||||
</div>
|
||||
<div>
|
||||
{{ t('delivery') }}
|
||||
{{ formatDateTitle(ticket.landed) }}
|
||||
</div>
|
||||
<div>
|
||||
{{ t(ticket.method != 'PICKUP' ? 'agency' : 'warehouse') }}
|
||||
{{ ticket.agency }}
|
||||
</div>
|
||||
</QCardSection>
|
||||
<QCardSection class="no-padding q-mb-md q-gutter-y-xs">
|
||||
<div class="text-subtitle1 text-bold">
|
||||
{{ t('deliveryAddress') }}
|
||||
</div>
|
||||
<div>{{ ticket.nickname }}</div>
|
||||
<div>{{ ticket.street }}</div>
|
||||
<div>
|
||||
{{ ticket.postalCode }} {{ ticket.city }} ({{
|
||||
ticket.province
|
||||
}})
|
||||
</div>
|
||||
</QCardSection>
|
||||
<QCardSection
|
||||
class="no-padding q-mb-md text-subtitle1 text-bold column"
|
||||
>
|
||||
<span class="text-right">
|
||||
{{ t('total') }} {{ currency(ticket.taxBase) }}
|
||||
</span>
|
||||
<span class="text-right">
|
||||
{{ t('totalTax') }} {{ currency(ticket.total) }}
|
||||
</span>
|
||||
</QCardSection>
|
||||
<QSeparator inset />
|
||||
<QList v-for="row in rows" :key="row.itemFk">
|
||||
<QItem>
|
||||
<QItemSection avatar>
|
||||
<VnImg
|
||||
storage="catalog"
|
||||
size="200x200"
|
||||
:id="row.image"
|
||||
rounded
|
||||
/>
|
||||
</QItemSection>
|
||||
<QItemSection>
|
||||
<QItemLabel lines="1">
|
||||
{{ row.concept }}
|
||||
</QItemLabel>
|
||||
<QItemLabel lines="1" caption>
|
||||
{{ row.value5 }} {{ row.value6 }} {{ row.value7 }}
|
||||
</QItemLabel>
|
||||
<QItemLabel lines="1">
|
||||
{{ row.quantity }} x {{ currency(row.price) }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
<QItemSection side class="total">
|
||||
<QItemLabel>
|
||||
<span class="discount" v-if="row.discount">
|
||||
{{ currency(lineDiscountSubtotal(row)) }} -
|
||||
{{ currency(row.discount) }} =
|
||||
</span>
|
||||
{{ currency(lineSubtotal(row)) }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</QList>
|
||||
</QCard>
|
||||
</template>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
shippingInformation: Shipping Information
|
||||
preparation: Preparation
|
||||
delivery: Delivery
|
||||
agency: Agency
|
||||
warehouse: Store
|
||||
deliveryAddress: Delivery address
|
||||
total: Total
|
||||
totalTax: Total + IVA
|
||||
es-ES:
|
||||
shippingInformation: Datos de envío
|
||||
preparation: Preparación
|
||||
delivery: Entrega
|
||||
agency: Agencia
|
||||
warehouse: Almacén
|
||||
deliveryAddress: Dirección de entrega
|
||||
total: Total
|
||||
totalTax: Total + IVA
|
||||
ca-ES:
|
||||
shippingInformation: Dades d'enviament
|
||||
preparation: Preparació
|
||||
delivery: Lliurament
|
||||
agency: Agència
|
||||
warehouse: Magatzem
|
||||
deliveryAddress: Adreça de lliurament
|
||||
total: Total
|
||||
totalTax: Total + IVA
|
||||
fr-FR:
|
||||
shippingInformation: Informations sur la livraison
|
||||
preparation: Préparation
|
||||
delivery: Livraison
|
||||
warehouse: Entrepôt
|
||||
deliveryAddress: Adresse de livraison
|
||||
total: Total
|
||||
totalTax: Total + IVA
|
||||
pt-PT:
|
||||
shippingInformation: Dados de envio
|
||||
preparation: Preparação
|
||||
delivery: Entrega
|
||||
agency: Agência
|
||||
warehouse: Armazém
|
||||
deliveryAddress: Endereço de entrega
|
||||
total: Total
|
||||
totalTax: Total + IVA
|
||||
</i18n>
|
|
@ -1,8 +1,8 @@
|
|||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { appStore } from 'stores/app';
|
||||
import { useAppStore } from 'stores/app';
|
||||
|
||||
const $props = defineProps({
|
||||
const props = defineProps({
|
||||
baseURL: {
|
||||
type: String,
|
||||
default: null
|
||||
|
@ -23,23 +23,27 @@ const $props = defineProps({
|
|||
id: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
rounded: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
const app = appStore();
|
||||
const app = useAppStore();
|
||||
const show = ref(false);
|
||||
const url = computed(() => {
|
||||
return `${$props.baseURL ?? app.imageUrl}/${$props.storage}/${$props.size}/${$props.id}`;
|
||||
return `${props.baseURL ?? app.imageUrl}/${props.storage}/${props.size}/${props.id}`;
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<QImg
|
||||
:class="{ zoomIn: $props.zoomSize }"
|
||||
:class="{ zoomIn: props.zoomSize, rounded: props.rounded }"
|
||||
:src="url"
|
||||
v-bind="$attrs"
|
||||
@click="show = !show"
|
||||
spinner-color="primary"
|
||||
/>
|
||||
<QDialog v-model="show" v-if="$props.zoomSize">
|
||||
<QDialog v-model="show" v-if="props.zoomSize">
|
||||
<QImg
|
||||
:src="url"
|
||||
size="full"
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps({
|
||||
noDataLabel: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
hideBottom: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
rowsPerPageOptions: {
|
||||
type: Array,
|
||||
default: () => [0]
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QTable
|
||||
v-bind="$attrs"
|
||||
:no-data-label="props.noDataLabel || t('noInvoicesFound')"
|
||||
:hide-bottom="props.hideBottom"
|
||||
:rows-per-page-options="props.rowsPerPageOptions"
|
||||
table-header-class="vntable-header-default"
|
||||
>
|
||||
<template v-for="(_, slotName) in $slots" v-slot:[slotName]="slotProps">
|
||||
<slot :name="slotName" v-bind="slotProps" />
|
||||
</template>
|
||||
</QTable>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.vntable-header-default {
|
||||
background-color: $accent !important;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,38 @@
|
|||
import { userStore as useUserStore } from 'stores/user';
|
||||
|
||||
import axios from 'axios';
|
||||
import { useQuasar } from 'quasar';
|
||||
|
||||
export function usePrintService() {
|
||||
const quasar = useQuasar();
|
||||
const userStore = useUserStore();
|
||||
const token = userStore.token;
|
||||
|
||||
function sendEmail(path, params) {
|
||||
return axios.post(path, params).then(() =>
|
||||
quasar.notify({
|
||||
message: 'Notification sent',
|
||||
type: 'positive',
|
||||
icon: 'check'
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function openReport(path, params) {
|
||||
params = Object.assign(
|
||||
{
|
||||
access_token: token
|
||||
},
|
||||
params
|
||||
);
|
||||
|
||||
const query = new URLSearchParams(params).toString();
|
||||
|
||||
window.open(`api/${path}?${query}`);
|
||||
}
|
||||
|
||||
return {
|
||||
sendEmail,
|
||||
openReport
|
||||
};
|
||||
}
|
|
@ -36,3 +36,9 @@ a.link {
|
|||
.q-page-sticky.fixed-bottom-right {
|
||||
margin: 18px;
|
||||
}
|
||||
.no-border-radius {
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
.no-padding {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
$primary: #1a1a1a;
|
||||
$secondary: #26a69a;
|
||||
$accent: #8cc63f;
|
||||
|
||||
$gray-light: #ddd;
|
||||
$dark: #1d1d1d;
|
||||
$dark-page: #121212;
|
||||
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
export default {
|
||||
date: {
|
||||
days: [
|
||||
'Diumenge',
|
||||
'Dilluns',
|
||||
'Dimarts',
|
||||
'Dimecres',
|
||||
'Dijous',
|
||||
'Divendres',
|
||||
'Dissabte'
|
||||
],
|
||||
daysShort: ['Dg', 'Dl', 'Dt', 'Dc', 'Dj', 'Dv', 'Ds'],
|
||||
months: [
|
||||
'Gener',
|
||||
'Febrer',
|
||||
'Març',
|
||||
'Abril',
|
||||
'Maig',
|
||||
'Juny',
|
||||
'Juliol',
|
||||
'Agost',
|
||||
'Setembre',
|
||||
'Octubre',
|
||||
'Novembre',
|
||||
'Desembre'
|
||||
],
|
||||
monthsShort: [
|
||||
'Gen',
|
||||
'Feb',
|
||||
'Mar',
|
||||
'Abr',
|
||||
'Mai',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Ago',
|
||||
'Set',
|
||||
'Oct',
|
||||
'Nov',
|
||||
'Des'
|
||||
]
|
||||
},
|
||||
of: 'de',
|
||||
// Menu
|
||||
home: 'Inici',
|
||||
catalog: 'Catàleg',
|
||||
pendingOrders: 'Comandes pendents',
|
||||
confirmedOrders: 'Comandes confirmades',
|
||||
invoices: 'Factures',
|
||||
agencyPackages: 'Paquets per agència',
|
||||
accountConfig: 'Configuració',
|
||||
addressesList: 'Adreces',
|
||||
addressDetails: 'Configuració',
|
||||
checkout: 'Configurar encàrrec',
|
||||
//
|
||||
orderLoadedIntoBasket: 'Comanda carregada a la cistella!'
|
||||
};
|
|
@ -37,7 +37,7 @@ export default {
|
|||
'November',
|
||||
'December'
|
||||
],
|
||||
shortMonths: [
|
||||
monthsShort: [
|
||||
'Jan',
|
||||
'Feb',
|
||||
'Mar',
|
||||
|
@ -56,6 +56,17 @@ export default {
|
|||
// menu
|
||||
home: 'Home',
|
||||
catalog: 'Catalog',
|
||||
pendingOrders: 'Pending orders',
|
||||
confirmedOrders: 'Confirmed orders',
|
||||
invoices: 'Invoices',
|
||||
agencyPackages: 'Bundles by agency',
|
||||
accountConfig: 'Configuration',
|
||||
addressesList: 'Addresses',
|
||||
addressDetails: 'Configuration',
|
||||
checkout: 'Configure order',
|
||||
//
|
||||
orderLoadedIntoBasket: 'Order loaded into basket!',
|
||||
|
||||
orders: 'Orders',
|
||||
order: 'Pending order',
|
||||
ticket: 'Order',
|
||||
|
@ -76,5 +87,6 @@ export default {
|
|||
addressEdit: 'Edit address',
|
||||
dataSaved: 'Data saved',
|
||||
save: 'Save',
|
||||
cancel: 'Cancel'
|
||||
cancel: 'Cancel',
|
||||
of: 'of'
|
||||
};
|
||||
|
|
|
@ -46,7 +46,7 @@ export default {
|
|||
'Noviembre',
|
||||
'Diciembre'
|
||||
],
|
||||
shortMonths: [
|
||||
monthsShort: [
|
||||
'Ene',
|
||||
'Feb',
|
||||
'Mar',
|
||||
|
@ -65,6 +65,17 @@ export default {
|
|||
// Menu
|
||||
home: 'Inicio',
|
||||
catalog: 'Catálogo',
|
||||
pendingOrders: 'Pedidos pendientes',
|
||||
confirmedOrders: 'Pedidos confirmados',
|
||||
invoices: 'Facturas',
|
||||
agencyPackages: 'Bultos por agencia',
|
||||
accountConfig: 'Configuración',
|
||||
addressesList: 'Direcciones',
|
||||
addressDetails: 'Configuración',
|
||||
checkout: 'Configurar pedido',
|
||||
//
|
||||
orderLoadedIntoBasket: '¡Pedido cargado en la cesta!',
|
||||
|
||||
orders: 'Pedidos',
|
||||
order: 'Pedido pendiente',
|
||||
ticket: 'Pedido',
|
||||
|
@ -94,5 +105,6 @@ export default {
|
|||
addressEdit: 'Editar dirección',
|
||||
dataSaved: 'Datos guardados',
|
||||
save: 'Guardar',
|
||||
cancel: 'Cancelar'
|
||||
cancel: 'Cancelar',
|
||||
of: 'de'
|
||||
};
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
export default {
|
||||
date: {
|
||||
days: [
|
||||
'Dimanche',
|
||||
'Lundi',
|
||||
'Mardi',
|
||||
'Mercredi',
|
||||
'Jeudi',
|
||||
'Vendredi',
|
||||
'Samedi'
|
||||
],
|
||||
daysShort: ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'],
|
||||
months: [
|
||||
'Janvier',
|
||||
'Février',
|
||||
'Mars',
|
||||
'Avril',
|
||||
'Mai',
|
||||
'Juin',
|
||||
'Juillet',
|
||||
'Août',
|
||||
'Septembre',
|
||||
'Octobre',
|
||||
'Novembre',
|
||||
'Décembre'
|
||||
],
|
||||
monthsShort: [
|
||||
'Jan',
|
||||
'Fév',
|
||||
'Mar',
|
||||
'Avr',
|
||||
'Mai',
|
||||
'Juin',
|
||||
'Juil',
|
||||
'Aoû',
|
||||
'Sep',
|
||||
'Oct',
|
||||
'Nov',
|
||||
'Déc'
|
||||
]
|
||||
},
|
||||
of: 'de',
|
||||
// Menu
|
||||
home: 'Accueil',
|
||||
catalog: 'Catalogue',
|
||||
pendingOrders: 'Commandes en attente',
|
||||
confirmedOrders: 'Commandes confirmées',
|
||||
invoices: 'Factures',
|
||||
agencyPackages: 'Liste par agence',
|
||||
accountConfig: 'Configuration',
|
||||
addressesList: 'Adresses',
|
||||
addressDetails: 'Configuration',
|
||||
checkout: "Définissez l'ordre",
|
||||
//
|
||||
orderLoadedIntoBasket: 'Commande chargée dans le panier!'
|
||||
};
|
|
@ -1,7 +1,13 @@
|
|||
import enUS from './en-US'
|
||||
import esES from './es-ES'
|
||||
import enUS from './en-US';
|
||||
import esES from './es-ES';
|
||||
import frFR from './fr-FR';
|
||||
import ptPT from './pt-PT';
|
||||
import caES from './ca-ES';
|
||||
|
||||
export default {
|
||||
'en-US': enUS,
|
||||
'es-ES': esES
|
||||
}
|
||||
'es-ES': esES,
|
||||
'fr-FR': frFR,
|
||||
'pt-PT': ptPT,
|
||||
'ca-ES': caES
|
||||
};
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
export default {
|
||||
date: {
|
||||
days: [
|
||||
'Domingo',
|
||||
'Segunda-feira',
|
||||
'Terça-feira',
|
||||
'Quarta-feira',
|
||||
'Quinta-feira',
|
||||
'Sexta-feira',
|
||||
'Sábado'
|
||||
],
|
||||
daysShort: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'],
|
||||
months: [
|
||||
'Janeiro',
|
||||
'Fevereiro',
|
||||
'Março',
|
||||
'Abril',
|
||||
'Maio',
|
||||
'Junho',
|
||||
'Julho',
|
||||
'Agosto',
|
||||
'Setembro',
|
||||
'Outubro',
|
||||
'Novembro',
|
||||
'Dezembro'
|
||||
],
|
||||
monthsShort: [
|
||||
'Jan',
|
||||
'Fev',
|
||||
'Mar',
|
||||
'Abr',
|
||||
'Mai',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Ago',
|
||||
'Set',
|
||||
'Out',
|
||||
'Nov',
|
||||
'Dez'
|
||||
]
|
||||
},
|
||||
of: 'de',
|
||||
|
||||
// Menu
|
||||
home: 'Principio',
|
||||
catalog: 'Catálogo',
|
||||
pendingOrders: 'Pedidos pendentes',
|
||||
confirmedOrders: 'Pedidos confirmados',
|
||||
invoices: 'Facturas',
|
||||
agencyPackages: 'Bultos por agencia',
|
||||
accountConfig: 'Configuração',
|
||||
addressesList: 'Moradas',
|
||||
addressDetails: 'Configuração',
|
||||
checkout: 'Configurar encomenda',
|
||||
//
|
||||
orderLoadedIntoBasket: 'Pedido carregado na cesta!'
|
||||
};
|
|
@ -1,73 +1,114 @@
|
|||
import { date as qdate, format } from 'quasar'
|
||||
const { pad } = format
|
||||
import { i18n } from 'src/boot/i18n';
|
||||
jsegarra marked this conversation as resolved
jsegarra
commented
TODO: revisar si vale la pena crear un fichero para cada filter TODO: revisar si vale la pena crear un fichero para cada filter
wbuezas
commented
Esto lo pensamos mejor y si vemos que vale la pena lo metemos para la proxima PR Esto lo pensamos mejor y si vemos que vale la pena lo metemos para la proxima PR
|
||||
import { date as qdate, format } from 'quasar';
|
||||
const { pad } = format;
|
||||
|
||||
export function currency(val) {
|
||||
return typeof val === 'number' ? val.toFixed(2) + '€' : val
|
||||
return typeof val === 'number' ? val.toFixed(2) + '€' : val;
|
||||
}
|
||||
|
||||
export function date(val, format) {
|
||||
if (val == null) return val
|
||||
if (val == null) return val;
|
||||
if (!(val instanceof Date)) {
|
||||
val = new Date(val)
|
||||
val = new Date(val);
|
||||
}
|
||||
return qdate.formatDate(val, format, window.i18n.tm('date'))
|
||||
return qdate.formatDate(val, format, i18n.global.tm('date'));
|
||||
}
|
||||
|
||||
export const formatDate = (timeStamp, format = 'YYYY-MM-DD') => {
|
||||
if (!timeStamp) return '';
|
||||
const { messages, locale } = i18n.global;
|
||||
|
||||
return qdate.formatDate(timeStamp, format, {
|
||||
days: messages.value[locale.value].date.days,
|
||||
months: messages.value[locale.value].date.months,
|
||||
daysShort: messages.value[locale.value].date.daysShort,
|
||||
monthsShort: messages.value[locale.value].date.monthsShort
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Date} timeStamp - La marca de tiempo que se va a formatear. Si no se proporciona, la función devolverá una cadena vacía.
|
||||
* @param {Object} options - Un objeto que contiene las opciones de formato.
|
||||
* @param {boolean} options.showTime - Indica si se debe mostrar la hora en el formato de la fecha.
|
||||
* @param {boolean} options.showSeconds - Indica si se deben mostrar los segundos en el formato de la hora. Solo se aplica si showTime es true.
|
||||
* @param {boolean} options.shortDay - Indica si se debe usar una versión corta del día (por ejemplo, "Mon" en lugar de "Monday").
|
||||
* @returns {string} La fecha formateada como un título.
|
||||
*/
|
||||
export const formatDateTitle = (
|
||||
timeStamp,
|
||||
options = { showTime: false, showSeconds: false, shortDay: false }
|
||||
) => {
|
||||
if (!timeStamp) return '';
|
||||
const { t } = i18n.global;
|
||||
|
||||
const timeFormat = options.showTime
|
||||
? options.showSeconds
|
||||
? ` [${t('at')}] hh:mm:ss`
|
||||
: ` [${t('at')}] hh:mm`
|
||||
: '';
|
||||
const day = options.shortDay ? 'dd' : 'dddd';
|
||||
|
||||
const formatString = `${day}, D [${t('of')}] MMMM [${t('of')}] YYYY${timeFormat}`;
|
||||
|
||||
const formattedString = formatDate(timeStamp, formatString);
|
||||
return formattedString;
|
||||
};
|
||||
|
||||
export function relDate(val) {
|
||||
if (val == null) return val
|
||||
if (val == null) return val;
|
||||
if (!(val instanceof Date)) {
|
||||
val = new Date(val)
|
||||
val = new Date(val);
|
||||
}
|
||||
|
||||
const dif = qdate.getDateDiff(new Date(), val, 'days')
|
||||
let day
|
||||
const dif = qdate.getDateDiff(new Date(), val, 'days');
|
||||
let day;
|
||||
|
||||
switch (dif) {
|
||||
case 0:
|
||||
day = 'today'
|
||||
break
|
||||
day = 'today';
|
||||
break;
|
||||
case 1:
|
||||
day = 'yesterday'
|
||||
break
|
||||
day = 'yesterday';
|
||||
break;
|
||||
case -1:
|
||||
day = 'tomorrow'
|
||||
break
|
||||
day = 'tomorrow';
|
||||
break;
|
||||
}
|
||||
|
||||
if (day) {
|
||||
day = window.i18n.t(day)
|
||||
day = i18n.global.t(day);
|
||||
} else {
|
||||
if (dif > 0 && dif <= 7) {
|
||||
day = qdate.formatDate(val, 'ddd', window.i18n.tm('date'))
|
||||
day = qdate.formatDate(val, 'ddd', i18n.global.tm('date'));
|
||||
} else {
|
||||
day = qdate.formatDate(val, 'ddd, MMMM Do', window.i18n.tm('date'))
|
||||
day = qdate.formatDate(val, 'ddd, MMMM Do', i18n.global.tm('date'));
|
||||
}
|
||||
}
|
||||
|
||||
return day
|
||||
return day;
|
||||
}
|
||||
|
||||
export function relTime(val) {
|
||||
if (val == null) return val
|
||||
if (val == null) return val;
|
||||
if (!(val instanceof Date)) {
|
||||
val = new Date(val)
|
||||
val = new Date(val);
|
||||
}
|
||||
return relDate(val) + ' ' + qdate.formatDate(val, 'H:mm:ss')
|
||||
return relDate(val) + ' ' + qdate.formatDate(val, 'H:mm:ss');
|
||||
}
|
||||
|
||||
export function elapsedTime(val) {
|
||||
if (val == null) return val
|
||||
if (val == null) return val;
|
||||
if (!(val instanceof Date)) {
|
||||
val = new Date(val)
|
||||
val = new Date(val);
|
||||
}
|
||||
const now = new Date().getTime()
|
||||
val = Math.floor((now - val.getTime()) / 1000)
|
||||
const now = new Date().getTime();
|
||||
val = Math.floor((now - val.getTime()) / 1000);
|
||||
|
||||
const hours = Math.floor(val / 3600)
|
||||
val -= hours * 3600
|
||||
const minutes = Math.floor(val / 60)
|
||||
val -= minutes * 60
|
||||
const seconds = val
|
||||
const hours = Math.floor(val / 3600);
|
||||
val -= hours * 3600;
|
||||
const minutes = Math.floor(val / 60);
|
||||
val -= minutes * 60;
|
||||
const seconds = val;
|
||||
|
||||
return `${pad(hours, 2)}:${pad(minutes, 2)}:${pad(seconds, 2)}`
|
||||
return `${pad(hours, 2)}:${pad(minutes, 2)}:${pad(seconds, 2)}`;
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ onMounted(() => fetchLanguagesSql());
|
|||
icon="location_on"
|
||||
rounded
|
||||
no-caps
|
||||
:to="{ name: 'AddressesList' }"
|
||||
:to="{ name: 'addressesList' }"
|
||||
/>
|
||||
<QBtn
|
||||
:label="t('changePassword')"
|
||||
|
|
|
@ -32,7 +32,7 @@ watch(
|
|||
async val => await getProvinces(val)
|
||||
);
|
||||
|
||||
const goBack = () => router.push({ name: 'AddressesList' });
|
||||
const goBack = () => router.push({ name: 'addressesList' });
|
||||
|
||||
const getCountries = async () => {
|
||||
countriesOptions.value = await jApi.query(
|
||||
|
|
|
@ -3,6 +3,8 @@ import { useI18n } from 'vue-i18n';
|
|||
import { ref, onMounted, inject } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import CardList from 'src/components/ui/CardList.vue';
|
||||
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
import { useVnConfirm } from 'src/composables/useVnConfirm.js';
|
||||
|
||||
|
@ -17,7 +19,7 @@ const defaultAddress = ref(null);
|
|||
const clientId = ref(null);
|
||||
|
||||
const goToAddressDetails = (id = 0) =>
|
||||
router.push({ name: 'AddressDetails', params: { id } });
|
||||
router.push({ name: 'addressDetails', params: { id } });
|
||||
|
||||
const getDefaultAddress = async () => {
|
||||
try {
|
||||
|
@ -93,42 +95,33 @@ onMounted(async () => {
|
|||
no-caps
|
||||
/>
|
||||
</Teleport>
|
||||
<QPage class="column items-center">
|
||||
<QList
|
||||
class="full-width rounded-borders shadow-1 shadow-transition"
|
||||
style="max-width: 544px"
|
||||
separator
|
||||
>
|
||||
<QItem
|
||||
<QPage class="vn-w-sm">
|
||||
<QList class="rounded-borders shadow-1 shadow-transition" separator>
|
||||
<CardList
|
||||
v-for="(address, index) in addresses"
|
||||
:key="index"
|
||||
clickable
|
||||
v-ripple
|
||||
:rounded="false"
|
||||
tag="label"
|
||||
class="full-width row items-center justify-between address-item"
|
||||
style="padding: 20px"
|
||||
>
|
||||
<QItemSection>
|
||||
<div class="row">
|
||||
<template #prepend>
|
||||
<QRadio
|
||||
v-model="defaultAddress"
|
||||
:val="address.id"
|
||||
class="q-mr-sm"
|
||||
@update:model-value="changeDefaultAddress"
|
||||
/>
|
||||
<div>
|
||||
<QItemLabel class="text-bold q-mb-sm">
|
||||
</template>
|
||||
<template #content>
|
||||
<span class="text-bold q-mb-sm">
|
||||
{{ address.nickname }}
|
||||
</QItemLabel>
|
||||
<QItemLabel>{{ address.street }}</QItemLabel>
|
||||
<QItemLabel>
|
||||
</span>
|
||||
<span>{{ address.street }}</span>
|
||||
<span>
|
||||
{{ address.postalCode }},
|
||||
{{ address.city }}
|
||||
</QItemLabel>
|
||||
</div>
|
||||
</div>
|
||||
</QItemSection>
|
||||
<QItemSection class="actions-wrapper" side>
|
||||
</span>
|
||||
</template>
|
||||
<template #actions>
|
||||
<QBtn
|
||||
icon="delete"
|
||||
flat
|
||||
|
@ -147,25 +140,12 @@ onMounted(async () => {
|
|||
rounded
|
||||
@click.stop="goToAddressDetails(address.id)"
|
||||
/>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</CardList>
|
||||
</QList>
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.address-item {
|
||||
.actions-wrapper {
|
||||
visibility: hidden;
|
||||
}
|
||||
&:hover {
|
||||
.actions-wrapper {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
addAddress: Add address
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
import { ref, inject, onMounted, computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import VnTable from 'src/components/ui/VnTable.vue';
|
||||
|
||||
const jApi = inject('jApi');
|
||||
const { t } = useI18n();
|
||||
|
||||
|
@ -53,32 +55,15 @@ onMounted(() => getPackages());
|
|||
|
||||
<template>
|
||||
<QPage class="flex justify-center q-pa-md">
|
||||
<QTable
|
||||
<VnTable
|
||||
:columns="columns"
|
||||
:rows="packages"
|
||||
:loading="loading"
|
||||
class="q-mt-lg"
|
||||
style="max-width: 100%; height: max-content"
|
||||
table-header-class="packages-table-header"
|
||||
hide-bottom
|
||||
>
|
||||
<template #body-cell-id="{ row }">
|
||||
<QTd auto-width @click.stop>
|
||||
<QBtn flat color="blue">{{ row.id }}</QBtn>
|
||||
<ItemDescriptorProxy :id="row.id" />
|
||||
</QTd>
|
||||
</template>
|
||||
</QTable>
|
||||
style="height: max-content; max-width: 100%"
|
||||
/>
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.packages-table-header {
|
||||
background-color: $accent !important;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
agency: Agency
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<template>Basket view</template>
|
|
@ -343,19 +343,23 @@
|
|||
</style>
|
||||
|
||||
<script>
|
||||
import { date, currency } from 'src/lib/filters.js';
|
||||
import { date as qdate } from 'quasar';
|
||||
import { date, currency, formatDate } from 'src/lib/filters.js';
|
||||
import axios from 'axios';
|
||||
import { useAppStore } from 'stores/app';
|
||||
|
||||
const CancelToken = axios.CancelToken;
|
||||
|
||||
export default {
|
||||
name: 'HederaCatalog',
|
||||
setup() {
|
||||
const appStore = useAppStore();
|
||||
return { appStore };
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
uid: 0,
|
||||
search: '',
|
||||
orderDate: qdate.formatDate(new Date(), 'YYYY/MM/DD'),
|
||||
orderDate: formatDate(new Date(), 'YYYY/MM/DD'),
|
||||
category: null,
|
||||
categories: [],
|
||||
type: null,
|
||||
|
@ -446,7 +450,7 @@ export default {
|
|||
if (!value) return;
|
||||
|
||||
const res = await this.$jApi.execQuery(
|
||||
`CALL myBasket_getAvailable;
|
||||
`CALL myOrder_getAvailable(${this.appStore.basketOrderId});
|
||||
SELECT DISTINCT t.id, l.name
|
||||
FROM vn.item i
|
||||
JOIN vn.itemType t ON t.id = i.typeFk
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<template>Checkout</template>
|
|
@ -1,183 +0,0 @@
|
|||
<template>
|
||||
<Teleport :to="$actions">
|
||||
<QSelect
|
||||
v-model="year"
|
||||
:options="years"
|
||||
color="white"
|
||||
dark
|
||||
standout
|
||||
dense
|
||||
rounded
|
||||
/>
|
||||
</Teleport>
|
||||
<div class="vn-w-sm">
|
||||
<div
|
||||
v-if="!invoices?.length"
|
||||
class="text-subtitle1 text-center text-grey-7 q-pa-md"
|
||||
>
|
||||
{{ $t('noInvoicesFound') }}
|
||||
</div>
|
||||
<QCard v-if="invoices?.length">
|
||||
<QTable
|
||||
:columns="columns"
|
||||
:pagination="pagination"
|
||||
:rows="invoices"
|
||||
row-key="id"
|
||||
hide-header
|
||||
hide-bottom
|
||||
>
|
||||
<template v-slot:body="props">
|
||||
<QTr :props="props">
|
||||
<QTd key="ref" :props="props">
|
||||
{{ props.row.ref }}
|
||||
</QTd>
|
||||
<QTd key="issued" :props="props">
|
||||
{{ date(props.row.issued, 'ddd, MMMM Do') }}
|
||||
</QTd>
|
||||
<QTd key="amount" :props="props">
|
||||
{{ currency(props.row.amount) }}
|
||||
</QTd>
|
||||
<QTd key="hasPdf" :props="props">
|
||||
<QBtn
|
||||
v-if="props.row.hasPdf"
|
||||
icon="download"
|
||||
:title="$t('downloadInvoicePdf')"
|
||||
:href="invoiceUrl(props.row.id)"
|
||||
target="_blank"
|
||||
flat
|
||||
round
|
||||
/>
|
||||
<QIcon
|
||||
v-else
|
||||
name="warning"
|
||||
:title="$t('notDownloadable')"
|
||||
color="warning"
|
||||
size="24px"
|
||||
/>
|
||||
</QTd>
|
||||
</QTr>
|
||||
</template>
|
||||
</QTable>
|
||||
</QCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { date, currency } from 'src/lib/filters.js';
|
||||
|
||||
export default {
|
||||
name: 'OrdersPendingIndex',
|
||||
data() {
|
||||
const curYear = new Date().getFullYear();
|
||||
const years = [];
|
||||
|
||||
for (let year = curYear - 5; year <= curYear; year++) {
|
||||
years.push(year);
|
||||
}
|
||||
|
||||
return {
|
||||
columns: [
|
||||
{ name: 'ref', label: 'serial', field: 'ref', align: 'left' },
|
||||
{
|
||||
name: 'issued',
|
||||
label: 'issued',
|
||||
field: 'issued',
|
||||
align: 'left'
|
||||
},
|
||||
{ name: 'amount', label: 'amount', field: 'amount' },
|
||||
{
|
||||
name: 'hasPdf',
|
||||
label: 'download',
|
||||
field: 'hasPdf',
|
||||
align: 'center'
|
||||
}
|
||||
],
|
||||
pagination: {
|
||||
rowsPerPage: 0
|
||||
},
|
||||
year: curYear,
|
||||
years,
|
||||
invoices: null
|
||||
};
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
await this.loadData();
|
||||
},
|
||||
|
||||
watch: {
|
||||
async year() {
|
||||
await this.loadData();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
date,
|
||||
currency,
|
||||
|
||||
async loadData() {
|
||||
const params = {
|
||||
from: new Date(this.year, 0),
|
||||
to: new Date(this.year, 11, 31, 23, 59, 59)
|
||||
};
|
||||
this._invoices = await this.$jApi.query(
|
||||
`SELECT id, ref, issued, amount, hasPdf
|
||||
FROM myInvoice
|
||||
WHERE issued BETWEEN #from AND #to
|
||||
ORDER BY issued DESC
|
||||
LIMIT 500`,
|
||||
params
|
||||
);
|
||||
},
|
||||
|
||||
invoiceUrl(id) {
|
||||
return (
|
||||
'?' +
|
||||
new URLSearchParams({
|
||||
srv: 'rest:dms/invoice',
|
||||
invoice: id,
|
||||
access_token: this.$user.token
|
||||
}).toString()
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
noInvoicesFound: No invoices found
|
||||
serial: Serial
|
||||
issued: Date
|
||||
amount: Import
|
||||
downloadInvoicePdf: Download invoice PDF
|
||||
notDownloadable: Not available for download, request the invoice to your salesperson
|
||||
es-ES:
|
||||
noInvoicesFound: No se han encontrado facturas
|
||||
serial: Serie
|
||||
issued: Fecha
|
||||
amount: Importe
|
||||
downloadInvoicePdf: Descargar factura en PDF
|
||||
notDownloadable: No disponible para descarga, solicita la factura a tu comercial
|
||||
ca-ES:
|
||||
noInvoicesFound: No s'han trobat factures
|
||||
serial: Sèrie
|
||||
issued: Data
|
||||
amount: Import
|
||||
downloadInvoicePdf: Descarregar PDF
|
||||
notDownloadable: No disponible per cescarrega, sol·licita la factura al teu comercial
|
||||
fr-FR:
|
||||
noInvoicesFound: Aucune facture trouvée
|
||||
serial: Série
|
||||
issued: Date
|
||||
amount: Montant
|
||||
downloadInvoicePdf: Télécharger le PDF
|
||||
notDownloadable: Non disponible en téléchargement, demander la facture à votre commercial
|
||||
pt-PT:
|
||||
noInvoicesFound: Nenhuma fatura encontrada
|
||||
serial: Serie
|
||||
issued: Data
|
||||
amount: Importe
|
||||
downloadInvoicePdf: Baixar PDF
|
||||
notDownloadable: Não disponível para download, solicite a fatura ao seu comercial
|
||||
</i18n>
|
|
@ -0,0 +1,159 @@
|
|||
<script setup>
|
||||
import { ref, onMounted, inject, computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import VnTable from 'src/components/ui/VnTable.vue';
|
||||
|
||||
import { currency, formatDate } from 'src/lib/filters.js';
|
||||
import { usePrintService } from 'src/composables/usePrintService';
|
||||
// import { date as qdate } from 'quasar';
|
||||
|
||||
const { t } = useI18n();
|
||||
const jApi = inject('jApi');
|
||||
const { openReport } = usePrintService();
|
||||
|
||||
const currentYear = ref(Date.vnNew().getFullYear());
|
||||
const years = ref([]);
|
||||
const invoices = ref([]);
|
||||
|
||||
const columns = computed(() => [
|
||||
{ name: 'ref', label: t('invoice'), field: 'ref', align: 'left' },
|
||||
{
|
||||
name: 'issued',
|
||||
label: t('issued'),
|
||||
field: 'issued',
|
||||
jsegarra marked this conversation as resolved
jsegarra
commented
Revisamos el formateo de fechas porque aparece en ingles Revisamos el formateo de fechas porque aparece en ingles
wbuezas
commented
Util de formateo de fechas con traducciones creada. Commit: Util de formateo de fechas con traducciones creada.
Commit: https://gitea.verdnatura.es/verdnatura/hedera-web/commit/887ee8aea47247e720fd6afd86468d69a4f49596
|
||||
align: 'left',
|
||||
format: val => formatDate(val, 'D MMM YYYY')
|
||||
},
|
||||
{
|
||||
name: 'amount',
|
||||
label: t('amount'),
|
||||
field: 'amount',
|
||||
format: val => currency(val)
|
||||
},
|
||||
{
|
||||
name: 'hasPdf',
|
||||
field: 'hasPdf',
|
||||
align: 'center'
|
||||
}
|
||||
]);
|
||||
|
||||
const fetchInvoices = async () => {
|
||||
const params = {
|
||||
from: new Date(currentYear.value, 0),
|
||||
jsegarra marked this conversation as resolved
jsegarra
commented
Si esto devuelve un PDF, podriamos aprovechar para user usePrintservice(Lilium) Si esto devuelve un PDF, podriamos aprovechar para user usePrintservice(Lilium)
wbuezas
commented
Agregado. Commit: Agregado.
Commit: https://gitea.verdnatura.es/verdnatura/hedera-web/commit/f2c8b90324b0f051734e7752cd73a3a36c531015
|
||||
to: new Date(currentYear.value, 11, 31, 23, 59, 59)
|
||||
};
|
||||
invoices.value = await jApi.query(
|
||||
`SELECT id, ref, issued, amount, hasPdf
|
||||
FROM myInvoice
|
||||
WHERE issued BETWEEN #from AND #to
|
||||
ORDER BY issued DESC
|
||||
LIMIT 100`,
|
||||
params
|
||||
);
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
await fetchInvoices();
|
||||
for (let year = currentYear.value - 5; year <= currentYear.value; year++) {
|
||||
years.value.push(year);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Teleport :to="$actions">
|
||||
<QSelect
|
||||
v-model="currentYear"
|
||||
:options="years"
|
||||
color="white"
|
||||
dark
|
||||
standout
|
||||
dense
|
||||
rounded
|
||||
@update:model-value="fetchInvoices()"
|
||||
/>
|
||||
</Teleport>
|
||||
<div class="vn-w-sm">
|
||||
<VnTable
|
||||
:columns="columns"
|
||||
:rows="invoices"
|
||||
:hide-header="!invoices.length"
|
||||
>
|
||||
<template #body-cell-hasPdf="{ row }">
|
||||
<QTd
|
||||
auto-width
|
||||
jsegarra
commented
Podemos aplicar el estilo de la tabla de Agencias? Podemos aplicar el estilo de la tabla de Agencias?
wbuezas
commented
Cree el componente Commit: Cree el componente `VnTable` y lo aplique en las vistas `PackagesView` e `InvoicesView`.
Commit: https://gitea.verdnatura.es/verdnatura/hedera-web/commit/f36eb1bd88c019265f9c789f1f1db3ae369b5891
jsegarra
commented
Ok, Ok,
TODO: para la próxima PR añadir la función de ordenar la columna en InvoicesView porque para packages si que está
|
||||
@click.stop
|
||||
class="flex full-width justify-center items-center"
|
||||
>
|
||||
<QBtn
|
||||
v-if="row.hasPdf"
|
||||
icon="download"
|
||||
target="_blank"
|
||||
flat
|
||||
round
|
||||
@click="openReport(`InvoiceOuts/${row.id}/download`)"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('downloadInvoicePdf') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
<QIcon
|
||||
v-else
|
||||
name="warning"
|
||||
:title="t('notDownloadable')"
|
||||
color="warning"
|
||||
size="sm"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('requestTheInvoiceToComercial') }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
</QTd>
|
||||
</template>
|
||||
</VnTable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
noInvoicesFound: No invoices found
|
||||
invoice: Invoice
|
||||
issued: Date
|
||||
amount: Import
|
||||
downloadInvoicePdf: Download invoice PDF
|
||||
notDownloadable: Not available for download, request the invoice to your salesperson
|
||||
requestTheInvoiceToComercial: Request the invoice to your salesperson
|
||||
es-ES:
|
||||
noInvoicesFound: No se han encontrado facturas
|
||||
invoice: Factura
|
||||
issued: Fecha
|
||||
amount: Importe
|
||||
downloadInvoicePdf: Descargar factura en PDF
|
||||
notDownloadable: No disponible para descarga, solicita la factura a tu comercial
|
||||
requestTheInvoiceToComercial: Solicita la factura a tu comercial
|
||||
ca-ES:
|
||||
noInvoicesFound: No s'han trobat factures
|
||||
invoice: Factura
|
||||
issued: Data
|
||||
amount: Import
|
||||
downloadInvoicePdf: Descarregar PDF
|
||||
jsegarra marked this conversation as resolved
Outdated
jsegarra
commented
Factura en vez de serie. O ya estaba este literal? Factura en vez de serie. O ya estaba este literal?
wbuezas
commented
Modificado. Commit: Modificado.
Commit: https://gitea.verdnatura.es/verdnatura/hedera-web/commit/7c96106faa22e3ccdbf6aa993b254cff8b8157f4
|
||||
notDownloadable: No disponible per cescarrega, sol·licita la factura al teu comercial
|
||||
requestTheInvoiceToComercial: Sol·licita la factura al teu comercial
|
||||
fr-FR:
|
||||
noInvoicesFound: Aucune facture trouvée
|
||||
invoice: Facture
|
||||
issued: Date
|
||||
amount: Montant
|
||||
downloadInvoicePdf: Télécharger le PDF
|
||||
notDownloadable: Non disponible en téléchargement, demander la facture à votre commercial
|
||||
requestTheInvoiceToComercial: Demander la facture à votre commercial
|
||||
pt-PT:
|
||||
noInvoicesFound: Nenhuma fatura encontrada
|
||||
invoice: Fatura
|
||||
issued: Data
|
||||
amount: Importe
|
||||
downloadInvoicePdf: Baixar PDF
|
||||
notDownloadable: Não disponível para download, solicite a fatura ao seu comercial
|
||||
requestTheInvoiceToComercial: Solicite a fatura ao seu comercial
|
||||
</i18n>
|
|
@ -1,70 +1,135 @@
|
|||
<script setup>
|
||||
import { ref, onMounted, inject } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import CardList from 'src/components/ui/CardList.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnConfirm from 'src/components/ui/VnConfirm.vue';
|
||||
|
||||
import { currency, formatDateTitle } from 'src/lib/filters.js';
|
||||
import { tpvStore } from 'stores/tpv';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const jApi = inject('jApi');
|
||||
|
||||
const showAmountToPayDialog = ref(null);
|
||||
const amountToPay = ref(null);
|
||||
const orders = ref(null);
|
||||
const debt = ref(0);
|
||||
const tpv = tpvStore();
|
||||
|
||||
onMounted(async () => {
|
||||
await tpv.check(route);
|
||||
|
||||
orders.value = await jApi.query('CALL myTicket_list(NULL, NULL)');
|
||||
debt.value = await jApi.getValue('SELECT -myClient_getDebt(NULL)');
|
||||
});
|
||||
|
||||
const onPayClick = async () => {
|
||||
showAmountToPayDialog.value = true;
|
||||
|
||||
if (debt.value <= 0) {
|
||||
amountToPay.value = -debt.value;
|
||||
}
|
||||
jsegarra
commented
Podemos hacer un modal en vez del prompt? Podemos hacer un modal en vez del prompt?
Se que es lo que hay en producción, pero ahora tenemos Vue
wbuezas
commented
Prompt reemplazado. Commit: Prompt reemplazado.
Commit: https://gitea.verdnatura.es/verdnatura/hedera-web/commit/44627dbc8a4b69debd80491a4a191d2edce6c6ec
jsegarra
commented
Apuntamos para la próxima PR aplicar la condicion de solo numeros, porque si está vacio o hay letras te dice en rojo lo siguiente: "La cantidad debe ser un número positivo e inferior o igual al importe pendiente" Apuntamos para la próxima PR aplicar la condicion de solo numeros, porque si está vacio o hay letras te dice en rojo lo siguiente: "La cantidad debe ser un número positivo e inferior o igual al importe pendiente"
|
||||
};
|
||||
|
||||
const onConfirmPay = async () => {
|
||||
if (amountToPay.value) {
|
||||
const amount = amountToPay.value.toString().replace('.', ',');
|
||||
amountToPay.value = parseFloat(amount);
|
||||
await tpv.pay(amountToPay.value);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Teleport :to="$actions">
|
||||
<div class="balance">
|
||||
<span class="label">{{ $t('balance') }}</span>
|
||||
<span class="label">{{ t('balance') }}</span>
|
||||
<span class="amount" :class="{ negative: debt < 0 }">
|
||||
{{ currency(debt || 0) }}
|
||||
</span>
|
||||
<QIcon
|
||||
name="info"
|
||||
:title="$t('paymentInfo')"
|
||||
class="info"
|
||||
size="24px"
|
||||
/>
|
||||
<QIcon name="info" class="info" size="sm">
|
||||
<QTooltip max-width="450px">
|
||||
{{ t('paymentInfo') }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
</div>
|
||||
<QBtn
|
||||
icon="payments"
|
||||
:label="$t('makePayment')"
|
||||
:label="t('makePayment')"
|
||||
@click="onPayClick()"
|
||||
rounded
|
||||
no-caps
|
||||
/>
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('makePayment') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
<QBtn
|
||||
to="/ecomerce/basket"
|
||||
:to="{ name: 'basket' }"
|
||||
icon="shopping_cart"
|
||||
:label="$t('shoppingCart')"
|
||||
:label="t('shoppingCart')"
|
||||
rounded
|
||||
no-caps
|
||||
/>
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('shoppingCart') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
</Teleport>
|
||||
<div class="vn-w-sm">
|
||||
<QPage class="vn-w-sm">
|
||||
<div
|
||||
v-if="!orders?.length"
|
||||
class="text-subtitle1 text-center text-grey-7 q-pa-md"
|
||||
>
|
||||
{{ $t('noOrdersFound') }}
|
||||
{{ t('noOrdersFound') }}
|
||||
</div>
|
||||
<QCard v-if="orders?.length">
|
||||
<QList bordered separator padding>
|
||||
<QItem
|
||||
<QList v-if="orders?.length">
|
||||
<CardList
|
||||
v-for="order in orders"
|
||||
:key="order.id"
|
||||
:to="`ticket/${order.id}`"
|
||||
clickable
|
||||
v-ripple
|
||||
tag="label"
|
||||
>
|
||||
<QItemSection>
|
||||
<QItemLabel>
|
||||
{{ date(order.landed, 'ddd, MMMM Do') }}
|
||||
<template #content>
|
||||
<QItemLabel
|
||||
class="full-width text-bold q-mb-sm flex row justify-between"
|
||||
>
|
||||
<span>{{ formatDateTitle(order.landed) }}</span>
|
||||
<span>{{ currency(order.total) }}</span>
|
||||
</QItemLabel>
|
||||
<QItemLabel caption>#{{ order.id }}</QItemLabel>
|
||||
<QItemLabel caption>{{ order.nickname }}</QItemLabel>
|
||||
<QItemLabel caption>{{ order.agency }}</QItemLabel>
|
||||
</QItemSection>
|
||||
<QItemSection side top> {{ order.total }}€ </QItemSection>
|
||||
</QItem>
|
||||
<QItemLabel>#{{ order.id }}</QItemLabel>
|
||||
<QItemLabel>{{ order.nickname }}</QItemLabel>
|
||||
<QItemLabel>{{ order.agency }}</QItemLabel>
|
||||
</template>
|
||||
</CardList>
|
||||
</QList>
|
||||
</QCard>
|
||||
<QPageSticky>
|
||||
<QBtn
|
||||
fab
|
||||
icon="add_shopping_cart"
|
||||
color="accent"
|
||||
to="/ecomerce/catalog"
|
||||
:title="$t('startOrder')"
|
||||
:to="{ name: 'catalog' }"
|
||||
:title="t('startOrder')"
|
||||
/>
|
||||
</QPageSticky>
|
||||
</div>
|
||||
<VnConfirm
|
||||
v-model="showAmountToPayDialog"
|
||||
:message="t('amountToPay')"
|
||||
:promise="onConfirmPay"
|
||||
>
|
||||
<template #customHTML>
|
||||
<VnInput
|
||||
v-model="amountToPay"
|
||||
:clearable="false"
|
||||
class="full-width"
|
||||
/>
|
||||
</template>
|
||||
</VnConfirm>
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -92,50 +157,6 @@
|
|||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { date, currency } from 'src/lib/filters.js';
|
||||
import { tpvStore } from 'stores/tpv';
|
||||
|
||||
export default {
|
||||
name: 'OrdersPendingIndex',
|
||||
data() {
|
||||
return {
|
||||
orders: null,
|
||||
debt: 0,
|
||||
tpv: tpvStore()
|
||||
};
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
await this.tpv.check(this.$route);
|
||||
|
||||
this.orders = await this.$jApi.query('CALL myTicket_list(NULL, NULL)');
|
||||
this.debt = await this.$jApi.getValue('SELECT -myClient_getDebt(NULL)');
|
||||
},
|
||||
|
||||
methods: {
|
||||
date,
|
||||
currency,
|
||||
|
||||
async onPayClick() {
|
||||
let amount = -this.debt;
|
||||
amount = amount <= 0 ? null : amount;
|
||||
|
||||
let defaultAmountStr = '';
|
||||
if (amount !== null) {
|
||||
defaultAmountStr = amount;
|
||||
}
|
||||
amount = prompt(this.$t('amountToPay'), defaultAmountStr);
|
||||
|
||||
if (amount != null) {
|
||||
amount = parseFloat(amount.replace(',', '.'));
|
||||
await this.tpv.pay(amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
startOrder: Start order
|
||||
|
@ -148,6 +169,7 @@ en-US:
|
|||
disregards future orders. For get your order shipped, this amount must be
|
||||
equal to or greater than 0. If you want to make a down payment, click the
|
||||
payment button, delete the suggested amount and enter the amount you want.
|
||||
amountToPay: 'Amount to pay (€):'
|
||||
es-ES:
|
||||
startOrder: Empezar pedido
|
||||
noOrdersFound: No se encontrado pedidos
|
||||
|
@ -160,6 +182,7 @@ es-ES:
|
|||
esta cantidad debe ser igual o mayor que 0. Si quieres realizar una entrega a
|
||||
cuenta, pulsa el botón de pago, borra la cantidad sugerida e introduce la
|
||||
cantidad que desees.
|
||||
amountToPay: 'Cantidad a pagar (€):'
|
||||
ca-ES:
|
||||
startOrder: Començar encàrrec
|
||||
noOrdersFound: No s'han trobat comandes
|
||||
|
@ -172,6 +195,7 @@ ca-ES:
|
|||
enviat, aquesta quantitat ha de ser igual o més gran que 0. Si vols fer un
|
||||
lliurament a compte, prem el botó de pagament, esborra la quantitat suggerida
|
||||
e introdueix la quantitat que vulguis.
|
||||
amountToPay: 'Quantitat a pagar (€):'
|
||||
fr-FR:
|
||||
startOrder: Acheter
|
||||
noOrdersFound: Aucune commande trouvée
|
||||
|
@ -184,6 +208,7 @@ fr-FR:
|
|||
commande est expédiée, ce montant doit être égal ou supérieur à 0. Si vous
|
||||
voulez faire un versement, le montant suggéré effacé et entrez le montant que
|
||||
vous souhaitez.
|
||||
amountToPay: 'Montant à payer (€):'
|
||||
pt-PT:
|
||||
startOrder: Iniciar encomenda
|
||||
noOrdersFound: Nenhum pedido encontrado
|
||||
|
@ -196,4 +221,5 @@ pt-PT:
|
|||
quantidade deve ser igual ou superior a 0. Se queres realizar um depósito à
|
||||
conta, clique no botão de pagamento, apague a quantidade sugerida e introduza
|
||||
a quantidade que deseje.
|
||||
amountToPay: 'Valor a pagar (€):'
|
||||
</i18n>
|
|
@ -0,0 +1,135 @@
|
|||
<script setup>
|
||||
import { ref, inject, onMounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import CardList from 'src/components/ui/CardList.vue';
|
||||
import { currency, formatDateTitle } from 'src/lib/filters.js';
|
||||
import { useVnConfirm } from 'src/composables/useVnConfirm.js';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
import { useAppStore } from 'src/stores/app.js';
|
||||
|
||||
const jApi = inject('jApi');
|
||||
const { t } = useI18n();
|
||||
const { openConfirmationModal } = useVnConfirm();
|
||||
const { notify } = useNotify();
|
||||
const store = useAppStore();
|
||||
const router = useRouter();
|
||||
|
||||
const orders = ref([]);
|
||||
|
||||
const getOrders = async () => {
|
||||
try {
|
||||
orders.value = await jApi.query(
|
||||
`SELECT o.id, o.sent, o.deliveryMethodFk, o.taxableBase,
|
||||
a.nickname, am.description agency
|
||||
FROM myOrder o
|
||||
JOIN myAddress a ON a.id = o.addressFk
|
||||
JOIN vn.agencyMode am ON am.id = o.agencyModeFk
|
||||
WHERE NOT o.isConfirmed
|
||||
ORDER BY o.sent DESC`
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Error getting orders:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const removeOrder = async (id, index) => {
|
||||
try {
|
||||
await jApi.execQuery(
|
||||
`START TRANSACTION;
|
||||
DELETE FROM hedera.myOrder WHERE ((id = #id));
|
||||
COMMIT`,
|
||||
{
|
||||
id
|
||||
}
|
||||
);
|
||||
orders.value.splice(index, 1);
|
||||
notify(t('dataSaved'), 'positive');
|
||||
} catch (error) {
|
||||
console.error('Error removing order:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const loadOrder = orderId => {
|
||||
store.loadIntoBasket(orderId);
|
||||
router.push({ name: 'catalog' });
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
getOrders();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Teleport :to="$actions">
|
||||
<QBtn
|
||||
:to="{ name: 'checkout' }"
|
||||
icon="add_shopping_cart"
|
||||
:label="t('newOrder')"
|
||||
rounded
|
||||
no-caps
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('newOrder') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
</Teleport>
|
||||
<QPage class="vn-w-sm">
|
||||
<CardList
|
||||
v-for="(order, index) in orders"
|
||||
:key="index"
|
||||
:to="{ name: 'basket', params: { id: order.id } }"
|
||||
>
|
||||
<template #content>
|
||||
<QItemLabel class="text-bold q-mb-sm">{{
|
||||
formatDateTitle(order.sent)
|
||||
}}</QItemLabel>
|
||||
<QItemLabel> #{{ order.id }} </QItemLabel>
|
||||
<QItemLabel>{{ order.nickname }}</QItemLabel>
|
||||
<QItemLabel>{{ order.agency }}</QItemLabel>
|
||||
<QItemLabel>{{ currency(order.taxableBase) }}</QItemLabel>
|
||||
</template>
|
||||
<template #actions>
|
||||
<QBtn
|
||||
icon="delete"
|
||||
flat
|
||||
rounded
|
||||
@click.stop.prevent="
|
||||
openConfirmationModal(
|
||||
null,
|
||||
t('areYouSureDeleteOrder'),
|
||||
() => removeOrder(order.id, index)
|
||||
)
|
||||
"
|
||||
/>
|
||||
<QBtn
|
||||
icon="shopping_bag"
|
||||
flat
|
||||
rounded
|
||||
@click.stop.prevent="loadOrder(order.id)"
|
||||
/>
|
||||
</template>
|
||||
</CardList>
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
newOrder: New order
|
||||
areYouSureDeleteOrder: Are you sure you want to delete the order?
|
||||
es-ES:
|
||||
newOrder: Nuevo pedido
|
||||
areYouSureDeleteOrder: ¿Seguro que quieres borrar el pedido?
|
||||
ca-ES:
|
||||
newOrder: Nova comanda
|
||||
areYouSureDeleteOrder: Segur que vols esborrar la comanda?
|
||||
fr-FR:
|
||||
newOrder: Nouvelle commande
|
||||
areYouSureDeleteOrder: Êtes-vous sûr de vouloir supprimer la commande?
|
||||
pt-PT:
|
||||
newOrder: Novo pedido
|
||||
areYouSureDeleteOrder: Tem certeza de que deseja excluir o pedido?
|
||||
</i18n>
|
|
@ -1,145 +0,0 @@
|
|||
<template>
|
||||
<Teleport :to="$actions">
|
||||
<QBtn
|
||||
icon="print"
|
||||
:label="$t('printDeliveryNote')"
|
||||
@click="onPrintClick()"
|
||||
rounded
|
||||
no-caps
|
||||
/>
|
||||
</Teleport>
|
||||
<div>
|
||||
<QCard class="vn-w-sm">
|
||||
<QCardSection>
|
||||
<div class="text-h6">#{{ ticket.id }}</div>
|
||||
</QCardSection>
|
||||
<QCardSection>
|
||||
<div class="text-h6">{{ $t('shippingInformation') }}</div>
|
||||
<div>
|
||||
{{ $t('preparation') }}
|
||||
{{ date(ticket.shipped, 'ddd, MMMM Do') }}
|
||||
</div>
|
||||
<div>
|
||||
{{ $t('delivery') }}
|
||||
{{ date(ticket.shipped, 'ddd, MMMM Do') }}
|
||||
</div>
|
||||
<div>
|
||||
{{ $t(ticket.method != 'PICKUP' ? 'agency' : 'warehouse') }}
|
||||
{{ ticket.agency }}
|
||||
</div>
|
||||
</QCardSection>
|
||||
<QCardSection>
|
||||
<div class="text-h6">{{ $t('deliveryAddress') }}</div>
|
||||
<div>{{ ticket.nickname }}</div>
|
||||
<div>{{ ticket.street }}</div>
|
||||
<div>
|
||||
{{ ticket.postalCode }} {{ ticket.city }} ({{
|
||||
ticket.province
|
||||
}})
|
||||
</div>
|
||||
</QCardSection>
|
||||
<QSeparator inset />
|
||||
<QList v-for="row in rows" :key="row.itemFk">
|
||||
<QItem>
|
||||
<QItemSection avatar>
|
||||
<QAvatar size="68px">
|
||||
<img
|
||||
:src="`${$app.imageUrl}/catalog/200x200/${row.image}`"
|
||||
/>
|
||||
</QAvatar>
|
||||
</QItemSection>
|
||||
<QItemSection>
|
||||
<QItemLabel lines="1">
|
||||
{{ row.concept }}
|
||||
</QItemLabel>
|
||||
<QItemLabel lines="1" caption>
|
||||
{{ row.value5 }} {{ row.value6 }} {{ row.value7 }}
|
||||
</QItemLabel>
|
||||
<QItemLabel lines="1">
|
||||
{{ row.quantity }} x {{ currency(row.price) }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
<QItemSection side class="total">
|
||||
<QItemLabel>
|
||||
<span class="discount" v-if="row.discount">
|
||||
{{ currency(discountSubtotal(row)) }} -
|
||||
{{ currency(row.discount) }} =
|
||||
</span>
|
||||
{{ currency(subtotal(row)) }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</QList>
|
||||
</QCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.total {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { date, currency } from 'src/lib/filters.js';
|
||||
|
||||
export default {
|
||||
name: 'OrdersConfirmedView',
|
||||
|
||||
data() {
|
||||
return {
|
||||
ticket: {},
|
||||
rows: null,
|
||||
services: null,
|
||||
packages: null
|
||||
};
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
const params = {
|
||||
ticket: parseInt(this.$route.params.id)
|
||||
};
|
||||
this.ticket = await this.$jApi.getObject(
|
||||
'CALL myTicket_get(#ticket)',
|
||||
params
|
||||
);
|
||||
this.rows = await this.$jApi.query(
|
||||
'CALL myTicket_getRows(#ticket)',
|
||||
params
|
||||
);
|
||||
this.services = await this.$jApi.query(
|
||||
'CALL myTicket_getServices(#ticket)',
|
||||
params
|
||||
);
|
||||
this.packages = await this.$jApi.query(
|
||||
'CALL myTicket_getPackages(#ticket)',
|
||||
params
|
||||
);
|
||||
},
|
||||
|
||||
methods: {
|
||||
date,
|
||||
currency,
|
||||
|
||||
discountSubtotal(line) {
|
||||
return line.quantity * line.price;
|
||||
},
|
||||
|
||||
subtotal(line) {
|
||||
const discount = line.discount;
|
||||
return this.discountSubtotal(line) * ((100 - discount) / 100);
|
||||
},
|
||||
|
||||
onPrintClick() {
|
||||
const params = new URLSearchParams({
|
||||
access_token: this.$user.token,
|
||||
recipientId: this.$user.id,
|
||||
type: 'deliveryNote'
|
||||
});
|
||||
window.open(
|
||||
`/api/Tickets/${this.ticket.id}/delivery-note-pdf?${params.toString()}`
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,74 @@
|
|||
<script setup>
|
||||
import { onMounted, inject, ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import TicketDetails from 'src/components/ui/TicketDetails.vue';
|
||||
|
||||
import { userStore as useUserStore } from 'stores/user';
|
||||
|
||||
const { t } = useI18n();
|
||||
const jApi = inject('jApi');
|
||||
const route = useRoute();
|
||||
const userStore = useUserStore();
|
||||
|
||||
const ticket = ref({});
|
||||
const rows = ref([]);
|
||||
const services = ref(null);
|
||||
const packages = ref(null);
|
||||
|
||||
onMounted(async () => {
|
||||
const params = {
|
||||
ticket: parseInt(route.params.id)
|
||||
};
|
||||
ticket.value = await jApi.getObject('CALL myTicket_get(#ticket)', params);
|
||||
rows.value = await jApi.query('CALL myTicket_getRows(#ticket)', params);
|
||||
services.value = await jApi.query(
|
||||
'CALL myTicket_getServices(#ticket)',
|
||||
params
|
||||
);
|
||||
packages.value = await jApi.query(
|
||||
'CALL myTicket_getPackages(#ticket)',
|
||||
params
|
||||
);
|
||||
});
|
||||
|
||||
const onPrintClick = () => {
|
||||
const params = new URLSearchParams({
|
||||
access_token: userStore.token,
|
||||
recipientId: userStore.id,
|
||||
type: 'deliveryNote'
|
||||
});
|
||||
window.open(
|
||||
`/api/Tickets/${ticket.value.id}/delivery-note-pdf?${params.toString()}`
|
||||
);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Teleport :to="$actions">
|
||||
<QBtn
|
||||
icon="print"
|
||||
:label="t('printDeliveryNote')"
|
||||
@click="onPrintClick()"
|
||||
rounded
|
||||
no-caps
|
||||
/>
|
||||
</Teleport>
|
||||
<QPage>
|
||||
<TicketDetails :rows="rows" :ticket="ticket" />
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
printDeliveryNote: Print delivery note
|
||||
es-ES:
|
||||
printDeliveryNote: Imprimir albarán
|
||||
ca-ES:
|
||||
printDeliveryNote: Imprimir albarà
|
||||
fr-FR:
|
||||
printDeliveryNote: Imprimer bulletin de livraison
|
||||
pt-PT:
|
||||
printDeliveryNote: Imprimir nota de entrega
|
||||
</i18n>
|
|
@ -1,12 +1,13 @@
|
|||
import { route } from 'quasar/wrappers'
|
||||
import { appStore } from 'stores/app'
|
||||
import { route } from 'quasar/wrappers';
|
||||
import { useAppStore } from 'stores/app';
|
||||
import {
|
||||
createRouter,
|
||||
createMemoryHistory,
|
||||
createWebHistory,
|
||||
createWebHashHistory
|
||||
} from 'vue-router'
|
||||
import routes from './routes'
|
||||
} from 'vue-router';
|
||||
import routes from './routes';
|
||||
import { i18n } from 'src/boot/i18n';
|
||||
|
||||
/*
|
||||
* If not building with SSR mode, you can
|
||||
|
@ -22,7 +23,7 @@ export default route(function (/* { store, ssrContext } */) {
|
|||
? createMemoryHistory
|
||||
: process.env.VUE_ROUTER_MODE === 'history'
|
||||
? createWebHistory
|
||||
: createWebHashHistory
|
||||
: createWebHashHistory;
|
||||
|
||||
const Router = createRouter({
|
||||
scrollBehavior: () => ({ left: 0, top: 0 }),
|
||||
|
@ -34,18 +35,18 @@ export default route(function (/* { store, ssrContext } */) {
|
|||
history: createHistory(
|
||||
process.env.MODE === 'ssr' ? void 0 : process.env.VUE_ROUTER_BASE
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
Router.afterEach((to, from) => {
|
||||
if (from.name === to.name) return
|
||||
const app = appStore()
|
||||
if (from.name === to.name) return;
|
||||
const app = useAppStore();
|
||||
app.$patch({
|
||||
title: window.i18n.t(to.name || 'home'),
|
||||
title: i18n.global.t(to.name || 'home'),
|
||||
subtitle: null,
|
||||
useRightDrawer: false,
|
||||
rightDrawerOpen: true
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
return Router
|
||||
})
|
||||
return Router;
|
||||
});
|
||||
|
|
|
@ -4,7 +4,7 @@ const routes = [
|
|||
component: () => import('layouts/LoginLayout.vue'),
|
||||
children: [
|
||||
{
|
||||
name: 'Login',
|
||||
name: 'login',
|
||||
path: '/login/:email?',
|
||||
component: () => import('pages/Login/LoginView.vue')
|
||||
},
|
||||
|
@ -35,19 +35,24 @@ const routes = [
|
|||
component: () => import('src/pages/Cms/HomeView.vue')
|
||||
},
|
||||
{
|
||||
name: 'orders',
|
||||
name: 'confirmedOrders',
|
||||
path: '/ecomerce/orders',
|
||||
component: () => import('pages/Ecomerce/Orders.vue')
|
||||
component: () => import('pages/Ecomerce/OrdersView.vue')
|
||||
},
|
||||
{
|
||||
name: 'ticket',
|
||||
path: '/ecomerce/ticket/:id',
|
||||
component: () => import('pages/Ecomerce/Ticket.vue')
|
||||
component: () => import('pages/Ecomerce/TicketView.vue')
|
||||
},
|
||||
{
|
||||
name: 'invoices',
|
||||
path: '/ecomerce/invoices',
|
||||
component: () => import('pages/Ecomerce/Invoices.vue')
|
||||
component: () => import('pages/Ecomerce/InvoicesView.vue')
|
||||
},
|
||||
{
|
||||
name: 'pendingOrders',
|
||||
path: '/ecomerce/pending',
|
||||
component: () => import('pages/Ecomerce/PendingOrders.vue')
|
||||
},
|
||||
{
|
||||
name: 'catalog',
|
||||
|
@ -55,22 +60,32 @@ const routes = [
|
|||
component: () => import('pages/Ecomerce/Catalog.vue')
|
||||
},
|
||||
{
|
||||
name: 'packages',
|
||||
name: 'basket',
|
||||
path: '/ecomerce/basket/:id?',
|
||||
component: () => import('pages/Ecomerce/BasketView.vue')
|
||||
},
|
||||
{
|
||||
name: 'checkout',
|
||||
path: '/ecomerce/checkout',
|
||||
component: () => import('pages/Ecomerce/CheckoutView.vue')
|
||||
},
|
||||
{
|
||||
name: 'agencyPackages',
|
||||
path: '/agencies/packages',
|
||||
component: () => import('src/pages/Agencies/PackagesView.vue')
|
||||
},
|
||||
{
|
||||
name: 'Account',
|
||||
name: 'accountConfig',
|
||||
path: '/account/conf',
|
||||
component: () => import('pages/Account/AccountConfig.vue')
|
||||
},
|
||||
{
|
||||
name: 'AddressesList',
|
||||
name: 'addressesList',
|
||||
path: '/account/address-list',
|
||||
component: () => import('pages/Account/AddressList.vue')
|
||||
},
|
||||
{
|
||||
name: 'AddressDetails',
|
||||
name: 'addressDetails',
|
||||
path: '/account/address/:id?',
|
||||
component: () => import('pages/Account/AddressDetails.vue')
|
||||
}
|
||||
|
|
|
@ -1,19 +1,52 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { jApi } from 'boot/axios'
|
||||
import { defineStore } from 'pinia';
|
||||
import { jApi } from 'boot/axios';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
|
||||
export const appStore = defineStore('hedera', {
|
||||
const { notify } = useNotify();
|
||||
|
||||
export const useAppStore = defineStore('hedera', {
|
||||
state: () => ({
|
||||
title: null,
|
||||
subtitle: null,
|
||||
imageUrl: '',
|
||||
useRightDrawer: false,
|
||||
rightDrawerOpen: false
|
||||
rightDrawerOpen: false,
|
||||
basketOrderId: null,
|
||||
isHeaderMounted: false
|
||||
}),
|
||||
|
||||
actions: {
|
||||
async init() {
|
||||
this.getBasketOrderId();
|
||||
},
|
||||
|
||||
getBasketOrderId() {
|
||||
this.basketOrderId = localStorage.getItem('hederaBasket');
|
||||
},
|
||||
|
||||
async loadConfig() {
|
||||
const imageUrl = await jApi.getValue('SELECT url FROM imageConfig')
|
||||
this.$patch({ imageUrl })
|
||||
const imageUrl = await jApi.getValue('SELECT url FROM imageConfig');
|
||||
this.$patch({ imageUrl });
|
||||
},
|
||||
|
||||
async checkOrder(orderId) {
|
||||
try {
|
||||
const resultSet = await jApi.execQuery(
|
||||
'CALL myOrder_checkConfig(#id)',
|
||||
{ id: orderId }
|
||||
);
|
||||
resultSet.fetchValue();
|
||||
} catch (err) {
|
||||
console.error('Error checking order', err);
|
||||
}
|
||||
},
|
||||
|
||||
loadIntoBasket(orderId) {
|
||||
if (this.basketOrderId !== orderId) {
|
||||
localStorage.setItem('hederaBasket', orderId);
|
||||
this.basketOrderId = orderId;
|
||||
notify('orderLoadedIntoBasket', 'positive');
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { store } from 'quasar/wrappers'
|
||||
import { createPinia } from 'pinia'
|
||||
import { store } from 'quasar/wrappers';
|
||||
import { createPinia } from 'pinia';
|
||||
|
||||
/*
|
||||
* If not building with SSR mode, you can
|
||||
|
@ -11,10 +11,10 @@ import { createPinia } from 'pinia'
|
|||
*/
|
||||
|
||||
export default store((/* { ssrContext } */) => {
|
||||
const pinia = createPinia()
|
||||
const pinia = createPinia();
|
||||
|
||||
// You can add Pinia plugins here
|
||||
// pinia.use(SomePiniaPlugin)
|
||||
|
||||
return pinia
|
||||
})
|
||||
return pinia;
|
||||
});
|
||||
|
|
Movemos la etiqueta script setup al principio del archivo
Modificado.
Commit:
b66c47955c