458 lines
18 KiB
Vue
458 lines
18 KiB
Vue
<script setup>
|
|
import { ref, computed } from 'vue';
|
|
import { useRoute } from 'vue-router';
|
|
import { useI18n } from 'vue-i18n';
|
|
import { useArrayData } from 'src/composables/useArrayData';
|
|
import { getTotal } from 'src/composables/getTotal';
|
|
import { toCurrency } from 'src/filters';
|
|
import FetchData from 'src/components/FetchData.vue';
|
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
|
import CrudModel from 'src/components/CrudModel.vue';
|
|
import VnInputNumber from 'src/components/common/VnInputNumber.vue';
|
|
import VnSelectDialog from 'src/components/common/VnSelectDialog.vue';
|
|
import CreateNewExpenseForm from 'src/components/CreateNewExpenseForm.vue';
|
|
import { getExchange } from 'src/composables/getExchange';
|
|
import { useAccountShortToStandard } from 'src/composables/useAccountShortToStandard';
|
|
|
|
const { t } = useI18n();
|
|
|
|
const arrayData = useArrayData();
|
|
const route = useRoute();
|
|
const invoiceIn = computed(() => arrayData.store.data);
|
|
const currency = computed(() => invoiceIn.value?.currency?.code);
|
|
const expenses = ref([]);
|
|
const sageTaxTypes = ref([]);
|
|
const sageTransactionTypes = ref([]);
|
|
const rowsSelected = ref([]);
|
|
const invoiceInFormRef = ref();
|
|
|
|
defineProps({
|
|
actionIcon: {
|
|
type: String,
|
|
default: 'add',
|
|
},
|
|
});
|
|
|
|
const columns = computed(() => [
|
|
{
|
|
name: 'expense',
|
|
label: t('Expense'),
|
|
field: (row) => row.expenseFk,
|
|
options: expenses.value,
|
|
model: 'expenseFk',
|
|
optionValue: 'id',
|
|
optionLabel: (row) => `${row.id}: ${row.name}`,
|
|
sortable: true,
|
|
align: 'left',
|
|
},
|
|
{
|
|
name: 'taxablebase',
|
|
label: t('Taxable base'),
|
|
field: (row) => row.taxableBase,
|
|
model: 'taxableBase',
|
|
sortable: true,
|
|
align: 'left',
|
|
},
|
|
{
|
|
name: 'sageiva',
|
|
label: t('Sage iva'),
|
|
field: (row) => row.taxTypeSageFk,
|
|
options: sageTaxTypes.value,
|
|
model: 'taxTypeSageFk',
|
|
optionValue: 'id',
|
|
optionLabel: (row) => `${row.id}: ${row.vat}`,
|
|
sortable: true,
|
|
align: 'left',
|
|
},
|
|
{
|
|
name: 'sagetransaction',
|
|
label: t('Sage transaction'),
|
|
field: (row) => row.transactionTypeSageFk,
|
|
options: sageTransactionTypes.value,
|
|
model: 'transactionTypeSageFk',
|
|
optionValue: 'id',
|
|
optionLabel: (row) => `${row.id}: ${row.transaction}`,
|
|
sortable: true,
|
|
align: 'left',
|
|
},
|
|
{
|
|
name: 'rate',
|
|
label: t('Rate'),
|
|
sortable: true,
|
|
field: (row) => taxRate(row, row.taxTypeSageFk),
|
|
align: 'left',
|
|
},
|
|
{
|
|
name: 'foreignvalue',
|
|
label: t('Foreign value'),
|
|
sortable: true,
|
|
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).toFixed(2);
|
|
}
|
|
|
|
function autocompleteExpense(evt, row, col) {
|
|
const val = evt.target.value;
|
|
if (!val) return;
|
|
|
|
const param = isNaN(val) ? row[col.model] : val;
|
|
const lookup = expenses.value.find(
|
|
({ id }) => id == useAccountShortToStandard(param)
|
|
);
|
|
|
|
if (lookup) row[col.model] = lookup;
|
|
}
|
|
</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"
|
|
:go-to="`/invoice-in/${$route.params.id}/due-day`"
|
|
>
|
|
<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>
|
|
<VnSelectDialog
|
|
v-model="row[col.model]"
|
|
:options="col.options"
|
|
:option-value="col.optionValue"
|
|
:option-label="col.optionLabel"
|
|
:filter-options="['id', 'name']"
|
|
:tooltip="t('Create a new expense')"
|
|
@keydown.tab="autocompleteExpense($event, row, col)"
|
|
>
|
|
<template #option="scope">
|
|
<QItem v-bind="scope.itemProps">
|
|
{{ `${scope.opt.id}: ${scope.opt.name}` }}
|
|
</QItem>
|
|
</template>
|
|
<template #form>
|
|
<CreateNewExpenseForm
|
|
@on-data-saved="$refs.expensesRef.fetch()"
|
|
/>
|
|
</template>
|
|
</VnSelectDialog>
|
|
</QTd>
|
|
</template>
|
|
<template #body-cell-taxablebase="{ row }">
|
|
<QTd>
|
|
<VnInputNumber
|
|
clear-icon="close"
|
|
v-model="row.taxableBase"
|
|
clearable
|
|
/>
|
|
</QTd>
|
|
</template>
|
|
<template #body-cell-sageiva="{ row, col }">
|
|
<QTd>
|
|
<VnSelect
|
|
v-model="row[col.model]"
|
|
:options="col.options"
|
|
:option-value="col.optionValue"
|
|
:option-label="col.optionLabel"
|
|
:filter-options="['id', 'vat']"
|
|
data-cy="vat-sageiva"
|
|
>
|
|
<template #option="scope">
|
|
<QItem v-bind="scope.itemProps">
|
|
<QItemSection>
|
|
<QItemLabel>{{ scope.opt.vat }}</QItemLabel>
|
|
<QItemLabel>
|
|
{{ `#${scope.opt.id}` }}
|
|
</QItemLabel>
|
|
</QItemSection>
|
|
</QItem>
|
|
</template>
|
|
</VnSelect>
|
|
</QTd>
|
|
</template>
|
|
<template #body-cell-sagetransaction="{ row, col }">
|
|
<QTd>
|
|
<VnSelect
|
|
v-model="row[col.model]"
|
|
:options="col.options"
|
|
:option-value="col.optionValue"
|
|
:option-label="col.optionLabel"
|
|
: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>
|
|
</VnSelect>
|
|
</QTd>
|
|
</template>
|
|
<template #body-cell-foreignvalue="{ row }">
|
|
<QTd>
|
|
<VnInputNumber
|
|
:class="{
|
|
'no-pointer-events': !isNotEuro(currency),
|
|
}"
|
|
:disable="!isNotEuro(currency)"
|
|
v-model="row.foreignValue"
|
|
@update:model-value="
|
|
async (val) => {
|
|
if (!isNotEuro(currency)) return;
|
|
row.taxableBase = await getExchange(
|
|
val,
|
|
row.currencyFk,
|
|
invoiceIn.issued
|
|
);
|
|
}
|
|
"
|
|
/>
|
|
</QTd>
|
|
</template>
|
|
<template #bottom-row>
|
|
<QTr class="bg">
|
|
<QTd />
|
|
<QTd />
|
|
<QTd>
|
|
{{ getTotal(rows, 'taxableBase', { currency: 'default' }) }}
|
|
</QTd>
|
|
<QTd />
|
|
<QTd />
|
|
<QTd>
|
|
{{
|
|
getTotal(rows, null, { cb: taxRate, currency: 'default' })
|
|
}}</QTd
|
|
>
|
|
<QTd>
|
|
<template v-if="isNotEuro(invoiceIn.currency.code)">
|
|
{{
|
|
getTotal(rows, 'foreignValue', {
|
|
currency: invoiceIn.currency.code,
|
|
})
|
|
}}
|
|
</template>
|
|
</QTd>
|
|
</QTr>
|
|
</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>
|
|
<VnSelectDialog
|
|
:label="t('Expense')"
|
|
class="full-width"
|
|
v-model="props.row['expenseFk']"
|
|
:options="expenses"
|
|
option-value="id"
|
|
:option-label="(row) => `${row.id}:${row.name}`"
|
|
:filter-options="['id', 'name']"
|
|
:tooltip="t('Create a new expense')"
|
|
>
|
|
<template #option="scope">
|
|
<QItem v-bind="scope.itemProps">
|
|
{{ `${scope.opt.id}: ${scope.opt.name}` }}
|
|
</QItem>
|
|
</template>
|
|
<template #form>
|
|
<CreateNewExpenseForm />
|
|
</template>
|
|
</VnSelectDialog>
|
|
</QItem>
|
|
<QItem>
|
|
<VnInputNumber
|
|
:label="t('Taxable base')"
|
|
:class="{
|
|
'no-pointer-events': isNotEuro(currency),
|
|
}"
|
|
class="full-width"
|
|
:disable="isNotEuro(currency)"
|
|
clear-icon="close"
|
|
v-model="props.row.taxableBase"
|
|
clearable
|
|
/>
|
|
</QItem>
|
|
<QItem>
|
|
<VnSelect
|
|
:label="t('Sage iva')"
|
|
class="full-width"
|
|
v-model="props.row['taxTypeSageFk']"
|
|
:options="sageTaxTypes"
|
|
option-value="id"
|
|
:option-label="(row) => `${row.id}:${row.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>
|
|
</VnSelect>
|
|
</QItem>
|
|
<QItem>
|
|
<VnSelect
|
|
class="full-width"
|
|
v-model="props.row['transactionTypeSageFk']"
|
|
:options="sageTransactionTypes"
|
|
option-value="id"
|
|
:option-label="
|
|
(row) => `${row.id}:${row.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>
|
|
</VnSelect>
|
|
</QItem>
|
|
<QItem>
|
|
{{ toCurrency(taxRate(props.row), currency) }}
|
|
</QItem>
|
|
<QItem>
|
|
<VnInputNumber
|
|
:label="t('Foreign value')"
|
|
class="full-width"
|
|
:class="{
|
|
'no-pointer-events': !isNotEuro(currency),
|
|
}"
|
|
:disable="!isNotEuro(currency)"
|
|
v-model="props.row.foreignValue"
|
|
/>
|
|
</QItem>
|
|
</QList>
|
|
</QCard>
|
|
</div>
|
|
</template>
|
|
</QTable>
|
|
</template>
|
|
</CrudModel>
|
|
<QPageSticky position="bottom-right" :offset="[25, 25]">
|
|
<QBtn
|
|
color="primary"
|
|
icon="add"
|
|
size="lg"
|
|
shortcut="+"
|
|
round
|
|
@click="invoiceInFormRef.insert()"
|
|
>
|
|
<QTooltip>{{ t('Add tax') }}</QTooltip>
|
|
</QBtn>
|
|
</QPageSticky>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
.bg {
|
|
background-color: var(--vn-light-gray);
|
|
}
|
|
@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;
|
|
}
|
|
.default-icon {
|
|
cursor: pointer;
|
|
border-radius: 50px;
|
|
background-color: $primary;
|
|
}
|
|
</style>
|
|
<i18n>
|
|
es:
|
|
Expense: Gasto
|
|
Create a new expense: Crear nuevo gasto
|
|
Add tax: Crear gasto
|
|
Taxable base: Base imp.
|
|
Sage tax: Sage iva
|
|
Sage transaction: Sage transacción
|
|
Rate: Tasa
|
|
Foreign value: Divisa
|
|
</i18n>
|