salix-front/src/pages/InvoiceIn/Card/InvoiceInVat.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>