Bizoneness: When SAP B1  Should Crash

Bizoneness: When SAP B1 Should Crash

While this summer has been a source of chaos for me, I've wanted to return to regular writing here. I haven't gotten a topic that was suitable to write about lately until a recent incident brought up a topic that I do have a controversial take on: Let SAP B1 crash.?


Many of you will never deal with this, but as you get into the world of add-ons and external files, you'll find that this becomes more of an issue. My shipping add-on was the culprit in my case, but it dragged SQL and my dashboard add-on into the mess.?

My shipping add-on has a table I'll call SHIP to preserve the vendor's identity. SHIP receives data from online shipping systems, such as the cost of shipping and the tracking number. I use SHIP in all kinds of reports and dashboards in SAP B1 to tell the shipping status and tracking numbers.?

One afternoon recently, tickets came in about errors people were suddenly getting in SAP. While multiple modules and dashboards were involved, all had the same message:?

Conversion failed when converting the varchar 
value 'SAMPLE' to data type int        

The error message from a sales order mentioned SHIP. I checked the data with a simple?

SELECT * FROM SHIP         

Unfortunately, that was a mess. There was no good way to sort the columns. SHIP has all of its columns as?varChar?strings, and dates, in particular, come in multiple formats. So I tried again, this time casting the date

 SELECT CAST(ShipDate as DATE) as ShippingDate ,* FROM SHIP         

Using double-clicking on the columns, I ordered the data by date and found that errant row.

This package was manually entered into FedEx, and the shipping person put SAMPLE for the DocNum. DocNum is a varChar in SHIP, so neither Fedex nor my shipping module complained about that string in a number. Even the query above didn't complain. The problem was here:?


Select
    T1.DocNum,
    T0.CAST(ShipDate as date),
    T0.TrackingNum 
FROM 
  SHIP T0 
  INNER JOIN ORDR T1 ON t1.Docnum = t0.docnum        

Parameters and joins from ORDR would compare the integer value for DocNum, against the varchar SHIP.DocNum. Usually, implicit casting handles this. SQL assumes the string is supposed to be a number and automatically casts it correctly. But when the SHIP.DocNum is SAMPLE, then casting fails, and we get the error we saw above.?

Through the shipper online tools, we were able to fix the data, but it brought about a question: is there a fix for this when there is bad data -- Should I do that?

The fix is to use TRY_CAST to force cast SHIP.docnum to an integer. One version would be this?

  WITH Shipping as (
   SELECT 
      TRY_CAST(T0.Docnum) AS DocNum,
      T0.CAST(ShipDate as date) AS ShippingDate,
      T0.TrackingNum AS TrackingNumber
   FROM SHIP T0 
  )
 
 Select 
    T1.DocNum,
    T0.ShipDate,
    T0.TrackingNumber 
FROM 
  Shipping T0 
  INNER JOIN ORDR T1 ON t1.Docnum = t0.docnum        

I do all my Casting in the WITH so it is easier to use in the main SELECT. Unlike CAST, which would cause an error if unable to cast, TRY_CAST will successfully cast the number or return NULL, preventing the system from crashing.?

And this gets to the core question: should you use TRY_CAST and prevent crashes? I want it to crash, especially if there is no way to indicate a problem. Within SAP B1, there are ways of handling the situation by preventing bad data from getting into the system. I can add validation routines using a product like Boyum B1 Usability Package. However, external data like the shipping data here, EDI transmissions, and E-commerce portals is not so easy since you do not have control of the input. Hard fails often get you to the root of the problem a lot faster. Preventing failures may produce more problems.

For example, SHIP.DocNum is a primary key. If the value is NULL, that means this shipment is not tied to any sales order. If not tied to a sales order, it is not tied to a delivery document or invoice, and inventory is not allocated properly, nor will shipping charges show properly as an expense. Indeed as far as SAP and our Shipping add-on are concerned, the shipment disappears. This could happen multiple times, and no one knows unless staff wonder why a particular order is missing from invoices.?

If there's a problem once with a hard fail scenario, everybody knows it. For IT, it becomes easier to track down the culprit, but there is a Social aspect to this too. Since SAP B1 implementations are usually small businesses, everyone knows all the other users. Thus, every user knows who screwed up their day. Social pressure keeps users from making the same mistake twice.?

Some errors can be handled. Sometimes there is enough information to fill in the problem. For example, suppose SHIP returns the delivery date as a varChar. I could cast that to a date, and if it is corrupt, use the sales order's date, which happens to be a dateTime.?

 CASE WHEN TRY_CAST(t0.DueDate as DATE) IS NULL
     THEN t1.dueDate
     ELSE CAST(CAST(t0.DueDate as DATE) as DATETIME)
 END as DueDate        

I use TRY_CAST to determine if the cast will work and replace the data. You can't do this for a key, but you might get away with it with some other data.?

When handling errors, especially data errors, be careful how you do them. Not every error should be smoothed over just to make everything look nice. Instead, you want to find root causes and fix the errors so you don't see any crashes. To do that, you want to know about some of them either as symptoms of a bigger problem or as the problem itself.

要查看或添加评论,请登录

Steven Lipton的更多文章

  • LucaERP part 5: Building A Form

    LucaERP part 5: Building A Form

    In this series of biweekly columns, we're building an ERP from scratch, starting with the general ledger. We've learned…

  • BizOneness: Aggregate Functions in HANA

    BizOneness: Aggregate Functions in HANA

    #HANA #Bizoneness Over the last few newsletters, we've been looking at the basics of HANA. This time, I'd like to look…

  • LucaP: The Anatomy of User Interfaces for ERP

    LucaP: The Anatomy of User Interfaces for ERP

    #LucaP In the last installment of building the LucaP ERP, we discussed the part of the general journal. We have yet to…

  • Bizoneness: Common SQL and HANA types, Operators, and Functions

    Bizoneness: Common SQL and HANA types, Operators, and Functions

    Bizoneness: Common SQL and HANA types, operators, and Functions #Bizoneness #HANA Two weeks ago, I wrote the first of…

    1 条评论
  • LucaP ERP part 3: Adding General Journal Entries

    LucaP ERP part 3: Adding General Journal Entries

    #LucaP *Note:As I've gained a few followers and subscriptions in the last few weeks, I want to remind everyone that I…

  • Bizoneness: Introducing HANA

    Bizoneness: Introducing HANA

    #Bizoneness While I had planned something about the General Ledger for this newsletter, circumstances changed, so I'll…

    3 条评论
  • LucaP ERP part2: The Chart of Accounts

    LucaP ERP part2: The Chart of Accounts

    #LucaP #ERP In the ERP application, we have established credits and debits on a ledger with the five major accounts…

  • The Bizoneness Migration Guide

    The Bizoneness Migration Guide

    #Bizoneness Data migration is taking data from one source and merging it into another source. If you're very lucky, it…

  • LucaP ERP Part 1: The Basic Equation

    LucaP ERP Part 1: The Basic Equation

    #LucaP How do you start to understand the workings of an ERP system? By making one. In this biweekly column, we’ll make…

  • BizOneness: Item Filtering and Identification in SAP Business One.

    BizOneness: Item Filtering and Identification in SAP Business One.

    #Bizoneness The inventory Master Data IN SAP Business One is a collection of everything you buy, sell, and make. Unlike…

社区洞察

其他会员也浏览了