feat(Ticket): refs #7984 add currency
gitea/salix-front/pipeline/pr-dev This commit looks good Details

This commit is contained in:
Alex Moreno 2025-01-22 15:04:50 +01:00
parent 1ac0672f1e
commit 583a774d7d
13 changed files with 162 additions and 82 deletions

View File

@ -14,8 +14,8 @@ const $props = defineProps({
default: undefined, default: undefined,
}, },
foreignField: { foreignField: {
type: String, type: [String, undefined],
default: 'foreignPrice', default: undefined,
}, },
localField: { localField: {
type: String, type: String,
@ -27,22 +27,36 @@ const $props = defineProps({
description: 'find currency code in array data model', description: 'find currency code in array data model',
}, },
}); });
const foreignFieldComputed = ref();
const arrayData = $props.arrayDataModel && useArrayData($props.arrayDataModel); const arrayData = $props.arrayDataModel && useArrayData($props.arrayDataModel);
const foreignValue = computed(() => $props.model?.[$props.foreignField]); const foreignValue = ref();
const localValue = computed(() => $props.model?.[$props.localField]); const localValue = computed(() => $props.model?.[$props.localField]);
const currency = computed( const currency = computed(
() => $props.currencyCode ?? arrayData.store.data?.currency?.code () => $props.currencyCode ?? arrayData.store.data?.currency?.code,
);
const toCurrencyLabel = computed(() =>
toCurrency(foreignValue.value ?? localValue.value, currency.value)
); );
const toCurrencyLabel = ref();
const currencyCodeModel = ref(); const currencyCodeModel = ref();
// onMounted(() => { onMounted(() => {
// if ($props.arrayDataModel) { // if ($props.arrayDataModel) {
// currencyCodeModel.value = arrayData.store.data?.currency?.code; // currencyCodeModel.value = arrayData.store.data?.currency?.code;
// } // }
// }); foreignFieldComputed.value =
$props.foreignField ??
'foreign' +
$props.localField.charAt(0).toUpperCase() +
$props.localField.slice(1);
foreignValue.value = $props.model[foreignFieldComputed.value];
getLabel();
});
function getLabel() {
toCurrencyLabel.value = toCurrency(
foreignValue.value ?? localValue.value,
currency.value,
);
}
</script> </script>
<template> <template>

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { defineProps, ref } from 'vue'; import { ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
const { t } = useI18n(); const { t } = useI18n();

View File

@ -10,6 +10,8 @@ import VnInput from 'src/components/common/VnInput.vue';
import VnSelect from 'src/components/common/VnSelect.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnSelectWorker from 'src/components/common/VnSelectWorker.vue'; import VnSelectWorker from 'src/components/common/VnSelectWorker.vue';
import { getDifferences, getUpdatedValues } from 'src/filters'; import { getDifferences, getUpdatedValues } from 'src/filters';
import VnSelectCompany from 'src/components/common/VnSelectCompany.vue';
import VnSelectCurrency from 'src/components/common/VnSelectCurrency.vue';
const route = useRoute(); const route = useRoute();
const { t } = useI18n(); const { t } = useI18n();
@ -37,7 +39,7 @@ const exprBuilder = (param, value) => {
function onBeforeSave(formData, originalData) { function onBeforeSave(formData, originalData) {
return getUpdatedValues( return getUpdatedValues(
Object.keys(getDifferences(formData, originalData)), Object.keys(getDifferences(formData, originalData)),
formData formData,
); );
} }
</script> </script>
@ -142,6 +144,10 @@ function onBeforeSave(formData, originalData) {
:input-debounce="0" :input-debounce="0"
/> />
</VnRow> </VnRow>
<VnRow>
<VnSelectCompany v-model="data.defaultCompanyFk" />
<VnSelectCurrency v-model="data.defaultCurrencyFk" />
</VnRow>
<VnRow> <VnRow>
<VnSelect <VnSelect
url="Clients" url="Clients"
@ -160,7 +166,7 @@ function onBeforeSave(formData, originalData) {
<QIcon name="info" class="cursor-pointer"> <QIcon name="info" class="cursor-pointer">
<QTooltip>{{ <QTooltip>{{
t( t(
'In case of a company succession, specify the grantor company' 'In case of a company succession, specify the grantor company',
) )
}}</QTooltip> }}</QTooltip>
</QIcon> </QIcon>

View File

@ -15,6 +15,7 @@ import InvoiceOutDescriptorProxy from 'pages/InvoiceOut/Card/InvoiceOutDescripto
import RouteDescriptorProxy from 'src/pages/Route/Card/RouteDescriptorProxy.vue'; import RouteDescriptorProxy from 'src/pages/Route/Card/RouteDescriptorProxy.vue';
import VnTable from 'src/components/VnTable/VnTable.vue'; import VnTable from 'src/components/VnTable/VnTable.vue';
import CustomerDescriptorProxy from '../Card/CustomerDescriptorProxy.vue'; import CustomerDescriptorProxy from '../Card/CustomerDescriptorProxy.vue';
import VnCurrency from 'src/components/ui/VnCurrency.vue';
const { t } = useI18n(); const { t } = useI18n();
const route = useRoute(); const route = useRoute();
@ -42,6 +43,7 @@ const filter = {
}, },
}, },
}, },
{ relation: 'currency' },
], ],
where: { clientFk: route.params.id }, where: { clientFk: route.params.id },
order: ['shipped DESC', 'id'], order: ['shipped DESC', 'id'],
@ -114,7 +116,7 @@ const columns = computed(() => [
action: ({ id }) => action: ({ id }) =>
window.open( window.open(
router.resolve({ params: { id }, name: 'TicketSale' }).href, router.resolve({ params: { id }, name: 'TicketSale' }).href,
'_blank' '_blank',
), ),
isPrimary: true, isPrimary: true,
}, },
@ -213,9 +215,19 @@ const getItemPackagingType = (ticketSales) => {
:color="setTotalPriceColor(row)" :color="setTotalPriceColor(row)"
text-color="black" text-color="black"
> >
{{ toCurrency(row.totalWithVat) }} <VnCurrency
:model="row"
local-field="totalWithVat"
:currency-code="row.currency.code"
/>
</QBadge> </QBadge>
<span v-else> {{ toCurrency(row.totalWithVat) }}</span> <span v-else>
<VnCurrency
:model="row"
local-field="totalWithVat"
:currency-code="row.currency.code"
/>
</span>
</template> </template>
<template #column-state="{ row }"> <template #column-state="{ row }">
<span v-if="row.invoiceOut" @click.stop> <span v-if="row.invoiceOut" @click.stop>

View File

@ -149,7 +149,7 @@ function sendPdfInvoice({ address }) {
const createInvoiceInCorrection = async () => { const createInvoiceInCorrection = async () => {
const { data: correctingId } = await axios.post( const { data: correctingId } = await axios.post(
'InvoiceIns/corrective', 'InvoiceIns/corrective',
Object.assign(correctionFormData, { id: entityId.value }) Object.assign(correctionFormData, { id: entityId.value }),
); );
push({ path: `/invoice-in/${correctingId}/summary` }); push({ path: `/invoice-in/${correctingId}/summary` });
}; };
@ -272,7 +272,6 @@ const createInvoiceInCorrection = async () => {
> >
<template #option="{ itemProps, opt }"> <template #option="{ itemProps, opt }">
<QItem v-bind="itemProps"> <QItem v-bind="itemProps">
{{ console.log('opt: ', opt) }}
<QItemSection> <QItemSection>
<QItemLabel <QItemLabel
>{{ opt.id }} - >{{ opt.id }} -
@ -311,11 +310,11 @@ const createInvoiceInCorrection = async () => {
<i18n> <i18n>
en: en:
isNotLinked: The entry {bookEntry} has been deleted with {accountingEntries} entries isNotLinked: The entry {bookEntry} has been deleted with {accountingEntries} entries
isLinked: The entry has been linked to Sage. Please contact administration for further information isLinked: The entry has been linked to Sage. Please contact administration for further information
assertAction: Are you sure you want to {action} this invoice? assertAction: Are you sure you want to {action} this invoice?
es: es:
isNotLinked: Se ha eliminado el asiento {bookEntry} con {accountingEntries} apuntes isNotLinked: Se ha eliminado el asiento {bookEntry} con {accountingEntries} apuntes
isLinked: El asiento fue enlazado a Sage, por favor contacta con administración isLinked: El asiento fue enlazado a Sage, por favor contacta con administración
assertAction: Estas seguro de querer {action} esta factura? assertAction: Estas seguro de querer {action} esta factura?
</i18n> </i18n>

View File

@ -74,7 +74,7 @@ function setTotal(response) {
total.value = response; total.value = response;
state.set('orderTotal', response); state.set('orderTotal', response);
} }
const orderTotal = computed(() => state.get('orderTotal')); const orderTotal = computed(() => state.get('orderTotal') ?? {});
const total = ref(); const total = ref();
</script> </script>

View File

@ -155,7 +155,7 @@ onMounted(() => {
async function fetchClientAddress(id, formData = {}) { async function fetchClientAddress(id, formData = {}) {
const { data } = await axios.get( const { data } = await axios.get(
`Clients/${id}/addresses?filter[order]=isActive DESC` `Clients/${id}/addresses?filter[order]=isActive DESC`,
); );
addressOptions.value = data; addressOptions.value = data;
formData.addressId = data.defaultAddressFk; formData.addressId = data.defaultAddressFk;
@ -300,11 +300,11 @@ const getDateColor = (date) => {
option-label="agencyMode" option-label="agencyMode"
/> />
<VnSelectCompany <VnSelectCompany
v-model="data.defaultCompanyFk" v-model="data.companyId"
v-model:client-id="data.clientFk" v-model:client-id="data.clientFk"
/> />
<VnSelectCurrency <VnSelectCurrency
v-model="data.defaultCurrencyFk" v-model="data.currencyId"
v-model:client-id="data.clientFk" v-model:client-id="data.clientFk"
/> />
</template> </template>

View File

@ -250,10 +250,7 @@ onMounted(async () => {
</template> </template>
<template #body-cell-price="{ row }"> <template #body-cell-price="{ row }">
<QTd class="number"> <QTd class="number">
<VnCurrency <VnCurrency :model="row.component" array-data-model="ticketData" />
:model="row.component"
:currency-code="formData.currency.code"
/>
</QTd> </QTd>
</template> </template>
<template #body-cell-newPrice="{ row }"> <template #body-cell-newPrice="{ row }">

View File

@ -43,7 +43,7 @@ const cancel = () => {
<template> <template>
<QPopupProxy ref="QPopupProxyRef"> <QPopupProxy ref="QPopupProxyRef">
<div class="container"> <div class="container">
<QSpinner v-if="!mana" color="primary" size="md" /> <QSpinner v-if="!mana && mana !== 0" color="primary" size="md" />
<div v-else> <div v-else>
<div class="header">Mana: {{ toCurrency(mana) }}</div> <div class="header">Mana: {{ toCurrency(mana) }}</div>
<div class="q-pa-md"> <div class="q-pa-md">

View File

@ -5,11 +5,13 @@ import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import CatalogItem from 'components/ui/CatalogItem.vue'; import CatalogItem from 'components/ui/CatalogItem.vue';
import VnCurrency from 'src/components/ui/VnCurrency.vue';
import { toCurrency } from 'filters/index'; import { useArrayData } from 'composables/useArrayData';
const { t } = useI18n(); const { t } = useI18n();
const route = useRoute(); const route = useRoute();
const arrayData = useArrayData('ticketData');
const { store } = arrayData;
const salesFilter = { const salesFilter = {
include: { include: {
@ -37,7 +39,12 @@ const sales = ref([]);
<div class="q-mt-md full-width row justify-between items-center"> <div class="q-mt-md full-width row justify-between items-center">
<span class="text-h6">{{ sale.quantity }}</span> <span class="text-h6">{{ sale.quantity }}</span>
<span>{{ t('by') }}</span> <span>{{ t('by') }}</span>
<span class="text-h6">{{ toCurrency(sale?.price) }}</span> <span class="text-h6">
<VnCurrency
:model="sale"
:currency-code="store.data.currency.code"
/>
</span>
</div> </div>
</template> </template>
</CatalogItem> </CatalogItem>

View File

@ -58,7 +58,7 @@ const canProceed = ref();
watch( watch(
() => route.params.id, () => route.params.id,
() => tableRef.value.reload() () => tableRef.value.reload(),
); );
const columns = computed(() => [ const columns = computed(() => [
@ -201,7 +201,7 @@ const changeQuantity = async (sale) => {
await updateQuantity(sale); await updateQuantity(sale);
} catch (e) { } catch (e) {
const { quantity } = tableRef.value.CrudModelRef.originalData.find( const { quantity } = tableRef.value.CrudModelRef.originalData.find(
(s) => s.id === sale.id (s) => s.id === sale.id,
); );
sale.quantity = quantity; sale.quantity = quantity;
throw e; throw e;
@ -506,7 +506,7 @@ async function isSalePrepared(item) {
componentProps: { componentProps: {
title: t('Item prepared'), title: t('Item prepared'),
message: t( message: t(
'This item is already prepared. Do you want to continue?' 'This item is already prepared. Do you want to continue?',
), ),
data: item, data: item,
}, },
@ -528,7 +528,7 @@ watch(
if (newItemFk) { if (newItemFk) {
updateItem(newRow.value); updateItem(newRow.value);
} }
} },
); );
</script> </script>
@ -598,7 +598,7 @@ watch(
openConfirmationModal( openConfirmationModal(
t('Continue anyway?'), t('Continue anyway?'),
t('You are going to delete lines of the ticket'), t('You are going to delete lines of the ticket'),
removeSales removeSales,
) )
" "
> >

View File

@ -21,6 +21,7 @@ import ZoneDescriptorProxy from 'src/pages/Zone/Card/ZoneDescriptorProxy.vue';
import VnSelect from 'src/components/common/VnSelect.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnToSummary from 'src/components/ui/VnToSummary.vue'; import VnToSummary from 'src/components/ui/VnToSummary.vue';
import TicketDescriptorMenu from './TicketDescriptorMenu.vue'; import TicketDescriptorMenu from './TicketDescriptorMenu.vue';
import VnCurrency from 'src/components/ui/VnCurrency.vue';
const route = useRoute(); const route = useRoute();
const { notify } = useNotify(); const { notify } = useNotify();
@ -277,19 +278,38 @@ function toTicketUrl(section) {
<QCard class="vn-one"> <QCard class="vn-one">
<VnTitle :text="t('ticket.summary.summaryAmount')" /> <VnTitle :text="t('ticket.summary.summaryAmount')" />
<div class="bodyCard"> <div class="bodyCard">
<VnLv <VnLv :label="t('globals.subtotal')">
:label="t('globals.subtotal')" <template #value>
:value="toCurrency(entity.totalWithoutVat)" <VnCurrency
/> :model="entity"
<VnLv :currency-code="entity.currency.code"
:label="t('globals.vat')" local-field="totalWithoutVat"
:value="toCurrency(entity.totalWithVat - entity.totalWithoutVat)" />
/> </template>
<VnLv </VnLv>
:label="t('ticket.summary.total')" <VnLv :label="t('globals.vat')">
:value="toCurrency(ticket.totalWithVat)" <template #value>
style="font-weight: bold" <VnCurrency
/> :model="{
vat: entity.totalWithVat - entity.totalWithoutVat,
foreignVat:
entity.foreignTotalWithVat -
entity.foreignTotalWithoutVat,
}"
:currency-code="entity.currency.code"
local-field="vat"
/>
</template>
</VnLv>
<VnLv :label="t('ticket.summary.total')" style="font-weight: bold">
<template #value>
<VnCurrency
:model="entity"
:currency-code="entity.currency.code"
local-field="totalWithVat"
/>
</template>
</VnLv>
</div> </div>
</QCard> </QCard>
<QCard class="vn-max"> <QCard class="vn-max">
@ -447,16 +467,28 @@ function toTicketUrl(section) {
:item="props.row.item" :item="props.row.item"
></FetchedTags> ></FetchedTags>
</QTd> </QTd>
<QTd>{{ props.row.price }} </QTd> <QTd>
<VnCurrency
:model="props.row"
:currency-code="entity.currency.code"
/>
</QTd>
<QTd>{{ props.row.discount }} %</QTd> <QTd>{{ props.row.discount }} %</QTd>
<QTd <QTd>
>{{ <VnCurrency
toCurrency( :model="{
props.row.quantity * amount:
props.row.quantity *
props.row.price * props.row.price *
((100 - props.row.discount) / 100) ((100 - props.row.discount) / 100),
) foreignAmount:
}} props.row.quantity *
props.row.foreignPrice *
((100 - props.row.discount) / 100),
}"
local-field="amount"
:currency-code="entity.currency.code"
/>
</QTd> </QTd>
<QTd>{{ dashIfEmpty(props.row.item.itemPackingTypeFk) }}</QTd> <QTd>{{ dashIfEmpty(props.row.item.itemPackingTypeFk) }}</QTd>
</QTr> </QTr>
@ -501,7 +533,12 @@ function toTicketUrl(section) {
<QTr :props="props"> <QTr :props="props">
<QTd>{{ props.row.quantity }}</QTd> <QTd>{{ props.row.quantity }}</QTd>
<QTd>{{ props.row.description }}</QTd> <QTd>{{ props.row.description }}</QTd>
<QTd>{{ toCurrency(props.row.price) }}</QTd> <QTd>
<VnCurrency
:model="props.row"
:currency-code="entity.currency.code"
/>
</QTd>
<QTd>{{ props.row.taxClass.description }}</QTd> <QTd>{{ props.row.taxClass.description }}</QTd>
<QTd>{{ <QTd>{{
toCurrency(props.row.quantity * props.row.price) toCurrency(props.row.quantity * props.row.price)

View File

@ -1,11 +1,11 @@
<script setup> <script setup>
import axios from 'axios'; import axios from 'axios';
import { computed, ref, onBeforeMount } from 'vue'; import { computed, ref, onBeforeMount, markRaw } from 'vue';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar'; import { QChip, useQuasar } from 'quasar';
import { toDate, toCurrency, dashIfEmpty } from 'src/filters/index'; import { toDate, dashIfEmpty } from 'src/filters/index';
import useNotify from 'src/composables/useNotify'; import useNotify from 'src/composables/useNotify';
import TicketSummary from './Card/TicketSummary.vue'; import TicketSummary from './Card/TicketSummary.vue';
import { useSummaryDialog } from 'src/composables/useSummaryDialog'; import { useSummaryDialog } from 'src/composables/useSummaryDialog';
@ -24,6 +24,7 @@ import TicketProblems from 'src/components/TicketProblems.vue';
import VnSection from 'src/components/common/VnSection.vue'; import VnSection from 'src/components/common/VnSection.vue';
import VnSelectCompany from 'src/components/common/VnSelectCompany.vue'; import VnSelectCompany from 'src/components/common/VnSelectCompany.vue';
import VnSelectCurrency from 'src/components/common/VnSelectCurrency.vue'; import VnSelectCurrency from 'src/components/common/VnSelectCurrency.vue';
import VnCurrency from 'src/components/ui/VnCurrency.vue';
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
@ -194,7 +195,6 @@ const columns = computed(() => [
component: 'number', component: 'number',
inWhere: true, inWhere: true,
}, },
format: (row) => toCurrency(row.totalWithVat),
}, },
{ {
align: 'left', align: 'left',
@ -252,7 +252,7 @@ const fetchAvailableAgencies = async (formData) => {
const defaultAgency = agenciesOptions.value.find( const defaultAgency = agenciesOptions.value.find(
(agency) => (agency) =>
agency.agencyModeFk === selectedClient.value.defaultAddress.agencyModeFk agency.agencyModeFk === selectedClient.value.defaultAddress.agencyModeFk,
); );
if (defaultAgency) formData.agencyModeId = defaultAgency.agencyModeFk; if (defaultAgency) formData.agencyModeId = defaultAgency.agencyModeFk;
@ -349,7 +349,7 @@ function openBalanceDialog(ticket) {
const description = ref([]); const description = ref([]);
const firstTicketClientId = checkedTickets[0].clientFk; const firstTicketClientId = checkedTickets[0].clientFk;
const isSameClient = checkedTickets.every( const isSameClient = checkedTickets.every(
(ticket) => ticket.clientFk === firstTicketClientId (ticket) => ticket.clientFk === firstTicketClientId,
); );
if (!isSameClient) { if (!isSameClient) {
@ -388,7 +388,7 @@ async function onSubmit() {
description: dialogData.value.value.description, description: dialogData.value.value.description,
clientFk: dialogData.value.value.clientFk, clientFk: dialogData.value.value.clientFk,
email: email[0].email, email: email[0].email,
} },
); );
if (data) notify('globals.dataSaved', 'positive'); if (data) notify('globals.dataSaved', 'positive');
@ -407,32 +407,32 @@ function setReference(data) {
switch (data) { switch (data) {
case 1: case 1:
newDescription = `${t( newDescription = `${t(
'ticketList.creditCard' 'ticketList.creditCard',
)}, ${dialogData.value.value.description.replace( )}, ${dialogData.value.value.description.replace(
/^(Credit Card, |Cash, |Transfers, )/, /^(Credit Card, |Cash, |Transfers, )/,
'' '',
)}`; )}`;
break; break;
case 2: case 2:
newDescription = `${t( newDescription = `${t(
'ticketList.cash' 'ticketList.cash',
)}, ${dialogData.value.value.description.replace( )}, ${dialogData.value.value.description.replace(
/^(Credit Card, |Cash, |Transfers, )/, /^(Credit Card, |Cash, |Transfers, )/,
'' '',
)}`; )}`;
break; break;
case 3: case 3:
newDescription = `${newDescription.replace( newDescription = `${newDescription.replace(
/^(Credit Card, |Cash, |Transfers, )/, /^(Credit Card, |Cash, |Transfers, )/,
'' '',
)}`; )}`;
break; break;
case 4: case 4:
newDescription = `${t( newDescription = `${t(
'ticketList.transfers' 'ticketList.transfers',
)}, ${dialogData.value.value.description.replace( )}, ${dialogData.value.value.description.replace(
/^(Credit Card, |Cash, |Transfers, )/, /^(Credit Card, |Cash, |Transfers, )/,
'' '',
)}`; )}`;
break; break;
case 3317: case 3317:
@ -444,6 +444,10 @@ function setReference(data) {
dialogData.value.value.description = newDescription; dialogData.value.value.description = newDescription;
} }
function isLittle(row) {
return row.totalWithVat > 0 && row.totalWithVat < 50;
}
</script> </script>
<template> <template>
@ -543,14 +547,18 @@ function setReference(data) {
</span> </span>
</template> </template>
<template #column-totalWithVat="{ row }"> <template #column-totalWithVat="{ row }">
<QChip <component
v-if="row.totalWithVat > 0 && row.totalWithVat < 50" :is="isLittle(row) ? markRaw(QChip) : 'span'"
class="bg-warning" :class="{ 'bg-warning': isLittle(row) }"
dense dense
square square
> >
{{ row.totalWithVat }} <VnCurrency
</QChip> :model="row"
:currency-code="row.currencyCode"
local-field="totalWithVat"
/>
</component>
</template> </template>
<template #more-create-dialog="{ data }"> <template #more-create-dialog="{ data }">
<VnRow> <VnRow>