-2

I am in the search for a solution to allow the loading of an XML file where the value named as Total allows a difference of 1 cent above or below the actual value, for this solution I am getting the value of Total from the XML file as follows.

$xml = new SimpleXMlElement( $_FILES['XmlToUpload']['tmp_name'], 0, true );
$total = (float)$xml['Total'];

To explain in detail what I want to achieve I will put the following example, when reading the Total node of the XML file this gets as a value the following Total= "9840.00", what I want to allow when loading the XML file is that it allows that total to have a difference of more or less 1 cent, that is, that even if the Total of the XML file has a value of Total="9839.99" or Total="9840.01" it allows the file to be loaded.

The XML file is loaded as follows:

$fileXML = $_FILES['XmlToUpload']['name'];
$pathXML = "//LOCATION/XML/";
    
$filepathXML = $pathXML.$fileXML;



if(move_uploaded_file( $_FILES['XmlToUpload']['tmp_name'], $pathXML . $fileXML)){

echo 'Success Upload File'; 

}

I hope someone can give me some guidance on how to do this validation.

Update 1:

I add a short and representative sample of my XML file in which the Total node that was desired to validate is located

<cfdi:Voucher xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
Certificated="m4gfzi9yNXuC0A=" 
Condition="002" 
Date="2021-06-29T16:02:16" 
Number="4938" 
Payment="23" 
NoCertificated="404627114" 
Total="9840.00" 
Version="3.3">
</cfdi:Voucher>

Update 2:

I tried to make a modification in my code based on one of the answers they give me, but I can't exist, it may be some additional error I have, what I tried was to add two new variables, one adding to the total an amount of "0.01" and the other variable subtract the amount of "0.01".

The variables are declared as follows:

$totalMgS = $total + "0.01";
$totalMgI = $total - "0.01";

It is important to make it clear that $total I mentioned before at the beginning of the question, which I use to read the XML file.

Based on one of the responses I created the following validation in the loading section of my XML file:

if($total <= $totalMgS && $total >= $totalMgI){
if(move_uploaded_file( $_FILES['XmlToUpload']['tmp_name'], $pathXML . $fileXML)){

echo 'Success Upload File'; 

}
}else{
echo "It doesn't fit!";

When I test an XML file where $total has a difference of 0.01, the file is not loaded and throws me the message of It doesn't fit!.

Any changes I need to make to my validation?

Update 3:

Going back to the topic again and to validate it, I was analyzing all this time that I have to compare the total with another, and indeed now I visualize it like this, that other number with which I have to compare it is stored in a database table which I am displaying in an HTML tag as follows:

<div class="form-group row" >
                        <label for="lblName" class="col-sm-3 col-form-label">Total:</label>
                        <div class="col-sm-8">
                          <input type="text" name="txtTotal" class="form-control" id="txtTotal" disabled>
                        </div>
                     </div>

Then I declare in my Javascript the variable:

var TotalTable = $("#txtTotal").val();

Then I add it to my data to later be called from PHP.

datosForm.append("TotalTable", $("#txTotal").val());

And finally I call it from PHP.

$TotalReal = (isset($_POST["TotalTable"]));

I have called my variable in PHP as $TotalReal

To validate that the total is the same that I have in my HTML tag and that it is the same that I have stored in the database, I print the variable $TotalReal but this only returns 1.

I am printing it as follows:

echo $TotalReal;

Is there something I need to change in how I am getting the variable from Javascript? Or do I have to do some conversion to data type float?

  • 3
    One cent more or less than what "actual value"? Are you looking to allow anything within 0.01 of a whole number, or does the value need to be within 0.01 of some known value or different value in the XML file? – EMF Jul 25 '22 at 17:56
  • 1
    Can you edit your question and add a **short, representative** sample of `$xml`? – Jack Fleeting Jul 25 '22 at 18:55
  • @EMF One cent more or one cent of the actual value found in the XML file to be uploaded –  Jul 25 '22 at 19:33
  • @JackFleeting I just added an update to my question with the XML file you requested –  Jul 25 '22 at 19:44
  • @user11804298 According to your answer, the actual value will always be within one cent of the actual value since they are the exact same number. If Total = 1500.47 it is within 1 cent of 1500.47. – EMF Jul 25 '22 at 20:42

4 Answers4

1

Float is not suitable for commercial calculations where accuracy is important. Calculations with float values often bring inaccuracies. Example:

$diff = "9840.01" - "9840.00";
var_dump($diff);  //float(0.010000000000218)
var_dump($diff > 0.01);  //bool(true)

The difference from the default is exactly 1 cent. A comparison by float would show that the difference is more than 1 cent. I don't want to go into detail about the causes, there are thousands of posts on the net (Why not use Double or Float to represent currency?)

One possible solution is to use BCMath (Arbitrary Precision Mathematics).

function isDiffGt1Cent(string $actual, string $ref): bool
{
  $diff = bcsub($actual,$ref,3);
  return bccomp($diff,"0.01",3) === 1 OR bccomp($diff,"-0.01",3) === -1;
}

$d0 = isDiffGt1Cent("9840.00","9840.00");  // bool(false)
$d1 = isDiffGt1Cent("9840.01","9840.00");  // bool(false)
$d2 = isDiffGt1Cent("9839.99","9840.00");  // bool(false)
$d3 = isDiffGt1Cent("9840.02","9840.00");  // bool(true)
$d4 = isDiffGt1Cent("9839.98","9840.00");  // bool(true)
jspit
  • 7,276
  • 1
  • 9
  • 17
  • Thank you for your answer, you could add an example based on the question code, so you could make use of it and give a validity to your answer –  Jul 28 '22 at 16:06
0

First, the sample xml in your question is not well formed since it doesn't include a namespace declaration for cfdi.

Assuming that's corrected as in the example below, you can use xpath to compare the Total value with your limits and than do whatever you need to do:

#note the change to the first line
$xml = '<cfdi:Voucher xmlns:cfdi="http://www.sat.gob.mx/cfd/3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              Certificated="m4gfzi9yNXuC0A=" 
              Condition="002" 
              Date="2021-06-29T16:02:16" 
              Number="4938" 
              Payment="23" 
              NoCertificated="404627114" 
              Total="9840.00" 
              Version="3.3">
</cfdi:Voucher>
';
$doc = new SimpleXMLElement($xml);
#one way to handle the namespaces:
$target = $doc->xpath('//*[local-name()="Voucher"]/@Total')[0];

if ($target <= 9840.01 && $target >= 9839.99) {
  echo "It fits!";
} else {
  echo "It doesn't fit!";
};
Jack Fleeting
  • 24,385
  • 6
  • 23
  • 45
  • I appreciate your answer but I think I was not clear enough to explain it, I will explain it again, in the example that adds the total is defined as 9840.00, but not always the total is going to be this value, I will load different XML files with different totals. –  Jul 25 '22 at 20:47
  • @user11804298 Yes, it's just an example, based on your sample xml. The basic concept is that you extract the value from `Total` and compare the extracted to whatever you need to compare it to (presumably two variables defined somewhere else) - in this example, 0.01 above or below that value. – Jack Fleeting Jul 25 '22 at 21:39
  • I see that you are already comparing it with a static value, it is important to remember that I am going to load different XML files which will contain different values in the total –  Jul 25 '22 at 21:49
  • @user11804298 It's static only in this example. In real life, you presumably load these two values from somewhere and then compare the extracted `Total` value to them. – Jack Fleeting Jul 25 '22 at 22:20
  • I still do not know your example, it is a little difficult for me to understand because that validation, taking into account that the `Total` value will change in the different XML files that I am going to load, you could give me an example based on my question to see if I can understand a little more what you want to imply, I thank you infinitely –  Jul 26 '22 at 15:26
  • @user11804298 I'm sorry but I don't understand the problem: let's say you have 3 xml files which have 3 `Total` values: 100, 150, 200. You need to compare each of these 3 `Total` values with each of 3 pairs of **other** numbers (to see if a specific `Total` value fits inside a specific pair) - where do you get these pairs of numbers??? – Jack Fleeting Jul 26 '22 at 16:42
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/246785/discussion-between-user11804298-and-jack-fleeting). –  Jul 26 '22 at 16:50
0

Instead of working with floats and comparing to 2 decimal places. What you can do is multiple the $total variable with 100 and compare it in the range of +-1 as shown below:

//taking an example
$total = 9840.00*100;

$totalMgS = $total + 1;
$totalMgI = $total - 1;


//fetching the total from the xml file
$xml = new SimpleXMlElement( $_FILES['XmlToUpload']['tmp_name'], 0, true );
$xmlTotal = (float)$xml['Total'];

//in case the total has more than 2 decimal places then use the function number_format to bring it down to 2 decimal places

$newtotal = number_format($xmlTotal,2);
$finalTotal = $newtotal*100;

if($finalTotal <= $totalMgS && $finalTotal >= $totalMgI){
   if(move_uploaded_file( $_FILES['XmlToUpload']['tmp_name'], $pathXML . $fileXML)){
      echo 'Success Upload File'; 
   }
}else{
   echo "It doesn't fit!";
}

If the total is changed from 9840.00 to 9839.99 or from 9840.00 to 9840.01 the above code will work.

Sushant Bassi
  • 364
  • 3
  • 16
  • I was validating your answer, but when I try to load an XML file with a difference of 1 cent in the total that I extract from the reading of the XML file the validation tells me that It does not fit!, some additional modification that I must make in my validation ? –  Aug 04 '22 at 13:21
  • Firstly I would like to know if the "$total" variable is a static entity (the variable that is used for comparison with the total of the XML file), that you want to compare with the "total" field from the XML file. Since I don't see a flaw in the code if the above case is what you meant. – Sushant Bassi Aug 05 '22 at 09:24
0

Whenever you're doing comparisons with floats, you want to take into account that some numbers can't be represented perfectly in IEEE 754, such as 0.1.

One way you can go about it is to validate for an error range.

For example, if you want to validate if x is within 0.1 of a value y you can do abs(x - y) < 0.1.

That said, as everyone is mentioning, floats are not perfect for money but I think you should be fine as long as you're not running long computations. (php uses doubles, so it shouldn't be affected but rounding unless you're adding a lot of numbers without truncation, very big numbers [over billions] or if very tiny amount of money matter to you [eg. bitcoin&stuff])

Rafael Dantas
  • 78
  • 1
  • 7