Diseño de códigos en sistemas empresariales ── Cómo definir códigos de producto y cliente, y el dígito de control
· Actualizado el: · Go Komura · Diseño de códigos, Dígito de control, Sistemas empresariales, Base de datos, C#, .NET, Excel, Tabla de decisión, Diseño, Desarrollo en Windows
«Vamos a migrar a un nuevo sistema, así que defina el sistema de códigos de producto» ── esta tarea aparece siempre en la construcción de sistemas empresariales y en la migración de libros de Excel. Y un código decidido de improviso, sobre la marcha, suele terminar viviendo más tiempo que el propio sistema. Aunque el sistema se sustituya cada diez años, el número de cliente que se entregó a los proveedores y el código de producto impreso en los formularios antiguos siguen ahí.
Por otro lado, en el diseño de códigos se ha acumulado una cantidad sorprendente de conocimiento heredado. Números que se operan a gran escala, como el código JAN, el número de tarjeta de crédito o el Mi Number japonés, tienen publicados —junto con las razones de cada decisión de diseño— desde el método de detección de errores de entrada hasta la asignación de cada dígito. Partiendo de esa base, este artículo ordena las reglas para definir códigos de producto, de cliente o de comprobante en sistemas empresariales, y un mecanismo utilizable para ello: el dígito de control.
1. Antes que nada, la conclusión
- El código debe limitarse a identificar; el significado se guarda como atributo en la base de datos. El «código significativo», que incrusta el departamento, la categoría o el año en sus dígitos, siempre termina rompiéndose con las reorganizaciones de la empresa o los cambios de clasificación. El Mi Number japonés está diseñado precisamente como un número sin significado.1
- A los códigos que las personas introducen, transcriben o leen en voz alta se les añade un dígito de control. Está documentado empíricamente que la mayoría de los errores de entrada son «una sola cifra mal tecleada» y «un intercambio entre cifras adyacentes»2, y el dígito de control está diseñado precisamente para detectar estos dos casos.
- El tipo de caracteres se decide según el uso operativo. Si interviene el teléfono, el fax o la escritura a mano, use solo números. Si necesita ahorrar dígitos, use un alfanumérico que excluya los caracteres confusos (I, L, O, etc.), como Crockford Base323.
- Reserve el número de dígitos con la referencia «diez veces el volumen futuro, más un dígito», y si usa ceros a la izquierda, trate el código como cadena de texto en todo el sistema. En el momento en que Excel interpreta el código como un número, elimina los ceros iniciales y redondea a 0 todo lo que pase del dígito 15.4
- Un código que ya se usó una vez no se reutiliza aunque quede vacante. El número que permanece en formularios antiguos, registros y sistemas de clientes o proveedores acabaría señalando a un objeto distinto.
- Si va a poner un código de barras en el producto para distribuirlo, no use un código propio: adopte el código JAN (estándar GS1).5
2. ¿Debe el código tener significado? ── La primera y mayor bifurcación
Lo primero que hay que decidir al diseñar un código no es el número de dígitos ni el tipo de caracteres, sino si el código debe tener significado.
Un diseño habitual es este: «el código de producto tiene 8 dígitos; los dos primeros son el departamento, los tres siguientes la categoría y los tres últimos un número secuencial». En el momento de decidirlo parece ordenado, pero al cabo de unos años ocurre lo siguiente.
- Una reorganización fusiona departamentos y los productos con el código de departamento antiguo quedan en el aire. Si se renumeran, dejan de coincidir con los formularios antiguos; si se dejan como están, la tabla maestra se llena de excepciones donde «el departamento del código no coincide con el departamento real».
- Aparecen productos que cruzan categorías (a la vez alimento y artículo diverso, por ejemplo), y hace falta una norma interna para decidir qué código asignarles.
- Una sola categoría acumula tantos productos que se agotan los tres dígitos del secuencial, y empieza a operar la excepción de «pedir prestado, solo para esta categoría, un número libre de otro departamento».
La causa es clara: la organización y la clasificación cambian, pero se grabó el código dando por hecho que no cambiarían. Por eso el principio es el contrario: el código debe ser solo un símbolo que identifique de forma unívoca al objeto, y atributos como el departamento o la categoría deben vivir en columnas de la base de datos. Como atributo, por muchas veces que cambie basta con un UPDATE, y el código sale indemne.
Los sistemas de numeración del Estado japonés también se construyeron siguiendo este principio. El Mi Number es un número sin significado, de 11 dígitos más 1 dígito de verificación, generado convirtiendo el código del registro de residentes «mediante un método que no introduce artificio alguno».1 Que carezca de significado sirve tanto para que no se pueda inferir ningún atributo personal a partir del número, como para que un cambio de atributo (una mudanza, un cambio de apellido) no obligue a cambiar el número.
Aun así, en la práctica sigue siendo habitual pedir «que con solo mirar el principio se sepa si es cliente o proveedor», así que la tabla de decisión queda de la siguiente manera.
| Método | Ejemplo | Fortaleza | Punto de ruptura | Dónde encaja |
|---|---|---|---|---|
| Código totalmente significativo | 02-104-317 (departamento-categoría-secuencial) |
Al leerlo se conocen los atributos | Hay que renumerar con cada reorganización, cambio de categoría o agotamiento parcial de dígitos | Solo cuando el número de objetos no varía y la clasificación está fijada institucionalmente |
| Secuencial sin significado | 10000317 |
No se rompe pase lo que pase; la numeración es sencilla | La persona no puede deducir nada del código | Sistemas donde los atributos siempre se muestran junto al código en pantallas e informes |
| Híbrido | C-0317-5 (1 carácter de tipo + secuencial + dígito de control) |
Solo evita la confusión de tipo; el punto de ruptura es mínimo | Cambio en la definición del tipo (poco frecuente) | La opción por defecto si hay dudas; mantenga el tipo tan simple como «cliente/proveedor/producto» |
Lo recomendado son las dos últimas filas de la tabla. Cuanto más significado se incruste, más semillas de ruptura se están sembrando.
3. Tipo de caracteres y número de dígitos ── Un compromiso entre eficiencia de información y legibilidad
Lo siguiente que se decide es el tipo de caracteres y el número de dígitos. Esto es puramente aritmética: cuantos más tipos de caracteres se puedan usar en un dígito, más objetos se pueden representar con el mismo número de dígitos.
| Tipo de caracteres | Cuántos se representan con 6 dígitos | Resistencia a lectura en voz alta / escritura a mano |
|---|---|---|
| Solo números (10 tipos) | 1 millón | ◎ Resiste bien el teléfono, el fax y la escritura a mano |
| 32 tipos: letras mayúsculas y números sin los caracteres confusos (Crockford Base323) | Unos 1070 millones | ○ Con protección contra lecturas erróneas, aunque en voz alta es peor que los números |
| Letras mayúsculas + números (36 tipos) | Unos 2180 millones | △ Siempre se producen confusiones entre 0 y O, o entre 1 e I |
Los criterios de decisión son estos dos.
- ¿Ese código se lee por teléfono, o se escribe a mano o se envía por fax? Si se cumple aunque sea una sola de estas condiciones, se recomienda usar solo números. En el momento en que se mezclan letras, aparece el coste de tener que confirmar («¿i de Italia? ¿uno?») y el riesgo de una lectura errónea.
- ¿Usando solo números el código se volvería demasiado largo? Solo cuando se necesita comprimir el número de dígitos —por ejemplo, porque el número de objetos supera las decenas de millones— tiene sentido plantearse un alfanumérico. Incluso entonces conviene usar, en lugar de los 36 tipos sin más, un alfabeto ya diseñado para ello, como Crockford Base32, que excluye
I,L,O(y tambiénU, para evitar palabras malsonantes accidentales). Esta especificación lleva la protección contra lecturas erróneas hasta el final: al decodificar acepta también minúsculas, e interpretaiolcomo1, yocomo0.3
El número de dígitos no se calcula a partir del «volumen actual», sino a la inversa, a partir del «volumen que se podría alcanzar en los 20 o 30 años que van a seguir vivos el sistema y los formularios», y a eso se le añade un dígito de margen. Es equivalente decirlo como reservar el número de dígitos que representa diez veces el volumen que se podría alcanzar (con 100 000 casos, 6 dígitos). Si se añade un dígito de control, ese dígito se cuenta aparte de este margen (capítulo 4). Cuando en los capítulos 1 y 9 se escribe «diez veces el volumen futuro más un dígito», se refiere a la suma de «el cuerpo = la parte de diez veces» más «el dígito de control». Si se prevén 100 000 casos, el recuento queda en 6 dígitos de cuerpo más 1 dígito de control, es decir, 7 dígitos.
Lo temible del desbordamiento de dígitos se trata en el capítulo 8, pero el coste de no provocar en la propia empresa una reforma integral como la que supuso el paso del código postal japonés de 5 a 7 dígitos (1998) es, sencillamente, un dígito más.
Otra cosa que ayuda de forma discreta pero eficaz son los separadores. Igual que el número de una tarjeta de crédito se imprime en grupos de 4 dígitos, las personas solo pueden manejar con precisión una cadena larga de números en bloques de 3 o 4 cifras. Si va a mostrar un código de 8 dígitos o más en un formulario o en pantalla, muéstrelo separado, como 1234-5678. Ahora bien, el principio es que el separador no forma parte del dato: se añade solo al mostrarlo (capítulo 6).
4. Dígito de control ── Que el propio código detecte los errores de entrada
4.1 Los errores de entrada son, en realidad, de dos tipos
El dígito de control (también llamado dígito de verificación) es una cifra que se añade al final (o al principio) del código, calculada a partir de las demás cifras, de modo que permite detectar en el acto un error en el código introducido. El propio dígito de verificación del Mi Number tiene su objetivo recogido explícitamente en el reglamento: «con el objetivo de confirmar que no hay errores al introducirlo en un equipo informático».1
Qué fórmula de cálculo es buena depende de qué tipo de errores comete un ser humano. Según el estudio clásico de Verhoeff, que analizó datos de errores de transcripción del sistema neerlandés de giro postal y otros similares, entre el 60 % y el 95 % de los errores son una equivocación de una sola cifra (single error), con una diferencia aplastante sobre el resto. En segundo lugar, los errores de dos cifras suponen entre el 10 % y el 20 %, y la mayoría son de dos cifras adyacentes, en particular el intercambio (transposición) del tipo ab→ba. El resto de categorías (el tipo aa→bb, los intercambios saltando una posición, etc.) suponen cada una apenas entre el 0,5 % y el 1,5 % del total.2
Es decir, el rendimiento de un método de dígito de control puede evaluarse, en la práctica, mediante estos dos puntos: «cuánto es capaz de detectar el error de una sola cifra» y «cuánto es capaz de detectar la transposición de cifras adyacentes».
4.2 Métodos utilizados en la práctica y su capacidad de detección (tabla de decisión)
Antes de leer la tabla, aclaremos cómo interpretar los nombres de los métodos. «Módulo NN» significa «el método que usa el resto de dividir entre NN» (módulo 11, el resto de dividir entre 11). «Ponderación» (weight) es el factor por el que se multiplica cada cifra; con «ponderación 3-1», desde el extremo derecho se multiplica alternando por 3 y por 1: 3, 1, 3, 1… En cualquiera de estos métodos, lo único que se hace es «multiplicar cada cifra por el factor establecido, sumarlas, y a partir del resto de dividir esa suma entre un número fijo, construir la cifra de control».
| Método | Dónde se usa | Error de 1 cifra | Transposición adyacente | Características |
|---|---|---|---|---|
| Módulo 10, ponderación 3-1 | Estándar GS1 (código JAN y similares), ISBN-136 | Detecta todos | Se le escapan los pares con diferencia 5 (05↔50, 16↔61, etc.) |
Prácticamente la única opción si se trabaja con código de barras |
| Luhn (módulo 10) | Número de tarjeta de crédito7 | Detecta todos | Solo se le escapa el par 09↔90 |
El más sencillo de implementar; candidato por defecto para el código propio |
| Módulo 11 (ponderado) | Mi Number8, ISBN antiguo | Detecta casi todos | Detecta casi todos | Alta capacidad de detección, pero tiene el problema del «ajuste del resto» (se explica más abajo) |
| Módulo 9 (ponderado) | Número de persona jurídica9 | Se le escapa 0↔9 |
Se le escapa el par 0↔9 |
Al dividir entre 9 no puede distinguir 0 de 9 |
| Damm / Verhoeff | Métodos académicos | Detecta todos | Detecta todos | Se implementan con una tabla de consulta (Damm10) o con operaciones de grupo (Verhoeff2) |
Algunas aclaraciones adicionales.
- El método JAN (módulo 10, ponderación 3-1) multiplica alternando por 3 y por 1, empezando por el extremo derecho, suma el resultado, y toma como dígito de control la cifra de las unidades de «10 − (el resto de dividir la suma entre 10)».6 La suma no cambia con una transposición cuando la diferencia entre el factor 3 y el factor 1, multiplicada por la diferencia entre las cifras, es un múltiplo de 10; es decir, cuando la diferencia entre las dos cifras es exactamente 5. Solo ese patrón queda sin detectar.
- Luhn es el método que H. P. Luhn, de IBM, patentó en 19547: duplica cifras alternas y, si el resultado supera 9, le resta 9 antes de sumar. La única transposición adyacente que se le escapa es el par
09↔90. Tiene un buen equilibrio entre facilidad de implementación y capacidad de detección, así que si va a adoptar un método nuevo para su propio código, empiece por este y no tendrá problemas. - El módulo 11 del Mi Number, gracias a que 11 es un número primo, tiene en teoría una capacidad de detección alta, pero existe un pliegue: cuando el resto es 0 o 1, el dígito de control se fija uniformemente en 08, y solo los errores que se mueven entre estas dos clases quedan sin detectar. El antiguo ISBN (ISBN-10), con el mismo módulo 11, resolvió este problema «escribiendo el resto 10 como
X», pero a cambio arrastró la molestia operativa de que «aparezca una X en una cifra que se supone que es un número». Conviene saber que si se adopta un método de la familia del módulo 11, siempre viene acompañado de este problema de «cómo encajar 11 restos posibles en 10 dígitos». - El módulo 9 del número de persona jurídica tiene el diseño poco frecuente de colocar el dígito de control al principio11, pero como usa el resto de dividir entre 9,
0y9resultan congruentes, y tanto el error de tecleo como el intercambio entre ambos pasan sin detectarse. - Los métodos de Damm y de Verhoeff detectan la totalidad tanto de los errores de una cifra como de las transposiciones adyacentes. Verhoeff fue el primero en construir un código decimal que detecta todos los errores de una cifra y todas las transposiciones adyacentes2, y Damm ofreció una construcción más sencilla, que consiste solo en consultar una única tabla de operación de un cuasigrupo (quasigroup).10 Si la prioridad absoluta es la capacidad de detección, estos son los métodos a elegir, pero para combatir los errores de entrada de un sistema empresarial, Luhn o el método JAN son más que suficientes en la práctica.
Que esta tabla compare los métodos según las dos columnas «error de 1 cifra» y «transposición adyacente» se debe a que, en el estudio de Verhoeff visto en 4.1 —una recopilación por categorías de los registros de errores de transcripción de números que las personas transcribían e introducían realmente, en el sistema neerlandés de giro postal y en otros similares—, estas dos categorías concentraban la mayor parte de los errores.2 Dicho de otro modo, si la tendencia de errores de su propio código es claramente distinta (por ejemplo, si la mayoría son confusiones al leer un 1 manuscrito como un 7), será más eficaz revisar el tipo de caracteres o el formato del formulario que comparar métodos.
4.3 Códigos que deben llevar dígito de control y códigos que no lo necesitan
El criterio de decisión es «si pasa por las manos y los ojos de una persona». Merece la pena añadirlo al código de producto que se teclea desde un pedido en papel, al número de socio que se comunica por teléfono o al número de comprobante que se escribe a mano en el terreno. En cambio, no hace falta en identificadores internos que solo circulan entre sistemas, ni en códigos que solo se introducen seleccionándolos en pantalla. Si no hay una fuente de error (una persona), tampoco tiene sentido detectarlo.
5. Ejemplos de implementación ── C# y Excel/VBA
Todos los métodos principales se pueden implementar en unas pocas líneas, o como mucho una decena. Solo hay que tener cuidado en recibir el código siempre como cadena de texto (el motivo se explica en el capítulo 6).
C#
Primero, el método JAN (GTIN-13). Calcula el dígito de control a partir de un cuerpo de 12 dígitos.6
public static int Gtin13CheckDigit(string body12)
{
if (body12.Length != 12 || !body12.All(char.IsAsciiDigit))
throw new ArgumentException("Debe especificar 12 dígitos numéricos", nameof(body12));
int sum = 0;
for (int i = 0; i < 12; i++)
{
int digit = body12[11 - i] - '0'; // Contar desde el extremo derecho
sum += (i % 2 == 0) ? digit * 3 : digit; // El extremo derecho ×3, luego alternando
}
return (10 - sum % 10) % 10;
}
A continuación, Luhn. Si va a aplicarlo a su propio código de cliente o número de socio, con estos dos métodos es suficiente.
public static int LuhnCheckDigit(string body)
{
int sum = 0;
for (int i = 0; i < body.Length; i++)
{
int digit = body[body.Length - 1 - i] - '0';
if (i % 2 == 0) // Duplicar una cifra sí, una no, empezando junto al dígito de control
{
digit *= 2;
if (digit > 9) digit -= 9;
}
sum += digit;
}
return (10 - sum % 10) % 10;
}
public static bool IsValidLuhn(string code) =>
code.Length >= 2 && code.All(char.IsAsciiDigit) &&
LuhnCheckDigit(code[..^1]) == code[^1] - '0';
Vale la pena seguir este código a mano una sola vez, para que quede claro qué hace. Tomemos como cuerpo del código de cliente 1000317 y calculemos el dígito de control. Se duplican las cifras 1.ª, 3.ª, 5.ª… contando desde el extremo derecho del cuerpo, y si el resultado de duplicar supera 9, se le resta 9. Eso es todo.
| Cifra del cuerpo (desde la derecha) | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|
| Número | 7 | 1 | 3 | 0 | 0 | 0 | 1 |
| ¿Se duplica? | ○ | ○ | ○ | ○ | |||
| Después del cálculo | 14→5 | 1 | 6 | 0 | 0 | 0 | 2 |
La suma es 5+1+6+0+0+0+2 = 14. El dígito de control es la cifra de las unidades de «10 − (el resto de dividir la suma entre 10)», así que 10 − 4 = 6. El código completo queda 10003176, que coincide con el valor que devuelve LuhnCheckDigit("1000317") arriba.
Para verificar el código ya completo, se cuenta desde el extremo derecho incluyendo el dígito de control, y se duplican la 2.ª, 4.ª… posiciones desde la derecha antes de sumar. 6+5+1+6+0+0+0+2 = 20, que es divisible entre 10, así que el código es correcto. Probemos ahora con errores de tecleo habituales.
- Si se intercambian dos cifras adyacentes y se teclea
10003716: 6+2+7+6+0+0+0+2 = 23. No es divisible entre 10, así que se detecta. - Si se equivoca en una sola cifra y se teclea
10008176: 6+5+1+7+0+0+0+2 = 21. Esto también se detecta.
Como el único criterio de decisión es «si la suma es múltiplo de 10», se puede verificar incluso en el terreno con solo una calculadora. Como vimos en el capítulo 4, lo único que se le escapa a Luhn es el intercambio 09↔90.
Incluyo también la verificación del número de persona jurídica, útil para la comprobación de entrada en la tabla maestra de clientes y proveedores. Es una transcripción directa de la fórmula del reglamento ministerial9 (la primera cifra es el dígito de control, y las 12 siguientes son el número base).
public static bool IsValidCorporateNumber(string code)
{
if (code.Length != 13 || !code.All(char.IsAsciiDigit)) return false;
int sum = 0;
for (int n = 1; n <= 12; n++)
{
int p = code[13 - n] - '0'; // La cifra menos significativa del número base es la posición n=1
sum += p * (n % 2 == 0 ? 2 : 1); // Posición impar ×1, posición par ×2
}
return 9 - sum % 9 == code[0] - '0';
}
Al verificarlo con el ejemplo que aparece en el material de la Agencia Tributaria Nacional (número de sociedad 700110005901 → número de persona jurídica 8700110005901): suma de las cifras impares 11 + suma de las cifras pares 13×2 = 37; el resto de dividir 37 entre 9 es 1; 9−1=8, que coincide con el dígito de control.11
Fórmulas de Excel y VBA
Hay situaciones —como hacer inventario en Excel de la tabla maestra existente antes de una migración, o que el departamento de sistemas revise una lista distribuida por el área de negocio— en las que conviene verificar sobre la marcha, sin salir del entorno de desarrollo. Para verificar Luhn basta con una sola fórmula (suponiendo que el código está en A2; se usan LET y SEQUENCE, disponibles en Microsoft 365 o Excel 2021 en adelante).
=LET(s,A2&"", n,LEN(s), i,SEQUENCE(n),
d,MID(s,i,1)*1,
x,IF(MOD(n-i,2)=1, d*2, d),
y,IF(x>9, x-9, x),
MOD(SUM(y),10)=0)
Esto duplica las cifras donde n-i es impar, es decir, la 2.ª, 4.ª… posición desde la derecha; si el resultado supera 9 le resta 9, suma todo y comprueba si es divisible entre 10. Es el mismo procedimiento que el cálculo a mano de antes. Tenga en cuenta que en una celda donde el código está como valor numérico, los ceros iniciales ya se han perdido (capítulo 6). Convierta la columna a texto antes de verificar.
Si quiere distribuir el archivo incluyendo versiones antiguas de Excel, o si necesita alternar entre varios métodos, conviene definirlo como función de VBA, de modo que se pueda llamar desde la hoja de cálculo como =IsValidLuhn(A2).
' Verificación con el método Luhn. Pase siempre el código como cadena de texto para no perder los ceros iniciales.
Public Function IsValidLuhn(ByVal code As String) As Boolean
Dim i As Long, n As Long, d As Long, total As Long
Dim c As String
n = Len(code)
If n < 2 Then Exit Function ' Devuelve el valor por defecto False
For i = 1 To n
c = Mid$(code, n - i + 1, 1) ' Contar desde el extremo derecho
If c < "0" Or c > "9" Then Exit Function
d = CLng(c)
If i Mod 2 = 0 Then ' Duplicar la 2.ª, 4.ª... posición desde la derecha
d = d * 2
If d > 9 Then d = d - 9
End If
total = total + d
Next i
IsValidLuhn = (total Mod 10 = 0)
End Function
Con la misma estructura se pueden escribir también el método JAN (×3 y ×1 desde el extremo derecho) o el del número de persona jurídica (posición impar ×1, posición par ×2). Solo cambian los factores y el número por el que se divide.
También conviene aplicar cierto ingenio al usarlo en la pantalla de entrada. En lugar de mostrar simplemente «el código no es válido» cuando el dígito de control no coincide, si se llega a consultar la tabla maestra y mostrar el nombre correspondiente para que el usuario lo confirme («¿10003176: Empresa XX, es correcto?»), incluso las entradas erróneas que sí pasan el dígito de control (cuando se teclea, por error, otro código que sí existe) se pueden atrapar con la vista humana.
6. Trampas habituales en la práctica
Aunque el sistema de códigos en sí sea bueno, hay patrones clásicos que lo echan a perder en la implementación y en la operación diaria.
- La pérdida de ceros y el redondeo a 15 dígitos en Excel. Cuando Excel interpreta el contenido de una celda como número, elimina los ceros iniciales y, además, como la precisión efectiva de un número es de 15 dígitos, sustituye por 0 todo lo que pase de la cifra 16.4 Es decir, el código de cliente
00123se convierte en123, y en números largos del orden de un número de tarjeta de crédito, las últimas cifras se transforman en0. En sistemas con integración por CSV, incluya desde el principio en el procedimiento operativo que el código se trate como texto en todo el recorrido (especificando el tipo texto en Power Query, y el tipo de columna en el lado de la importación). - Guardarlo en la base de datos con un tipo numérico. Los ceros iniciales se pierden igual que en Excel, y un
BETWEENpensado para «acotar por un rango de códigos» arrastra códigos con un número de dígitos distinto. Como el código no es objeto de cálculo, el principio es usar un tipo de texto de longitud fija. El orden de clasificación también se diseña como texto (si el número de dígitos es fijo, ordenar como texto equivale a ordenar como número). - Incluir el guion en el propio dato. El día en que
1234-5678y12345678empiecen a convivir como registros distintos, todo se acaba. Se guarda el código desnudo, y el separador se añade al mostrarlo. Al introducir un dato, elimine guiones y espacios antes de verificarlo. - Inconsistencia entre mayúsculas y minúsculas. Si se usan letras, normalícelas a mayúsculas antes de guardarlas, y unifíquelo usted mismo en la aplicación en lugar de depender del criterio de ordenación (collation).
- Reutilizar un número vacante. Está prohibido pensar «el código de cliente 1000317 ya está de baja, así que lo reasignamos a un cliente nuevo». Las facturas antiguas, los registros y los sistemas de los proveedores conservan la correspondencia anterior, y en una auditoría o en la investigación de un incidente acabaría señalando a otra persona distinta. El principio es que un código dado de baja queda vacante para siempre.
7. Códigos que no debe definir usted mismo ── Cuándo adoptar un estándar existente
Un código que se queda dentro de la empresa se puede diseñar con total libertad, pero si va a imprimir un código de barras en el producto y hacerlo circular fuera de la empresa (venta minorista, comercio electrónico, logística), use el código JAN (GTIN) y no un código propio. El código JAN se establece a partir de un código de operador GS1 concedido en préstamo, y no se puede decidir por cuenta propia dentro de la empresa.5 El método de cálculo del dígito de control también está fijado por el estándar.6
El punto clave de diseño en este caso es no forzar la unificación entre el código interno y el código JAN. Como un mismo producto puede tener distintos códigos JAN según el número de unidades por paquete, o cambiar de JAN al modificarse la especificación, la práctica estándar es que la tabla maestra de productos tenga como columnas separadas el «código de producto interno» (equivalente a la clave primaria, numerado por la propia empresa) y el «código JAN» (un atributo, del que puede haber varios). Aquí también se aplica sin modificaciones el principio de «el código identifica, el significado —la correspondencia con el estándar externo— es un atributo».
8. Vida útil del sistema de códigos y su migración
Por muy cuidadosamente que se diseñe, todo sistema de códigos termina llegando al final de su vida útil. El caso típico es el desbordamiento de dígitos. Cuando se empieza a vislumbrar el límite superior del secuencial, la única opción es «aumentar el número de dígitos», pero ese número está grabado no solo en la definición de columna de la tabla maestra, sino también en el diseño de los formularios, el ancho de impresión del código de barras, la especificación de los archivos de intercambio con clientes y proveedores, e incluso en los sistemas del lado de esos clientes y proveedores. Se acaba realizando, tanto en la propia empresa como en todos los proveedores, una migración comparable a la ampliación del código postal japonés a 7 dígitos (1998).
Precisamente por eso es tan eficaz el «margen de un dígito» del capítulo 3, pero aun así, cuando llega a ser necesaria una migración, los principios son estos tres.
- Cree una tabla maestra de correspondencia entre lo antiguo y lo nuevo, y haga que durante el periodo de migración se pueda buscar por ambos códigos. Las consultas de los proveedores llegarán con el código antiguo.
- Si la clave interna y el código ya están separados, la migración queda contenida en la capa de presentación y en la tabla maestra. Si se usa el propio código de negocio como clave primaria de la base de datos, arrastra consigo las claves foráneas de todas las tablas. En un diseño nuevo, se recomienda separar la clave interna (numeración automática) del código que se muestra.
- Distribuya los cambios de esquema mediante migraciones bajo control de versiones. La forma de garantizar que un cambio en el número de dígitos de la columna del código llegue a todos los clientes y a todos los entornos está tratada en el artículo sobre migración de esquemas de base de datos.
9. Resumen
- El primer principio del diseño de códigos es «el código identifica, el significado es un atributo». Si se graba el departamento o la categoría en el código, cada cambio en la organización o en la clasificación se convierte directamente en una ruptura del código. Que el Mi Number sea un número sin significado no es casualidad.1
- El tipo de caracteres y el número de dígitos son un compromiso entre eficiencia de información y legibilidad. Si hay lectura en voz alta o escritura a mano, use solo números; si necesita comprimir dígitos, use un alfabeto con protección contra lecturas erróneas (Crockford Base323). El número de dígitos: diez veces el volumen futuro, más un dígito.
- La realidad de los errores de entrada es «entre el 60 % y el 90 % son de una sola cifra, y la mayor parte del resto son transposiciones adyacentes».2 Añada un dígito de control a los códigos que pasan por manos humanas; si duda sobre el método, elija Luhn, y si va sobre un código de barras, el estándar GS16. El problema del «ajuste del resto» de la familia del módulo 11, y el hecho de que el módulo 9 del número de persona jurídica no distinga entre 0 y 9, son propiedades que conviene conocer al elegir el método.
- En la implementación, el principio es la coherencia como cadena de texto. La pérdida de ceros y el redondeo a 15 dígitos en Excel4, guardarlo con tipo numérico, la mezcla del guion en el dato y la reutilización de un número vacante son los cuatro grandes accidentes que se ven en el terreno.
- El código de producto que sale al exterior se apoya en JAN (GS1) y se guarda en una columna distinta del código interno. De cara a una futura migración por desbordamiento de dígitos, mantenga separadas la clave interna y el código que se muestra.
Un sistema de códigos es, en la práctica, una «especificación externa de facto»: una vez que se distribuye, el coste de corregirlo después es órdenes de magnitud mayor. Cuando en la definición de requisitos de un sistema nuevo surja el tema de los códigos, recorra la lista de comprobación de este artículo antes que las pantallas o las funcionalidades.
Artículos relacionados
- Cómo llevar el esquema de base de datos de una aplicación empresarial bajo control de versiones ── La práctica de la migración para evitar que «cada cliente tenga una base de datos distinta»
- Sustituir un libro de Excel por una lista de SharePoint ── Deje atrás el «libro que se rompe» gracias a la compartición, el historial y la integración con flujos
- Automatizar procesos de negocio con Excel y CSV mediante PowerShell ── Recetas prácticas de agregación, conciliación y generación de informes
- Cómo elegir dónde guardar los datos de una aplicación de Windows ── Tabla de decisión entre SQLite, JSON, el Registro y Access
Áreas de consultoría relacionadas
KomuraSoft LLC se ocupa del diseño del sistema de códigos y de la tabla maestra al construir o sustituir un sistema empresarial, de la investigación de desbordamientos y duplicados en sistemas de códigos existentes y de sus planes de migración, y de la implementación de comprobaciones de entrada (dígito de control, verificación contra la tabla maestra). También atendemos, dentro del ámbito de la consultoría matemática, consultas como la comparación de métodos de dígito de control de este artículo: evaluar mediante fórmulas «qué método detecta qué tipo de error y en qué medida» para traducirlo en una decisión de diseño.
- Desarrollo de aplicaciones Windows
- Modernización y mantenimiento de software Windows existente
- Consultoría técnica y revisión de diseño
- Consultoría matemática y optimización de procesos
- Contacto
Referencias
-
Reglamento de ejecución de la Ley sobre el Uso del Número de Identificación de Personas Físicas Concretas en los Trámites Administrativos (Orden gubernamental n.º 155 de 2014), artículo 6. Sobre que el número que debe constituir el Mi Number está formado por un número de 11 dígitos, obtenido convirtiendo el código del registro de residentes mediante un método que no introduce artificio alguno, más un dígito de verificación añadido a continuación (un entero de 0 a 9 calculado con el objetivo de confirmar que no hay errores al introducir el Mi Number en un equipo informático). ↩ ↩2 ↩3 ↩4
-
J. Verhoeff, Error Detecting Decimal Codes, Mathematical Centre Tracts 29, Mathematisch Centrum, Ámsterdam. Sobre la frecuencia de los tipos de error basada en el análisis de muestras de errores de transcripción de sistemas reales (el error de una cifra es la categoría mayoritaria, entre el 60 % y el 95 %; el error de dos cifras, entre el 10 % y el 20 %, en su mayoría transposiciones de cifras adyacentes; y categorías minoritarias como el twin error, cada una entre el 0,5 % y el 1,5 %), y sobre la construcción por parte del autor de un código decimal que detecta todos los errores de una cifra y todas las transposiciones adyacentes (en el libro, transposition designa el intercambio de cifras adyacentes). ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Douglas Crockford, Base 32. Sobre que el alfabeto de 32 caracteres excluye la I y la L, confusas con el 1, y la O, confusa con el 0 (además de la U, para evitar palabras malsonantes accidentales); sobre que al decodificar se aceptan tanto mayúsculas como minúsculas, tratando i y l como 1, y o como 0; y sobre el mecanismo del símbolo de verificación mediante módulo 37. ↩ ↩2 ↩3 ↩4
-
Soporte de Microsoft, Conservar los ceros iniciales y los números grandes. Sobre que la precisión efectiva de un número en Excel es de un máximo de 15 dígitos, de modo que en números de 16 dígitos o más —como un número de tarjeta de crédito— la parte que excede los 15 dígitos se cambia a 0; sobre la eliminación de los ceros iniciales; y sobre la solución de tratar la columna como texto. ↩ ↩2 ↩3
-
GS1 Japan, Código de operador GS1 y GTIN (código JAN). Sobre que el uso del código JAN requiere un trámite de registro para recibir en préstamo un código de operador GS1. ↩ ↩2
-
GS1 Japan, Método de cálculo del dígito de control. Sobre cómo se calcula el dígito de control del GTIN-13 (el tipo estándar del código JAN): usando la suma obtenida al multiplicar alternando por 3 y por 1 desde el extremo derecho, y restando de 10 el resto de dividir esa suma entre 10. ↩ ↩2 ↩3 ↩4 ↩5
-
H. P. Luhn, US Patent 2,950,048 “Computer for Verifying Numbers” (solicitada en 1954, concedida en 1960). Sobre un método que añade un dígito de control al extremo derecho del número original y verifica el número mediante una suma cruzada que usa cifras sustitutas (la suma de las cifras del número duplicado). ↩ ↩2
-
Orden sobre el Número de Identificación de Personas Físicas, la Tarjeta de Número de Identificación de Personas Físicas y la Provisión de Información Personal Específica prevista en la Ley sobre el Uso del Número de Identificación de Personas Físicas Concretas en los Trámites Administrativos (Orden ministerial del Ministerio de Asuntos Internos y Comunicaciones n.º 85 de 2014), artículo 5. Sobre la fórmula del dígito de verificación (a la cifra Pn que ocupa la posición n desde la menos significativa, dentro de los 11 dígitos que no son el dígito de verificación, se le multiplica por el peso Qn: n+1 cuando 1≦n≦6, y n−5 cuando 7≦n≦11; se suma todo, se divide entre 11 y se resta el resto de 11; si el resto es 1 o menos, se toma como 0). ↩ ↩2
-
Orden ministerial sobre la Asignación del Número de Persona Jurídica (Orden ministerial del Ministerio de Hacienda n.º 70 de 2014), artículo 2. Sobre la fórmula del dígito de verificación del número de persona jurídica (a la cifra Pn que ocupa la posición n desde la menos significativa del número base se le multiplica por el peso Qn: 1 si n es impar, 2 si es par; se suma todo, se divide entre 9 y se resta el resto de 9). ↩ ↩2
-
H. M. Damm, Totally anti-symmetric quasigroups for all orders n≠2,6, Discrete Mathematics, Vol. 307, 2007. Sobre la existencia, para todos los órdenes salvo el 2 y el 6, de los cuasigrupos totalmente antisimétricos que sirven de base a un método de dígito de control capaz de detectar todos los errores de una cifra y todas las transposiciones adyacentes. ↩ ↩2
-
Agencia Tributaria Nacional de Japón, Cálculo del dígito de control. Sobre que el número de persona jurídica está formado por un número base de 12 dígitos precedido de un dígito de verificación, y sobre el ejemplo de cálculo que obtiene el dígito de control 8 a partir del número de sociedad 700110005901. ↩ ↩2
Artículos relacionados
Artículos recientes con las mismas etiquetas para profundizar en temas cercanos.
Cómo versionar el esquema de la base de datos de una aplicación empresarial — Migraciones que evitan que «cada cliente tenga una base de datos distinta»
Guía práctica para versionar el esquema de bases de datos de aplicaciones empresariales dispersas entre clientes: PRAGMA user_version, mi...
Cómo elegir la comunicación entre procesos en Windows — canalizaciones con nombre / TCP / gRPC / memoria compartida / COM: tabla de decisión
Cómo elegir la comunicación entre procesos en Windows: comparamos en una tabla de decisión las canalizaciones con nombre, el TCP local, g...
Cómo elegir el destino de almacenamiento de datos en una app de Windows ── Tabla de decisión: SQLite / JSON / Registro / Access
Dónde y con qué guardar los datos de una app de escritorio Windows: uso de AppData/ProgramData, ventajas y trampas de SQLite, JSON, Regis...
Por qué usar .NET Generic Host y BackgroundService en una aplicación de escritorio
Resumimos cómo usar Generic Host y BackgroundService en herramientas y apps residentes de Windows para organizar el inicio, el procesamie...
Guía práctica de FileSystemWatcher - Cómo evitar pérdidas de eventos y duplicados
Analizamos el uso de FileSystemWatcher: pérdida de eventos, notificaciones duplicadas, trampas de finalización, reescaneo, claim atómico ...
Temas relacionados
Estas páginas sitúan el tema en un contexto más amplio de servicios y decisiones.
Temas técnicos de Windows
Portal sobre desarrollo de Windows, investigación de fallos y aprovechamiento de activos existentes.
Servicios relacionados con este tema
El artículo está directamente relacionado con los siguientes servicios.
Desarrollo de aplicaciones para Windows
Aplicaciones empresariales, integración de dispositivos y herramientas de comunicación, de los requisitos al desarrollo.
Reutilización y migración de activos existentes
Reutilización y migración de activos COM / ActiveX / OCX y dependencias de 32 o 64 bits.
Preguntas frecuentes
Preguntas habituales en las consultas sobre el tema del artículo.
- ¿A qué tipo de código se le debe añadir un dígito de control?
- Se añade a los códigos en los que intervienen personas al introducirlos, transcribirlos o leerlos en voz alta. Son ejemplos típicos el código de producto que se teclea a partir de un formulario en papel, el número de socio que se comunica por teléfono o el número de comprobante que se escribe a mano. En cambio, no es necesario en identificadores internos que solo circulan entre sistemas (como la clave primaria de una base de datos), porque un código que nunca pasa por manos humanas no sufre errores de tecleo, y el propósito del dígito de control es precisamente detectar errores de entrada. El propio dígito de verificación del Mi Number japonés tiene, según la normativa, el objetivo expreso de «confirmar que no hay errores al introducirlo en un equipo informático».
- ¿Se puede dar al código de producto un significado de departamento o de categoría?
- El principio general es que «el código sirve solo para identificar, y el significado se guarda como atributo en la base de datos». Si se incrusta el departamento, la categoría o el año en los dígitos del código, cada reorganización de la empresa o cambio de clasificación obligará a renumerar los códigos, y se romperá la correspondencia con los formularios antiguos y con los números ya entregados a los clientes o proveedores. Cuando de verdad se necesite que una persona pueda distinguir el tipo de un vistazo, la solución práctica habitual es limitarse a un único carácter de prefijo que indique el tipo, y dejar el resto como número secuencial.
- ¿Se puede añadir un dígito de control a un sistema de códigos ya existente?
- Técnicamente es posible, pero como se añade un dígito más, afecta a la tabla maestra, a todos los formularios, a los intercambios de datos con clientes y proveedores, y a todo el material impreso. En la práctica se convierte en un proyecto de migración del propio sistema de códigos, así que lo más realista es hacerlo coincidir con el momento en que, por ejemplo, se renueve el sistema para resolver un desbordamiento de dígitos. Como solución provisional hasta entonces, basta con incorporar en la pantalla de entrada una verificación de existencia contra la tabla maestra (comprobar si el código introducido existe realmente) y mostrar el nombre correspondiente para que el usuario lo confirme; solo con eso ya se reduce considerablemente el daño real de las entradas erróneas.
- ¿Se pueden usar UUID o ULID como código de negocio?
- Como clave interna de la base de datos no hay ningún problema, pero no son adecuados como «código para mostrar» que las personas deben leer en voz alta o transcribir. Un UUID tiene 36 caracteres, demasiado largo para comunicarlo por teléfono o por fax. Si se separa en dos capas —una clave interna (UUID o numeración automática) y un código visible para las personas (un número secuencial corto más un dígito de control)— se satisfacen ambas necesidades a la vez. Aunque en el futuro haya que cambiar el sistema del código visible, si la clave interna se mantiene estable, el impacto queda contenido en la capa de presentación.
Perfil del autor
Página de presentación del autor del artículo.
Go Komura
Representante de KomuraSoft LLC
Especializado en desarrollo de software para Windows, consultoría técnica e investigación de fallos, sobre todo en proyectos con sistemas existentes y errores difíciles de reproducir.