0
0
Fork 0
This commit is contained in:
William Buezas 2024-06-11 22:40:07 -03:00
parent e9051f1ee8
commit 89f6873b03
4 changed files with 625 additions and 1 deletions

View File

@ -0,0 +1,150 @@
<script setup>
import { ref, onMounted, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { toCurrency } from 'src/filters';
import VnInput from 'src/components/common/VnInput.vue';
import axios from 'axios';
import useNotify from 'src/composables/useNotify.js';
const $props = defineProps({
id: {
type: Number,
required: true,
},
type: {
type: String,
default: 'price', // 'discount' or 'price
},
});
const { t } = useI18n();
const { notify } = useNotify();
const edit = ref({});
const usesMana = ref(null);
const inputLabel = computed(() => ($props.type === 'price' ? t('Price') : t('Discount')));
const getUsesMana = async () => {
const { data } = await axios.get('Sales/usesMana');
usesMana.value = data;
};
const getMana = async () => {
const { data } = await axios.get(`Tickets/${$props.id}/getSalesPersonMana`);
edit.value.mana = data;
await getUsesMana();
console.log('edit', edit.value);
// this.$.$applyAsync(() => {
// this.$.editDiscount.relocate();
// this.$.editPricePopover.relocate();
// });
};
const updatePrice = async () => {
try {
const sale = edit.value.sale;
const newPrice = edit.value.price;
if (newPrice != null && newPrice != sale.price) {
await axios.post(`Sales/${sale.id}/updatePrice`, { newPrice });
sale.price = newPrice;
edit.value = null;
notify('globals.dataSaved', 'positive');
// this.$http
// .post(query, { newPrice })
// .then((res) => {
// sale.price = res.data.price;
// this.edit = null;
// this.vnApp.showSuccess(this.$t('Data saved!'));
// })
// .finally(() => this.resetChanges());
}
await getMana();
} catch (err) {
console.error('Error updating price', err);
}
};
const getNewPrice = () => {
if (edit.value.sale) {
const sale = edit.value.sale;
let newDiscount = sale.discount;
let newPrice = edit.value.price || sale.price;
if (edit.value.discount != null) newDiscount = edit.value.discount;
if (edit.value.price != null) newPrice = edit.value.price;
const price = sale.quantity * newPrice;
const discount = (newDiscount * price) / 100;
return price - discount;
}
return 0;
};
onMounted(async () => {
await getMana();
});
</script>
<template>
<QPopupProxy>
<div class="container">
<QSpinner v-if="!edit.mana" color="orange" size="md" />
<div v-else>
<div class="header">Mana: {{ toCurrency(edit.mana) }}</div>
<div class="q-pa-md">
<VnInput
v-model.number="edit.price"
:label="inputLabel"
@change="updatePrice(row)"
/>
<div class="column items-center q-mt-lg">
<span class="text-primary">{{ t('New price') }}</span>
<span class="text-subtitle1">{{
toCurrency(getNewPrice())
}}</span>
</div>
</div>
</div>
<div class="row">
<QBtn color="primary" class="no-border-radius" dense style="width: 50%">
{{ t('globals.cancel') }}
</QBtn>
<QBtn color="primary" class="no-border-radius" dense style="width: 50%">
{{ t('globals.save') }}
</QBtn>
</div>
</div>
</QPopupProxy>
</template>
<style lang="scss" scoped>
.container {
background-color: $dark;
width: 230px;
}
.header {
height: 54px;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
background-color: $primary;
font-size: 1.2rem;
font-weight: bold;
min-width: 230px;
}
</style>
<i18n>
es:
Price: Precio
Discount: Descuento
New price: Nuevo precio
</i18n>

View File

@ -1 +1,455 @@
<template>Ticket sale</template>
<script setup>
import { onMounted, ref, computed, reactive, onUnmounted } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRouter, useRoute } from 'vue-router';
import FetchData from 'components/FetchData.vue';
import FetchedTags from 'components/ui/FetchedTags.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
// import ItemSummary from '../Item/Card/ItemSummary.vue';
import VnPaginate from 'components/ui/VnPaginate.vue';
import TicketEditManaProxy from './TicketEditMana.vue';
import { useStateStore } from 'stores/useStateStore';
import { useSession } from 'composables/useSession';
import { toCurrency, toPercentage } from 'src/filters';
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
import { useVnConfirm } from 'composables/useVnConfirm';
import axios from 'axios';
import RightMenu from 'src/components/common/RightMenu.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import useNotify from 'src/composables/useNotify.js';
const route = useRoute();
const router = useRouter();
const { getTokenMultimedia } = useSession();
const token = getTokenMultimedia();
const stateStore = useStateStore();
const { t } = useI18n();
const { openConfirmationModal } = useVnConfirm();
const { notify } = useNotify();
const salesDataRef = ref(null);
const isLocked = ref(false);
const isTicketEditable = ref(false);
const salesOptions = ref([]);
const allColumnNames = ref([]);
const itemsWithNameOptions = ref([]);
const manaCode = ref('mana');
const columns = computed(() => [
{
label: '',
name: 'statusIcons',
align: 'left',
},
{
label: '',
name: 'picture',
align: 'left',
},
{
label: t('ticketSale.visible'),
name: 'visible',
field: 'visible',
align: 'left',
sortable: true,
},
{
label: t('ticketSale.available'),
name: 'available',
field: 'available',
align: 'left',
sortable: true,
},
{
label: t('ticketSale.id'),
name: 'itemFk',
field: 'itemFk',
align: 'left',
sortable: true,
},
{
label: t('ticketSale.quantity'),
name: 'quantity',
field: 'quantity',
align: 'left',
sortable: true,
},
{
label: t('ticketSale.item'),
name: 'item',
field: 'item',
align: 'left',
sortable: true,
},
{
label: t('ticketSale.price'),
name: 'price',
field: 'price',
align: 'left',
sortable: true,
format: (val) => toCurrency(val),
},
{
label: t('ticketSale.discount'),
name: 'discount',
field: 'discount',
align: 'left',
sortable: true,
},
{
label: t('ticketSale.amount'),
name: 'amount',
field: 'amount',
align: 'left',
sortable: true,
},
{
label: t('ticketSale.packaging'),
name: 'packaging',
field: 'packaging',
align: 'left',
sortable: true,
},
{
label: '',
name: 'actions',
align: 'left',
columnFilter: null,
},
]);
const redirectToItemCreate = () => {
router.push({ name: 'ItemCreate' });
};
// const redirectToItemSummary = (id) => {
// router.push({ name: 'ItemSummary', params: { id } });
// };
// const cloneItem = async (itemFk) => {
// try {
// const { data } = await axios.post(`Items/${itemFk}/clone`);
// if (!data) return;
// router.push({ name: 'ItemTags', params: { id: data.id } });
// } catch (err) {
// console.error('Error cloning item', err);
// }
// };
const resetChanges = async () => salesDataRef.value.fetch();
const updateQuantity = async (sale) => {
console.log('updateQuantity');
try {
const payload = { quantity: sale.quantity };
await axios.post(`Sales/${sale.id}/updateQuantity`, payload);
notify('globals.dataSaved', 'positive');
await resetChanges();
} catch (err) {
console.error('Error updating quantity', err);
}
};
const addSale = async (sale) => {
try {
const payload = {
barcode: sale.itemFk,
quantity: sale.quantity,
};
const { data } = await axios.post(`tickets/${route.params.id}/addSale`, payload);
if (!data) return;
const newSale = data;
sale.id = newSale.id;
sale.image = newSale.item.image;
sale.subName = newSale.item.subName;
sale.concept = newSale.concept;
sale.quantity = newSale.quantity;
sale.discount = newSale.discount;
sale.price = newSale.price;
sale.item = newSale.item;
notify('globals.dataSaved', 'positive');
await resetChanges();
} catch (err) {
console.error('Error adding sale', err);
}
};
const changeQuantity = (sale) => {
if (!sale.itemFk || sale.quantity == null) return;
if (!sale.id) return addSale(sale);
updateQuantity(sale);
};
const updateConcept = async (sale) => {
try {
const data = { newConcept: sale.concept };
await axios.post(`Sales/${sale.id}/updateConcept`, data);
notify('globals.dataSaved', 'positive');
await resetChanges();
} catch (err) {
console.error('Error updating concept', err);
}
};
onMounted(async () => {
stateStore.rightDrawer = true;
const filteredColumns = columns.value.filter(
(col) => col.name !== 'picture' && col.name !== 'actions'
);
allColumnNames.value = filteredColumns.map((col) => col.name);
});
onUnmounted(() => (stateStore.rightDrawer = false));
</script>
<template>
<FetchData
:url="`Tickets/${route.params.id}/isEditable`"
auto-load
@on-fetch="(data) => (isTicketEditable = data)"
/>
<FetchData
:url="`Tickets/${route.params.id}/isLocked`"
auto-load
@on-fetch="(data) => (isLocked = data)"
/>
<FetchData
ref="salesDataRef"
:url="`Tickets/${route.params.id}/getSales`"
auto-load
@on-fetch="(data) => (salesOptions = data)"
/>
<FetchData
url="Items/withName"
:filter="{ fields: ['id', 'name'], order: 'id DESC' }"
auto-load
@on-fetch="(data) => (itemsWithNameOptions = data)"
/>
<VnSubToolbar>
<template #st-data> </template>
</VnSubToolbar>
<RightMenu>
<template #right-panel>
<ItemListFilter data-key="ItemList" />
</template>
</RightMenu>
<!-- <VnPaginate
ref="paginateRef"
data-key="ItemList"
url="Items/filter"
:order="['isActive DESC', 'name', 'id']"
:limit="12"
:expr-builder="exprBuilder"
:user-params="params"
:keep-opts="['userParams']"
:offset="50"
auto-load
>
<template #body="{ rows }"> -->
<QTable
:rows="salesOptions"
:columns="columns"
row-key="id"
:pagination="{ rowsPerPage: 0 }"
class="full-width q-mt-md"
selection="multiple"
:no-data-label="t('globals.noResults')"
>
<template #body-cell-statusIcons="{ row }">
<QTd class="q-gutter-x-xs">
<router-link
v-if="row.claim?.claimFk"
:to="{ name: 'ClaimBasicData', params: { id: row.claim?.claimFk } }"
>
<QIcon color="primary" name="vn:claims" size="xs">
<QTooltip>
{{ t('ticket.summary.unavailable') }}:
{{ row.claim?.claimFk }}
</QTooltip>
</QIcon>
</router-link>
<QIcon v-if="row.visible < 0" color="primary" name="warning" size="xs">
<QTooltip>
{{ t('ticketSale.visible') }}: {{ row.visible || 0 }}
</QTooltip>
</QIcon>
<QIcon v-if="row.reserved" color="primary" name="vn:reserva" size="xs">
<QTooltip>
{{ t('ticket.summary.reserved') }}
</QTooltip>
</QIcon>
<QIcon
v-if="row.itemShortage"
color="primary"
name="vn:unavailable"
size="xs"
>
<QTooltip>
{{ t('ticket.summary.itemShortage') }}
</QTooltip>
</QIcon>
<QIcon
v-if="row.hasComponentLack"
color="primary"
name="vn:components"
size="xs"
>
<QTooltip>
{{ t('ticket.summary.hasComponentLack') }}
</QTooltip>
</QIcon>
</QTd>
</template>
<template #body-cell-picture="{ row }">
<QTd>
<QImg
:src="`/api/Images/catalog/50x50/${row.itemFk}/download?access_token=${token}`"
spinner-color="primary"
:ratio="1"
height="50px"
width="50px"
style="border-radius: 50%"
/>
</QTd>
</template>
<template #body-cell-visible="{ row }">
<QTd @click.stop>
<QBadge :color="row.visible < 0 ? 'alert' : 'transparent'" dense>
{{ row.visible }}
</QBadge>
</QTd>
</template>
<template #body-cell-available="{ row }">
<QTd @click.stop>
<QBadge :color="row.available < 0 ? 'alert' : 'transparent'" dense>
{{ row.available }}
</QBadge>
</QTd>
</template>
<template #body-cell-itemFk="{ row }">
<QTd @click.stop>
<div v-if="row.id">
<QBtn flat color="primary" dense>
{{ row.itemFk }}
</QBtn>
<ItemDescriptorProxy :id="row.itemFk" />
</div>
<VnSelect
v-else
:options="itemsWithNameOptions"
hide-selected
option-label="name"
option-value="id"
v-model="row.itemFk"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel> #{{ scope.opt?.id }} </QItemLabel>
<QItemLabel caption>{{ scope.opt?.name }}</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelect>
</QTd>
</template>
<template #body-cell-quantity="{ row }">
<QTd @click.stop>
<VnInput v-model.number="row.quantity" @change="changeQuantity(row)" />
</QTd>
</template>
<template #body-cell-item="{ row }">
<QTd class="col">
<div class="column">
<span>{{ row.concept }}</span>
<span class="color-vn-label">{{ row.item?.subName }}</span>
<FetchedTags :item="row.item" :max-length="6" />
</div>
<QPopupProxy>
<VnInput v-model="row.concept" @change="updateConcept(row)" />
</QPopupProxy>
</QTd>
</template>
<template #body-cell-price="{ row }">
<QTd @click.stop>
<QBtn flat color="primary" dense>
{{ toCurrency(row.price) }}
</QBtn>
<TicketEditManaProxy
:id="route.params.id"
@reset-changes="resetChanges()"
/>
</QTd>
</template>
<template #body-cell-discount="{ row }">
<QTd @click.stop>
<QBtn flat color="primary" dense>
{{ toPercentage(row.discount) }}
</QBtn>
<TicketEditManaProxy
:id="route.params.id"
@reset-changes="resetChanges()"
/>
</QTd>
</template>
<!-- <template #body-cell-actions="{ row }">
<QTd>
<QIcon
@click.stop="
openConfirmationModal(
t(`All it's properties will be copied`),
t('Do you want to clone this item?'),
() => cloneItem(row.id)
)
"
class="q-ml-sm"
color="primary"
name="vn:clone"
size="sm"
>
<QTooltip>
{{ t('globals.clone') }}
</QTooltip>
</QIcon>
<QIcon
@click.stop="viewSummary(row.id, ItemSummary)"
class="q-ml-md"
color="primary"
name="preview"
size="sm"
>
<QTooltip class="text-no-wrap">
{{ t('Preview') }}
</QTooltip>
</QIcon>
</QTd>
</template> -->
</QTable>
<pre>{{ salesOptions }}</pre>
<!-- </template>
</VnPaginate> -->
<QPageSticky :offset="[20, 20]">
<QBtn @click="redirectToItemCreate()" color="primary" fab icon="add" />
<QTooltip class="text-no-wrap">
{{ t('New item') }}
</QTooltip>
</QPageSticky>
</template>
<i18n>
es:
New item: Nuevo artículo
</i18n>

View File

@ -0,0 +1,10 @@
ticketSale:
id: Id
visible: Visible
available: Available
quantity: Quantity
item: Item
price: Price
discount: Disc
amount: Amount
packaging: Packaging

View File

@ -1,2 +1,12 @@
Search ticket: Buscar ticket
You can search by ticket id or alias: Puedes buscar por id o alias del ticket
ticketSale:
id: Id
visible: Visible
available: Disponible
quantity: Cantidad
item: Artículo
price: Precio
discount: Dto
amount: Cantidad
packaging: Encajado