Kardex
Kardex
Section titled “Kardex”Dominio: https://test-api-factura.edw-dev.com
Dominio: https://api-financiero.e-dinky.com
Los endpoints de kardex permiten consultar el historial de movimientos de inventario de un item específico y exportar esta información en diferentes formatos.
Consultar Kardex
Section titled “Consultar Kardex”Endpoint: GET /api/v1/items/{code}/kardex
Descripción: Obtiene el historial completo de movimientos de inventario (kardex) de un item específico.
Headers Requeridos
Section titled “Headers Requeridos”Accept: application/jsonAuthorization: Bearer {token}
Parámetros de Ruta
Section titled “Parámetros de Ruta”Parámetro | Tipo | Requerido | Descripción |
---|---|---|---|
code | string | Sí | Código del item |
Parámetros de Consulta Opcionales
Section titled “Parámetros de Consulta Opcionales”Parámetro | Tipo | Descripción |
---|---|---|
date_from | string | Fecha de inicio (YYYY-MM-DD) |
date_to | string | Fecha de fin (YYYY-MM-DD) |
movement_type | string | Tipo de movimiento (INCOME, OUTCOME) |
page | integer | Número de página (por defecto: 1) |
per_page | integer | Elementos por página (por defecto: 50, máximo: 100) |
Ejemplos de Implementación
Section titled “Ejemplos de Implementación”# Consultar kardex completocurl -X GET "https://dev-facturacion.e-dinky.test/api/v1/items/PROD-001/kardex" \ -H "Accept: application/json" \ -H "Authorization: Bearer your_token_here"
# Consultar kardex con filtroscurl -X GET "https://dev-facturacion.e-dinky.test/api/v1/items/PROD-001/kardex?date_from=2024-01-01&date_to=2024-01-31&movement_type=INCOME" \ -H "Accept: application/json" \ -H "Authorization: Bearer your_token_here"
# Consultar con paginacióncurl -X GET "https://dev-facturacion.e-dinky.test/api/v1/items/PROD-001/kardex?page=2&per_page=25" \ -H "Accept: application/json" \ -H "Authorization: Bearer your_token_here"
use Illuminate\Support\Facades\Http;
class KardexService{ private $token; private $baseUrl;
public function __construct($token, $baseUrl = 'https://dev-facturacion.e-dinky.test') { $this->token = $token; $this->baseUrl = $baseUrl; }
public function getKardex($itemCode, $filters = []) { $url = "{$this->baseUrl}/api/v1/items/{$itemCode}/kardex";
$response = Http::withHeaders([ 'Accept' => 'application/json', 'Authorization' => 'Bearer ' . $this->token ])->get($url, $filters);
if ($response->successful()) { return $response->json(); }
throw new Exception('Error al consultar kardex: ' . $response->json()['message']); }
public function getKardexByDateRange($itemCode, $dateFrom, $dateTo) { return $this->getKardex($itemCode, [ 'date_from' => $dateFrom, 'date_to' => $dateTo ]); }
public function getIncomeMovements($itemCode) { return $this->getKardex($itemCode, [ 'movement_type' => 'INCOME' ]); }
public function getOutcomeMovements($itemCode) { return $this->getKardex($itemCode, [ 'movement_type' => 'OUTCOME' ]); }}
// Uso$kardexService = new KardexService($token);
try { // Kardex completo $kardex = $kardexService->getKardex('PROD-001');
// Kardex del último mes $lastMonth = $kardexService->getKardexByDateRange( 'PROD-001', now()->subMonth()->format('Y-m-d'), now()->format('Y-m-d') );
// Solo ingresos $incomes = $kardexService->getIncomeMovements('PROD-001');
echo "Total de movimientos: " . count($kardex['payload']['movements']);
} catch (Exception $e) { echo "Error: " . $e->getMessage();}
const axios = require('axios');
class KardexService { constructor(token, baseUrl = 'https://dev-facturacion.e-dinky.test') { this.token = token; this.baseUrl = baseUrl; this.headers = { 'Accept': 'application/json', 'Authorization': `Bearer ${token}` }; }
async getKardex(itemCode, filters = {}) { try { const response = await axios.get( `${this.baseUrl}/api/v1/items/${itemCode}/kardex`, { headers: this.headers, params: filters } ); return response.data; } catch (error) { throw new Error(`Error al consultar kardex: ${error.response?.data?.message || error.message}`); } }
async getKardexByDateRange(itemCode, dateFrom, dateTo) { return this.getKardex(itemCode, { date_from: dateFrom, date_to: dateTo }); }
async getMovementsByType(itemCode, movementType) { return this.getKardex(itemCode, { movement_type: movementType }); }
async getKardexPaginated(itemCode, page = 1, perPage = 50) { return this.getKardex(itemCode, { page: page, per_page: perPage }); }
async getCurrentStock(itemCode) { const kardex = await this.getKardex(itemCode, { per_page: 1 }); return kardex.payload.current_stock; }}
// Usoconst kardexService = new KardexService(token);
// Ejemplos de uso(async () => { try { // Kardex completo const kardex = await kardexService.getKardex('PROD-001'); console.log('Movimientos totales:', kardex.payload.movements.length);
// Kardex del último mes const lastMonth = new Date(); lastMonth.setMonth(lastMonth.getMonth() - 1); const dateFrom = lastMonth.toISOString().split('T')[0]; const dateTo = new Date().toISOString().split('T')[0];
const monthlyKardex = await kardexService.getKardexByDateRange('PROD-001', dateFrom, dateTo); console.log('Movimientos del último mes:', monthlyKardex.payload.movements.length);
// Solo ingresos const incomes = await kardexService.getMovementsByType('PROD-001', 'INCOME'); console.log('Ingresos:', incomes.payload.movements.length);
// Stock actual const currentStock = await kardexService.getCurrentStock('PROD-001'); console.log('Stock actual:', currentStock);
} catch (error) { console.error('Error:', error.message); }})();
Respuesta Exitosa (200 OK)
Section titled “Respuesta Exitosa (200 OK)”{ "message": "Kardex obtenido exitosamente", "status": "OK", "payload": { "item": { "code": "PROD-001", "description": "Producto de ejemplo", "unit": "UND", "type": "PRODUCT" }, "current_stock": 125, "total_movements": 15, "movements": [ { "id": "MOV-2024-015", "date": "2024-01-15T14:30:00Z", "type": "INCOME", "quantity": 50, "unit_cost": 20.00, "total_cost": 1000.00, "balance_before": 75, "balance_after": 125, "description": "Compra a proveedor ABC", "reference": "COMP-2024-001", "document_type": "PURCHASE", "document_number": "FACT-001-001-000001" }, { "id": "MOV-2024-014", "date": "2024-01-14T10:15:00Z", "type": "OUTCOME", "quantity": 10, "unit_cost": 18.50, "total_cost": 185.00, "balance_before": 85, "balance_after": 75, "description": "Venta a cliente XYZ", "reference": "FACT-001-001-000123", "document_type": "INVOICE", "document_number": "FACT-001-001-000123" } ], "summary": { "total_income": 500, "total_outcome": 375, "net_movement": 125, "average_cost": 19.25, "total_value": 2406.25 }, "pagination": { "current_page": 1, "per_page": 50, "total_pages": 1, "total_items": 15, "has_next_page": false, "has_previous_page": false } }}
Exportar Kardex
Section titled “Exportar Kardex”Endpoint: GET /api/v1/items/{code}/kardex-export
Descripción: Exporta el kardex de un item en formato Excel (.xlsx) o PDF.
Headers Requeridos
Section titled “Headers Requeridos”Accept: application/jsonAuthorization: Bearer {token}
Parámetros de Ruta
Section titled “Parámetros de Ruta”Parámetro | Tipo | Requerido | Descripción |
---|---|---|---|
code | string | Sí | Código del item |
Parámetros de Consulta
Section titled “Parámetros de Consulta”Parámetro | Tipo | Requerido | Descripción |
---|---|---|---|
format | string | Sí | Formato de exportación (excel, pdf) |
date_from | string | No | Fecha de inicio (YYYY-MM-DD) |
date_to | string | No | Fecha de fin (YYYY-MM-DD) |
movement_type | string | No | Tipo de movimiento (INCOME, OUTCOME) |
include_summary | boolean | No | Incluir resumen (por defecto: true) |
Ejemplos de Implementación
Section titled “Ejemplos de Implementación”# Exportar kardex completo en Excelcurl -X GET "https://dev-facturacion.e-dinky.test/api/v1/items/PROD-001/kardex-export?format=excel" \ -H "Accept: application/json" \ -H "Authorization: Bearer your_token_here" \ -o "kardex_PROD-001.xlsx"
# Exportar kardex en PDF con filtroscurl -X GET "https://dev-facturacion.e-dinky.test/api/v1/items/PROD-001/kardex-export?format=pdf&date_from=2024-01-01&date_to=2024-01-31" \ -H "Accept: application/json" \ -H "Authorization: Bearer your_token_here" \ -o "kardex_PROD-001_enero.pdf"
# Exportar solo ingresos en Excelcurl -X GET "https://dev-facturacion.e-dinky.test/api/v1/items/PROD-001/kardex-export?format=excel&movement_type=INCOME" \ -H "Accept: application/json" \ -H "Authorization: Bearer your_token_here" \ -o "ingresos_PROD-001.xlsx"
use Illuminate\Support\Facades\Http;use Illuminate\Support\Facades\Storage;
class KardexExportService{ private $token; private $baseUrl;
public function __construct($token, $baseUrl = 'https://dev-facturacion.e-dinky.test') { $this->token = $token; $this->baseUrl = $baseUrl; }
public function exportKardex($itemCode, $format = 'excel', $filters = []) { $url = "{$this->baseUrl}/api/v1/items/{$itemCode}/kardex-export";
$params = array_merge(['format' => $format], $filters);
$response = Http::withHeaders([ 'Accept' => 'application/json', 'Authorization' => 'Bearer ' . $this->token ])->get($url, $params);
if ($response->successful()) { $result = $response->json(); return $result['payload']['download_url']; }
throw new Exception('Error al exportar kardex: ' . $response->json()['message']); }
public function downloadKardexFile($itemCode, $format = 'excel', $filters = []) { $downloadUrl = $this->exportKardex($itemCode, $format, $filters);
// Descargar el archivo $fileResponse = Http::withHeaders([ 'Authorization' => 'Bearer ' . $this->token ])->get($downloadUrl);
if ($fileResponse->successful()) { $extension = $format === 'excel' ? 'xlsx' : 'pdf'; $filename = "kardex_{$itemCode}_" . date('Y-m-d') . ".{$extension}";
Storage::put("exports/{$filename}", $fileResponse->body());
return [ 'success' => true, 'filename' => $filename, 'path' => "exports/{$filename}", 'size' => strlen($fileResponse->body()) ]; }
throw new Exception('Error al descargar archivo'); }
public function exportKardexByDateRange($itemCode, $dateFrom, $dateTo, $format = 'excel') { return $this->exportKardex($itemCode, $format, [ 'date_from' => $dateFrom, 'date_to' => $dateTo ]); }
public function exportMonthlyKardex($itemCode, $year, $month, $format = 'excel') { $dateFrom = "{$year}-{$month}-01"; $dateTo = date('Y-m-t', strtotime($dateFrom));
return $this->exportKardexByDateRange($itemCode, $dateFrom, $dateTo, $format); }}
// Uso$exportService = new KardexExportService($token);
try { // Exportar kardex completo en Excel $downloadUrl = $exportService->exportKardex('PROD-001', 'excel'); echo "Archivo disponible en: {$downloadUrl}";
// Descargar y guardar archivo $result = $exportService->downloadKardexFile('PROD-001', 'pdf', [ 'date_from' => '2024-01-01', 'date_to' => '2024-01-31' ]);
if ($result['success']) { echo "Archivo guardado: {$result['filename']} ({$result['size']} bytes)"; }
// Exportar kardex mensual $monthlyUrl = $exportService->exportMonthlyKardex('PROD-001', 2024, 1, 'excel'); echo "Kardex de enero 2024: {$monthlyUrl}";
} catch (Exception $e) { echo "Error: " . $e->getMessage();}
const axios = require('axios');const fs = require('fs');const path = require('path');
class KardexExportService { constructor(token, baseUrl = 'https://dev-facturacion.e-dinky.test') { this.token = token; this.baseUrl = baseUrl; this.headers = { 'Accept': 'application/json', 'Authorization': `Bearer ${token}` }; }
async exportKardex(itemCode, format = 'excel', filters = {}) { try { const params = { format, ...filters };
const response = await axios.get( `${this.baseUrl}/api/v1/items/${itemCode}/kardex-export`, { headers: this.headers, params: params } );
return response.data.payload.download_url; } catch (error) { throw new Error(`Error al exportar kardex: ${error.response?.data?.message || error.message}`); } }
async downloadKardexFile(itemCode, format = 'excel', filters = {}, outputDir = './downloads') { try { const downloadUrl = await this.exportKardex(itemCode, format, filters);
// Descargar el archivo const fileResponse = await axios.get(downloadUrl, { headers: { 'Authorization': `Bearer ${this.token}` }, responseType: 'arraybuffer' });
// Crear directorio si no existe if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); }
const extension = format === 'excel' ? 'xlsx' : 'pdf'; const filename = `kardex_${itemCode}_${new Date().toISOString().split('T')[0]}.${extension}`; const filepath = path.join(outputDir, filename);
fs.writeFileSync(filepath, fileResponse.data);
return { success: true, filename: filename, filepath: filepath, size: fileResponse.data.length }; } catch (error) { throw new Error(`Error al descargar archivo: ${error.message}`); } }
async exportKardexByDateRange(itemCode, dateFrom, dateTo, format = 'excel') { return this.exportKardex(itemCode, format, { date_from: dateFrom, date_to: dateTo }); }
async exportMonthlyKardex(itemCode, year, month, format = 'excel') { const dateFrom = `${year}-${month.toString().padStart(2, '0')}-01`; const dateTo = new Date(year, month, 0).toISOString().split('T')[0];
return this.exportKardexByDateRange(itemCode, dateFrom, dateTo, format); }
async exportMultipleItems(itemCodes, format = 'excel', filters = {}) { const results = [];
for (const itemCode of itemCodes) { try { const downloadUrl = await this.exportKardex(itemCode, format, filters); results.push({ itemCode: itemCode, success: true, downloadUrl: downloadUrl }); } catch (error) { results.push({ itemCode: itemCode, success: false, error: error.message }); } }
return results; }}
// Usoconst exportService = new KardexExportService(token);
(async () => { try { // Exportar kardex en Excel const downloadUrl = await exportService.exportKardex('PROD-001', 'excel'); console.log('Archivo disponible en:', downloadUrl);
// Descargar y guardar archivo const result = await exportService.downloadKardexFile('PROD-001', 'pdf', { date_from: '2024-01-01', date_to: '2024-01-31' });
if (result.success) { console.log(`Archivo guardado: ${result.filename} (${result.size} bytes)`); }
// Exportar múltiples items const multipleResults = await exportService.exportMultipleItems( ['PROD-001', 'PROD-002', 'PROD-003'], 'excel', { movement_type: 'INCOME' } );
multipleResults.forEach(result => { if (result.success) { console.log(`✓ ${result.itemCode}: ${result.downloadUrl}`); } else { console.log(`✗ ${result.itemCode}: ${result.error}`); } });
} catch (error) { console.error('Error:', error.message); }})();
Respuesta Exitosa (200 OK)
Section titled “Respuesta Exitosa (200 OK)”{ "message": "Kardex exportado exitosamente", "status": "OK", "payload": { "export_id": "EXP-2024-001", "item_code": "PROD-001", "format": "excel", "download_url": "https://dev-facturacion.e-dinky.test/downloads/kardex/EXP-2024-001.xlsx", "expires_at": "2024-01-16T14:30:00Z", "file_size": 45678, "total_movements": 15, "date_range": { "from": "2024-01-01", "to": "2024-01-31" }, "filters_applied": { "movement_type": null, "include_summary": true } }}
Respuestas de Error
Section titled “Respuestas de Error”404 Not Found
Section titled “404 Not Found”{ "message": "Item no encontrado", "status": "NOT_FOUND", "error": "El item con código 'PROD-999' no existe"}
422 Unprocessable Entity
Section titled “422 Unprocessable Entity”{ "message": "Error de validación", "status": "UNPROCESSABLE_ENTITY", "errors": { "format": [ "El formato debe ser 'excel' o 'pdf'" ], "date_from": [ "La fecha de inicio debe ser anterior a la fecha de fin" ] }}
Códigos de Respuesta
Section titled “Códigos de Respuesta”Código | Descripción |
---|---|
200 | Consulta/exportación exitosa |
400 | Error en los parámetros enviados |
401 | Token de autorización inválido |
404 | Item no encontrado |
422 | Error de validación |
500 | Error interno del servidor |
Filtros Avanzados
Section titled “Filtros Avanzados”Por Rango de Fechas
Section titled “Por Rango de Fechas”curl "https://dev-facturacion.e-dinky.test/api/v1/items/PROD-001/kardex?date_from=2024-01-01&date_to=2024-01-31"
Por Tipo de Movimiento
Section titled “Por Tipo de Movimiento”# Solo ingresoscurl "https://dev-facturacion.e-dinky.test/api/v1/items/PROD-001/kardex?movement_type=INCOME"
# Solo salidascurl "https://dev-facturacion.e-dinky.test/api/v1/items/PROD-001/kardex?movement_type=OUTCOME"
Combinando Filtros
Section titled “Combinando Filtros”curl "https://dev-facturacion.e-dinky.test/api/v1/items/PROD-001/kardex?date_from=2024-01-01&movement_type=INCOME&per_page=25"
Análisis de Kardex
Section titled “Análisis de Kardex”Cálculo de Rotación de Inventario
Section titled “Cálculo de Rotación de Inventario”// PHP - Calcular rotación de inventariofunction calculateInventoryTurnover($itemCode, $token) { $kardexService = new KardexService($token);
// Obtener movimientos del último año $yearAgo = date('Y-m-d', strtotime('-1 year')); $today = date('Y-m-d');
$kardex = $kardexService->getKardexByDateRange($itemCode, $yearAgo, $today);
$totalOutcome = 0; $averageStock = 0; $stockReadings = [];
foreach ($kardex['payload']['movements'] as $movement) { if ($movement['type'] === 'OUTCOME') { $totalOutcome += $movement['quantity']; } $stockReadings[] = $movement['balance_after']; }
$averageStock = array_sum($stockReadings) / count($stockReadings); $turnoverRatio = $averageStock > 0 ? $totalOutcome / $averageStock : 0;
return [ 'item_code' => $itemCode, 'period' => '12 months', 'total_outcome' => $totalOutcome, 'average_stock' => round($averageStock, 2), 'turnover_ratio' => round($turnoverRatio, 2), 'days_of_supply' => $turnoverRatio > 0 ? round(365 / $turnoverRatio, 0) : 0 ];}
Análisis de Tendencias
Section titled “Análisis de Tendencias”// JavaScript - Análisis de tendencias de stockconst analyzeTrends = async (itemCode, token) => { const kardexService = new KardexService(token);
// Obtener kardex de los últimos 6 meses const sixMonthsAgo = new Date(); sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6);
const kardex = await kardexService.getKardexByDateRange( itemCode, sixMonthsAgo.toISOString().split('T')[0], new Date().toISOString().split('T')[0] );
const movements = kardex.payload.movements; const monthlyData = {};
movements.forEach(movement => { const month = movement.date.substring(0, 7); // YYYY-MM
if (!monthlyData[month]) { monthlyData[month] = { income: 0, outcome: 0, net: 0, endStock: 0 }; }
if (movement.type === 'INCOME') { monthlyData[month].income += movement.quantity; } else { monthlyData[month].outcome += movement.quantity; }
monthlyData[month].net = monthlyData[month].income - monthlyData[month].outcome; monthlyData[month].endStock = movement.balance_after; });
// Calcular tendencias const months = Object.keys(monthlyData).sort(); const stockTrend = months.map(month => monthlyData[month].endStock);
const isIncreasing = stockTrend[stockTrend.length - 1] > stockTrend[0]; const averageMonthlyChange = months.length > 1 ? (stockTrend[stockTrend.length - 1] - stockTrend[0]) / (months.length - 1) : 0;
return { itemCode, period: '6 months', monthlyData, trend: { direction: isIncreasing ? 'increasing' : 'decreasing', averageMonthlyChange: Math.round(averageMonthlyChange * 100) / 100, currentStock: stockTrend[stockTrend.length - 1], initialStock: stockTrend[0] } };};
Formatos de Exportación
Section titled “Formatos de Exportación”Excel (.xlsx)
Section titled “Excel (.xlsx)”- Ventajas: Fácil manipulación, cálculos automáticos, filtros
- Uso recomendado: Análisis detallado, reportes internos
- Contenido: Todas las columnas de datos, gráficos opcionales
- Ventajas: Formato fijo, fácil compartir, profesional
- Uso recomendado: Reportes oficiales, auditorías
- Contenido: Resumen ejecutivo, datos tabulares, gráficos
Mejores Prácticas
Section titled “Mejores Prácticas”1. Filtrado Eficiente
Section titled “1. Filtrado Eficiente”- Usa rangos de fechas específicos para consultas grandes
- Filtra por tipo de movimiento cuando sea relevante
- Implementa paginación para datasets grandes
2. Exportación Responsable
Section titled “2. Exportación Responsable”- Limita las exportaciones a rangos de fechas razonables
- Usa formatos apropiados según el uso final
- Implementa cache para exportaciones frecuentes
3. Análisis de Datos
Section titled “3. Análisis de Datos”- Calcula métricas clave: rotación, días de suministro
- Identifica patrones estacionales
- Monitorea tendencias de stock
4. Gestión de Archivos
Section titled “4. Gestión de Archivos”- Los archivos exportados expiran en 24 horas
- Descarga inmediatamente después de la exportación
- Implementa manejo de errores para descargas
Casos de Uso Comunes
Section titled “Casos de Uso Comunes”1. Auditoría de Inventario
Section titled “1. Auditoría de Inventario”# Exportar kardex completo para auditoríacurl -X GET "https://dev-facturacion.e-dinky.test/api/v1/items/PROD-001/kardex-export?format=pdf&include_summary=true" \ -H "Authorization: Bearer $TOKEN" \ -o "auditoria_PROD-001.pdf"
2. Análisis de Costos
Section titled “2. Análisis de Costos”# Obtener solo ingresos para análisis de costoscurl -X GET "https://dev-facturacion.e-dinky.test/api/v1/items/PROD-001/kardex?movement_type=INCOME&date_from=2024-01-01" \ -H "Authorization: Bearer $TOKEN"
3. Reporte Mensual
Section titled “3. Reporte Mensual”# Exportar kardex del mes actualcurl -X GET "https://dev-facturacion.e-dinky.test/api/v1/items/PROD-001/kardex-export?format=excel&date_from=2024-01-01&date_to=2024-01-31" \ -H "Authorization: Bearer $TOKEN" \ -o "kardex_enero_2024.xlsx"
4. Verificación de Stock
Section titled “4. Verificación de Stock”# Consultar últimos movimientoscurl -X GET "https://dev-facturacion.e-dinky.test/api/v1/items/PROD-001/kardex?per_page=10" \ -H "Authorization: Bearer $TOKEN"
Notas Importantes
Section titled “Notas Importantes”- 📊 Datos en tiempo real: El kardex refleja el estado actual del inventario
- 📅 Historial completo: Se mantiene registro de todos los movimientos
- 🔒 Datos inmutables: Los movimientos no pueden modificarse una vez registrados
- 📈 Cálculos automáticos: Los balances se calculan automáticamente
- ⏰ Archivos temporales: Las exportaciones expiran en 24 horas
- 📋 Formatos múltiples: Soporte para Excel y PDF
- 🔍 Filtros flexibles: Múltiples opciones de filtrado disponibles
- 📊 Resúmenes incluidos: Estadísticas automáticas en exportaciones