Error: This document contains one or more posting holds, Dynamics GP Edit List

If you are experiencing this on SOP Sales Invoice posting edit lists, then maybe you are using drop ship items. Also perhaps you have the Shipping Notification Tool installed?

Error this document contains one or more posting holds

There is no posting hold as such on the document, the posting hold mechanism is being hijacked by the shipping tool to prevent posting of the document. It is actually because GP does not think the item in question have been shipped yet and so is preventing it from posting. Ensure that the item was marked as shipped using the ship notification window.

The setting in the INI file needs changing if you want to change this behaviour, details of the shipping notification window are in this post as is more information on the INI settings to change if you want to change the behaviour:

 Dynamics GP Drop Shipping Sales invoices before purchase invoice with Shipment Notification Tool (SNT)

Empty Matched To Shipment in Purchasing Invoice Entry of Dynamics GP

About from time to time on Euro transactions we get a yellow triangle and empty “Matched to Shipment” field in the Purchasing Invoice Entry window of GP.

Purchasing Invoice Entry Yellow Triangle

The problem is always the same, the fields

  • CURNCYID
  • CURRNIDX
  • XCHGRATE
  • RATECALC

Are all either default or missing for the item row in the table POP10500.

I am still looking for the break through clue as to what causes this, I’m guessing network outages or other hardware failures interrupting processing somewhere. In the meantime I just fix the issue when it comes up, but having fixed it a couple of times I wrote a script to help.

If this is the problem that you are seeing too then the following script can be used to fix it for a given PO Number.

BEGIN TRANSACTION

UPDATE pl
SET CURNCYID = ph.CURNCYID
,CURRNIDX = ph.CURRNIDX
,XCHGRATE = ph.XCHGRATE
,RATECALC = ph.RATECALC
FROM POP10100 ph
JOIN POP10500 pl ON ph.PONUMBER = pl.PONUMBER
WHERE ph.PONUMBER = '{your po number}'
AND pl.CURNCYID = ''

ROLLBACK TRANSACTION

As always, I can’t know your GP environment, so this should be thoroughly tested in a test environment before running the script on production data. I can’t take any responsibility for what might happen on your systems.

Let me know if this helped you in the comments, it motivates me to blog more…

OLE Notes Migration

It was time to get our old OLE notes migrated. GP has stopped using OLE notes containers for attachments to notes in the system, mostly due to the need to work with the web browser hosting of GP.  I didn’t want to import the attachments back into the new GP attachments feature, as no one had been screaming for the ones that had vanished after the upgrade (only a few enquiries). However I did want to make them available should someone need them enough (don’t ask me for criteria for the enough!). So the plan is to run the first part of the migration tool only, the extract and not the import. For details of the tool see the post by encore:

The OLE note migration utility in Dynamics GP

The OLE notes directory totalled 46GB with 30k documents, running the migration utility brought the extracted size down to 3GB. Evidence is that the object containers were not efficient!

So the lesson is that you might want to extract your OLE notes even if you don’t intend to use them as it will help your file sizes, in our case the storage is backed by a SAN that will be compressing things anyway, extracting the files makes them useable from the directory.

What you get after extraction is a directory structure that starts with the dynamics GP product ID, and then has a folder for each noteidx under that. The note index is the record id for the note in the notes table. So for every product that uses notes and had attachments there should be a top level folder, then subfolders for the note attachments.

It looks like the same kind of scheme used by the newer GP attachments feature, see the post:

Document attach feature Dynamics GP database and BusObjKey formats

the directory name is the hex version of the note index that the contents of that directory link to.

This is good enough for me to recover any documents requested by looking at the note index and converting it.

SSMS SQL Hazard

Let me point out a hazard using SSMS, when developing a SQL delete query, especially more complex ones that take time to build up. I also think about some of the ways I work and how they minimise the chances of damage to data.
Let us say we are developing the simple query to remove orphan records from the prices table of Dynamics GP:
 
DELETE IV00107
SELECT *
FROM IV00107
LEFT JOIN IV00108
ON IV00107.ITEMNMBR=IV00108.ITEMNMBR
and IV00107.CURNCYID=IV00108.CURNCYID
AND IV00107.PRCLEVEL=IV00108.PRCLEVEL
AND IV00107.UOFM=IV00108.UOFM
WHERE
IV00107.PRCLEVEL='LIST'
AND IV00108.ITEMNMBR IS NULL
 
…as we develop it, I highlight from SELECT down to NULL and hit F5 to run it. That will only run the “select”, to see what it will be deleting later. Later I would put double dash in front of the select to and run the whole statement to get the delete to execute.
 
Let us say we have a break, then come back having got the approval to delete the records. So our eye catches the delete and so we highlight from DELETE to NULL and hit F5. Oh no! We have just deleted everything in IV00107! Think about it, the delete and SELECT are interpreted (correctly) as two different operations.
 
Working this way, developing the record set to remove as a SELECT, then adding the DELETE to the top ready to be ran is a common pattern for me. Although I usually put a double dash in front of the DELETE as shown below to prevent this mistake or the mistake of just hitting F5 during testing, without remembering to select first. The action of uncommenting the DELETE triggers my mind to also then comment out the SELECT.
 
--DELETE IV00107
SELECT *
FROM IV00107
LEFT JOIN IV00108
ON IV00107.ITEMNMBR=IV00108.ITEMNMBR
and IV00107.CURNCYID=IV00108.CURNCYID
AND IV00107.PRCLEVEL=IV00108.PRCLEVEL
AND IV00107.UOFM=IV00108.UOFM
WHERE
IV00107.PRCLEVEL='LIST'
AND IV00108.ITEMNMBR IS NULL

I made this mistake against a test SQL database today, a reminder of why you should develop against a test environment. Luckily I use SSMSBoost, a productivity pack plug in for SSMS. This addin to SSMS will warn when you are executing a delete without a where clause, throwing up a fatal action guard window,  so it prevented me from actually causing any damage.
 
ssmsboost fatal action guard window
 
Immediately I could see what I had done and hit the No button.
 
Another factor that led to this potential mistake was not aliasing my tables like I normally would do, if I had written the following then the DELETE would not have been able to find the table i7 and hence would not have executed and would return;

Msg 208, Level 16, State 1, Line x
Invalid object name 'i7'

DELETE TOP(1000) i7
SELECT *
FROM IV00107 i7
LEFT JOIN IV00108 i8
ON i7.ITEMNMBR=i8.ITEMNMBR
and i7.CURNCYID=i8.CURNCYID
AND i7.PRCLEVEL=i8.PRCLEVEL
AND i7.UOFM=i8.UOFM
WHERE
i7.PRCLEVEL='LIST'
AND i8.ITEMNMBR IS NULL
 
Yet another practice that would have helped is that I also would normally have the TOP statement in the query. I would then pressing F5 ten times, in this case until there are no more rows to process. I use TOP to make it run faster and not cause lock escalation (we have 2million records in price table). Other times if there is too much to remove from F5, I’d put it in a loop with a @ROWCOUNT check to see if anything is left to process, that would not have stopped a disaster though. In reality a delete on this table would have taken so long I would soon see my mistake and cancel the query before it committed, but I’m showing a principle here in this post. So the TOP statement would have prevented quite so much data loss.
 
Luckily I didn’t end up with any issues at all, but as is often the case an accident only happens when the perfect storm of factors come together. Keeping to my normal way of working protects me but I thought others might learn from why I work the way I do.
 
If I had removed the records in production, then it would not have been a bit deal in this case as we restore our production database into our test company regularly and automatically (see my post on how to do this). As these prices do not change much from day to to, I could have just squirted the missing records from that company into production, then restored to point in time the test company and again re-synced the prices from that restore, resulting in no impact on users.  This is one of the reasons I’m a fan of having fresh copies of production available in test, also useful for patching up mistakes users make in a timely manner.