feat: refs #8363 added new clone form, modified list and created new composable
This commit is contained in:
parent
ac12e1454a
commit
a38470577c
|
@ -0,0 +1,168 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import axios from 'axios';
|
||||
import { useQuasar } from 'quasar';
|
||||
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnInputDate from 'src/components/common/VnInputDate.vue';
|
||||
import VnRow from 'components/ui/VnRow.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
row: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const quasar = useQuasar();
|
||||
const isLoading = ref(false);
|
||||
const closeButton = ref(null);
|
||||
const data = $props.row;
|
||||
const emit = defineEmits(['onDataSaved']);
|
||||
|
||||
const onSubmit = async () => {
|
||||
if (isLoading.value) return;
|
||||
isLoading.value = true;
|
||||
const params = data;
|
||||
await axios.post('FixedPrices', params);
|
||||
quasar.notify({
|
||||
type: 'positive',
|
||||
message: t('globals.dataSaved'),
|
||||
});
|
||||
isLoading.value = false;
|
||||
closeForm();
|
||||
emit('onDataSaved');
|
||||
};
|
||||
|
||||
const closeForm = () => {
|
||||
if (closeButton.value) closeButton.value.click();
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QForm @submit="onSubmit()" class="all-pointer-events">
|
||||
<QCard class="q-pa-lg">
|
||||
<span ref="closeButton" class="close-icon" v-close-popup>
|
||||
<QIcon name="close" size="sm" />
|
||||
</span>
|
||||
<div class="q-pb-md">
|
||||
<span class="title">{{ t('Clone item') }}</span>
|
||||
</div>
|
||||
<VnRow>
|
||||
<VnSelect
|
||||
url="Items/search"
|
||||
v-model="data.itemFk"
|
||||
:label="t('item.fixedPrice.itemName')"
|
||||
:fields="['id', 'name']"
|
||||
:filter-options="['id', 'name']"
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
:required="true"
|
||||
sort-by="name ASC"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>
|
||||
{{ scope.opt.name }}
|
||||
</QItemLabel>
|
||||
<QItemLabel caption> #{{ scope.opt.id }} </QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnInput
|
||||
:label="t('item.fixedPrice.groupingPrice')"
|
||||
v-model="data.rate2"
|
||||
/>
|
||||
<VnInput
|
||||
:label="t('item.fixedPrice.packingPrice')"
|
||||
v-model="data.rate3"
|
||||
/>
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnInputDate
|
||||
:label="t('item.fixedPrice.started')"
|
||||
v-model="data.started"
|
||||
/>
|
||||
<VnInputDate :label="t('item.fixedPrice.ended')" v-model="data.ended" />
|
||||
</VnRow>
|
||||
<VnRow>
|
||||
<VnSelect
|
||||
:label="t('globals.warehouse')"
|
||||
url="Warehouses"
|
||||
v-model="data.warehouseFk"
|
||||
:fields="['id', 'name']"
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
hide-selected
|
||||
:required="true"
|
||||
sort-by="name ASC"
|
||||
>
|
||||
<template #option="scope">
|
||||
<QItem v-bind="scope.itemProps">
|
||||
<QItemSection>
|
||||
<QItemLabel>
|
||||
{{ scope.opt.name }}
|
||||
</QItemLabel>
|
||||
<QItemLabel caption> #{{ scope.opt.id }} </QItemLabel>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
</VnRow>
|
||||
<div class="q-mt-lg row justify-end">
|
||||
<QBtn
|
||||
:label="t('globals.cancel')"
|
||||
type="reset"
|
||||
color="primary"
|
||||
flat
|
||||
:disabled="isLoading"
|
||||
:loading="isLoading"
|
||||
v-close-popup
|
||||
/>
|
||||
<QBtn
|
||||
:label="t('globals.save')"
|
||||
type="submit"
|
||||
color="primary"
|
||||
class="q-ml-sm"
|
||||
:disabled="isLoading"
|
||||
:loading="isLoading"
|
||||
@click="onSubmit()"
|
||||
/>
|
||||
</div>
|
||||
</QCard>
|
||||
</QForm>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.title {
|
||||
font-size: 17px;
|
||||
font-weight: bold;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.countLines {
|
||||
font-size: 24px;
|
||||
color: $primary;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Clone item: Clonar artículo
|
||||
Item id: Id artículo
|
||||
</i18n>
|
|
@ -28,6 +28,7 @@ const $props = defineProps({
|
|||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const emit = defineEmits(['onDataSaved']);
|
||||
|
||||
const inputs = {
|
||||
input: markRaw(VnInput),
|
||||
|
@ -47,6 +48,7 @@ const onSubmit = async () => {
|
|||
$props.rows.forEach((row) => {
|
||||
row[selectedField.value.name] = newValue.value;
|
||||
});
|
||||
emit('onDataSaved', $props.rows);
|
||||
closeForm();
|
||||
};
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { createWrapper, axios } from 'app/test/vitest/helper';
|
||||
import EditForm from 'components/EditTableCellValueForm.vue';
|
||||
import EditForm from 'src/components/EditFixedPriceForm.vue';
|
||||
import { vi, afterEach, beforeAll, describe, expect, it } from 'vitest';
|
||||
|
||||
const fieldA = 'fieldA';
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
import axios from 'axios';
|
||||
|
||||
export async function beforeSave(data, getChanges, modelOrigin) {
|
||||
console.log('data: ', data);
|
||||
try {
|
||||
const changes = data.updates;
|
||||
if (!changes) return data;
|
||||
const patchPromises = [];
|
||||
|
||||
for (const change of changes) {
|
||||
console.log('change: ', change);
|
||||
let patchData = {};
|
||||
|
||||
if ('hasMinPrice' in change.data) {
|
||||
|
|
|
@ -1,35 +1,30 @@
|
|||
<script setup>
|
||||
import { onMounted, ref, onUnmounted, computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import axios from 'axios';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
import { beforeSave } from 'src/composables/updateMinPriceBeforeSave';
|
||||
|
||||
import FetchedTags from 'components/ui/FetchedTags.vue';
|
||||
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import EditTableCellValueForm from 'src/components/EditTableCellValueForm.vue';
|
||||
import EditFixedPriceForm from 'src/components/EditFixedPriceForm.vue';
|
||||
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
||||
import RightMenu from 'src/components/common/RightMenu.vue';
|
||||
import VnTable from 'src/components/VnTable/VnTable.vue';
|
||||
import VnColor from 'src/components/common/VnColor.vue';
|
||||
import VnImg from 'src/components/ui/VnImg.vue';
|
||||
|
||||
import { toDate } from 'src/filters';
|
||||
import { isLower, isBigger } from 'src/filters/date.js';
|
||||
import ItemFixedPriceFilter from './ItemFixedPriceFilter.vue';
|
||||
import ItemDescriptorProxy from './Card/ItemDescriptorProxy.vue';
|
||||
import CloneFixedPriceForm from 'src/components/CloneFixedPriceForm.vue';
|
||||
|
||||
const quasar = useQuasar();
|
||||
const stateStore = useStateStore();
|
||||
const { t } = useI18n();
|
||||
const { notify } = useNotify();
|
||||
const tableRef = ref();
|
||||
const editTableCellDialogRef = ref(null);
|
||||
const fixedPrices = ref([]);
|
||||
const editFixedPriceForm = ref(null);
|
||||
const cloneFixedPriceForm = ref(null);
|
||||
const cloneRow = ref({});
|
||||
const selectedRows = ref([]);
|
||||
const hasSelectedRows = computed(() => selectedRows.value.length > 0);
|
||||
const dateColor = 'var(--vn-label-text-color)';
|
||||
|
@ -39,21 +34,6 @@ onMounted(async () => {
|
|||
onUnmounted(() => (stateStore.rightDrawer = false));
|
||||
|
||||
const columns = computed(() => [
|
||||
{
|
||||
align: 'center',
|
||||
label: t('lines.image'),
|
||||
name: 'image',
|
||||
columnField: {
|
||||
component: VnImg,
|
||||
attrs: ({ row }) => {
|
||||
return {
|
||||
id: row.itemFk,
|
||||
};
|
||||
},
|
||||
},
|
||||
width: '50px',
|
||||
columnFilter: false,
|
||||
},
|
||||
{
|
||||
name: 'itemFk',
|
||||
label: t('item.fixedPrice.itemFk'),
|
||||
|
@ -63,7 +43,7 @@ const columns = computed(() => [
|
|||
columnFilter: {
|
||||
inWhere: true,
|
||||
},
|
||||
width: '60px',
|
||||
width: '55px',
|
||||
},
|
||||
{
|
||||
labelAbbreviation: '',
|
||||
|
@ -71,7 +51,7 @@ const columns = computed(() => [
|
|||
name: 'hex',
|
||||
columnSearch: false,
|
||||
isEditable: false,
|
||||
width: '12px',
|
||||
width: '10px',
|
||||
component: 'select',
|
||||
attrs: {
|
||||
url: 'Inks',
|
||||
|
@ -101,22 +81,22 @@ const columns = computed(() => [
|
|||
component: 'number',
|
||||
create: true,
|
||||
createOrder: 3,
|
||||
width: '55px',
|
||||
width: '40px',
|
||||
},
|
||||
{
|
||||
label: t('item.fixedPrice.packingPrice'),
|
||||
labelAbbreviation: 'pack.',
|
||||
labelAbbreviation: 'Pack.',
|
||||
toolTip: t('item.fixedPrice.packingPrice'),
|
||||
name: 'rate3',
|
||||
component: 'number',
|
||||
create: true,
|
||||
createOrder: 4,
|
||||
width: '55px',
|
||||
width: '40px',
|
||||
},
|
||||
{
|
||||
name: 'hasMinPrice',
|
||||
label: t('item.fixedPrice.hasMinPrice'),
|
||||
labelAbbreviation: 'MP',
|
||||
labelAbbreviation: t('item.fixedPrice.MP'),
|
||||
toolTip: t('item.fixedPrice.hasMinPrice'),
|
||||
label: t('Has min price'),
|
||||
component: 'checkbox',
|
||||
|
@ -131,7 +111,7 @@ const columns = computed(() => [
|
|||
toolTip: t('item.fixedPrice.minPrice'),
|
||||
name: 'minPrice',
|
||||
component: 'number',
|
||||
width: '55px',
|
||||
width: '45px',
|
||||
style: (row) => {
|
||||
if (!row?.hasMinPrice) return { color: 'var(--vn-label-color)' };
|
||||
},
|
||||
|
@ -146,7 +126,7 @@ const columns = computed(() => [
|
|||
},
|
||||
create: true,
|
||||
createOrder: 5,
|
||||
width: '70px',
|
||||
width: '55px',
|
||||
},
|
||||
{
|
||||
label: t('item.fixedPrice.ended'),
|
||||
|
@ -157,7 +137,7 @@ const columns = computed(() => [
|
|||
},
|
||||
create: true,
|
||||
createOrder: 6,
|
||||
width: '70px',
|
||||
width: '55px',
|
||||
},
|
||||
{
|
||||
align: 'center',
|
||||
|
@ -172,122 +152,45 @@ const columns = computed(() => [
|
|||
},
|
||||
create: true,
|
||||
format: (row, dashIfEmpty) => dashIfEmpty(row.warehouseName),
|
||||
width: '80px',
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
name: 'tableActions',
|
||||
actions: [
|
||||
{
|
||||
title: t('delete'),
|
||||
icon: 'delete',
|
||||
action: (row) => confirmRemove(row),
|
||||
title: t('globals.clone'),
|
||||
icon: 'vn:clone',
|
||||
action: (row) => openCloneFixedPriceForm(row),
|
||||
isPrimary: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
const updateMinPrice = async (value, props) => {
|
||||
props.row.hasMinPrice = value;
|
||||
await upsertPrice({
|
||||
row: props.row,
|
||||
col: { field: 'hasMinPrice' },
|
||||
rowIndex: props.rowIndex,
|
||||
});
|
||||
const openEditFixedPriceForm = () => {
|
||||
editFixedPriceForm.value.show();
|
||||
};
|
||||
|
||||
const validations = ({ row }) => {
|
||||
const requiredFields = [
|
||||
'itemFk',
|
||||
'started',
|
||||
'ended',
|
||||
'rate2',
|
||||
'rate3',
|
||||
'warehouseFk',
|
||||
];
|
||||
const isValid = requiredFields.every(
|
||||
(field) => row[field] !== null && row[field] !== undefined,
|
||||
);
|
||||
return isValid;
|
||||
};
|
||||
const upsertPrice = async (props, resetMinPrice = false) => {
|
||||
const isValid = validations({ ...props });
|
||||
if (!isValid) {
|
||||
return;
|
||||
}
|
||||
const { row } = props;
|
||||
const changes = tableRef.value.CrudModelRef.getChanges();
|
||||
if (changes?.updates?.length > 0) {
|
||||
if (resetMinPrice) row.hasMinPrice = 0;
|
||||
}
|
||||
if (!changes.updates && !changes.creates) return;
|
||||
const data = await upsertFixedPrice(row);
|
||||
Object.assign(tableRef.value.CrudModelRef.formData[props.rowIndex], data);
|
||||
notify(t('globals.dataSaved'), 'positive');
|
||||
tableRef.value.reload();
|
||||
const openCloneFixedPriceForm = (row) => {
|
||||
cloneRow.value = (({ itemFk, rate2, rate3, started, ended, warehouseFk }) => ({
|
||||
itemFk,
|
||||
rate2,
|
||||
rate3,
|
||||
started,
|
||||
ended,
|
||||
warehouseFk,
|
||||
}))(JSON.parse(JSON.stringify(row)));
|
||||
|
||||
cloneFixedPriceForm.value.show();
|
||||
};
|
||||
|
||||
async function upsertFixedPrice(row) {
|
||||
const { data } = await axios.patch('FixedPrices/upsertFixedPrice', row);
|
||||
data.hasMinPrice = data.hasMinPrice ? 1 : 0;
|
||||
return data;
|
||||
}
|
||||
|
||||
const openEditTableCellDialog = () => {
|
||||
editTableCellDialogRef.value.show();
|
||||
};
|
||||
|
||||
const onEditCellDataSaved = async () => {
|
||||
selectedRows.value = [];
|
||||
tableRef.value.reload();
|
||||
};
|
||||
|
||||
const removeFuturePrice = async () => {
|
||||
selectedRows.value.forEach(({ id }) => {
|
||||
const rowIndex = fixedPrices.value.findIndex(({ id }) => id === id);
|
||||
removePrice(id, rowIndex);
|
||||
});
|
||||
};
|
||||
|
||||
function confirmRemove(item, isFuture) {
|
||||
const promise = async () =>
|
||||
isFuture ? removeFuturePrice(item.id) : removePrice(item.id);
|
||||
quasar.dialog({
|
||||
component: VnConfirm,
|
||||
componentProps: {
|
||||
title: t('globals.rowWillBeRemoved'),
|
||||
message: t('globals.confirmDeletion'),
|
||||
promise,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const removePrice = async (id) => {
|
||||
await axios.delete(`FixedPrices/${id}`);
|
||||
notify(t('globals.dataSaved'), 'positive');
|
||||
tableRef.value.reload({});
|
||||
};
|
||||
const dateStyle = (date) =>
|
||||
date
|
||||
? {
|
||||
color: 'var(--vn-black-text-color)',
|
||||
}
|
||||
: { color: dateColor, 'background-color': 'transparent' };
|
||||
|
||||
async function cloneFixedPrice(rows) {
|
||||
for (let row of rows) {
|
||||
let clonedLine = {
|
||||
itemFk: row.itemFk,
|
||||
warehouseFk: row.warehouseFk,
|
||||
rate2: row.rate2,
|
||||
rate3: row.rate3,
|
||||
started: row.started,
|
||||
ended: row.ended,
|
||||
};
|
||||
console.log('clonedLine: ', clonedLine);
|
||||
await axios.post('FixedPrices', clonedLine);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -301,7 +204,7 @@ async function cloneFixedPrice(rows) {
|
|||
<QBtnGroup push style="column-gap: 10px">
|
||||
<QBtn
|
||||
:disable="!hasSelectedRows"
|
||||
@click="openEditTableCellDialog()"
|
||||
@click="openEditFixedPriceForm()"
|
||||
color="primary"
|
||||
icon="vn:wand"
|
||||
flat
|
||||
|
@ -312,19 +215,6 @@ async function cloneFixedPrice(rows) {
|
|||
{{ t('Edit fixed price(s)') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
<QBtn
|
||||
:disable="!hasSelectedRows"
|
||||
@click="cloneFixedPrice(selectedRows)"
|
||||
color="primary"
|
||||
icon="vn:clone"
|
||||
flat
|
||||
:label="t('globals.clone')"
|
||||
data-cy="FixedPriceToolbarCloneBtn"
|
||||
>
|
||||
<QTooltip>
|
||||
{{ t('Clone fixed price(s)') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
</QBtnGroup>
|
||||
</Teleport>
|
||||
<VnTable
|
||||
|
@ -352,11 +242,6 @@ async function cloneFixedPrice(rows) {
|
|||
auto-load
|
||||
:beforeSaveFn="(data, getChanges) => beforeSave(data, getChanges, 'FixedPrices')"
|
||||
>
|
||||
<template #column-image="{ row }">
|
||||
<div class="image-wrapper">
|
||||
<VnImg :id="row.itemFk" class="rounded" />
|
||||
</div>
|
||||
</template>
|
||||
<template #column-hex="{ row }">
|
||||
<VnColor :colors="row?.hexJson" style="height: 100%; min-width: 2000px" />
|
||||
</template>
|
||||
|
@ -370,14 +255,14 @@ async function cloneFixedPrice(rows) {
|
|||
</template>
|
||||
<template #column-started="{ row }">
|
||||
<div class="editable-text q-pb-xxs">
|
||||
<QBadge :style="dateStyle(isLower(row?.ended))">
|
||||
<QBadge class="badge" :style="dateStyle(isLower(row?.ended))">
|
||||
{{ toDate(row?.started) }}
|
||||
</QBadge>
|
||||
</div>
|
||||
</template>
|
||||
<template #column-ended="{ row }">
|
||||
<div class="editable-text q-pb-xxs">
|
||||
<QBadge :style="dateStyle(isBigger(row?.ended))">
|
||||
<QBadge class="badge" :style="dateStyle(isBigger(row?.ended))">
|
||||
{{ toDate(row?.ended) }}
|
||||
</QBadge>
|
||||
</div>
|
||||
|
@ -386,7 +271,7 @@ async function cloneFixedPrice(rows) {
|
|||
<VnSelect
|
||||
url="Items/search"
|
||||
v-model="data.itemFk"
|
||||
:label="t('Article')"
|
||||
:label="t('item.fixedPrice.itemName')"
|
||||
:fields="['id', 'name']"
|
||||
:filter-options="['id', 'name']"
|
||||
option-label="name"
|
||||
|
@ -434,8 +319,8 @@ async function cloneFixedPrice(rows) {
|
|||
</template>
|
||||
</VnTable>
|
||||
|
||||
<QDialog ref="editTableCellDialogRef">
|
||||
<EditTableCellValueForm
|
||||
<QDialog ref="editFixedPriceForm">
|
||||
<EditFixedPriceForm
|
||||
edit-url="FixedPrices/editFixedPrice"
|
||||
:rows="selectedRows"
|
||||
:fields-options="
|
||||
|
@ -445,8 +330,12 @@ async function cloneFixedPrice(rows) {
|
|||
)
|
||||
"
|
||||
:beforeSave="beforeSave"
|
||||
@on-data-saved="tableRef.CrudModelRef.saveChanges()"
|
||||
/>
|
||||
</QDialog>
|
||||
<QDialog ref="cloneFixedPriceForm">
|
||||
<CloneFixedPriceForm :row="cloneRow" @on-data-saved="tableRef.reload()" />
|
||||
</QDialog>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
.q-table th,
|
||||
|
@ -499,6 +388,13 @@ tbody tr.highlight .q-td {
|
|||
color: var(--vn-label-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.badge {
|
||||
background-color: $warning;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Add fixed price: Añadir precio fijado
|
||||
|
|
|
@ -167,6 +167,8 @@ item:
|
|||
started: Started
|
||||
ended: Ended
|
||||
warehouse: Warehouse
|
||||
MP: MP
|
||||
itemName: Item
|
||||
create:
|
||||
name: Name
|
||||
tag: Tag
|
||||
|
|
|
@ -173,6 +173,8 @@ item:
|
|||
started: Inicio
|
||||
ended: Fin
|
||||
warehouse: Almacén
|
||||
MP: PM
|
||||
itemName: Nombre
|
||||
create:
|
||||
name: Nombre
|
||||
tag: Etiqueta
|
||||
|
|
Loading…
Reference in New Issue