salix-front/src/pages/Travel/ExtraCommunity.vue

511 lines
15 KiB
Vue

<script setup>
import { onMounted, ref, computed } from 'vue';
import { QBtn, QField, QPopupEdit } from 'quasar';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue';
import TravelDescriptorProxy from 'src/pages/Travel/Card/TravelDescriptorProxy.vue';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import ExtraCommunityFilter from './ExtraCommunityFilter.vue';
import VnInput from 'src/components/common/VnInput.vue';
import EntryDescriptorProxy from '../Entry/Card/EntryDescriptorProxy.vue';
import { useStateStore } from 'stores/useStateStore';
import { toCurrency } from 'src/filters';
import { useArrayData } from 'composables/useArrayData';
import { toDate } from 'src/filters';
import { usePrintService } from 'composables/usePrintService';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import axios from 'axios';
const router = useRouter();
const stateStore = useStateStore();
const { t } = useI18n();
const { openReport } = usePrintService();
const shippedFrom = ref(Date.vnNew());
const landedTo = ref(Date.vnNew());
const arrayData = useArrayData('ExtraCommunity', {
url: 'Travels/extraCommunityFilter',
limit: 0,
order: [
'landed ASC',
'shipped ASC',
'travelFk',
'loadPriority',
'agencyModeFk',
'supplierName',
'evaNotes',
],
userParams: {
continent: 'AM',
shippedFrom: shippedFrom.value,
landedTo: landedTo.value,
},
});
const rows = computed(() => arrayData.store.data || []);
const draggedRowIndex = ref(null);
const targetRowIndex = ref(null);
const entryRowIndex = ref(null);
const draggedEntry = ref(null);
const tableColumnComponents = {
id: {
component: QBtn,
attrs: { flat: true, color: 'primary' },
},
cargoSupplierNickname: {
component: QBtn,
attrs: { flat: true, color: 'primary', dense: true },
},
agencyModeName: {
component: 'span',
attrs: {},
},
invoiceAmount: {
component: 'span',
attrs: {},
},
ref: {
component: QField,
attrs: { readonly: true, dense: true, class: 'cursor-pointer' },
},
stickers: {
component: 'span',
attrs: {},
},
kg: {
component: QField,
attrs: { readonly: true, dense: true, class: 'cursor-pointer' },
},
loadedKg: {
component: 'span',
attrs: {},
},
volumeKg: {
component: 'span',
attrs: {},
},
warehouseOutName: {
component: 'span',
attrs: {},
},
shipped: {
component: 'span',
attrs: {},
},
warehouseInName: {
component: 'span',
attrs: {},
},
landed: {
component: 'span',
attrs: {},
},
};
const columns = computed(() => [
{
label: 'id',
field: 'id',
name: 'id',
align: 'center',
showValue: true,
sortable: true,
},
{
label: t('supplier.pageTitles.supplier'),
field: 'cargoSupplierNickname',
name: 'cargoSupplierNickname',
align: 'left',
showValue: true,
sortable: true,
},
{
label: t('globals.agency'),
field: 'agencyModeName',
name: 'agencyModeName',
align: 'left',
showValue: true,
sortable: true,
},
{
label: t('globals.amount'),
name: 'invoiceAmount',
field: 'entries',
align: 'left',
showValue: true,
sortable: true,
format: (value) =>
toCurrency(
value
? value.reduce((sum, entry) => {
return sum + (entry.invoiceAmount || 0);
}, 0)
: 0
),
},
{
label: t('globals.reference'),
field: 'ref',
name: 'ref',
align: 'left',
showValue: false,
sortable: true,
},
{
label: t('globals.packages'),
field: 'stickers',
name: 'stickers',
align: 'left',
showValue: true,
sortable: true,
},
{
label: t('kg'),
field: 'kg',
name: 'kg',
align: 'left',
showValue: false,
sortable: true,
},
{
label: t('physicKg'),
field: 'loadedKg',
name: 'loadedKg',
align: 'left',
showValue: true,
sortable: true,
},
{
label: 'KG Vol.',
field: 'volumeKg',
name: 'volumeKg',
align: 'left',
showValue: true,
sortable: true,
},
{
label: t('globals.wareHouseOut'),
field: 'warehouseOutName',
name: 'warehouseOutName',
align: 'left',
showValue: true,
sortable: true,
},
{
label: t('shipped'),
field: 'shipped',
name: 'shipped',
align: 'left',
showValue: true,
sortable: true,
format: (value) => toDate(value.substring(0, 10)),
},
{
label: t('globals.wareHouseIn'),
field: 'warehouseInName',
name: 'warehouseInName',
align: 'left',
showValue: true,
sortable: true,
},
{
label: t('landed'),
field: 'landed',
name: 'landed',
align: 'left',
showValue: true,
sortable: true,
format: (value) => toDate(value.substring(0, 10)),
},
]);
async function getData() {
await arrayData.fetch({ append: false });
}
const openReportPdf = () => {
const params = {
...arrayData.store.userParams,
limit: arrayData.store.limit,
};
openReport('Travels/extra-community-pdf', params);
};
const saveFieldValue = async (val, field, index) => {
try {
const id = rows.value[index].id;
const params = { [field]: val };
await axios.patch(`Travels/${id}`, params);
} catch (err) {
console.error('Error updating travel');
}
};
const navigateToTravelId = (id) => {
router.push({ path: `/travel/${id}` });
};
const stopEventPropagation = (event, col) => {
if (!['ref', 'id', 'cargoSupplierNickname', 'kg'].includes(col.name)) return;
event.preventDefault();
event.stopPropagation();
};
onMounted(async () => {
stateStore.rightDrawer = true;
shippedFrom.value.setDate(shippedFrom.value.getDate() - 2);
shippedFrom.value.setHours(0, 0, 0, 0);
landedTo.value.setDate(landedTo.value.getDate() + 7);
landedTo.value.setHours(23, 59, 59, 59);
await getData();
});
const handleDragStart = (event, rowIndex, entryIndex) => {
draggedRowIndex.value = rowIndex;
entryRowIndex.value = entryIndex;
event.dataTransfer.effectAllowed = 'move';
};
const handleDragEnter = (_, targetIndex) => {
targetRowIndex.value = targetIndex;
};
const handleDragOver = (e) => {
e.preventDefault();
};
const saveRowDrop = async (targetRowIndex) => {
const entryId = draggedEntry.value.id;
const travelId = rows.value[targetRowIndex].id;
await axios.patch(`Entries/${entryId}`, { travelFk: travelId });
};
const moveRow = async (draggedRowIndex, targetRowIndex, entryIndex) => {
try {
if (draggedRowIndex === targetRowIndex) return;
// Remover entry de la row original
draggedEntry.value = rows.value[draggedRowIndex].entries.splice(entryIndex, 1)[0];
//Si la row de destino por alguna razón no tiene la propiedad entry la creamos
if (!rows.value[targetRowIndex].entries) rows.value[targetRowIndex].entries = [];
// Añadir entry a la row de destino
rows.value[targetRowIndex].entries.push(draggedEntry.value);
await saveRowDrop(targetRowIndex);
} catch (err) {
cleanDragAndDropData();
console.error('Error moving row', err);
}
};
const handleDrop = () => {
if (!draggedRowIndex.value && !targetRowIndex.value) return;
moveRow(draggedRowIndex.value, targetRowIndex.value, entryRowIndex.value);
cleanDragAndDropData();
};
const cleanDragAndDropData = () => {
draggedRowIndex.value = null;
targetRowIndex.value = null;
entryRowIndex.value = null;
draggedEntry.value = null;
};
</script>
<template>
<template v-if="stateStore.isHeaderMounted()">
<Teleport to="#searchbar">
<VnSearchbar
data-key="ExtraCommunity"
:limit="20"
:label="t('searchExtraCommunity')"
/>
</Teleport>
</template>
<VnSubToolbar class="bg-vn-dark justify-end">
<template #st-actions>
<QBtn
color="primary"
icon-right="picture_as_pdf"
no-caps
@click="openReportPdf()"
>
<QTooltip>
{{ t('Open as PDF') }}
</QTooltip>
</QBtn>
</template>
</VnSubToolbar>
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
<QScrollArea class="fit text-grey-8">
<ExtraCommunityFilter data-key="ExtraCommunity" />
</QScrollArea>
</QDrawer>
<QPage class="column items-center q-pa-md">
<QTable
:rows="rows"
:columns="columns"
hide-bottom
row-key="clientId"
:pagination="{ rowsPerPage: 0 }"
class="full-width"
table-style="user-select: none;"
>
<template #body="props">
<QTr
:props="props"
class="cursor-pointer bg-vn-primary-row"
@click="navigateToTravelId(props.row.id)"
@dragenter="handleDragEnter($event, props.rowIndex)"
@dragover="handleDragOver($event)"
@drop="handleDrop()"
>
<QTd
v-for="col in props.cols"
:key="col.name"
:props="props"
@click="stopEventPropagation($event, col)"
:class="{ 'dashed-border': targetRowIndex === props.rowIndex }"
>
<component
:is="tableColumnComponents[col.name].component"
v-bind="tableColumnComponents[col.name].attrs"
>
<!-- Editable 'ref' and 'kg' QField slot -->
<template
v-if="col.name === 'ref' || col.name === 'kg'"
#control
>
<div
class="self-center full-width no-outline"
tabindex="0"
>
{{ col.value }}
</div>
<QPopupEdit
:key="col.name"
v-model="col.value"
label-set="Save"
label-cancel="Close"
>
<VnInput
v-model="rows[props.pageIndex][col.field]"
dense
autofocus
@keyup.enter="
saveFieldValue(
rows[props.pageIndex][col.field],
col.field,
props.rowIndex
)
"
/>
</QPopupEdit>
</template>
<template v-if="col.showValue">
{{ col.value }}
</template>
<!-- Main Row Descriptors -->
<TravelDescriptorProxy
v-if="col.name === 'id'"
:id="props.row.id"
/>
<SupplierDescriptorProxy
v-if="col.name === 'cargoSupplierNickname'"
:id="props.row.cargoSupplierFk"
/>
</component>
</QTd>
</QTr>
<QTr
v-for="(entry, index) in props.row.entries"
:key="index"
:props="props"
class="bg-vn-secondary-row cursor-pointer"
@dragstart="handleDragStart($event, props.rowIndex, index)"
@dragenter="handleDragEnter($event, props.rowIndex)"
@dragover="handleDragOver($event)"
@drop="handleDrop()"
:draggable="true"
:class="{
'dragged-row':
entryRowIndex === index && props.rowIndex === draggedRowIndex,
}"
>
<template v-if="entry">
<QTd class="row justify-center">
<QBtn flat color="primary">{{ entry.id }} </QBtn>
<EntryDescriptorProxy :id="entry.id" />
</QTd>
<QTd>
<QBtn flat color="primary" dense>{{
entry.supplierName
}}</QBtn>
<SupplierDescriptorProxy :id="entry.supplierFk" />
</QTd>
<QTd></QTd>
<QTd
><span>{{ toCurrency(entry.invoiceAmount) }}</span></QTd
>
<QTd
><span>{{ entry.reference }}</span></QTd
>
<QTd
><span>{{ entry.stickers }}</span></QTd
>
<QTd></QTd>
<QTd
><span>{{ entry.loadedkg }}</span></QTd
>
<QTd
><span>{{ entry.volumeKg }}</span></QTd
>
<QTd></QTd>
<QTd></QTd>
<QTd></QTd>
<QTd></QTd>
</template>
</QTr>
</template>
</QTable>
</QPage>
</template>
<style scoped lang="scss">
.dashed-border {
border-bottom: 1px dashed #ccc !important;
border-top: 1px dashed #ccc !important;
}
.dragged-row {
background-color: $secondary;
}
</style>
<i18n>
en:
searchExtraCommunity: Search for extra community shipping
kg: BI. KG
physicKg: Phy. KG
shipped: W. shipped
landed: W. landed
es:
searchExtraCommunity: Buscar por envío extra comunitario
kg: KG Bloq.
physicKg: KG físico
shipped: F. envío
landed: F. llegada
Open as PDF: Abrir como PDF
</i18n>