Microsoft Dynamics Ax 2009 Pick List & Packing Slip post through code

22. June 2010

This one is going to be a bit lengthy. We needed a way to Pick/Pack/Ship a sales order line using a single method call in Microsoft Dynamics Ax 2009. We used this code in a WCF service that allowed us to make a single call to pick, pack, and ship a single line from a sales order.

Below is the entire codebase I wrote (with comments) to accomplish this goal. If you have any questions please drop me an email.

   1: // classDeclaration
   2: class VendorIntegration_Shipment
   3: {
   4:     WMSOrderTransType_Output   wmsOrderTransType;
   5:     WMSOrderTrans              wmsOrderTrans;
   6:     DlvMode                    dlvMode;
   7:     InventTable                inventTable;
   8:     WMSRouteId                 pickListId;
   9:     PackingSlipId              packingSlipId;
  10:     CustDlvModeId              custDlvModeId;
  11:     DlvModeId                  dlvModeId;
  12:     str 10                     tradingPartner;
  13: }
  14:  
  15: // new instance method
  16: // Trading Partner: string used to determine calling vendor
  17: // PickListId: Picklist from the sales order
  18: // CustDlvModeId: Vendor's Shipping Method (we will look up ours from an xref table)
  19: void new(str 10 _tradingPartner, WMSRouteId _pickListId, CustDlvModeId _dlvMode)
  20: {
  21:     VendorIntegrationModeOfDeliveryXRef vendDlvModeXRef;
  22:     ;
  23:     // trading partner is a string value we used to determine the vendor so we can look up cross reference information
  24:     // specific to that vendor. Some vendors use different Shipping Method names than we do so we use a cross 
  25:     // reference table to store their Shipping methods to ours.
  26:     if(_tradingPartner == '' || _pickListId == '' || _dlvMode == '')
  27:     {
  28:         throw Global::error("One or more arguments were not supplied. (cstor).");
  29:     }
  30:  
  31:     tradingPartner = _tradingPartner;
  32:     pickListId = _pickListId;
  33:     // Check to see if the supplied shipping method is different
  34:     // from the Picking List.
  35:     if(WMSPickingRoute::find(_pickListId).DlvModeId != _dlvMode)
  36:     {
  37:         // Check to see if we have this shipping method
  38:         dlvMode = DlvMode::find(_dlvMode);
  39:         if(dlvMode == null)
  40:         {
  41:             // Look up the supplied shipping method in the vendorXRef table.
  42:             vendDlvModeXRef = VendorIntegrationModeOfDeliveryXRef::find(_tradingPartner, _dlvMode);
  43:             if(vendDlvModeXRef != null)
  44:             {
  45:                 //Wire it up
  46:                 dlvMode = DlvMode::find(vendDlvModeXRef.DlvModeId);
  47:             }
  48:             else
  49:             {
  50:                 throw Global::error("The supplied shipping method could not be found (VendShippingXRef).");
  51:             }
  52:         }
  53:     }
  54:     else
  55:     {
  56:         dlvMode = DlvMode::find(_dlvMode);
  57:     }
  58: }
  59:  
  60: // Primary method to kick off the PICK/PACK/SHIP
  61: // RecId: Specific record id from the WMSOrderTrans table generated AFTER PickList
  62: // Qty: Quantity to ship
  63: // InventSerialId: The serial # chosen to ship
  64: // TrackingNumbers: x++ Container of tracking numbers supplied for this shipment
  65: // ShipDate: The date of the shipment
  66: public void receiveShipmentLine(int64 _recId, int _quantity, InventSerialId _inventSerialId, container _trackingNumbers, Date _shipDate)
  67: {
  68:     ;
  69:     try
  70:     {
  71:         ttsBegin;
  72:  
  73:         // Look up the order transaction by picklist and recid
  74:         select forUpdate wmsOrderTrans where wmsOrderTrans.RecId == _recId && wmsOrderTrans.routeId == pickListId;
  75:  
  76:         // Construct the Order Trans type object
  77:         wmsOrderTransType = wmsOrderTransType::construct(wmsOrderTrans);
  78:  
  79:         // If the item is serialized, then compare and replace the inventory dimension. Custom method to check if Serialized Part
  80:         if(InventTable::isSerialized(wmsOrderTrans.itemId)) this.updatePickListLineSerial(_inventSerialId);
  81:  
  82:         // Pick the line
  83:         this.registerPickListLine();
  84:  
  85:         // Pack the line
  86:         this.registerPackingSlipLine();
  87:  
  88:         //Save the tracking numbers
  89:         this.saveTrackingNumbers(_trackingNumbers, _shipDate);
  90:  
  91:         ttsCommit;
  92:     }
  93:     catch (Exception::Error)
  94:     {
  95:         error("'Exception::Error' Unable to complete receiveShipmentLine. Transaction aborted.");
  96:     }
  97: }
  98:  
  99: // Used to update the PickList Registration
 100: private void updatePickListLineSerial(InventSerialId _inventSerialId)
 101: {
 102:     InventDim           inventDimOrig;
 103:     InventDim           inventDimNew;
 104:     ;
 105:  
 106:     // If not empty
 107:     if(_inventSerialId != '')
 108:     {
 109:         if(wmsOrderTrans != null)
 110:         {
 111:             // Look up the order transaction dimension
 112:             inventDimOrig       = InventDim::find(wmsOrderTrans.inventDimId);
 113:  
 114:             if(_inventSerialId != inventDimOrig.inventSerialId)
 115:             {
 116:                 // Find dim with the new serial number (our custom method to do the lookups)
 117:                 inventDimNew = InventDim::findOwnCondDispSiteLocSerial(inventDimOrig.configId,
 118:                     inventDimOrig.InventSizeId, inventDimOrig.InventColorId, inventDimOrig.InventSiteId,
 119:                     inventDimOrig.InventLocationId, _inventSerialId);
 120:  
 121:                 // Move the registration to the new part
 122:                 wmsOrderTransType.moveReservation(inventDimNew);
 123:             }
 124:         }
 125:         else
 126:         {
 127:             throw error("'Exception::Error' Unable to updatePickListLineSerial due to null wmsOrderTrans object. Transaction aborted.");
 128:         }
 129:     }
 130: }
 131:  
 132: // PickList Register this line
 133: private void registerPickListLine()
 134: {
 135:     container   pks;
 136:     Map         dict;
 137:     ;
 138:     if(wmsOrderTrans != null)
 139:     {
 140:         // Create a Map (key/value pair)
 141:         dict = new Map(Types::Int64, Types::Container);
 142:         // Create a container
 143:         pks = [wmsOrderTrans.orderId, wmsOrderTrans.recVersion];
 144:         // Load the map with the recid and container
 145:         dict.insert(wmsOrderTrans.RecId, pks);
 146:         // Call finishPickingLines
 147:         wmsOrderTransType.finishPickingLine('', wmsOrderTrans.inventDim(), EmplTable::userId2EmplId(curUserId()), TimeNow());
 148:     }
 149:     else
 150:     {
 151:         throw error("'Exception::Error' Unable to registerPickListLine due to null wmsOrderTrans object. Transaction aborted.");
 152:     }
 153: }
 154:  
 155: // Register the packing slip
 156: private void registerPackingSlipLine()
 157: {
 158:     ;
 159:     if(wmsOrderTrans != null)
 160:     {
 161:         // Register PackingSlip using a custom method we wrote (supplied below)
 162:         packingSlipId = SalesFormLetter::createPackingSlipWithDlvMode(wmsOrderTrans.inventTransRefId, dlvMode.Code);
 163:     }
 164:     else
 165:     {
 166:         throw error("'Exception::Error' Unable to registerPackingSlipLine due to null wmsOrderTrans object. Transaction aborted.");
 167:     }
 168: }
 169:  
 170: // Method to save the tracking numbers for the shipment
 171: public void saveTrackingNumbers(container _trackingNumbers, Date _shipDate)
 172: {
 173:     ShipCarrierPackage      shipPkgCarrier;
 174:     container               trackMetaData;
 175:     int                     pos;
 176:     ;
 177:     if(wmsOrderTrans != null)
 178:     {
 179:         pos = 1;
 180:         while(pos <= conlen(_trackingNumbers))
 181:         {
 182:             trackMetaData = conpeek(_trackingNumbers, pos);
 183:  
 184:             shipPkgCarrier.SalesId                  = wmsOrderTrans.inventTransRefId;
 185:             shipPkgCarrier.Weight                   = conpeek(trackMetaData, 2);
 186:             shipPkgCarrier.ShipDate                 = _shipDate;
 187:             shipPkgCarrier.PackingSlipId            = packingSlipId;
 188:             shipPkgCarrier.ShipCarrierTrackingNum   = conpeek(trackMetaData, 1);
 189:             shipPkgCarrier.CarrierId                = dlvMode.ShipCarrierId;
 190:             shipPkgCarrier.insert();
 191:             pos++;
 192:         }
 193:     }
 194:     else
 195:     {
 196:         throw error("'Exception::Error' Unable to saveTrackingNumbers due to null wmsOrderTrans object. Transaction aborted.");
 197:     }
 198: }
 199:  
 200: // Custom PacklingSlip registration
 201: public static PackingSlipId createPackingSlipWithDlvMode(SalesId _salesId, CustDlvModeId _dlvMode)
 202: {
 203:     // 06/12/2010: CCK
 204:     // Needed to create a new packing slip method that allowed changing of the delivery mode.
 205:     SalesFormLetter_PackingSlip packingSlip;
 206:     SalesTable                  localSalesTable;
 207:     DlvMode                     dlvMode;
 208:     ;
 209:     // Retrieve the dlvmode record
 210:     dlvMode = dlvMode::find(_dlvMode);
 211:     // Retrieve the sales record
 212:     localSalesTable = SalesTable::find(_salesId);
 213:     // Construct the SalesFormLeter using the Document Status Enum
 214:     // The base class knows to construct the SalesFormLetter_PackingSlip from this enum
 215:     packingSlip = SalesFormLetter::construct(DocumentStatus::PackingSlip);
 216:  
 217:     // Set a few override methods (this is only so we can override the Shipping Method set in the Sales Order)
 218:     // There are times when the Shipping Method is changed @ shipment and allows us to override the
 219:     // Shipping method set in the sales order. If you need more information contact me. You can remove
 220:     // this code for your use.
 221:     if(strUpr(localSalesTable.DlvMode) != strUpr(_dlvMode))
 222:     {
 223:         packingSlip.parmOverrideDlvMode(dlvMode.Code);
 224:         packingSlip.parmOverrideShipCarrier(dlvMode.ShipCarrierId);
 225:     }
 226:     // Call update passing the the salesTable
 227:     packingSlip.update(localSalesTable, SystemDateGet(), SalesUpdate::PickingList, AccountOrder::None, NoYes::No, NoYes::No);
 228:     return packingSlip.getPackingListId();
 229: }

Microsoft Dynamics Ax 2009 ,