288 lines
9.5 KiB
Vue
288 lines
9.5 KiB
Vue
<script setup>
|
|
import { ref, computed } from 'vue';
|
|
import { useRoute, useRouter } from 'vue-router';
|
|
import { useI18n } from 'vue-i18n';
|
|
|
|
import VnInput from 'src/components/common/VnInput.vue';
|
|
import VnRow from 'components/ui/VnRow.vue';
|
|
import FetchData from 'components/FetchData.vue';
|
|
import VnSelect from 'components/common/VnSelect.vue';
|
|
import VnSelectDialog from 'src/components/common/VnSelectDialog.vue';
|
|
import FilterItemForm from 'src/components/FilterItemForm.vue';
|
|
|
|
import { useStateStore } from 'stores/useStateStore';
|
|
import axios from 'axios';
|
|
import useNotify from 'src/composables/useNotify.js';
|
|
import { toCurrency } from 'filters/index';
|
|
|
|
const stateStore = useStateStore();
|
|
const route = useRoute();
|
|
const router = useRouter();
|
|
const { t } = useI18n();
|
|
const { notify } = useNotify();
|
|
|
|
const importData = ref({
|
|
file: null,
|
|
invoice: null,
|
|
buys: [],
|
|
observation: null,
|
|
ref: null,
|
|
});
|
|
|
|
const inputFileRef = ref(null);
|
|
const lastItemBuysOptions = ref([]);
|
|
const packagingsOptions = ref([]);
|
|
|
|
const columns = computed(() => [
|
|
{
|
|
label: t('entry.buys.item'),
|
|
name: 'item',
|
|
field: 'itemFk',
|
|
options: lastItemBuysOptions.value,
|
|
optionValue: 'id',
|
|
optionLabel: 'name',
|
|
align: 'left',
|
|
},
|
|
{
|
|
label: t('globals.description'),
|
|
name: 'description',
|
|
field: 'description',
|
|
align: 'left',
|
|
},
|
|
{
|
|
label: t('entry.buys.size'),
|
|
name: 'size',
|
|
field: 'size',
|
|
align: 'left',
|
|
},
|
|
{
|
|
label: t('entry.buys.packing'),
|
|
name: 'packing',
|
|
field: 'packing',
|
|
align: 'left',
|
|
},
|
|
{
|
|
label: t('entry.buys.grouping'),
|
|
name: 'grouping',
|
|
field: 'grouping',
|
|
align: 'left',
|
|
},
|
|
{
|
|
label: t('entry.buys.buyingValue'),
|
|
name: 'buyingValue',
|
|
field: 'buyingValue',
|
|
align: 'left',
|
|
format: (val) => toCurrency(val),
|
|
},
|
|
{
|
|
label: t('entry.buys.packagingFk'),
|
|
name: 'packagingFk',
|
|
field: 'packagingFk',
|
|
options: packagingsOptions.value,
|
|
optionValue: 'id',
|
|
optionLabel: 'id',
|
|
align: 'left',
|
|
},
|
|
]);
|
|
|
|
const onFileChange = (e) => {
|
|
importData.value.file = e;
|
|
const reader = new FileReader();
|
|
reader.onload = (e) => fillData(e.target.result);
|
|
reader.readAsText(importData.value.file);
|
|
};
|
|
|
|
const fillData = async (rawData) => {
|
|
const data = JSON.parse(rawData);
|
|
const [invoice] = data.invoices;
|
|
|
|
importData.value.observation = invoice.tx_awb;
|
|
const companyName = invoice.tx_company;
|
|
const boxes = invoice.boxes;
|
|
const buys = [];
|
|
|
|
for (let box of boxes) {
|
|
const boxVolume = box.nu_length * box.nu_width * box.nu_height;
|
|
for (let product of box.products) {
|
|
const packing = product.nu_stems_bunch * product.nu_bunches;
|
|
buys.push({
|
|
description: product.nm_product,
|
|
companyName: companyName,
|
|
size: product.nu_length,
|
|
packing: packing,
|
|
grouping: product.nu_stems_bunch,
|
|
buyingValue: parseFloat(product.mny_rate_stem),
|
|
volume: boxVolume,
|
|
});
|
|
}
|
|
}
|
|
|
|
const boxesId = boxes.map((box) => box.id_box);
|
|
importData.value.ref = boxesId.join(', ');
|
|
await fetchBuys(buys);
|
|
};
|
|
|
|
const fetchBuys = async (buys) => {
|
|
try {
|
|
const params = { buys };
|
|
const { data } = await axios.post(
|
|
`Entries/${route.params.id}/importBuysPreview`,
|
|
params
|
|
);
|
|
importData.value.buys = data;
|
|
} catch (err) {
|
|
console.error('Error fetching buys');
|
|
}
|
|
};
|
|
|
|
const onSubmit = async () => {
|
|
try {
|
|
const params = importData.value;
|
|
const hasAnyEmptyRow = params.buys.some((buy) => {
|
|
return buy.itemFk === null;
|
|
});
|
|
|
|
if (hasAnyEmptyRow) {
|
|
notify(t('Some of the imported buys does not have an item'), 'negative');
|
|
return;
|
|
}
|
|
|
|
await axios.post(`Entries/${route.params.id}/importBuys`, params);
|
|
notify('globals.dataSaved', 'positive');
|
|
redirectToBuysView();
|
|
} catch (err) {
|
|
console.error('Error importing buys', err);
|
|
}
|
|
};
|
|
|
|
const redirectToBuysView = () => {
|
|
router.push({ name: 'EntryBuys' });
|
|
};
|
|
</script>
|
|
<template>
|
|
<FetchData
|
|
:url="`Entries/${route.params.id}/lastItemBuys`"
|
|
:filter="{ fields: ['id', 'name'] }"
|
|
order="id DESC"
|
|
@on-fetch="(data) => (lastItemBuysOptions = data)"
|
|
auto-load
|
|
/>
|
|
<FetchData
|
|
url="Packagings"
|
|
:filter="{ fields: ['id'], where: { isBox: true } }"
|
|
order="id ASC"
|
|
@on-fetch="(data) => (packagingsOptions = data)"
|
|
auto-load
|
|
/>
|
|
<QForm @submit="onSubmit()">
|
|
<Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
|
|
<div>
|
|
<QBtnGroup push class="q-gutter-x-sm">
|
|
<QBtn
|
|
:label="t('globals.cancel')"
|
|
color="primary"
|
|
icon="restart_alt"
|
|
flat
|
|
@click="redirectToBuysView()"
|
|
/>
|
|
<QBtn
|
|
:label="t('globals.save')"
|
|
color="primary"
|
|
icon="save"
|
|
type="submit"
|
|
:disable="!importData.file"
|
|
@click="onSubmit()"
|
|
/>
|
|
</QBtnGroup>
|
|
</div>
|
|
</Teleport>
|
|
<QCard class="q-pa-lg">
|
|
<VnRow class="row q-gutter-md q-mb-md">
|
|
<QFile
|
|
ref="inputFileRef"
|
|
:label="t('entry.buys.file')"
|
|
v-model="importData.file"
|
|
:multiple="false"
|
|
accept=".json"
|
|
@update:model-value="onFileChange($event)"
|
|
class="required"
|
|
>
|
|
<template #append>
|
|
<QIcon
|
|
name="vn:attach"
|
|
class="cursor-pointer"
|
|
@click="inputFileRef.pickFiles()"
|
|
>
|
|
<QTooltip>{{ t('globals.selectFile') }}</QTooltip>
|
|
</QIcon>
|
|
</template>
|
|
</QFile>
|
|
</VnRow>
|
|
<div v-if="importData.file">
|
|
<VnRow class="row q-gutter-md q-mb-md">
|
|
<VnInput
|
|
:label="t('entry.buys.reference')"
|
|
v-model="importData.ref"
|
|
/>
|
|
</VnRow>
|
|
<VnRow class="row q-gutter-md q-mb-md">
|
|
<VnInput
|
|
:label="t('entry.buys.observations')"
|
|
v-model="importData.observation"
|
|
/>
|
|
</VnRow>
|
|
<VnRow>
|
|
<QTable :columns="columns" :rows="importData.buys">
|
|
<template #body-cell-item="{ row, col }">
|
|
<QTd auto-width>
|
|
<VnSelectDialog
|
|
v-model="row[col.field]"
|
|
:options="col.options"
|
|
:option-value="col.optionValue"
|
|
:option-label="col.optionLabel"
|
|
hide-selected
|
|
action-icon="filter_alt"
|
|
>
|
|
<template #form>
|
|
<FilterItemForm
|
|
:url="`Entries/${route.params.id}/lastItemBuys`"
|
|
@item-selected="row[col.field] = $event"
|
|
/>
|
|
</template>
|
|
<template #option="scope">
|
|
<QItem v-bind="scope.itemProps">
|
|
<QItemSection>
|
|
<QItemLabel>
|
|
{{ scope.opt?.id }} -
|
|
{{ scope.opt?.name }}
|
|
</QItemLabel>
|
|
</QItemSection>
|
|
</QItem>
|
|
</template>
|
|
</VnSelectDialog>
|
|
</QTd>
|
|
</template>
|
|
<template #body-cell-packagingFk="{ row, col }">
|
|
<QTd auto-width>
|
|
<VnSelect
|
|
v-model="row[col.field]"
|
|
:options="col.options"
|
|
:option-value="col.optionValue"
|
|
:option-label="col.optionLabel"
|
|
hide-selected
|
|
/>
|
|
</QTd>
|
|
</template>
|
|
</QTable>
|
|
</VnRow>
|
|
</div>
|
|
</QCard>
|
|
</QForm>
|
|
</template>
|
|
|
|
<i18n>
|
|
es:
|
|
globals.selectFile: Selecciona un fichero
|
|
Some of the imported buys does not have an item: Algunas de las compras importadas no tienen un artículo
|
|
</i18n>
|