Identify production quantity including route scrap before posting

Situation:

  • Allow back-flushing of 92Kg of ingredient, even though only 90Kg are available; No negative stock allowed.
  • I did this by modifying the production order before reporting as finished.
  • For this I needed a way to find out how much the system will back-flush (e.g. 92Kg). The difficulty is that we use phantom BOMs and Routes, which makes the calculation rather complicated.
  • NOTE: Activating the production parameter "Physical reduction" (Journals tab), also reduces the quantity automatically to depleat only available stock. But we wanted more controll over this process.

Solution:

  • Access the functionality used by Dynamics Ax for this calculation.
  • Note: The production must be started for the phantoms to be exploded.

//bw start
//Changed on 22 May 2007 by TW
/* Description:
Get the expected BOM line qty to be used
Production must be started to take Phantom BOM/Routes into account
*/
static public Qty prodItemQty(ProdBOM _prodBom, Qty _seriesSize=0)
{
    ProdTable           prodTable = ProdTable::find(_prodBom.BOMId);
    BOMCalcData         bomCalcData;
    BOMCalcConsumption  itemCalcLine;
    UnitQty             bomProposal;
    InventQty           inventProposal;
    Qty                 seriesSize = _seriesSize;
    ;
    if (!seriesSize)
    {
        if(prodTable.ProdStatus==ProdStatus::StartedUp)
            seriesSize = prodTable.QtyStUp-(prodTable.reportedFinishedGood()+prodTable.reportedFinishedError());
        else
            seriesSize = prodTable.QtySched;
    }
    bomCalcData = BOMCalcData::newProdTable(seriesSize,_prodTable);
    itemCalcLine = BOMCalcConsumption::construct(_prodBom.Formula,bomCalcData);
    bomProposal = itemCalcLine.calcConsumption(_prodBom,
                                                ProdRoute::accError(_prodBOM.ProdId,_prodBOM.OprNum),
                                                NoYes::Yes);
    inventProposal = UnitConvert::qty(
                bomProposal,
                _prodBOM.UnitId,
                InventTableModule::find(_prodBOM.ItemId,ModuleInventPurchSales::Invent).UnitId,
                _prodBOM.ItemId);

    return inventProposal;
}
//bw end

Reporting as finished items with scrap on route – multiple reporting as finished

Situation:
  • Depending on the processing equipment we expect some percentage of the bulk to be waste e.g. 2% of 1000kg (i.e. when starting 980kg the system automatically reserves 1000kg of ingredients, 980kg are expected as good qty).
  • We have defined this as errorPct on the Workcenter, which gets copied to the route when created.
  • When processing we will start with ingredient to make 1000Kg but expect only 980Kg good qty the rest will be waste.
  • On the shop floor up to 5% might occur i.e. we might report 950 kg ( and 50 kg waste) as finished.
  • We will produce the 1000kg in two lots of 500kg and report them independently.

What DAX does:

  1. Create a job for 980 kg
  2. Start the job for 980 kg (reserves 1000kg of ingredients)
  3. Two reporting as finished scenarios:
    1. Report it as fished in two lots of 450 kg (unexpected high scrap of 50 kg per lot)
      1. First lot: the system suggests 980 kg good qty, no error qty; we enter 450kg / 40kg (assuming the system already accounts for 10 kg error qty)
      2. Second lot: the system suggest 10 kg good qty and 0 kg error qty! (Problem #1) Expected would be either 490 kg or 530 kg (in our situation 490 kg, as we will take what comes out of the process and will not add more ingredient to make good for error qty).
      3. Although all ingredients have been used up the system still expects 80kg more product to be finished. (Problem #2) The remain status will stay at Material consumption until the job is ended. The actual remain status should be Ended.
    2. Report it as fished in two lots of 495 kg (unexpected low scrap of 5 kg per lot)
      1. First lot: the system suggests 980kg good qty, no error qty; we enter 495kg / -5kg (assuming the system already accounts for 10 kg error qty)
      2. Second lot: the system suggest 0kg good qty and 15 kg error qty! (Problem #1) Expected would be either 490 kg or 485kg. We again enter 495kg / -5kg.
      3. Even though the system realises that no more qty is to be expected as finished (i.e. remain qty = 0) the remain status will still stay at Material consumption until the job is ended. (Problem #2) 
  4. Other combinations combinations of good qty and error quantity can be applied. All result in very weird behavior.
It is incomprehensible how anyone could use the system together with error quantities on the route. Luckily Dynamics Ax allows every customer to dive into the code an the culprits leading to Problem #1 are quickly found:
The class ProdUpdReportFinished has a method called proposalQtyGood and proposalQtyError the returns proposed good and error quantities.  Check the code below.
I will post the solution of Problem #2 it a future blog.
 
 
 
static InventQty proposalQtyGood(ProdId _prodId)
{
    ProdTable   prodTable              = ProdTable::find(_prodId);
    InventQty   routeReportedError     = prodTable.routeReportedError();
    InventQty   routeReportedTotal     = prodTable.routeReportedGood() + routeReportedError;
    InventQty   reportedFinishedGood   = prodTable.reportedFinishedGood();
    InventQty   reportedFinishedError  = prodTable.reportedFinishedError();
    InventQty   maxReportedError       = (reportedFinishedError < routeReportedError) ? routeReportedError : reportedFinishedError;
    InventQty   plannedOrder;
    ;
    if (prodTable.mandatoryRegister())
    {
        plannedOrder = InventTransSum::new2TransId().idRegistered(prodTable.InventTransId);
    }
    else
    {
//bw start
//Changed on 15 Mar 2007 by TW
/* Description:
Calculate the planned order qty relative to the qty started not relative to the qty on the route.
*/
        /* original code
        plannedOrder = routeReportedTotal;
        if (!plannedOrder)
        {
            plannedOrder= prodTable.QtyStUp;
            if (!plannedOrder)
                plannedOrder= prodTable.QtySched;
        }
        plannedOrder = plannedOrder – reportedFinishedGood – maxReportedError;
        */
        plannedOrder = prodTable.QtyStUp – reportedFinishedGood – reportedFinishedError;
//bw end
        if (plannedOrder < 0)
            plannedOrder = 0;
    }
    return plannedOrder;
}
 
static InventQty proposalQtyError(ProdId _prodId)
{
    InventQty   qtyError;
    ProdTable _prodTable= ProdTable::find(_prodId);
    ;
    if (_prodTable.mandatoryRegister())
        return 0;
    else
    {
        qtyError = _prodTable.routeReportedError() – _prodTable.reportedFinishedError();
        if (qtyError < 0)
            qtyError = 0;
    }
//bw start
//Changed on 15 Mar 2007 by TW
/* Description:
Never suggest an error Quantity!
The error Qty should only reflect finished product that has been compleated,
but that has failed some sort of quality check. I.e. this value is expected to be zero.
*/
    qtyError = 0;
//bw end
    return qtyError;
}