feat: mejorar la gestión de productos estancados y agregar paginación en el servicio de reportes
This commit is contained in:
parent
69c015d51b
commit
7c27200290
@ -6,11 +6,12 @@ import reportService from '@Services/reportService';
|
|||||||
import useCashRegister from '@Stores/cashRegister';
|
import useCashRegister from '@Stores/cashRegister';
|
||||||
import GoogleIcon from '@Shared/GoogleIcon.vue';
|
import GoogleIcon from '@Shared/GoogleIcon.vue';
|
||||||
import OpenModal from '@/pages/POS/CashRegister/OpenModal.vue';
|
import OpenModal from '@/pages/POS/CashRegister/OpenModal.vue';
|
||||||
|
import Table from '@Holos/Table.vue';
|
||||||
|
|
||||||
// State
|
// State
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const topProduct = ref(null);
|
const topProduct = ref(null);
|
||||||
const stagnantProducts = ref([]);
|
const stagnantProducts = ref({ data: [], total: 0 });
|
||||||
const loadingTopProduct = ref(true);
|
const loadingTopProduct = ref(true);
|
||||||
const loadingStagnantProducts = ref(true);
|
const loadingStagnantProducts = ref(true);
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
@ -43,7 +44,7 @@ const fetchTopProduct = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchStagnantProducts = async () => {
|
const fetchStagnantProducts = async (page = 1) => {
|
||||||
if (!filters.value.from_date || !filters.value.to_date) {
|
if (!filters.value.from_date || !filters.value.to_date) {
|
||||||
window.Notify.warning('Por favor selecciona ambas fechas para consultar el reporte.');
|
window.Notify.warning('Por favor selecciona ambas fechas para consultar el reporte.');
|
||||||
return;
|
return;
|
||||||
@ -54,11 +55,13 @@ const fetchStagnantProducts = async () => {
|
|||||||
const data = await reportService.getProductsWithoutMovement(
|
const data = await reportService.getProductsWithoutMovement(
|
||||||
filters.value.from_date,
|
filters.value.from_date,
|
||||||
filters.value.to_date,
|
filters.value.to_date,
|
||||||
true
|
true,
|
||||||
|
page
|
||||||
);
|
);
|
||||||
stagnantProducts.value = data.products;
|
stagnantProducts.value = data.products || { data: [], total: 0 };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
window.Notify.error('Error al cargar productos sin movimiento.');
|
window.Notify.error('Error al cargar productos sin movimiento.');
|
||||||
|
stagnantProducts.value = { data: [], total: 0 };
|
||||||
} finally {
|
} finally {
|
||||||
loadingStagnantProducts.value = false;
|
loadingStagnantProducts.value = false;
|
||||||
}
|
}
|
||||||
@ -191,43 +194,43 @@ onMounted(() => {
|
|||||||
<p>Cargando inventario...</p>
|
<p>Cargando inventario...</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Empty State -->
|
|
||||||
<div v-else-if="stagnantProducts.length === 0" class="p-12 flex flex-col items-center justify-center text-center">
|
|
||||||
<GoogleIcon name="celebration" class="text-6xl text-green-400 mb-4" />
|
|
||||||
<h3 class="font-semibold text-gray-700 dark:text-gray-300">¡Excelente rotación!</h3>
|
|
||||||
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">Todos los productos se han vendido en el período reciente.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Table -->
|
<!-- Table -->
|
||||||
<div v-else class="overflow-x-auto">
|
<Table
|
||||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
:items="stagnantProducts"
|
||||||
<thead class="bg-gray-50 dark:bg-gray-900/50">
|
:processing="loadingStagnantProducts"
|
||||||
<tr>
|
@send-pagination="(page) => fetchStagnantProducts(page)"
|
||||||
<th class="px-6 py-3 text-left text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase">Producto</th>
|
>
|
||||||
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase">Stock</th>
|
<template #head>
|
||||||
<th class="px-6 py-3 text-right text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase">Valor Inventario</th>
|
<th class="px-6 py-3 text-left text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">Producto</th>
|
||||||
</tr>
|
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">Stock</th>
|
||||||
</thead>
|
</template>
|
||||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
<template #body="{items}">
|
||||||
<tr v-for="product in stagnantProducts" :key="product.id">
|
<tr
|
||||||
<td class="px-6 py-4 whitespace-nowrap">
|
v-for="product in items"
|
||||||
<p class="text-sm font-semibold text-gray-900 dark:text-gray-100">{{ product.name }}</p>
|
:key="product.id"
|
||||||
<p class="text-xs text-gray-500 dark:text-gray-400 font-mono">{{ product.sku }}</p>
|
class="hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
|
||||||
</td>
|
>
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-center">
|
<td class="px-6 py-4 whitespace-nowrap">
|
||||||
<span class="text-sm font-bold" :class="product.stock > 0 ? 'text-orange-600 dark:text-orange-400' : 'text-gray-500'">
|
<p class="text-sm font-semibold text-gray-900 dark:text-gray-100">{{ product.name }}</p>
|
||||||
{{ product.stock }}
|
<p class="text-xs text-gray-500 dark:text-gray-400 font-mono">{{ product.sku }}</p>
|
||||||
</span>
|
</td>
|
||||||
</td>
|
<td class="px-6 py-4 whitespace-nowrap text-center">
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-right">
|
<span class="text-sm font-bold" :class="product.stock > 0 ? 'text-orange-600 dark:text-orange-400' : 'text-gray-500'">
|
||||||
<span class="text-sm font-semibold text-gray-700 dark:text-gray-300">
|
{{ product.stock }}
|
||||||
{{ formatCurrency(product.inventory_value) }}
|
</span>
|
||||||
</span>
|
</td>
|
||||||
</td>
|
</tr>
|
||||||
</tr>
|
</template>
|
||||||
</tbody>
|
<template #empty>
|
||||||
</table>
|
<td colspan="2" class="table-cell text-center">
|
||||||
</div>
|
<div class="flex flex-col items-center justify-center py-12">
|
||||||
|
<GoogleIcon name="celebration" class="text-6xl text-green-400 mb-4" />
|
||||||
|
<h3 class="font-semibold text-gray-700 dark:text-gray-300">¡Excelente rotación!</h3>
|
||||||
|
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">Todos los productos se han vendido en el período reciente.</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</template>
|
||||||
|
</Table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -21,9 +21,9 @@ const deletingCategory = ref(null);
|
|||||||
const searcher = useSearcher({
|
const searcher = useSearcher({
|
||||||
url: apiURL('categorias'),
|
url: apiURL('categorias'),
|
||||||
onSuccess: (r) => {
|
onSuccess: (r) => {
|
||||||
models.value = r.categories || [];
|
models.value = r.categories || { data: [], total: 0 };
|
||||||
},
|
},
|
||||||
onError: () => models.value = []
|
onError: () => models.value = { data: [], total: 0 }
|
||||||
});
|
});
|
||||||
|
|
||||||
const openCreateModal = () => {
|
const openCreateModal = () => {
|
||||||
|
|||||||
@ -18,6 +18,9 @@ import ImportModal from './ImportModal.vue';
|
|||||||
|
|
||||||
/** Estado */
|
/** Estado */
|
||||||
const models = ref([]);
|
const models = ref([]);
|
||||||
|
const totalInventoryValue = ref(0);
|
||||||
|
const categories = ref([]);
|
||||||
|
const selectedCategory = ref('');
|
||||||
const showCreateModal = ref(false);
|
const showCreateModal = ref(false);
|
||||||
const showEditModal = ref(false);
|
const showEditModal = ref(false);
|
||||||
const showDeleteModal = ref(false);
|
const showDeleteModal = ref(false);
|
||||||
@ -31,12 +34,31 @@ const searcher = useSearcher({
|
|||||||
url: apiURL('inventario'),
|
url: apiURL('inventario'),
|
||||||
onSuccess: (r) => {
|
onSuccess: (r) => {
|
||||||
models.value = r.products || { data: [], total: 0 };
|
models.value = r.products || { data: [], total: 0 };
|
||||||
|
totalInventoryValue.value = r.total_inventory_value || 0;
|
||||||
},
|
},
|
||||||
onError: () => {
|
onError: () => {
|
||||||
models.value = { data: [], total: 0 };
|
models.value = { data: [], total: 0 };
|
||||||
|
totalInventoryValue.value = 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const loadCategories = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(apiURL('categorias'), {
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${sessionStorage.token}`,
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const result = await response.json();
|
||||||
|
if (result.data && result.data.categories && result.data.categories.data) {
|
||||||
|
categories.value = result.data.categories.data;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error al cargar categorías:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const openCreateModal = () => {
|
const openCreateModal = () => {
|
||||||
showCreateModal.value = true;
|
showCreateModal.value = true;
|
||||||
};
|
};
|
||||||
@ -74,11 +96,15 @@ const closeImportModal = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onProductSaved = () => {
|
const onProductSaved = () => {
|
||||||
searcher.search();
|
searcher.search('', {
|
||||||
|
category_id: selectedCategory.value || ''
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onProductsImported = () => {
|
const onProductsImported = () => {
|
||||||
searcher.search();
|
searcher.search('', {
|
||||||
|
category_id: selectedCategory.value || ''
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const openSerials = (product) => {
|
const openSerials = (product) => {
|
||||||
@ -98,7 +124,9 @@ const confirmDelete = async (id) => {
|
|||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
Notify.success('Producto eliminado exitosamente');
|
Notify.success('Producto eliminado exitosamente');
|
||||||
closeDeleteModal();
|
closeDeleteModal();
|
||||||
searcher.search();
|
searcher.search('', {
|
||||||
|
category_id: selectedCategory.value || ''
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
Notify.error('Error al eliminar el producto');
|
Notify.error('Error al eliminar el producto');
|
||||||
}
|
}
|
||||||
@ -108,13 +136,18 @@ const confirmDelete = async (id) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleCategoryChange = () => {
|
||||||
|
searcher.search('', {
|
||||||
|
category_id: selectedCategory.value || ''
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const exportReport = async () => {
|
const exportReport = async () => {
|
||||||
try {
|
try {
|
||||||
isExporting.value = true;
|
isExporting.value = true;
|
||||||
|
|
||||||
// Puedes agregar filtros aquí si lo necesitas
|
|
||||||
const filters = {
|
const filters = {
|
||||||
// category_id: 5, // Opcional: filtrar por categoría específica
|
category_id: selectedCategory.value || null,
|
||||||
// with_serials_only: true, // Opcional: solo productos con seguimiento de seriales
|
// with_serials_only: true, // Opcional: solo productos con seguimiento de seriales
|
||||||
// low_stock_threshold: 10 // Opcional: solo productos con stock bajo o igual al umbral
|
// low_stock_threshold: 10 // Opcional: solo productos con stock bajo o igual al umbral
|
||||||
};
|
};
|
||||||
@ -132,7 +165,10 @@ const exportReport = async () => {
|
|||||||
|
|
||||||
/** Ciclos */
|
/** Ciclos */
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
searcher.search();
|
loadCategories();
|
||||||
|
searcher.search('', {
|
||||||
|
category_id: selectedCategory.value || ''
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -141,7 +177,7 @@ onMounted(() => {
|
|||||||
<SearcherHead
|
<SearcherHead
|
||||||
:title="$t('inventory.title')"
|
:title="$t('inventory.title')"
|
||||||
placeholder="Buscar por nombre o SKU..."
|
placeholder="Buscar por nombre o SKU..."
|
||||||
@search="(x) => searcher.search(x)"
|
@search="(x) => searcher.search(x, { category_id: selectedCategory || '' })"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="flex items-center gap-2 px-3 py-2 bg-emerald-600 hover:bg-emerald-700 text-white text-sm font-semibold rounded-lg transition-colors shadow-sm disabled:opacity-50 disabled:cursor-not-allowed"
|
class="flex items-center gap-2 px-3 py-2 bg-emerald-600 hover:bg-emerald-700 text-white text-sm font-semibold rounded-lg transition-colors shadow-sm disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
@ -171,6 +207,46 @@ onMounted(() => {
|
|||||||
</button>
|
</button>
|
||||||
</SearcherHead>
|
</SearcherHead>
|
||||||
|
|
||||||
|
<!-- Filtros -->
|
||||||
|
<div class="pt-4 pb-2">
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">Filtrar por categoría:</label>
|
||||||
|
<select
|
||||||
|
v-model="selectedCategory"
|
||||||
|
@change="handleCategoryChange"
|
||||||
|
class="px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100"
|
||||||
|
>
|
||||||
|
<option value="">Todas las categorías</option>
|
||||||
|
<option v-for="category in categories" :key="category.id" :value="category.id">
|
||||||
|
{{ category.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Estadísticas del Inventario -->
|
||||||
|
<div class="pt-4 pb-2">
|
||||||
|
<div class="bg-gradient-to-r from-indigo-500 to-purple-600 rounded-lg shadow-lg p-6">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<div class="bg-white/20 backdrop-blur-sm rounded-full p-3">
|
||||||
|
<GoogleIcon name="inventory_2" class="text-3xl text-white" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="text-indigo-100 text-sm font-medium">Valor Total del Inventario</p>
|
||||||
|
<p class="text-white text-3xl font-bold">
|
||||||
|
{{ formatCurrency(totalInventoryValue) }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-right">
|
||||||
|
<p class="text-indigo-100 text-sm font-medium">Total de Productos</p>
|
||||||
|
<p class="text-white text-2xl font-bold">{{ models.total || 0 }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="pt-2 w-full">
|
<div class="pt-2 w-full">
|
||||||
<Table
|
<Table
|
||||||
:items="models"
|
:items="models"
|
||||||
@ -178,11 +254,12 @@ onMounted(() => {
|
|||||||
@send-pagination="(page) => searcher.pagination(page)"
|
@send-pagination="(page) => searcher.pagination(page)"
|
||||||
>
|
>
|
||||||
<template #head>
|
<template #head>
|
||||||
<th class="px-6 py-3 text-left text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">SKU / CÓDIGO</th>
|
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">SKU / CÓDIGO</th>
|
||||||
<th class="px-6 py-3 text-left text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">PRODUCTO</th>
|
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">PRODUCTO</th>
|
||||||
<th class="px-6 py-3 text-left text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">CATEGORÍA</th>
|
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">CATEGORÍA</th>
|
||||||
<th class="px-6 py-3 text-right text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">PRECIO</th>
|
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">PRECIO</th>
|
||||||
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">STOCK</th>
|
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">STOCK</th>
|
||||||
|
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">TOTAL</th>
|
||||||
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">ACCIONES</th>
|
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">ACCIONES</th>
|
||||||
</template>
|
</template>
|
||||||
<template #body="{items}">
|
<template #body="{items}">
|
||||||
@ -191,21 +268,20 @@ onMounted(() => {
|
|||||||
:key="model.id"
|
:key="model.id"
|
||||||
class="hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
|
class="hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
|
||||||
>
|
>
|
||||||
<td class="px-6 py-4 whitespace-nowrap">
|
<td class="px-6 py-4 whitespace-nowrap text-center">
|
||||||
<span class="text-sm font-mono text-gray-600 dark:text-gray-400">{{ model.sku }}</span>
|
<span class="text-sm font-mono text-gray-600 dark:text-gray-400">{{ model.sku }}</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4">
|
<td class="px-6 py-4 text-center">
|
||||||
<div>
|
<div>
|
||||||
<p class="text-sm font-semibold text-gray-900 dark:text-gray-100">{{ model.name }}</p>
|
<p class="text-sm font-semibold text-gray-900 dark:text-gray-100">{{ model.name }}</p>
|
||||||
<p v-if="model.description" class="text-xs text-gray-500 dark:text-gray-400 mt-0.5">{{ model.description }}</p>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap">
|
<td class="px-6 py-4 whitespace-nowrap text-center">
|
||||||
<span class="text-sm text-gray-700 dark:text-gray-300">
|
<span class="text-sm text-gray-700 dark:text-gray-300">
|
||||||
{{ model.category?.name || '-' }}
|
{{ model.category?.name || '-' }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-right">
|
<td class="px-6 py-4 whitespace-nowrap text-center">
|
||||||
<div class="text-sm">
|
<div class="text-sm">
|
||||||
<p class="font-semibold text-gray-900 dark:text-gray-100">
|
<p class="font-semibold text-gray-900 dark:text-gray-100">
|
||||||
{{ formatCurrency(model.price?.retail_price) }}
|
{{ formatCurrency(model.price?.retail_price) }}
|
||||||
@ -217,15 +293,22 @@ onMounted(() => {
|
|||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-center">
|
<td class="px-6 py-4 whitespace-nowrap text-center">
|
||||||
<span
|
<span
|
||||||
class="font-bold text-base"
|
class="font-bold text-base"
|
||||||
:class="{
|
:class="{
|
||||||
'text-red-500': model.stock < 10,
|
'text-red-500': model.stock < 10,
|
||||||
'text-green-600': model.stock >= 10
|
'text-green-600': model.stock >= 10
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
{{ model.stock }}
|
{{ model.stock }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
|
<td class="px-6 py-4 whitespace-nowrap text-center">
|
||||||
|
<div class="text-sm">
|
||||||
|
<p class="font-semibold text-gray-900 dark:text-gray-100">
|
||||||
|
{{ formatCurrency(model.inventory_value) }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-center">
|
<td class="px-6 py-4 whitespace-nowrap text-center">
|
||||||
<div class="flex items-center justify-center gap-2">
|
<div class="flex items-center justify-center gap-2">
|
||||||
<button
|
<button
|
||||||
@ -256,7 +339,7 @@ onMounted(() => {
|
|||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
<template #empty>
|
<template #empty>
|
||||||
<td colspan="6" class="table-cell text-center">
|
<td colspan="8" class="table-cell text-center">
|
||||||
<div class="flex flex-col items-center justify-center py-8 text-gray-500">
|
<div class="flex flex-col items-center justify-center py-8 text-gray-500">
|
||||||
<GoogleIcon
|
<GoogleIcon
|
||||||
name="inventory_2"
|
name="inventory_2"
|
||||||
|
|||||||
@ -28,13 +28,17 @@ const reportService = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches products without movement.
|
* Fetches products without movement.
|
||||||
|
* @param {string|null} fromDate - Start date in YYYY-MM-DD format.
|
||||||
|
* @param {string|null} toDate - End date in YYYY-MM-DD format.
|
||||||
* @param {boolean} includeStockValue - Flag to include the inventory value in the response.
|
* @param {boolean} includeStockValue - Flag to include the inventory value in the response.
|
||||||
|
* @param {number} page - Page number for pagination.
|
||||||
* @returns {Promise<Object>}
|
* @returns {Promise<Object>}
|
||||||
*/
|
*/
|
||||||
async getProductsWithoutMovement(fromDate = null, toDate = null, includeStockValue = true) {
|
async getProductsWithoutMovement(fromDate = null, toDate = null, includeStockValue = true, page = 1) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const params = {
|
const params = {
|
||||||
include_stock_value: includeStockValue ? 1 : 0
|
include_stock_value: includeStockValue ? 1 : 0,
|
||||||
|
page
|
||||||
};
|
};
|
||||||
|
|
||||||
if(fromDate) params.from_date = fromDate;
|
if(fromDate) params.from_date = fromDate;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user