Versión 2.0.0: validar contra Consignación (suma de OpenInvQty) en lugar del stock
DP_Stock (v2) suma PDN1.OpenInvQty de las Entradas de Consignación abiertas del almacén, lo mismo que usa el FIFO. La columna se muestra como Consignación. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3f6395bfb0
commit
2182147adb
11 files changed
+32
-21
No files matched your search
@@ -107,7 +107,7 @@ export class PlanificacionesService {
|
|||||||
|
|
||||||
for (const { fila, stock } of consultas) {
|
for (const { fila, stock } of consultas) {
|
||||||
if (!stock) {
|
if (!stock) {
|
||||||
errores.push(`${fila.itemCode}: el artículo no existe o no tiene stock registrado en el almacén ${whsCode}.`);
|
errores.push(`${fila.itemCode}: el artículo no existe.`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const articulo: ArticuloADevolver = {
|
const articulo: ArticuloADevolver = {
|
||||||
|
|||||||
@@ -15,17 +15,28 @@ export interface ConsultaGuardada {
|
|||||||
hana: string;
|
hana: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "Stock" = lo que realmente se puede devolver: la suma de PDN1.OpenInvQty de las líneas abiertas de
|
||||||
|
* Entradas de mercancías de Consignación (U_DOT_TipoRecepcion = 'C') del almacén, las mismas que usa el
|
||||||
|
* FIFO. No es OITW.OnHand: el stock físico incluye mercancía que no es devolvible por esta vía.
|
||||||
|
* Devuelve fila (con 0) si el artículo existe pero no tiene consignación; ninguna si no existe.
|
||||||
|
* La columna se sigue llamando OnHand porque así se guarda en el UDO (U_OnHand).
|
||||||
|
*/
|
||||||
export const CONSULTA_STOCK: ConsultaGuardada = {
|
export const CONSULTA_STOCK: ConsultaGuardada = {
|
||||||
codigo: 'DP_Stock',
|
codigo: 'DP_Stock',
|
||||||
version: 1,
|
version: 2,
|
||||||
mssql:
|
mssql:
|
||||||
'SELECT T0.[ItemCode], T0.[ItemName], T1.[OnHand] ' +
|
'SELECT T0.[ItemCode], T0.[ItemName], ' +
|
||||||
'FROM OITM T0 INNER JOIN OITW T1 ON T1.[ItemCode] = T0.[ItemCode] ' +
|
'COALESCE((SELECT SUM(T2.[OpenInvQty]) FROM OPDN T1 INNER JOIN PDN1 T2 ON T2.[DocEntry] = T1.[DocEntry] ' +
|
||||||
'WHERE T1.[WhsCode] = :whs AND T0.[ItemCode] = :item',
|
"WHERE T1.[U_DOT_TipoRecepcion] = 'C' AND T2.[LineStatus] = 'O' " +
|
||||||
|
'AND T2.[ItemCode] = T0.[ItemCode] AND T2.[WhsCode] = :whs), 0) AS [OnHand] ' +
|
||||||
|
'FROM OITM T0 WHERE T0.[ItemCode] = :item',
|
||||||
hana:
|
hana:
|
||||||
'SELECT T0."ItemCode", T0."ItemName", T1."OnHand" ' +
|
'SELECT T0."ItemCode", T0."ItemName", ' +
|
||||||
'FROM OITM T0 INNER JOIN OITW T1 ON T1."ItemCode" = T0."ItemCode" ' +
|
'COALESCE((SELECT SUM(T2."OpenInvQty") FROM OPDN T1 INNER JOIN PDN1 T2 ON T2."DocEntry" = T1."DocEntry" ' +
|
||||||
'WHERE T1."WhsCode" = :whs AND T0."ItemCode" = :item',
|
"WHERE T1.\"U_DOT_TipoRecepcion\" = 'C' AND T2.\"LineStatus\" = 'O' " +
|
||||||
|
'AND T2."ItemCode" = T0."ItemCode" AND T2."WhsCode" = :whs), 0) AS "OnHand" ' +
|
||||||
|
'FROM OITM T0 WHERE T0."ItemCode" = :item',
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -136,7 +136,7 @@
|
|||||||
<table class="grid">
|
<table class="grid">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th></th><th>Artículo</th><th class="num">Cantidad</th><th class="num">Stock</th>
|
<th></th><th>Artículo</th><th class="num">Cantidad</th><th class="num">Consignación</th>
|
||||||
<th>Entrada</th><th>Fecha entrada</th><th class="num">Abierta</th><th class="num">Asignada</th>
|
<th>Entrada</th><th>Fecha entrada</th><th class="num">Abierta</th><th class="num">Asignada</th>
|
||||||
<th>Proveedor</th><th>Devolución</th>
|
<th>Proveedor</th><th>Devolución</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -260,7 +260,7 @@ export class PlanificacionDetallePage implements OnInit, OnDestroy {
|
|||||||
{ titulo: 'Artículo', ancho: 18 },
|
{ titulo: 'Artículo', ancho: 18 },
|
||||||
{ titulo: 'Descripción', ancho: 45 },
|
{ titulo: 'Descripción', ancho: 45 },
|
||||||
{ titulo: 'Cantidad', tipo: 'numero' },
|
{ titulo: 'Cantidad', tipo: 'numero' },
|
||||||
{ titulo: 'Stock', tipo: 'numero' },
|
{ titulo: 'Consignación', tipo: 'numero' },
|
||||||
{ titulo: 'Estado', ancho: 11 },
|
{ titulo: 'Estado', ancho: 11 },
|
||||||
{ titulo: 'Devolver', ancho: 10 },
|
{ titulo: 'Devolver', ancho: 10 },
|
||||||
{ titulo: 'Entrada', tipo: 'numero' },
|
{ titulo: 'Entrada', tipo: 'numero' },
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<section class="encabezado">
|
<section class="encabezado">
|
||||||
<div>
|
<div>
|
||||||
<h1>{{ reemplaza() ? 'Rehacer planificación Nº ' + reemplazaNumero() : 'Nueva planificación' }}</h1>
|
<h1>{{ reemplaza() ? 'Rehacer planificación Nº ' + reemplazaNumero() : 'Nueva planificación' }}</h1>
|
||||||
<p class="muted">Pegue los artículos y cantidades desde Excel; se valida el stock del almacén antes de procesar.</p>
|
<p class="muted">Pegue los artículos y cantidades desde Excel; se valida contra lo disponible en Consignación del almacén (cantidad abierta de las Entradas de mercancías) antes de procesar.</p>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn" routerLink="/planificaciones">Volver</a>
|
<a class="btn" routerLink="/planificaciones">Volver</a>
|
||||||
</section>
|
</section>
|
||||||
@@ -91,7 +91,7 @@
|
|||||||
<table class="grid">
|
<table class="grid">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Código</th><th>Descripción</th><th class="num">A devolver</th><th class="num">Stock</th><th>Estado</th>
|
<th>Código</th><th>Descripción</th><th class="num">A devolver</th><th class="num">Consignación</th><th>Estado</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -112,7 +112,7 @@
|
|||||||
</table>
|
</table>
|
||||||
@if (sinStock() > 0) {
|
@if (sinStock() > 0) {
|
||||||
<p class="muted nota">
|
<p class="muted nota">
|
||||||
{{ sinStock() }} artículo(s) sin stock suficiente quedarán fuera al procesar.
|
{{ sinStock() }} artículo(s) sin consignación suficiente quedarán fuera al procesar.
|
||||||
</p>
|
</p>
|
||||||
}
|
}
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ describe('NuevaPlanificacionPage', () => {
|
|||||||
expect(nombres[0]).toMatch(/^articulos-validados-01-\d{4}-\d{2}-\d{2}\.xlsx$/);
|
expect(nombres[0]).toMatch(/^articulos-validados-01-\d{4}-\d{2}-\d{2}\.xlsx$/);
|
||||||
const hoja = leerZip(new Uint8Array(await blobs[0].arrayBuffer())).get('xl/worksheets/sheet1.xml')!;
|
const hoja = leerZip(new Uint8Array(await blobs[0].arrayBuffer())).get('xl/worksheets/sheet1.xml')!;
|
||||||
expect(filasDeHoja(hoja)).toEqual([
|
expect(filasDeHoja(hoja)).toEqual([
|
||||||
['Código', 'Descripción', 'A devolver', 'Stock', 'Estado'],
|
['Código', 'Descripción', 'A devolver', 'Consignación', 'Estado'],
|
||||||
['A1', 'Libro', '5', '10', 'OK'],
|
['A1', 'Libro', '5', '10', 'OK'],
|
||||||
['A2', 'Libro', '5', '10', 'Sin stock'],
|
['A2', 'Libro', '5', '10', 'Sin stock'],
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ export class NuevaPlanificacionPage implements OnInit {
|
|||||||
{ titulo: 'Código', ancho: 18 },
|
{ titulo: 'Código', ancho: 18 },
|
||||||
{ titulo: 'Descripción', ancho: 50 },
|
{ titulo: 'Descripción', ancho: 50 },
|
||||||
{ titulo: 'A devolver', tipo: 'numero', ancho: 12 },
|
{ titulo: 'A devolver', tipo: 'numero', ancho: 12 },
|
||||||
{ titulo: 'Stock', tipo: 'numero', ancho: 12 },
|
{ titulo: 'Consignación', tipo: 'numero', ancho: 12 },
|
||||||
{ titulo: 'Estado', ancho: 12 },
|
{ titulo: 'Estado', ancho: 12 },
|
||||||
],
|
],
|
||||||
filas: lineas.map((l) => [l.itemCode, l.itemDescription, l.quantity, l.onHand, l.status === 'OK' ? 'OK' : 'Sin stock']),
|
filas: lineas.map((l) => [l.itemCode, l.itemDescription, l.quantity, l.onHand, l.status === 'OK' ? 'OK' : 'Sin stock']),
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ describe('asignarFifo', () => {
|
|||||||
expect(lineas[1]).toMatchObject({ itemCode: 'a001', applied: true, baseType: 20, baseRef: 1001 });
|
expect(lineas[1]).toMatchObject({ itemCode: 'a001', applied: true, baseType: 20, baseRef: 1001 });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('omite artículos sin stock suficiente', () => {
|
it('omite artículos sin consignación suficiente', () => {
|
||||||
const { lineas, advertencias } = asignarFifo([{ itemCode: 'A001', itemName: 'Art', quantity: 6, onHand: 5 }], new Map());
|
const { lineas, advertencias } = asignarFifo([{ itemCode: 'A001', itemName: 'Art', quantity: 6, onHand: 5 }], new Map());
|
||||||
expect(lineas).toEqual([]);
|
expect(lineas).toEqual([]);
|
||||||
expect(advertencias[0]).toContain('sin stock');
|
expect(advertencias[0]).toContain('sin consignación');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('advierte cuando las Entradas no alcanzan', () => {
|
it('advierte cuando las Entradas no alcanzan', () => {
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export function asignarFifo(articulos: ArticuloADevolver[], entradasPorItem: Map
|
|||||||
const padre = crearLineaPadre(articulo);
|
const padre = crearLineaPadre(articulo);
|
||||||
|
|
||||||
if (padre.status === 'SIN_STOCK') {
|
if (padre.status === 'SIN_STOCK') {
|
||||||
advertencias.push(`${articulo.itemCode}: sin stock suficiente (${articulo.onHand} de ${articulo.quantity}), se omite.`);
|
advertencias.push(`${articulo.itemCode}: sin consignación suficiente (${articulo.onHand} de ${articulo.quantity}), se omite.`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "devolucion-proveedores-web",
|
"name": "devolucion-proveedores-web",
|
||||||
"version": "1.1.0",
|
"version": "2.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "devolucion-proveedores-web",
|
"name": "devolucion-proveedores-web",
|
||||||
"version": "1.1.0",
|
"version": "2.0.0",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"libs/*",
|
"libs/*",
|
||||||
"apps/*"
|
"apps/*"
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "devolucion-proveedores-web",
|
"name": "devolucion-proveedores-web",
|
||||||
"version": "1.1.0",
|
"version": "2.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"libs/*",
|
"libs/*",
|
||||||
|
|||||||
Reference in new issue
Block a user