forked from verdnatura/salix-front
Entry buys import
This commit is contained in:
parent
596904c4f8
commit
64a4a800f7
|
@ -325,6 +325,15 @@ export default {
|
|||
buys: {
|
||||
groupingPrice: 'Grouping price',
|
||||
packingPrice: 'Packing price',
|
||||
reference: 'Reference',
|
||||
observations: 'Observations',
|
||||
item: 'Item',
|
||||
description: 'Description',
|
||||
size: 'Size',
|
||||
packing: 'Packing',
|
||||
grouping: 'Grouping',
|
||||
buyingValue: 'Buying value',
|
||||
packagingFk: 'Box',
|
||||
},
|
||||
},
|
||||
ticket: {
|
||||
|
|
|
@ -324,6 +324,15 @@ export default {
|
|||
buys: {
|
||||
groupingPrice: 'Precio grouping',
|
||||
packingPrice: 'Precio packing',
|
||||
reference: 'Referencia',
|
||||
observations: 'Observaciónes',
|
||||
item: 'Artículo',
|
||||
description: 'Descripción',
|
||||
size: 'Medida',
|
||||
packing: 'Packing',
|
||||
grouping: 'Grouping',
|
||||
buyingValue: 'Coste',
|
||||
packagingFk: 'Embalaje',
|
||||
},
|
||||
},
|
||||
ticket: {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
||||
|
@ -18,6 +18,7 @@ import useNotify from 'src/composables/useNotify.js';
|
|||
|
||||
const quasar = useQuasar();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
const stateStore = useStateStore();
|
||||
const { notify } = useNotify();
|
||||
|
@ -251,7 +252,7 @@ const deleteBuys = async () => {
|
|||
};
|
||||
|
||||
const importBuys = () => {
|
||||
// redirect to buys import view
|
||||
router.push({ name: 'EntryBuysImport' });
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
@ -0,0 +1,281 @@
|
|||
<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 VnSelectFilter from 'components/common/VnSelectFilter.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 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('entry.buys.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>
|
||||
<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">
|
||||
<div class="col">
|
||||
<QFile
|
||||
label="Standard"
|
||||
:multiple="false"
|
||||
v-model="importData.file"
|
||||
@update:model-value="onFileChange($event)"
|
||||
>
|
||||
<template #append>
|
||||
<QIcon name="vn:attach" class="cursor-pointer">
|
||||
<QTooltip>{{ t('Select a file') }}</QTooltip>
|
||||
</QIcon>
|
||||
</template>
|
||||
</QFile>
|
||||
</div>
|
||||
</VnRow>
|
||||
<div v-if="importData.file">
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnInput
|
||||
:label="t('entry.buys.reference')"
|
||||
v-model="importData.ref"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<VnInput
|
||||
:label="t('entry.buys.observations')"
|
||||
v-model="importData.observation"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<QTable
|
||||
:columns="columns"
|
||||
:rows="importData.buys"
|
||||
:pagination="{ rowsPerPage: 0 }"
|
||||
hide-pagination
|
||||
>
|
||||
<template #body-cell-item="{ row, col }">
|
||||
<QTd auto-width>
|
||||
<VnSelectFilter
|
||||
v-model="row[col.field]"
|
||||
:options="col.options"
|
||||
:option-value="col.optionValue"
|
||||
:option-label="col.optionLabel"
|
||||
hide-selected
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>
|
||||
{{ scope.opt?.id }} -
|
||||
{{ scope.opt?.name }}
|
||||
</QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelectFilter>
|
||||
</QTd>
|
||||
</template>
|
||||
<template #body-cell-packagingFk="{ row, col }">
|
||||
<QTd auto-width>
|
||||
<VnSelectFilter
|
||||
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:
|
||||
Select a file: Selecciona un fichero
|
||||
Some of the imported buys does not have an item: Algunas de las compras importadas no tienen un artículo
|
||||
</i18n>
|
|
@ -72,6 +72,11 @@ export default {
|
|||
},
|
||||
component: () => import('src/pages/Entry/Card/EntryBuys.vue'),
|
||||
},
|
||||
{
|
||||
path: 'buys/import',
|
||||
name: 'EntryBuysImport',
|
||||
component: () => import('src/pages/Entry/Card/EntryBuysImport.vue'),
|
||||
},
|
||||
{
|
||||
path: 'notes',
|
||||
name: 'EntryNotes',
|
||||
|
|
Loading…
Reference in New Issue