Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rediseño del redondeo de decimales #81

Closed
josemmo opened this issue Sep 24, 2021 · 14 comments
Closed

Rediseño del redondeo de decimales #81

josemmo opened this issue Sep 24, 2021 · 14 comments
Assignees

Comments

@josemmo
Copy link
Owner

josemmo commented Sep 24, 2021

Dado que este es un problema recurrente, voy a intentar rediseñar la forma en la que Facturae-PHP gestiona el redondeo de decimales dentro de una factura.

Ahora mismo se redondea un mismo valor varias veces con la pérdida de precisión que eso conlleva.
El objetivo es hacer el redondeo solo una vez (justo antes de generar el documento).

Este feat es un experimento y puede que no llegue a una versión estable si los validadores de FacturaE no admiten los cálculos de esta segunda forma.

@josemmo
Copy link
Owner Author

josemmo commented Sep 25, 2021

Acabo de hacer una prueba de implementación de esto y no pasa la validación del validador del Gobierno de España.

Prácticamente casi ningún campo puede tener más de dos decimales por esta restricción:
decimales-incorrectos

En el caso del schema v3.2 de FacturaE, que sí admite hasta 6 decimales (más bien obliga), tampoco serviría redondear solo al final ya que el resto de campos se tienen que redondear a dos decimales y las cuentas no salen:
valor-incorrecto

Teniendo en cuenta estas limitaciones no se me ocurre ninguna solución ya que, a mi parecer, estamos limitados por la especificación.


@NeoRazorX, la rama feat/issue-81 tiene las modificaciones que he hecho para probar este cambio.

Si se te ocurre alguna solución alternativa a #80, por favor, plantéala en esta conversación.

@shawe
Copy link

shawe commented Dec 2, 2021

@josemmo Me encuentro un caso que coincide totalmente con el descrito en la anterior issue.

Los totales correctos de la factura son:

Campo\Valor Sin redondeo Con redondeo Valor de Facturae-PHP
Base Imponible 39.533,1000 39.533,10 39.533,10
Impuestos 2.767,3170 2.767,32 2.767,33
Total 42.300,4170 42.300,42 42.300,43

Mientras que Facturae-PHP cuando hace el cálculo de los impuestos, está dando valores por encima.

Los datos esenciales a nivel de línea son:

  • Cantidad
  • Precio
  • Dto/s
  • Impuesto
    El resto de valores, son calculables y de los que no debe perderse su precisión para que el resultado se mantenga.

El error para mi, sin mirarlo en el código, está en en que se calcula el importe de los impuestos para mostrarlo para cada línea, y se hace un sumatorio de este. Pero el valor que se está mostrando está ya redondeado a 2 dígitos, pero realmente dispone de más precisión, y sumar con o El error para mi, sin mirarlo en el código, está en en que se calcula el importe de los impuestos para mostrarlo para cada línea, y se hace un sumatorio de este. Pero el valor que se está mostrando está ya redondeado a 2 dígitos, pero realmente dispone de más precisión, y sumar con o sin precisión lo cambia todo.
sin precisión lo cambia todo.

Ejemplo error.ods

Este archivo creo que ejemplifica bastante bien el problema. La columnas con valores calculados son G, H, M y N; y son las que ponen en contraste el problema

Si no recuerdo mal, fiscal y contablemente no hay nada al respecto que indique que debe hacerse de una u otra forma, lo que implica que ambas formas son correctas, pero todos los cálculos deben hacerse de la misma manera para evitar valores dispares.

La única forma para simplificar este problema que se me ocurre, es que en lugar de realizar cálculos dentro de Facturae-PHP, hagas un traspaso de la información tal y como se te facilita, ya que entonces, si hay errores serán provocados por el origen y no por el intermediario. Y en este caso concreto, los correspondientes totales y subtotales no se solicitan, sino que son los que calculas y con los que aparecen las discrepancias.

@josemmo
Copy link
Owner Author

josemmo commented Dec 5, 2021

Hola @shawe,

El error para mi, sin mirarlo en el código, está en en que se calcula el importe de los impuestos para mostrarlo para cada línea, y se hace un sumatorio de este

Sí, eso es lo que hace la librería: calcula los impuestos para cada línea con dos decimales y los impuestos totales de la factura se obtienen sumando los de cada línea ya redondeados.

Como ya he mencionado en anteriores ocasiones, esto de redondear con dos decimales no es porque a mí me apetezca sino porque lo pone en la especificación de FacturaE.

Por ejemplo, en los totales de la factura, el campo "TotalTaxOutputs" se define como el "sumatorio de todas las Cuotas y Recargos de Equivalencia", y las cuotas de cada línea (campo "InvoiceLine/Tax/TotalAmount") deben ir con dos decimales si son euros.

Esto no es ideal, pero hay que recordar que Facturae-PHP es una implementación de la especificación, y si la especificación está diseñada así poco se puede hacer.

Igual que le dije a @NeoRazorX, por favor, si se te ocurre una solución compártela en este issue.

@shawe
Copy link

shawe commented Dec 7, 2021

El tema del redondeo es para enviárselo como piden, no porqué se ciñan a rajatabla con ello como estás entendiendo. Yo mismo he retocado un archivo que en el sumatorio de Facturae-PHP daba 0.01 de más y lo rechazaban, se lo puse tal y como lo tenía la factura de origen, y se volvió a mandar y se aceptó sin problemas.

La definición que das, coincide con los valores que ya tenemos, otra cosa diferente, es que tu hagas el cálculo de la misma manera que nosotros, no arrastras todos los decimales hasta el final, y vas perdiendo precisión por el camino. Es importante tener en cuenta toda la precisión posible, pero en el momento de rellenar valores usar los redondeos tal y como solicita la especificación.

En cuando obtienes "sumando los de cada línea ya redondeados", es cuando empiezas a provocar ese descuadre, nadie va a pagar línea a línea, sino que va a pagar el total del documento. Con lo que si, nosotros podemos mostrar que los "subtotales por línea" difieren de la realidad, pero lo hacen porqué no reflejan la precisión real que requiere la operación, pero eso no implica que el cálculo que hacemos por detrás esté mal, lo que está mal es verlo redondeado con una precisión menor cuando aún no estamos en lo que te implica el pago final.

Si te estoy entiendo bien lo que justificaste, tu defiendes lo siguiente como correcto, si tienes 1000 líneas con total 123.123456789:
Tu harías 1000*123.12 = 123120, mientras que yo haría 1000*123.123456789 = 123123.456789
Y eso da 3.456789 de diferencia, cosa que entiendo que esa llegaría a ser la discrepancia que me encontraría pasando-le esos datos como ejemplo.
Aquí puedes verlo directamente: https://docs.google.com/spreadsheets/d/1aPVgJq834c79EiJ8X4_rvc5WJCGb_4TX7fSg22HC_aY/edit?usp=sharing

Intenta procesar con su validador este último ejemplo del drive, a ver si te lo acepta de la forma que te expongo, y de la que tu lo haces, posiblemente acepte ambas y ya ves que cantidad de error de cálculo puede llegar a implicar hacerlo de una u otra forma.

Pasé el archivo ods adjunto, para que vieras un caso concreto donde el fallo se produce, como está en el documento, que sucede con los sumatorios y redondeos, y los mismos resultados con lo que hace Facturae-PHP que es diferente y es el único que tiene esa discrepancia, y sinceramente, tampoco me cuadra el porqué de dicha diferencia. Entiendo que el ejemplo que te acabo de dar, también te serviría para provocar el problema con otros valores diferentes.

Puedes intentar las pruebas con productos que cuesten menos de 0.01€ y que deban venderse a centenares o millares, y usando eso de ejemplo, creo que verás todavía más evidente que siempre necesitas arrastrar toda la precisión, hasta el momento de rellenar el documento final.

Las soluciones que se me ocurren:

  • Si vas a hacer tu los sumatorios, arrastra toda la precisión posible hasta el final, y justo cuando vas a rellenar el XML, entonces redondeas todos los valores finales previamente calculados.
  • Si no vas a hacer tu los sumatorios, permite-nos pasar esos cálculos hechos por nosotros, ya que nosotros también los tenemos para poder mostrar las totalizaciones de los documentos, y a la vez, no tenemos discrepancias con la forma que tu lo hayas calculado.

@josemmo
Copy link
Owner Author

josemmo commented Dec 9, 2021

Hola @shawe,

Vuelvo a incidir en esta frase que ya dije en el mensaje anterior:

Como ya he mencionado en anteriores ocasiones, esto de redondear con dos decimales no es porque a mí me apetezca sino porque lo pone en la especificación de FacturaE.


[...] no arrastras todos los decimales hasta el final, y vas perdiendo precisión por el camino.

Que sí, soy consciente de que al redondear línea a línea se pierde precisión. Pero no podemos cambiar lo que dice la especificación.

Lo de redondear al final ya lo probé en la rama feat/issue-81 y el validador del Gobierno de España no da por válidas las facturas generadas porque no cumplen con la especificación (lo explico en detalle en este mensaje).


La verdad que los documentos de Word/Excel que enlazas no me sirven de mucho para intentar dar con una posible solución a este problema. Si pudieras subir una factura XML que cumpla el schema FacturaE 3.2.2 según como lo hacéis en vuestra organización (es decir, sin perder precisión) me sería de gran ayuda.

@shawe
Copy link

shawe commented Dec 9, 2021

Te adjunto 1 zip con 2 XML generados por tu propio código, partiendo de los datos del ejemplo de mi anterior mensaje.
La única diferencia entre ambos, es que el que se llama "facturae_totales_mal.xml" hace el cálculo como lo tienes en la versión 1.6.1, y en el que se llama "facturae_total_al_centimo.xml" lo he manipulado manualmente para que contenga los datos reales que yo tengo en la factura, que serían los únicos válidos (no hay 2 totales válidos para el mismo documento).

Muestras_facturae.zip

Puedes comparar entre ambos ficheros, y verás que sólo he modificado el sumatorio de todas las líneas, su cálculo de impuestos y su correspondiente total, que es lo que adjunto en las capturas.

Captura 2021-12-09 20:11:39 facturae_totales_mal xml — facturae_total_al_centimo xml
Captura 2021-12-09 20:11:43 facturae_totales_mal xml — facturae_total_al_centimo xml
Captura 2021-12-09 20:11:51 facturae_totales_mal xml — facturae_total_al_centimo xml

Ambos pasan el validador cuando realmente no debería ser así en base a lo que explicas. Pero sucede lo que te comenté, ambas formas son legalmente correctas aunque una cause más error que la otra.

Así están todas las líneas de esa factura en la base de datos:

image

Y estos son los valores de la cabecera de la factura que tuve que corregir en el XML, porqué no reflejaba la realidad de dicho documento:

image

@josemmo
Copy link
Owner Author

josemmo commented Dec 9, 2021

Muchas gracias, @shawe.

Este fin de semana intento sacar un rato para estudiarlo.

josemmo added a commit that referenced this issue Dec 17, 2021
- Redondeados valores totales en PropertiesTrait::getTotals()
- Actualizado test unitario
- Eliminadas líneas comentadas en src/Facturae.php

> Relacionado con #81
@josemmo
Copy link
Owner Author

josemmo commented Dec 18, 2021

Hola @shawe y @NeoRazorX,

Creo que ya tengo una versión de la librería que implementa correctamente el nuevo redondeo de decimales.
Como es un cambio mayor os agradecería que, si tenéis un rato, la probárais con facturas reales para comparar importes:

composer require josemmo/facturae-php:dev-feat/issue-81

josemmo added a commit that referenced this issue Dec 22, 2021
- Añadidas constantes a clase Facturae
- Actualizado FacturaeItem
- Actualizados ExportableTrait, PropertiesTrait y UtilsTrait
- Sustituido DecimalsTest por PrecisionTest
- Actualizado InvoiceTest

> Relacionado con #81
josemmo added a commit that referenced this issue Dec 22, 2021
- Actualizado anexo de constantes
- Añadido propiedades/precision.md

> Relacionado con #81
@josemmo
Copy link
Owner Author

josemmo commented Dec 22, 2021

He añadido una opción para poder cambiar entre el sistema de redondeo antiguo y el nuevo, por motivos de compatibilidad:

$fac = new Facturae();

// [...]

$fac->setPrecision(Facturae::PRECISION_LINE);    // Por defecto, redondeo antiguo
$fac->setPrecision(Facturae::PRECISION_INVOICE); // Nuevo sistema de redondeo

@shawe
Copy link

shawe commented Jan 17, 2022

Disculpa lo que me he demorado en darte respuesta.

image

Basándome en el ejemplo de las 1000 líneas que provocaban el desajuste, ahora no lo veo y el importe me cuadra y además me pasan las 2 primeras validaciones sin problemas.

En mi caso el único cambio ha sido el que has indicado como nuevo:

$fac->setPrecision(Facturae::PRECISION_INVOICE); // Nuevo sistema de redondeo

@josemmo
Copy link
Owner Author

josemmo commented Jan 22, 2022

Gracias por la respuesta, @shawe.

Voy a esperar unos días por si alguien más quiere dejar feedback y después sacaré la release.

@josemmo
Copy link
Owner Author

josemmo commented Feb 5, 2022

Ya está publicada la nueva versión (v1.7.0) que implementa los cambios de este issue.

@josemmo josemmo closed this as completed Feb 5, 2022
@akitamostodos
Copy link

akitamostodos commented Mar 8, 2022

Hola, he probado la última versión en la que cambia el redondeo, para que me quede claro comop lo hace sería:

1.- Por línea, en cada linea a cada producto se le calcula su iva / re correspondiente y se redondea por ejemplo
28.9 + 21% (6.069) pasa a ser 28.9 + 6.07 = 34,97

2.- Por factura, se arrastran todos los decimales
28.9 + 21% ( 6.069) = 34,969

Es correcto?.

o se calcula sumando todas las bases y de la base se le calcula el impuesto?.

Muchas gracias.

@josemmo
Copy link
Owner Author

josemmo commented Mar 12, 2022

Hola @akitamostodos,

Tienes varios ejemplos de cómo funcionan los dos modos de precisión en la documentación de la librería (https://josemmo.github.io/Facturae-PHP/propiedades/precision.html).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants