Tres cosas que WooCommerce hace con tus datos de coste y que nadie te cuenta
Un coste cero no se guarda. Un reembolso reescribe el coste guardado de cada línea del pedido. Desactivar la función puede borrar el historial de costes en el siguiente guardado. Las tres cosas son reales, las tres están en el núcleo y las tres salen de la misma decisión.
Actualizado 2026-10-23
WooCommerce 10.3 llevó el precio de coste al núcleo, y con él algo que la plataforma nunca había tenido: un coste guardado en la línea del pedido, en el momento de la venta. Esa es la parte que vale la pena. Un pedido de marzo sigue diciendo lo que costaba la mercancía en marzo, incluso después de dos subidas de precios del proveedor.
Hay tres comportamientos alrededor de ese campo que sorprenden a quien construye sobre él. No son errores en el sentido de un código que hace algo que su autor no quería: cada uno tiene un motivo defendible. Son sorpresas, y una sorpresa que descubres por el correo de un cliente sale más cara que una de la que has leído antes.
Todo lo que sigue se leyó en el código fuente de WooCommerce, versión 11.0.1, en septiembre de 2026. Damos los nombres de funciones y archivos para que puedas comprobarlo en lugar de creernos.
Una línea de código explica las tres
Cuando se guardan los datos de un artículo del pedido, el coste pasa por save_cogs_data() en
abstract-wc-order-item-type-data-store.php:
$cogs_value = (float) $cogs_value;
if ( 0.0 === $cogs_value ) {
$this->order_item_data_store->delete_metadata( $item->get_id(), '_cogs_value', '' );
} else {
$this->order_item_data_store->update_metadata( $item->get_id(), '_cogs_value', $cogs_value );
}
Vuelve a leerlo con una idea en la cabeza: aquí el cero no es un valor, el cero es un borrado.
No la sueltes. Las otras dos trampas son esta misma línea alcanzada por dos caminos distintos: uno a través de un reembolso y otro a través de una casilla de los ajustes. Entiende esta rama y podrás predecir las dos.
Trampa 1: un coste cero y la falta de coste son lo mismo
La consecuencia directa. Un producto que de verdad no te cuesta nada (una muestra gratis, una taza de regalo, un extra descargable) se guarda en la línea del pedido exactamente igual que un producto al que nadie le puso coste: como nada.
No hay indicador, ni cero, ni marca que diga cuál de los dos era. Y un producto sin coste introducido
devuelve 0 en get_cogs_total_value(), así que los dos caminos acaban en el mismo borrado.
Esto tiene un lado bueno. Como la ausencia significa de verdad no lo sabemos, un informe puede decir con honestidad qué parte de los ingresos tiene un coste detrás, en lugar de tratar en silencio lo desconocido como gratis. La cobertura es una cifra real, no una estimación.
También tiene una molestia permanente. Tus regalos de verdad se quedarán en la lista de costes que faltan para siempre, y ningún plugin puede sacarlos de ella, porque la información que los distinguiría nunca se escribió.
Hay una asimetría que conviene conocer, porque decide cómo debe construirse cualquier lista así. En el producto, un coste cero sí se guarda: el almacén de datos del producto solo borra cadenas vacías y matrices vacías, no ceros. Así que a nivel de producto, cero y sin coste se distinguen; a nivel de línea del pedido, no. Una lista de «productos sin coste» tiene que leerse, por tanto, en el producto, y la cobertura en las líneas del pedido. Dos fuentes de verdad distintas, por un único motivo honesto. Más sobre cero frente a sin coste.
Trampa 2: un reembolso reescribe el coste guardado de todo el pedido
Esta es la que cambia cifras que ya habías dado por buenas, y es la razón para leer esta página antes de montar una hoja de cálculo sobre estos datos.
Al final de wc_create_refund() en wc-order-functions.php:
if ( ...->feature_is_enabled() && $order->has_cogs() ) {
$order->calculate_cogs_total_value(); // <- the PARENT order
}
$order->save();
Esa llamada no se limita a las líneas reembolsadas. calculate_cogs_total_value_core() en
abstract-wc-order.php recorre todos los artículos del pedido padre y llama a
$item->calculate_cogs_value(), que lee el coste del producto tal como está hoy. Después,
save() vuelve a escribir todos los artículos cargados.
Así que reembolsar un artículo de un pedido de doce líneas recalcula el coste guardado de las doce, con los costes actuales de los productos. Si un precio del proveedor cambió entre medias, el historial cambia con él. Marzo deja de decir lo que costó marzo.
Ahora júntalo con la trampa 1. Si en algún momento se vació el coste del producto, el recálculo da 0,
y el 0 borra. El historial de costes del pedido no solo se reescribe: desaparece, y no hay nada
de donde restaurarlo.
Un reembolso no es un caso raro. Es un martes cualquiera. Cómo se ve esto en un informe.
Trampa 3: desactivar la función puede destruir datos en el siguiente guardado
Comprobado en WooCommerce 10.3.0: desmarcar la casilla del precio de coste no borra nada por sí solo. Conviene decirlo claramente, porque es lo contrario de lo que la gente teme.
El riesgo es lo que pasa después. Con la función desactivada, get_cogs_total_value()
y calculate_cogs_value() devuelven 0 y false. Y el cero, por la trampa 1, borra. Así que
cualquier escritura posterior que toque los artículos de un pedido (editar un pedido antiguo en el
escritorio, hacer un reembolso sobre él, una actualización por la API REST) ejecuta el recálculo,
obtiene cero y borra el coste guardado de esas líneas.
El orden en que llegan esos dos hechos importa. «Desactivarla es seguro» y «tocar un pedido después no lo es» son ciertas las dos, y solo juntas sirven de algo. La secuencia completa.
Por qué el núcleo lo hace así (en su defensa)
Ninguna de las tres es un descuido.
Borrar los ceros evita que la tabla de metadatos de los artículos del pedido se llene de filas que no aportan información, y en una tabla que crece con cada línea de cada pedido eso es una consideración real, no una microoptimización.
Recalcular al reembolsar es la opción coherente si consideras que el total del pedido debe coincidir con los datos actuales del producto después de modificarlo. La alternativa (reembolsos que dejan un total de coste en parte desfasado) también es defendible, pero no es claramente más correcta.
Devolver cero cuando la función está desactivada es lo que hace una función desactivada en el resto de WooCommerce. Solo se vuelve destructivo combinado con la primera decisión.
El coste de las tres cae en un sitio concreto: en quien trata la línea del pedido como un registro histórico inmutable. El núcleo nunca prometió que lo fuera. Simplemente es muy fácil suponerlo, porque parece exactamente una instantánea del coste.
Si construyes sobre estos datos
- Trata un valor que falta como desconocido, nunca como cero. La base de datos no puede decirte cuál era, así que un informe que supone cero se está inventando beneficio.
- Muestra la cobertura junto a cada cifra. «Se conoce el coste del 74 % de los ingresos» es la diferencia entre una cifra con la que se puede actuar y una cifra de aspecto preciso que se equivoca en silencio en una cuarta parte.
- Lee «sin coste» en el producto y la cobertura en las líneas del pedido. Por la trampa 1, cualquier otra combinación produce falsas alarmas.
- Exporta cada mes. Un CSV sacado a fin de mes es el único registro de tu historial de costes que un reembolso no puede reescribir. Es la costumbre más útil de esta página.
- No prometas un historial de costes inmutable salvo que lo guardes tú. El núcleo no lo ofrece, y construirlo significa escribir en la tienda en lugar de leerla: otro nivel de responsabilidad, y una decisión que hay que tomar a propósito.
Compruébalo tú
Todo esto se puede leer en el código fuente de WooCommerce: save_cogs_data() en
includes/data-stores/abstract-wc-order-item-type-data-store.php, wc_create_refund() en
includes/wc-order-functions.php y calculate_cogs_total_value_core() en
includes/abstracts/abstract-wc-order.php. Versión 11.0.1, leída en septiembre de 2026.
Si algo de esto se comporta de otra forma en tu tienda o en una versión posterior, dínoslo: estas páginas se corrigen cuando resultan estar equivocadas, y una corrección con fecha es más útil para todos que una página que se queda desfasada en silencio. Cómo estos mismos tres hechos dan forma a las cifras de nuestro propio informe está en cómo se calcula el beneficio.