public function cancel_grn()
 {
     $flash = Flash::Instance();
     $errors = array();
     if (!$this->CheckParams('gr_number')) {
         sendBack();
     }
     $poreceivedline = DataObjectFactory::Factory('POReceivedLine');
     $cc = new ConstraintChain();
     $cc->add(new Constraint('gr_number', '=', $this->_data['gr_number']));
     $poreceivedlines = $poreceivedline->getAll($cc);
     $db = DB::Instance();
     $db->StartTrans();
     foreach ($poreceivedlines as $poreceivedline_id => $value) {
         $poreceivedline = DataObjectFactory::Factory('POReceivedLine');
         $poreceivedline->load($poreceivedline_id);
         // Update the order line to reverse the GRN
         $porderline = DataObjectFactory::Factory('POrderLine');
         $porderline->load($poreceivedline->orderline_id);
         if (!is_null($porderline->stitem_id)) {
             $stitem = DataObjectFactory::Factory('STItem');
             $stitem->load($porderline->stitem_id);
             $qty_decimals = $stitem->qty_decimals;
         } else {
             $qty_decimals = maxdp($porderline->os_qty, $porderline->del_qty, $poreceivedline->received);
         }
         // get the received quantity for this orderline, excluding lines in the GRN
         $received_qty = $poreceivedline->getReceivedQty($porderline->id, $poreceivedline->gr_number);
         $porderline->os_qty = BCSUB($porderline->revised_qty, $received_qty, $qty_decimals);
         $porderline->del_qty = $received_qty;
         if ($porderline->del_qty > 0) {
             $porderline->status = $porderline->partReceivedStatus();
         } else {
             $porderline->status = $porderline->awaitingDeliveryStatus();
         }
         // Load the PO Header and save the orderline
         // to ensure header status is updated if required
         $porder = DataObjectFactory::Factory('POrder');
         $porder->load($porderline->order_id);
         if (!$porderline->save($porder)) {
             $errors[] = 'Error updating PO line ' . $db->ErrorMsg();
         }
         // Finally update the po received line
         $poreceivedline->status = $poreceivedline->cancelStatus();
         if (!$poreceivedline->save()) {
             $errors[] = 'Error updating GRN ' . $db->ErrorMsg();
         }
     }
     if (count($errors) === 0) {
         // Now get the transactions for the GRN
         // Note each GRN line has two transactions linked by transfer_id
         $sttransaction = DataObjectFactory::Factory('STTransaction');
         $sttransaction->identifierField = 'transfer_id';
         $cc = new ConstraintChain();
         $cc->add(new Constraint('process_name', '=', 'GR'));
         $cc->add(new Constraint('process_id', '=', $this->_data['gr_number']));
         $sttransactions = $sttransaction->getAll($cc);
         // Need to link new transfer ids to existing transactions transfer_ids
         $transfer_ids = array();
         $transferrule = DataObjectFactory::Factory('WHTransferrule');
         foreach ($sttransactions as $sttransaction_id => $value) {
             if (!isset($transfer_ids[$value])) {
                 $transfer_ids[$value] = $transferrule->getTransferId()->transfer_id;
             }
         }
         // Reverse each of the transaction pairs associated with the GRN lines
         foreach ($sttransactions as $sttransaction_id => $value) {
             $sttransaction = DataObjectFactory::Factory('STTransaction');
             $sttransaction->load($sttransaction_id);
             // create new transaction by setting new id value
             $test = $sttransaction->autoHandle($sttransaction->idField);
             if ($test !== false) {
                 $sttransaction->{$sttransaction->idField} = $test;
             } else {
                 $errors[] = 'Error getting identifier for new item';
             }
             // Reverse the quantity and save
             $sttransaction->transfer_id = $transfer_ids[$sttransaction->transfer_id];
             $sttransaction->created = $sttransaction->autoHandle('created');
             $sttransaction->createdby = EGS_USERNAME;
             $sttransaction->qty = $sttransaction->qty * -1;
             $sttransaction->save($errors);
         }
     }
     // Check for errors
     if (count($errors) > 0) {
         $flash->addErrors($errors);
         $flash->addError('Error cancelling GRN ' . $this->_data['gr_number']);
         $db->FailTrans();
     } else {
         $flash->addMessage('GRN ' . $this->_data['gr_number'] . ' Cancelled');
     }
     $db->CompleteTrans();
     sendTo($this->name, 'index', $this->_modules);
 }
 public static function getItemDetail($stitem)
 {
     // Get the current stock level for the item
     $in_stock = $stitem->currentBalance();
     $balance = $in_stock;
     $orders = array();
     // Get any Purchase Orders for the item, by date
     $porders = $stitem->getPOrderLines();
     foreach ($porders as $porder) {
         if (isset($orders[$porder->due_delivery_date . 'PO' . $porder->order_id])) {
             $orders[$porder->due_delivery_date . 'PO' . $porder->order_id]['on_order'] += round($stitem->convertToUoM($porder->stuom_id, $stitem->uom_id, $porder->os_qty), $stitem->qty_decimals);
         } else {
             $orders[$porder->due_delivery_date . 'PO' . $porder->order_id] = array('due_date' => un_fix_date($porder->due_delivery_date), 'order_type' => 'PO', 'reference' => $porder->order_number, 'reference_id' => $porder->order_id, 'stitem_id' => $porder->stitem_id, 'stitem' => $porder->stitem, 'uom_name' => $porder->uom_name, 'decimals' => $stitem->qty_decimals, 'on_order' => round($stitem->convertToUoM($porder->stuom_id, $stitem->uom_id, $porder->os_qty), $stitem->qty_decimals), 'required' => 0);
         }
     }
     // Get any Works Orders to make this item, by date
     $worders = $stitem->getWorkOrders();
     foreach ($worders as $worder) {
         $orders[$worder->required_by . 'WO' . $worder->id] = array('due_date' => un_fix_date($worder->required_by), 'order_type' => 'WO', 'reference' => $worder->wo_number, 'reference_id' => $worder->id, 'stitem_id' => $worder->stitem_id, 'stitem' => $worder->stitem, 'uom_name' => $stitem->uom_name, 'decimals' => $stitem->qty_decimals, 'on_order' => round($worder->outstandingQty(), $stitem->qty_decimals), 'required' => 0);
     }
     // Get any Sales Orders by date for the item
     $sorders = $stitem->getSOrderLines();
     foreach ($sorders as $sorder) {
         if (isset($orders[$sorder->due_despatch_date . 'SO' . $sorder->order_id])) {
             $orders[$sorder->due_despatch_date . 'SO' . $sorder->order_id]['required'] += round($stitem->convertToUoM($sorder->stuom_id, $stitem->uom_id, $sorder->os_qty), $stitem->qty_decimals);
         } else {
             $orders[$sorder->due_despatch_date . 'SO' . $sorder->order_id] = array('due_date' => un_fix_date($sorder->due_despatch_date), 'order_type' => 'SO', 'reference' => $sorder->order_number, 'reference_id' => $sorder->order_id, 'stitem_id' => $sorder->stitem_id, 'stitem' => $sorder->stitem, 'uom_name' => $sorder->uom_name, 'decimals' => $stitem->qty_decimals, 'on_order' => 0, 'required' => round($stitem->convertToUoM($sorder->stuom_id, $stitem->uom_id, $sorder->os_qty), $stitem->qty_decimals));
         }
     }
     // Get any Works Orders that use this item, by date
     $wostructures = $stitem->getWOStructures();
     foreach ($wostructures as $wostructure) {
         $orders[$wostructure->required_by . 'WO' . $wostructure->work_order_id] = array('due_date' => un_fix_date($wostructure->required_by), 'order_type' => 'WO', 'reference' => $wostructure->wo_number, 'reference_id' => $wostructure->work_order_id, 'stitem_id' => $wostructure->stitem_id, 'stitem' => $wostructure->ststructure, 'stitem_code' => $wostructure->stitem_code, 'uom_name' => $wostructure->uom, 'decimals' => $stitem->qty_decimals, 'on_order' => 0, 'required' => round($stitem->convertToUoM($wostructure->uom_id, $stitem->uom_id, $wostructure->outstandingQty()), $stitem->qty_decimals));
     }
     // Get any Purchase Orders for items that use this item, by date
     $porders = $stitem->getPOStructures();
     foreach ($porders as $porder) {
         $structure = DataObjectFactory::Factory('STitem');
         $structure->load($porder->ststructure_id);
         $mfstructure = DataObjectFactory::Factory('MFStructure');
         $mfstructure->loadBy(array('stitem_id', 'ststructure_id'), array($porder->stitem_id, $porder->ststructure_id));
         $required = $porder->required * 100 / (100 - $mfstructure->waste_pc);
         if (isset($orders[$porder->due_delivery_date . 'PO' . $porder->order_id])) {
             $orders[$porder->due_delivery_date . 'PO' . $porder->order_id]['required'] += round($structure->convertToUoM($porder->uom_id, $structure->uom_id, $required), $stitem->qty_decimals);
         } else {
             $orders[$porder->due_delivery_date . 'PO' . $porder->id] = array('due_date' => un_fix_date($porder->due_delivery_date), 'order_type' => 'PO', 'reference' => $porder->order_number, 'reference_id' => $porder->order_id, 'stitem_id' => $porder->stitem_id, 'stitem' => $porder->stitem, 'stitem_code' => $porder->item_code, 'uom_name' => $porder->uom_name, 'decimals' => $stitem->qty_decimals, 'on_order' => 0, 'required' => round($structure->convertToUoM($porder->uom_id, $structure->uom_id, $required), $stitem->qty_decimals));
         }
     }
     ksort($orders);
     // Now build the manufacturing plan for the order dates
     foreach ($orders as $key => $row) {
         $required = BCADD(sprintf('%0' . $row['decimals'] . 'f', $row['required']), 0, $row['decimals']);
         $on_order = BCADD(sprintf('%0' . $row['decimals'] . 'f', $row['on_order']), 0, $row['decimals']);
         $orders[$key]['required'] = $required;
         $orders[$key]['on_order'] = $on_order;
         $balance = BCSUB(BCADD($balance, $on_order, $row['decimals']), $required, $row['decimals']);
         $orders[$key]['in_stock'] = $balance;
         if ($orders[$key]['in_stock'] < 0) {
             $orders[$key]['in_stock'] = sprintf('%0.' . $row['decimals'] . 'f', 0);
         }
         $orders[$key]['shortfall'] = sprintf('%0.' . $row['decimals'] . 'f', 0);
         if ($balance < 0) {
             $orders[$key]['shortfall'] = bcsub(0, $balance, $row['decimals']);
         }
     }
     return $orders;
 }