492 lines
19 KiB
Vue
492 lines
19 KiB
Vue
<script setup>
|
|
import { ref, computed } from 'vue';
|
|
import { useRoute } from 'vue-router';
|
|
import { useI18n } from 'vue-i18n';
|
|
import { useQuasar } from 'quasar';
|
|
import axios from 'axios';
|
|
import { useArrayData } from 'src/composables/useArrayData';
|
|
import { toCurrency } from 'src/filters';
|
|
import FetchData from 'src/components/FetchData.vue';
|
|
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
|
import CrudModel from 'src/components/CrudModel.vue';
|
|
import VnCurrency from 'src/components/common/VnCurrency.vue';
|
|
|
|
const route = useRoute();
|
|
const { t } = useI18n();
|
|
const quasar = useQuasar();
|
|
|
|
const arrayData = useArrayData('InvoiceIn');
|
|
const invoiceIn = computed(() => arrayData.store.data);
|
|
|
|
const expenses = ref([]);
|
|
const sageTaxTypes = ref([]);
|
|
const sageTransactionTypes = ref([]);
|
|
const rowsSelected = ref([]);
|
|
const newExpense = ref({
|
|
code: undefined,
|
|
isWithheld: false,
|
|
description: undefined,
|
|
});
|
|
|
|
const invoiceInFormRef = ref();
|
|
const expensesRef = ref();
|
|
const newExpenseRef = ref();
|
|
|
|
const columns = computed(() => [
|
|
{
|
|
name: 'expense',
|
|
label: t('Expense'),
|
|
field: (row) => row.expenseFk,
|
|
options: expenses.value,
|
|
model: 'expenseFk',
|
|
optionValue: 'id',
|
|
optionLabel: 'id',
|
|
sortable: true,
|
|
tabIndex: 1,
|
|
align: 'left',
|
|
},
|
|
{
|
|
name: 'taxablebase',
|
|
label: t('Taxable base'),
|
|
field: (row) => toCurrency(row.taxableBase),
|
|
model: 'taxableBase',
|
|
sortable: true,
|
|
tabIndex: 2,
|
|
align: 'left',
|
|
},
|
|
{
|
|
name: 'sageiva',
|
|
label: t('Sage iva'),
|
|
field: (row) => row.taxTypeSageFk,
|
|
options: sageTaxTypes.value,
|
|
model: 'taxTypeSageFk',
|
|
optionValue: 'id',
|
|
optionLabel: 'vat',
|
|
sortable: true,
|
|
tabindex: 3,
|
|
align: 'left',
|
|
},
|
|
{
|
|
name: 'sagetransaction',
|
|
label: t('Sage transaction'),
|
|
field: (row) => row.transactionTypeSageFk,
|
|
options: sageTransactionTypes.value,
|
|
model: 'transactionTypeSageFk',
|
|
optionValue: 'id',
|
|
optionLabel: 'transaction',
|
|
sortable: true,
|
|
tabIndex: 4,
|
|
align: 'left',
|
|
},
|
|
{
|
|
name: 'rate',
|
|
label: t('Rate'),
|
|
sortable: true,
|
|
tabIndex: 5,
|
|
field: (row) => toCurrency(taxRate(row, row.taxTypeSageFk)),
|
|
align: 'left',
|
|
},
|
|
{
|
|
name: 'foreignvalue',
|
|
label: t('Foreign value'),
|
|
sortable: true,
|
|
tabIndex: 6,
|
|
field: (row) => row.foreignValue,
|
|
align: 'left',
|
|
},
|
|
]);
|
|
|
|
const filter = {
|
|
fields: [
|
|
'id',
|
|
'invoiceInFk',
|
|
'taxableBase',
|
|
'expenseFk',
|
|
'foreignValue',
|
|
'taxTypeSageFk',
|
|
'transactionTypeSageFk',
|
|
],
|
|
where: {
|
|
invoiceInFk: route.params.id,
|
|
},
|
|
};
|
|
|
|
const isNotEuro = (code) => code != 'EUR';
|
|
|
|
function taxRate(invoiceInTax) {
|
|
const sageTaxTypeId = invoiceInTax.taxTypeSageFk;
|
|
const taxRateSelection = sageTaxTypes.value.find(
|
|
(transaction) => transaction.id == sageTaxTypeId
|
|
);
|
|
const taxTypeSage = taxRateSelection?.rate ?? 0;
|
|
const taxableBase = invoiceInTax?.taxableBase ?? 0;
|
|
|
|
return (taxTypeSage / 100) * taxableBase;
|
|
}
|
|
|
|
async function addExpense() {
|
|
try {
|
|
if (!newExpense.value.code) throw new Error(t(`The code can't be empty`));
|
|
if (isNaN(newExpense.value.code))
|
|
throw new Error(t(`The code have to be a number`));
|
|
if (!newExpense.value.description)
|
|
throw new Error(t(`The description can't be empty`));
|
|
|
|
const data = [
|
|
{
|
|
id: newExpense.value.code,
|
|
isWithheld: newExpense.value.isWithheld,
|
|
name: newExpense.value.description,
|
|
},
|
|
];
|
|
|
|
await axios.post(`Expenses`, data);
|
|
await expensesRef.value.fetch();
|
|
quasar.notify({
|
|
type: 'positive',
|
|
message: t('globals.dataSaved'),
|
|
});
|
|
newExpenseRef.value.hide();
|
|
} catch (error) {
|
|
quasar.notify({
|
|
type: 'negative',
|
|
message: t(`${error.message}`),
|
|
});
|
|
}
|
|
}
|
|
</script>
|
|
<template>
|
|
<FetchData
|
|
ref="expensesRef"
|
|
url="Expenses"
|
|
auto-load
|
|
@on-fetch="(data) => (expenses = data)"
|
|
/>
|
|
<FetchData url="SageTaxTypes" auto-load @on-fetch="(data) => (sageTaxTypes = data)" />
|
|
<FetchData
|
|
url="sageTransactionTypes"
|
|
auto-load
|
|
@on-fetch="(data) => (sageTransactionTypes = data)"
|
|
/>
|
|
<CrudModel
|
|
ref="invoiceInFormRef"
|
|
v-if="invoiceIn"
|
|
data-key="InvoiceInTaxes"
|
|
url="InvoiceInTaxes"
|
|
:filter="filter"
|
|
:data-required="{ invoiceInFk: route.params.id }"
|
|
auto-load
|
|
v-model:selected="rowsSelected"
|
|
>
|
|
<template #body="{ rows }">
|
|
<QTable
|
|
v-model:selected="rowsSelected"
|
|
selection="multiple"
|
|
:columns="columns"
|
|
:rows="rows"
|
|
row-key="$index"
|
|
:grid="$q.screen.lt.sm"
|
|
>
|
|
<template #body-cell-expense="{ row, col }">
|
|
<QTd auto-width>
|
|
<VnSelectFilter
|
|
v-model="row[col.model]"
|
|
:options="col.options"
|
|
:option-value="col.optionValue"
|
|
:option-label="col.optionLabel"
|
|
:filter-options="['id', 'name']"
|
|
>
|
|
<template #option="scope">
|
|
<QItem v-bind="scope.itemProps">
|
|
{{ `${scope.opt.id}: ${scope.opt.name}` }}
|
|
</QItem>
|
|
</template>
|
|
<template #append>
|
|
<QIcon
|
|
name="close"
|
|
@click.stop="value = null"
|
|
class="cursor-pointer"
|
|
/>
|
|
<QBtn
|
|
padding="xs"
|
|
round
|
|
flat
|
|
icon="add_circle"
|
|
@click.stop="newExpenseRef.show()"
|
|
>
|
|
<QTooltip>
|
|
{{ t('Create expense') }}
|
|
</QTooltip>
|
|
</QBtn>
|
|
</template>
|
|
</VnSelectFilter>
|
|
</QTd>
|
|
</template>
|
|
<template #body-cell-taxablebase="{ row }">
|
|
<QTd>
|
|
<VnCurrency
|
|
:class="{
|
|
'no-pointer-events': isNotEuro(invoiceIn.currency.code),
|
|
}"
|
|
:disable="isNotEuro(invoiceIn.currency.code)"
|
|
label=""
|
|
clear-icon="close"
|
|
v-model="row.taxableBase"
|
|
clearable
|
|
/>
|
|
</QTd>
|
|
</template>
|
|
<template #body-cell-sageiva="{ row, col }">
|
|
<QTd>
|
|
<VnSelectFilter
|
|
v-model="row[col.model]"
|
|
:options="col.options"
|
|
:option-value="col.optionValue"
|
|
:option-label="col.optionLabel"
|
|
:filter-options="['id', 'vat']"
|
|
:autofocus="col.tabIndex == 1"
|
|
input-debounce="0"
|
|
>
|
|
<template #option="scope">
|
|
<QItem v-bind="scope.itemProps">
|
|
<QItemSection>
|
|
<QItemLabel>{{ scope.opt.vat }}</QItemLabel>
|
|
<QItemLabel>
|
|
{{ `#${scope.opt.id}` }}
|
|
</QItemLabel>
|
|
</QItemSection>
|
|
</QItem>
|
|
</template>
|
|
</VnSelectFilter>
|
|
</QTd>
|
|
</template>
|
|
<template #body-cell-sagetransaction="{ row, col }">
|
|
<QTd>
|
|
<VnSelectFilter
|
|
v-model="row[col.model]"
|
|
:options="col.options"
|
|
:option-value="col.optionValue"
|
|
:option-label="col.optionLabel"
|
|
:filter-options="['id', 'transaction']"
|
|
:autofocus="col.tabIndex == 1"
|
|
input-debounce="0"
|
|
>
|
|
<template #option="scope">
|
|
<QItem v-bind="scope.itemProps">
|
|
<QItemSection>
|
|
<QItemLabel>{{
|
|
scope.opt.transaction
|
|
}}</QItemLabel>
|
|
<QItemLabel>
|
|
{{ `#${scope.opt.id}` }}
|
|
</QItemLabel>
|
|
</QItemSection>
|
|
</QItem>
|
|
</template>
|
|
</VnSelectFilter>
|
|
</QTd>
|
|
</template>
|
|
<template #body-cell-foreignvalue="{ row }">
|
|
<QTd>
|
|
<QInput
|
|
:class="{
|
|
'no-pointer-events': !isNotEuro(invoiceIn.currency.code),
|
|
}"
|
|
:disable="!isNotEuro(invoiceIn.currency.code)"
|
|
v-model="row.foreignValue"
|
|
/>
|
|
</QTd>
|
|
</template>
|
|
<template #item="props">
|
|
<div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition">
|
|
<QCard bordered flat class="q-my-xs">
|
|
<QCardSection>
|
|
<QCheckbox v-model="props.selected" dense />
|
|
</QCardSection>
|
|
<QSeparator />
|
|
<QList>
|
|
<QItem>
|
|
<VnSelectFilter
|
|
:label="t('Expense')"
|
|
class="full-width"
|
|
v-model="props.row['expenseFk']"
|
|
:options="expenses"
|
|
option-value="id"
|
|
option-label="name"
|
|
:filter-options="['id', 'name']"
|
|
>
|
|
<template #option="scope">
|
|
<QItem v-bind="scope.itemProps">
|
|
{{ `${scope.opt.id}: ${scope.opt.name}` }}
|
|
</QItem>
|
|
</template>
|
|
</VnSelectFilter>
|
|
</QItem>
|
|
<QItem>
|
|
<VnCurrency
|
|
:label="t('Taxable base')"
|
|
:class="{
|
|
'no-pointer-events': isNotEuro(
|
|
invoiceIn.currency.code
|
|
),
|
|
}"
|
|
class="full-width"
|
|
:disable="isNotEuro(invoiceIn.currency.code)"
|
|
clear-icon="close"
|
|
v-model="props.row.taxableBase"
|
|
clearable
|
|
/>
|
|
</QItem>
|
|
<QItem>
|
|
<VnSelectFilter
|
|
:label="t('Sage iva')"
|
|
class="full-width"
|
|
v-model="props.row['taxTypeSageFk']"
|
|
:options="sageTaxTypes"
|
|
option-value="id"
|
|
option-label="vat"
|
|
:filter-options="['id', 'vat']"
|
|
>
|
|
<template #option="scope">
|
|
<QItem v-bind="scope.itemProps">
|
|
<QItemSection>
|
|
<QItemLabel>{{
|
|
scope.opt.vat
|
|
}}</QItemLabel>
|
|
<QItemLabel>
|
|
{{ `#${scope.opt.id}` }}
|
|
</QItemLabel>
|
|
</QItemSection>
|
|
</QItem>
|
|
</template>
|
|
</VnSelectFilter>
|
|
</QItem>
|
|
<QItem>
|
|
<VnSelectFilter
|
|
class="full-width"
|
|
v-model="props.row['transactionTypeSageFk']"
|
|
:options="sageTransactionTypes"
|
|
option-value="id"
|
|
option-label="transaction"
|
|
:filter-options="['id', 'transaction']"
|
|
>
|
|
<template #option="scope">
|
|
<QItem v-bind="scope.itemProps">
|
|
<QItemSection>
|
|
<QItemLabel>{{
|
|
scope.opt.transaction
|
|
}}</QItemLabel>
|
|
<QItemLabel>
|
|
{{ `#${scope.opt.id}` }}
|
|
</QItemLabel>
|
|
</QItemSection>
|
|
</QItem>
|
|
</template>
|
|
</VnSelectFilter>
|
|
</QItem>
|
|
<QItem>
|
|
{{ toCurrency(taxRate(props.row)) }}
|
|
</QItem>
|
|
<QItem>
|
|
<QInput
|
|
:label="t('Foreign value')"
|
|
class="full-width"
|
|
:class="{
|
|
'no-pointer-events': !isNotEuro(
|
|
invoiceIn.currency.code
|
|
),
|
|
}"
|
|
:disable="!isNotEuro(invoiceIn.currency.code)"
|
|
v-model="props.row.foreignValue"
|
|
/>
|
|
</QItem>
|
|
</QList>
|
|
</QCard>
|
|
</div>
|
|
</template>
|
|
</QTable>
|
|
</template>
|
|
</CrudModel>
|
|
<QDialog ref="newExpenseRef">
|
|
<QCard>
|
|
<QCardSection class="q-pb-none">
|
|
<QItem class="q-pa-none">
|
|
<span class="text-primary text-h6 full-width">
|
|
<QIcon name="edit" class="q-mr-xs" />
|
|
{{ t('New expense') }}
|
|
</span>
|
|
<QBtn icon="close" flat round dense v-close-popup />
|
|
</QItem>
|
|
</QCardSection>
|
|
<QCardSection class="q-pt-none">
|
|
<QItem>
|
|
<QInput :label="`${t('Code')}*`" v-model="newExpense.code" />
|
|
<QCheckbox
|
|
dense
|
|
size="sm"
|
|
:label="`${t('It\'s a withholding')}`"
|
|
v-model="newExpense.isWithheld"
|
|
/>
|
|
</QItem>
|
|
<QItem>
|
|
<QInput
|
|
:label="`${t('Descripction')}*`"
|
|
v-model="newExpense.description"
|
|
/>
|
|
</QItem>
|
|
</QCardSection>
|
|
<QCardActions class="justify-end">
|
|
<QBtn flat :label="t('globals.close')" color="primary" v-close-popup />
|
|
<QBtn :label="t('globals.save')" color="primary" @click="addExpense" />
|
|
</QCardActions>
|
|
</QCard>
|
|
</QDialog>
|
|
<QPageSticky position="bottom-right" :offset="[25, 25]">
|
|
<QBtn
|
|
color="primary"
|
|
icon="add"
|
|
size="lg"
|
|
round
|
|
@click="invoiceInFormRef.insert()"
|
|
/>
|
|
</QPageSticky>
|
|
</template>
|
|
<style lang="scss" scoped>
|
|
@media (max-width: $breakpoint-xs) {
|
|
.q-dialog {
|
|
.q-card {
|
|
&__section:not(:first-child) {
|
|
.q-item {
|
|
flex-direction: column;
|
|
|
|
.q-checkbox {
|
|
margin-top: 2rem;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.q-item {
|
|
min-height: 0;
|
|
}
|
|
</style>
|
|
<i18n>
|
|
es:
|
|
Expense: Gasto
|
|
Create expense: Crear gasto
|
|
Add tax: Crear gasto
|
|
Taxable base: Base imp.
|
|
Sage tax: Sage iva
|
|
Sage transaction: Sage transacción
|
|
Rate: Tasa
|
|
Foreign value: Divisa
|
|
New expense: Nuevo gasto
|
|
Code: Código
|
|
It's a withholding: Es una retención
|
|
Descripction: Descripción
|
|
The code can't be empty: El código no puede estar vacío
|
|
The description can't be empty: La descripción no puede estar vacía
|
|
The code have to be a number: El código debe ser un número.
|
|
</i18n>
|