Absolutely Not



  • I've been working on adding refund capabilities to the payment system. When I started, the lead architect showed me how the system figures out how much to refund. It gets passed a transaction request with N line items, each with a cost.  It tallies up the cost, and passes on a request to the payment gateway for that total.

    "But," he warned, "Sometimes the system will pass one or more line items with a negative number.  You don't refund -$5, you refund $5, so use Math.Abs (absolute) on those amounts."

    Seems perfectly reasonable. Until I fish through some of the really old code for some of the refund logic I'm replacing:

     

    dblTotal = 0.00;

    foreach (LineItem item in Refund.LineItems)

    {

              dblTotal +=  item.cost;

    }

    dblTotal = Math.Abs(dblTotal);

     



  •  In Soviet Russia, you pay refund,



  • Isn't the real question here, why is the system sometimes passing negative numbers, and other times not?  The above code would be fine if it was consistently negative (or positive).

     



  • @WhiskeyJack said:

    Isn't the real question here, why is the system sometimes passing negative numbers, and other times not?  The above code would be fine if it was consistently negative (or positive).
    Damn you for changing your post while I was replying!  Just for that, I'm going to quote your original post:

    @WhiskeyJack said:

    Perhaps I'm not seeing enough context to think there's a problem.  What's wrong here?  The total refund is the sum of all of the costs of the refund items.



  •  P = |(2) + (-3)|

    Q = |(2)| + |-3|

    Does P = Q?



  • @bstorer said:

    @WhiskeyJack said:
    Perhaps I'm not seeing enough context to think there's a problem.  What's wrong here?  The total refund is the sum of all of the costs of the refund items.
     

      Let P = |2 + (-3)|

    Let Q = |2| + |-3|

    Does P = Q?

     



  • Yeah, yeah, I screwed up.  Didn't read  the first paragraph closely enough, I thought they would all be either negative or positive numbers (but you weren't sure which).  In which case the loop would have worked.  I didn't realize that INDIVIDUAL prices could either be positive or negative until after I posted the original.

     

     



  • @halcyon1234 said:

     P = |(2) + (-3)|

    Q = |(2)| + |-3|

    Does P = Q?

     

     Hey!  Mind your P's and Q's, buddy!



  • TRWTF is the submitter. This is precisely correct. It sums all the refund amounts and correctly calculates the total refund.

    Taking the absolute value before adding would produce an incorrect result if a negative price were refunded. Consider:

    Customer buys a $450 laptop. They buy a -$40 downgrade from the standard 2GB RAM to the cheaper 512MB RAM. They pay $410. They return the laptop. The refund amounts are -450 and 40. This code correctly sums them, gets -$410 as the amount 'spent', and converts it to $410, the amount of the refund.

    Taking the absolute value before summing would incorrectly give the customer an additional refund for the downgrade, which makes no sense.



  • @joelkatz said:

    TRWTF is the submitter. This is precisely correct. It sums all the refund amounts and correctly calculates the total refund.

    Taking the absolute value before adding would produce an incorrect result if a negative price were refunded. Consider:

    Customer buys a $450 laptop. They buy a -$40 downgrade from the standard 2GB RAM to the cheaper 512MB RAM. They pay $410. They return the laptop. The refund amounts are -450 and 40. This code correctly sums them, gets -$410 as the amount 'spent', and converts it to $410, the amount of the refund.

    Taking the absolute value before summing would incorrectly give the customer an additional refund for the downgrade, which makes no sense.

    TRWTF is the total lack of reading comprehension on this site.



  •  Allow to reply with TRWTF is your reading skills.

     The object isn't an entire transaction.  It is a refund, and only a refund.  That's why its called Refund.LineItems.  The system puts together a list of items it wants refunded, and gives it to the transaction processor.  The transaction processor sums up the total, builds a request for whatever payment provider its using that day, and fires off a message saying "Give customer XYZ $50 back".

    There are several parts of the system that can build a Refund object and send it off to be processed. Since some of those were coded by people who, putting it nicely, didn't understand what the system was expecting (and since negative numbers aren't instantly rejected), it's possible to get:

     

    LineItem[0].Amount = $50 (ie: Refund the $50 webcam)

    LineItem[1].Amount = -$75 (ie: Refund the $75 memory stick)

    The customer should get back $125, not $50 + (-$75) = $50 - $75 = -$25.

    And if you read the docs for just about any payment processor out there, you'll see that they expect to receive a positive number in the Amount field of a transaction, even a Refund.

    So I think you're not not right.



  • @halcyon1234 said:

    So I think you're not not right.

    Wait, so he is right?

     

    Maybe you should have said he was "not not not right" or "not absolutely not right".


  • :belt_onion:

    @morbiuswilters said:

    Maybe you should have said he was "not not not right" or "not absolutely not right".
    No, that doesn't make no sense at all



  • @morbiuswilters said:

    Wait, so he is right?

     Maybe you should have said he was "not not not right" or "not absolutely not right".

     

    [Double-negative module not found. Please reinstall or contact support]

     In other news, anti anti-missile-missile missile!



  •  If you took the absolute value of each line item before summing, how would you process a refund that included items with negative prices? See my example of a laptop with a memory downgrade, or consider a customizable computer with a CPU downgrade. You need to keep the signs on the amounts until the end, where you convert from a negative "price" to a positive one for display.



  •  Here's on last example. Consider the following items:

    Customizable Computer: cost=$500 return=-$500

    RAM dowgrade: cost=-$50 return=$50

    OS (non-refundable) cost=$80 return=$0

    Keyboard (restocking fee applies) cost=$25 return=-$15

    Isn't it obvious that to calculate the refund, you add the return amounts for each item, then flip the sign of the total?



  • @bstorer said:

    @joelkatz said:

    TRWTF is the submitter. This is precisely correct. It sums all the refund amounts and correctly calculates the total refund.

    Taking the absolute value before adding would produce an incorrect result if a negative price were refunded. Consider:

    Customer buys a $450 laptop. They buy a -$40 downgrade from the standard 2GB RAM to the cheaper 512MB RAM. They pay $410. They return the laptop. The refund amounts are -450 and 40. This code correctly sums them, gets -$410 as the amount 'spent', and converts it to $410, the amount of the refund.

    Taking the absolute value before summing would incorrectly give the customer an additional refund for the downgrade, which makes no sense.

    TRWTF is the total lack of reading comprehension on this site.

    No, joelkatz is right. (Morbs may have realised this from his lack of response to the later posts, but he might just be asleep).
    Provided the signs in the DB are consistent (either all correct or all wrong), all will be well.



  • @Physics Phil said:

    (Morbs may have realised this from his lack of response to the later posts, but he might just be asleep).

    Um, what?  My only post in this thread was a half-hearted joke about double negatives. 



  • @Physics Phil said:

    Provided the signs in the DB are consistent (either all correct or all wrong), all will be well.
     

     If only they were. 

    Actually, the values come from more than just the db. There's many different "make a refund" functions in the system-- some that allow the user to refund a specific item, some that let the user type in a value (and doesn't sign-check it), and some that use offshored ASP code.

    All in all, it comes down to the fact (that was stressed) that the system can't be trusted to proide consistently signed values. It was a point stressed to me, and stressed to previous developers. And it was one of those previous developers who got their math processor from early 90s Intel.

     



  • If the system doesn't provide values with a consistent sign, there is no fix. How do you know whether the refund amount should be positive or negative? Downgrades should have a negative refund amount. Upgrades should have a positive refund amount. Credits or discounts should have a negative refund amount. Extra charges should have a positive refund amount.

    If you cannot rely on the sign, then you are screwed. The problem cannot be solved.

    Then there is a real WTF, but it's not in the code shown. It's in the fact that there is no way to tell whether the refund should be increased or decreased for each item. Increasing a cutomer's refund because he opted for a downgrade is not an acceptible (to me, at least) fix.



  •  While the system itself is certainly in contention for being a WTF, you still aren't getting this:

     There are two types of transactions that can occur.  A payment, or a refund. Each of them are composed of one or more line items that specify which items are being PURCHASED or REFUNDED, even if its "General refund" $100.

    The order taking system has enough logic to put all the payments in one request type, and refunds in another.  You will never, ever, ever get an upgrade in a refund. Ever. An upgrade is a purchase, and not a refund.

    Those seperate, distinct, non-similar and in no way mixed request types are sent to the payment system explicitly through a "Do a refund" or "Do a payment" requests. This is because with 99.9% of the payment gateways out there, a payment is different from a pre-auth is different from a void is different from an adjustment/refund. In fact, with any half-way decent provider, you don't need to specify the credit card number to do a refund, just the previous transaction number.

    And thus, the Do A Payment And Not An Upgrade Or A Purchase Or Anything Else method is called, it is explicitly a refund and only a refund. However, since many parts of the system construct a list of payment line items or refund line items, and use them to make calls to Do The Refund, and each part of those systems were programmed differently, the payment system cannot trust the sign it is passed.

    Which is why the sign is not an indicator of a payment or a refund.  The call to the methods OhLookAtMeImAPayment() or  GeeWhizImARefund() determine it, and each contain their own logic.

    So, in the end, if the goal is "Take each and every line item, ignore the sign, sum them up and make a Refund", then you don't sum them up then ignore the sign.

    And if you want to do a payment, you submit a payment.



  • @halcyon1234 said:

    While the system itself is certainly in contention for being a WTF, you still aren't getting this

    I'm getting it! The loops add up the total refund which can only be positive, but some of the numbers are negative for no special reason so:

    • $2 refund due to sale
    • -$4 refund due to staff's family member
    => $2 refund total.

    • -$2 refund because someone changed the sale function to return negative values and thought "what can I break, it takes the absolute nonetheless"
    • -$4 refund due to staff's family member
    => $6 refund total.

    Do I get a cookie?



  • @derula said:


    Do I get a cookie?

     

     ding You gain a cookie. Collect 11 more to complete quest.  (It sounds like a lot, but they're binary cookies)



  • You are completely missing my point. What if you return a laptop that has a RAM downgrade? You must subtract the downgrade amount from the laptop amount. Adding absolute values will not get you the correct return amount. Period.



  • Then it has to return something like "Refund (downgraded laptop) $410.00" or you're badly fucked. Since in that system some functions report the refunds as positive numbers and some as negative numbers, you're probably fucked anyway, but with the loop being as it is, it's worse than if it called abs() for each item.



  • @joelkatz said:

    You are completely missing my point. What if you return a laptop that has a RAM downgrade? You must subtract the downgrade amount from the laptop amount. Adding absolute values will not get you the correct return amount. Period.
    I love that you continue to insist that you know better than him how his system works.  Further, why should a refund system care about downgrades or anything else?  It only cares about the bottom line amount paid for that item, because that's what's going to be refunded.



  •  I love that you continue to insist that you know better than him how his system works.  Further, why should a refund system care about downgrades or anything else?  It only cares about the bottom line amount paid for that item, because that's what's going to be refunded.

    First, I'm not telling him how his system works. I don't know how his system works, nor do I care. Of course, he can always reply, "I have secret knowledge that shows that you are wrong and I am write". BFD.

    He is claiming that his original post shows a WTF. I am saying that the original post shows correct programming practice. If there's some secret reason the original post is wrong that he then has to come back and explain, then I am right. The real WTF is the submitter, because he mis-identified the WTF and had to explain why the code was wrong. In other words, it was not wrong for the reason the OP thought it was wrong. Otherwise, he would have explained that in the OP.

    Second, a refund system has to care about downgrades because downgrades affect the amount paid. A $500 laptop with a $50 RAM downgrade will result in an amount paid of $450. $500 for the laptop and -$50 for the downgrade. Negative prices are a fact of life, and good programming accounts for them. It is not a WTF to account for a negative refund amount that decreases the refund.

     Consider again a $500 laptop with a $40 restocking fee. The refund amount is $460, and has nothing to do with any amount that went into the amount paid.

     The way systems typically handle this is precisely as I explained. The laptop has a '$500' purchase cost and a '-$460' refund cost, or a new line item 'non-refundable item' is added, with a $0 purchase cost and a $40 refund cost. In either case, the refund amount comes to $460.

    The OP does not show a WTF. It shows correct code to handle typical pricing systems that have to refund items with upgrades, downgrades, restocking fees, and the like.

    If he has to say, "Yes, you're right. The code I said is a WTF is actually the normal, correct way to do things. But in my specific case, it's a WTF for reasons I didn't explain in my post." then that proves that he was wrong in the first place. The WTF is not what he claimed it to be, but in his understanding of how the code interacts with the rest of the system.



  • @bstorer said:

    @joelkatz said:

    You are completely missing my point. What if you return a laptop that has a RAM downgrade? You must subtract the downgrade amount from the laptop amount. Adding absolute values will not get you the correct return amount. Period.
    I love that you continue to insist that you know better than him how his system works.  Further, why should a refund system care about downgrades or anything else?  It only cares about the bottom line amount paid for that item, because that's what's going to be refunded.

     

    joelkatz is (mostly) right.  The code as given is (mostly) correct.  In a non-WTF system, the code would be the same but at the end you wouldn't need to Math.abs(xxx).  However, joelkatz is wrong in that he is trying to solve the actual problem (refunding money for purchased items) when the given code has nothing to do with that other than that it happens to be used within that context.  The given code is only suppose to add numbers together, some of which are negative, but for those we want to add their absolute values.  So the given code will not work for that.  However, the fact remains that the existing code was probably written by someone who didn't realize how WTF the rest of the system was.  adding the absolute values of a string of refund amounts would be WRONG WRONG WRONG in any normal system.



  • @tster said:

    However, the fact remains that the existing code was probably written by someone who didn't realize how WTF the rest of the system was.  adding the absolute values of a string of refund amounts would be WRONG WRONG WRONG in any normal system.
    It's really not the fault of the system so much as the API and documentation given to the vendors.  If refunds are always simply an amount of money to be given back, why support negative numbers at all?



  • @bstorer said:

    @tster said:

    However, the fact remains that the existing code was probably written by someone who didn't realize how WTF the rest of the system was.  adding the absolute values of a string of refund amounts would be WRONG WRONG WRONG in any normal system.
    It's really not the fault of the system so much as the API and documentation given to the vendors.  If refunds are always simply an amount of money to be given back, why support negative numbers at all?

     

    because some items can have a negative price (as joel pointed out).



  • @tster said:

     

    because some items can have a negative price (as joel pointed out).

    Except they can't, as halcyon1234 explained.  You seem to be speaking under the impression that this system generates the pricing, which doesn't seem to be the case.  My understanding is that this is strictly the payment system, meaning that calculating the amount of the refund falls outside its scope.  If that's the case (and halcyon1234, feel free to confirm or deny), it seems like his system simply gets told how much to refund, probably figures out how to issue the refund (mail a check, credit back to a credit card, whatever), and does so.


  • @bstorer said:

    @tster said:

     

    because some items can have a negative price (as joel pointed out).

    Except they can't, as halcyon1234 explained.  You seem to be speaking under the impression that this system generates the pricing, which doesn't seem to be the case.  My understanding is that this is strictly the payment system, meaning that calculating the amount of the refund falls outside its scope.  If that's the case (and halcyon1234, feel free to confirm or deny), it seems like his system simply gets told how much to refund, probably figures out how to issue the refund (mail a check, credit back to a credit card, whatever), and does so.
     

    yeah, I agree the code provided did not meet the requirements given for it.  But that doesn't mean the system isn't a WTF.  The very fact that this doesn't allow for negative prices inherently limits the flexibility of any system in which it is used.  



  • @tster said:

    The very fact that this doesn't allow for negative prices inherently limits the flexibility of any system in which it is used.  
    No it doesn't.  You can't refund negative money.  What do you do, cut a check for $-50?  That doesn't make sense.  If they owe you money, it's a receivable, not a refund.  Why should the same piece of code handle both?



  •  @joelkatz said:

    "I have secret knowledge that shows that you are wrong and I am write"

     I won't.  Instead I'll say i have secret knowledge that shows that you are wrong and I am right.

    I'll also say that I've posted the secret knowledge several times already in previous replies, so I'll just assume this message will go unread as well.

    For the record:

    PURCHASE ORDER

    Super-ass product - $75

    Awesome-O Discount - $25

    Payment: Charge $50, return order number 123

    REFUND REQUEST ON ORDER 123

    System goes "Hrm, Awesome-O discount is a discount and not a product, so it was never purchased, so I guess I can't refund that.  Instead I'll refund, how much was Order 123-- $50, ok?-- let's say $50.  Or -$50. Pass it on."

    The payment system's only function is, get this, to make payments.  Not to deal with discounts, or anything like that.  It's told "Give $X to Entity Y".  It really, really isn't a hard concept to grasp.  Any number of other parts of the system are responsible for creating an purchase/refund request by doing all the discount/price reduction logic.  Those parts of the system don't have to know how to process a refund. It's not their responsibiltiy.  They only need to know how to tell the right part of the system how much to refund.

    Or, more simply: you're wrong.



  • @halcyon1234 said:

    @joelkatz said:

    "I have secret knowledge that shows that you are wrong and I am write"

     I won't.  Instead I'll say i have secret knowledge that shows that you are wrong and I am right.

    Haha, too funny.



  •  @halcyon1234 said:



    Does P = Q?



    Reminds me of some Lewis-Carroll-like thinking:

    Do you mean "are the contents of the thing called P equal to the contents of the thing called Q?" or do you mean "Is P the same thing as Q?" or do you even mean "are the contents of the thing called P the same as the contents of the thing called Q?"

    P is not the same thing as Q to be sure.

    The contents of the thing called P might have the same interpretation as the thing called Q, without the contents being the same.

    The only way the contents of P can be identical to the contents of Q is if P and Q are, in fact, simply names of the same object. But the names are still not the same.



  • @too_many_usernames said:

    @halcyon1234 said:



    Does P = Q?



    Reminds me of some Lewis-Carroll-like thinking to lighten this thread a bit (and show the importance of communication):
    ...

     

    Dang edit timers are too short...insert bold modification above


  • @too_many_usernames said:

     @halcyon1234 said:



    Does P = Q?



    Reminds me of some Lewis-Carroll-like thinking:

    Do you mean "are the contents of the thing called P equal to the contents of the thing called Q?" or do you mean "Is P the same thing as Q?" or do you even mean "are the contents of the thing called P the same as the contents of the thing called Q?"

    P is not the same thing as Q to be sure.

    The contents of the thing called P might have the same interpretation as the thing called Q, without the contents being the same.

    The only way the contents of P can be identical to the contents of Q is if P and Q are, in fact, simply names of the same object. But the names are still not the same.

     

    P is not a Q, it's a stream



  • @too_many_usernames said:

     @halcyon1234 said:


    Does P = Q?



    Reminds me of some Lewis-Carroll-like thinking:

    Do you mean "are the contents of the thing called P equal to the contents of the thing called Q?" or do you mean "Is P the same thing as Q?" or do you even mean "are the contents of the thing called P the same as the contents of the thing called Q?"
     

     (insert d'oh).  I should have said "Does P == Q", since they're logic statements, and not values.



  •  I've explained this at least three times already. You refund negative money because some items, when returned, actually *decrease* the refund. You are correct, you can't refund negative money. That's why the original code takes the absolute value of the total.

     Here's the example again:

    Laptop: $500

    Memory Downgrade: $-50

     Note that you don't refund negative money. You refund $450. The absolute value of the total will be the amount of money to refund, which is exactly what the code calculates. The OP was suggesting the code should take the absolute value of each amount before summing it, but no matter what signs the modules provide, adding the absolute value of the two prices will generate $550, the wrong refund amount.

    TRWTF is the submitter, who thinks this code is broken because it doesn't sum the absolute value of the refund amount for each item but instead take the absolute value of the total. Taking the absolute value of the total is rational, to convert a negative "payment" amount to a positive "refund" amount for a refund. Taking the absolute value of each item would, in typical systems, result in a positive refund for things like downgrades and restocking fees.

    It is not a WTF for programmers to write rational, sensible code rather than horribly broken code. It is, however, a WTF if the system is so broken that one must write horribly broken code to work within it. So TRWTF is the OP, who blamed the rational code for not being bug-for-bug compliant with the system it lives in.



  • @joelkatz said:

     I've explained this at least three times already. You refund negative money because some items, when returned, actually *decrease* the refund. You are correct, you can't refund negative money. That's why the original code takes the absolute value of the total.
    And I've explained it at least as many: this is a payment processing system.  All price adjustments are handled on the front end.  How do you not get that?

    @joelkatz said:

    TRWTF is the submitter, who thinks this code is broken because it doesn't sum the absolute value of the refund amount for each item but instead take the absolute value of the total. Taking the absolute value of the total is rational, to convert a negative "payment" amount to a positive "refund" amount for a refund. Taking the absolute value of each item would, in typical systems, result in a positive refund for things like downgrades and restocking fees.
    Again: this is payment processing.  The front end handles restocking fees and such, this part just cuts checks or some equivalent.

    @joelkatz said:

    So TRWTF is the OP, who blamed the rational code for not being bug-for-bug compliant with the system it lives in.
    No, TRWTF is still you, for complaining that the OP's code is perfectly valid in another context, despite the fact that it is wrong in this context.



  • "No, TRWTF is still you, for complaining that the OP's code is perfectly valid in another context, despite the fact that it is wrong in this context."

    I'm sure there's some context in which 2+2=5 makes perfect sense and 2+2=4 is the dumbest thing one could possibly say. But if someone reports that as "what an idiot, he said 2+2=4", that shows something wrong with the reporter, not the statement. He failed to realize that his complaint is only valid in his specific context.

    It is not a WTF to do something that is correct in every context but the one bizarre context you happen to be in. That's simply a mistake of not recognizing context. The OP clearly though the code was generally incorrect.

    In fact, it is generally precisely correct, and only invalid in the special case of pre-adjusted prices with a random sign. I can assure you, that's a very rare case. If the OP understood that the code was only a WTF in that already-F'd-up situation, he would have said so in the original post.


Log in to reply