diff --git a/htdocs/core/modules/modStock.class.php b/htdocs/core/modules/modStock.class.php
index 3d46d606265..c9379fb25d6 100644
--- a/htdocs/core/modules/modStock.class.php
+++ b/htdocs/core/modules/modStock.class.php
@@ -172,6 +172,12 @@ class modStock extends DolibarrModules
$this->rights[9][3] = 0; // Permission by default for new user (0/1)
$this->rights[9][4] = 'inventory_advance'; // In php code, permission will be checked by test if ($user->rights->permkey->level1->level2)
$this->rights[9][5] = 'changePMP'; // In php code, permission will be checked by test if ($user->rights->permkey->level1->level2)
+
+ $this->rights[10][0] = 1016;
+ $this->rights[10][1] = 'inventoryDeletePermission'; // Permission label
+ $this->rights[10][3] = 0; // Permission by default for new user (0/1)
+ $this->rights[10][4] = 'inventory_advance'; // In php code, permission will be checked by test if ($user->rights->permkey->level1->level2)
+ $this->rights[10][5] = 'delete'; // In php code, permission will be checked by test if ($user->rights->permkey->level1->level2)
}
// Main menu entries
diff --git a/htdocs/langs/en_US/stocks.lang b/htdocs/langs/en_US/stocks.lang
index 0e9bd11ec4d..f9c76a73019 100644
--- a/htdocs/langs/en_US/stocks.lang
+++ b/htdocs/langs/en_US/stocks.lang
@@ -185,6 +185,7 @@ inventoryCreatePermission=Create new inventory
inventoryReadPermission=View inventories
inventoryWritePermission=Update inventories
inventoryValidatePermission=Validate inventory
+inventoryDeletePermission=Delete inventory
inventoryTitle=Inventory
inventoryListTitle=Inventories
inventoryListEmpty=No inventory in progress
@@ -243,4 +244,7 @@ InventoryRealQtyHelp=Set value to 0 to reset qty Keep field empty, or remove
UpdateByScaning=Update by scaning
UpdateByScaningProductBarcode=Update by scan (product barcode)
UpdateByScaningLot=Update by scan (lot|serial barcode)
-DisableStockChangeOfSubProduct=Deactivate the stock change for all the subproducts of this Kit during this movement.
\ No newline at end of file
+DisableStockChangeOfSubProduct=Deactivate the stock change for all the subproducts of this Kit during this movement.
+LabelOfInventoryMovemement=Inventory %s
+ReOpen=Reopen
+ConfirmFinish=Confirm closing
\ No newline at end of file
diff --git a/htdocs/product/inventory/card.php b/htdocs/product/inventory/card.php
index 92ad4c2176b..dda16746398 100644
--- a/htdocs/product/inventory/card.php
+++ b/htdocs/product/inventory/card.php
@@ -407,6 +407,12 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
print ''.$langs->trans("SetToDraft").' ';
}
}
+ // Back to validate
+ if ($object->status == $object::STATUS_RECORDED) {
+ if ($permissiontoadd) {
+ print ''.$langs->trans("ReOpen").' ';
+ }
+ }
// Modify
if ($object->status == $object::STATUS_DRAFT) {
diff --git a/htdocs/product/inventory/class/inventory.class.php b/htdocs/product/inventory/class/inventory.class.php
index ce585c51a10..e872d7e353a 100644
--- a/htdocs/product/inventory/class/inventory.class.php
+++ b/htdocs/product/inventory/class/inventory.class.php
@@ -52,7 +52,7 @@ class Inventory extends CommonObject
/**
* @var int Does object support extrafields ? 0=No, 1=Yes
*/
- public $isextrafieldmanaged = 1;
+ public $isextrafieldmanaged = 0;
/**
* @var string String with name of icon for inventory
@@ -254,6 +254,7 @@ class Inventory extends CommonObject
*/
public function validate(User $user, $notrigger = false)
{
+ global $conf;
$this->db->begin();
$result = 0;
@@ -328,6 +329,7 @@ class Inventory extends CommonObject
} else {
$this->db->rollback();
}
+ return $result;
}
/**
@@ -357,6 +359,51 @@ class Inventory extends CommonObject
} else {
$this->db->rollback();
}
+ return $result;
+ }
+
+ /**
+ * Set to Recorded
+ *
+ * @param User $user User that creates
+ * @param bool $notrigger false=launch triggers after, true=disable triggers
+ * @return int <0 if KO, Id of created object if OK
+ */
+ public function setRecorded(User $user, $notrigger = false)
+ {
+ $this->db->begin();
+
+ $result = $this->setStatut($this::STATUS_RECORDED, null, '', 'INVENTORY_RECORDED');
+
+ if ($result > 0) {
+ $this->db->commit();
+ } else {
+ $this->db->rollback();
+ return -1;
+ }
+ return $result;
+ }
+
+ /**
+ * Set to Canceled
+ *
+ * @param User $user User that creates
+ * @param bool $notrigger false=launch triggers after, true=disable triggers
+ * @return int <0 if KO, Id of created object if OK
+ */
+ public function setCanceled(User $user, $notrigger = false)
+ {
+ $this->db->begin();
+
+ $result = $this->setStatut($this::STATUS_CANCELED, null, '', 'INVENTORY_CANCELED');
+
+ if ($result > 0) {
+ $this->db->commit();
+ } else {
+ $this->db->rollback();
+ return -1;
+ }
+ return $result;
}
/**
@@ -566,9 +613,11 @@ class Inventory extends CommonObject
$labelStatus[self::STATUS_DRAFT] = $langs->trans('Draft');
$labelStatus[self::STATUS_VALIDATED] = $langs->trans('Validated').' ('.$langs->trans('Started').')';
$labelStatus[self::STATUS_CANCELED] = $langs->trans('Canceled');
+ $labelStatus[self::STATUS_RECORDED] = $langs->trans('Closed');
$labelStatusShort[self::STATUS_DRAFT] = $langs->trans('Draft');
$labelStatusShort[self::STATUS_VALIDATED] = $langs->trans('Started');
$labelStatusShort[self::STATUS_CANCELED] = $langs->trans('Canceled');
+ $labelStatusShort[self::STATUS_RECORDED] = $langs->trans('Closed');
return dolGetStatus($labelStatus[$status], $labelStatusShort[$status], '', 'status'.$status, $mode);
}
@@ -628,6 +677,7 @@ class Inventory extends CommonObject
public function initAsSpecimen()
{
$this->initAsSpecimenCommon();
+ $this->title = '';
}
}
diff --git a/htdocs/product/inventory/inventory.php b/htdocs/product/inventory/inventory.php
index 85be4ec7325..30f3a5347dd 100644
--- a/htdocs/product/inventory/inventory.php
+++ b/htdocs/product/inventory/inventory.php
@@ -27,6 +27,7 @@ include_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
include_once DOL_DOCUMENT_ROOT.'/product/inventory/class/inventory.class.php';
include_once DOL_DOCUMENT_ROOT.'/product/inventory/lib/inventory.lib.php';
+include_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
// Load translation files required by the page
$langs->loadLangs(array("stocks", "other", "productbatch"));
@@ -98,6 +99,71 @@ $now = dol_now();
* Actions
*/
+if ($action == 'cancel_record' && $permissiontoadd) {
+ $object->setCanceled($user);
+}
+
+if ($action == 'update' && $user->rights->stock->mouvement->creer) {
+ $stockmovment = new MouvementStock($db);
+ $stockmovment->origin = $object;
+
+ $sql = 'SELECT id.rowid, id.datec as date_creation, id.tms as date_modification, id.fk_inventory, id.fk_warehouse,';
+ $sql .= ' id.fk_product, id.batch, id.qty_stock, id.qty_view, id.qty_regulated';
+ $sql .= ' FROM '.MAIN_DB_PREFIX.'inventorydet as id';
+ $sql .= ' WHERE id.fk_inventory = '.$object->id;
+ $resql = $db->query($sql);
+ if ($resql) {
+ $num = $db->num_rows($resql);
+ $i = 0;
+ $totalarray = array();
+ while ($i < $num) {
+ $line = $db->fetch_object($resql);
+ $qty_view = $line->qty_view;
+ $qty_stock = $line->qty_stock;
+ $stock_movement_qty = $qty_view - $qty_stock;
+ if ($stock_movement_qty != 0) {
+ if ($stock_movement_qty < 0) {
+ $movement_type = 1;
+ } else {
+ $movement_type = 0;
+ }
+ $idstockmove = $stockmovment->_create($user, $line->fk_product, $line->fk_warehouse, $stock_movement_qty, $movement_type, 0, $langs->trans('LabelOfInventoryMovemement', $object->id), 'INV'.$object->id);
+ if ($idstockmove < 0) {
+ $error++;
+ setEventMessages($stockmovment->error, $stockmovment->errors, 'errors');
+ }
+ }
+ $i++;
+ }
+ if (!$error) {
+ $object->setRecorded($user);
+ }
+ }
+}
+
+if ($action =='updateinventorylines' && $permissiontoadd) {
+ $sql = 'SELECT id.rowid, id.datec as date_creation, id.tms as date_modification, id.fk_inventory, id.fk_warehouse,';
+ $sql .= ' id.fk_product, id.batch, id.qty_stock, id.qty_view, id.qty_regulated';
+ $sql .= ' FROM '.MAIN_DB_PREFIX.'inventorydet as id';
+ $sql .= ' WHERE id.fk_inventory = '.$object->id;
+
+ $resql = $db->query($sql);
+ if ($resql) {
+ $num = $db->num_rows($resql);
+ $i = 0;
+ $totalarray = array();
+ while ($i < $num) {
+ $line = $db->fetch_object($resql);
+ $lineid = $line->rowid;
+ $inventoryline = new InventoryLine($db);
+ $inventoryline->fetch($lineid);
+ $inventoryline->qty_view = GETPOST("id_".$inventoryline->id);
+ $inventoryline->update($user);
+ $i++;
+ }
+ }
+}
+
$parameters = array();
$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
if ($reshook < 0) {
@@ -223,6 +289,18 @@ if ($object->id > 0) {
$formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('ToClone'), $langs->trans('ConfirmCloneMyObject', $object->ref), 'confirm_clone', $formquestion, 'yes', 1);
}
+ // Confirmation to close
+ if ($action == 'record') {
+ $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('Close'), $langs->trans('ConfirmFinish'), 'update', '', 0, 1);
+ $action = 'view';
+ }
+
+ // Confirmation to close
+ if ($action == 'confirm_cancel') {
+ $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('Cancel'), $langs->trans('ConfirmCancel'), 'cancel_record', '', 0, 1);
+ $action = 'view';
+ }
+
// Call Hook formConfirm
$parameters = array('formConfirm' => $formconfirm, 'lineid' => $lineid);
$reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
@@ -307,24 +385,7 @@ if ($object->id > 0) {
// Buttons for actions
- if ($action == 'record') {
- print '
';
- } else {
+ if ($action != 'record') {
print ''."\n";
$parameters = array();
$reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
@@ -360,7 +421,8 @@ if ($object->id > 0) {
if ($object->status == Inventory::STATUS_VALIDATED) {
if ($permissiontoadd) {
- print '
id.'&action=record">'.$langs->trans("Finish").' '."\n";
+ print '
id.'&action=confirm_cancel">'.$langs->trans("Cancel").' '."\n";
+ print '
id.'&action=record">'.$langs->trans("Close").' '."\n";
} else {
print '
'.$langs->trans('Finish').' '."\n";
}
@@ -419,10 +481,12 @@ if ($object->id > 0) {
print '
';
print $form->textwithpicto($langs->trans("RealQty"), $langs->trans("InventoryRealQtyHelp"));
print ' ';
- // Actions
- print '
';
- print ' ';
- print '';
+ if ($object->status == $object::STATUS_VALIDATED) {
+ // Actions
+ print '
';
+ print ' ';
+ print '';
+ }
// Line to add a new line in inventory
if ($object->status == $object::STATUS_VALIDATED) {
@@ -505,15 +569,20 @@ if ($object->id > 0) {
}
print '
';
- print 'TODO';
+ print $obj->qty_stock;
print ' ';
print '
';
- print ' rowid).'">';
- print ' ';
- print '
';
- print ''.img_delete().' ';
- print ' ';
-
+ if ($object->status == $object::STATUS_VALIDATED) {
+ $qty_view = GETPOST("id_".$obj->rowid) ? GETPOST("id_".$obj->rowid) : $obj->qty_view;
+ print '
';
+ print '';
+ print '
';
+ print ''.img_delete().' ';
+ print ' ';
+ } else {
+ print $obj->qty_view;
+ print '';
+ }
print '';
$i++;
diff --git a/test/phpunit/InventoryTest.php b/test/phpunit/InventoryTest.php
new file mode 100644
index 00000000000..1d125a0311c
--- /dev/null
+++ b/test/phpunit/InventoryTest.php
@@ -0,0 +1,382 @@
+
+ * Copyright (C) 2018 Frédéric France
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ * or see https://www.gnu.org/
+ */
+
+/**
+ * \file test/phpunit/InventoryTest.php
+ * \ingroup test
+ * \brief PHPUnit test
+ * \remarks To run this script as CLI: phpunit filename.php
+ */
+
+global $conf,$user,$langs,$db;
+//define('TEST_DB_FORCE_TYPE','mysql'); // This is to force using mysql driver
+//require_once 'PHPUnit/Autoload.php';
+require_once dirname(__FILE__).'/../../htdocs/master.inc.php';
+require_once dirname(__FILE__).'/../../htdocs/product/inventory/class/inventory.class.php';
+
+if (empty($user->id)) {
+ print "Load permissions for admin user nb 1\n";
+ $user->fetch(1);
+ $user->getrights();
+}
+$conf->global->MAIN_DISABLE_ALL_MAILS=1;
+
+
+/**
+ * Class for PHPUnit tests
+ *
+ * @backupGlobals disabled
+ * @backupStaticAttributes enabled
+ * @remarks backupGlobals must be disabled to have db,conf,user and lang not erased.
+ */
+class InventoryTest extends PHPUnit\Framework\TestCase
+{
+ protected $savconf;
+ protected $savuser;
+ protected $savlangs;
+ protected $savdb;
+
+ /**
+ * Constructor
+ * We save global variables into local variables
+ *
+ * @return InventoryTest
+ */
+ public function __construct()
+ {
+ parent::__construct();
+
+ //$this->sharedFixture
+ global $conf,$user,$langs,$db;
+ $this->savconf=$conf;
+ $this->savuser=$user;
+ $this->savlangs=$langs;
+ $this->savdb=$db;
+
+ print __METHOD__." db->type=".$db->type." user->id=".$user->id;
+ //print " - db ".$db->db;
+ print "\n";
+ }
+
+ /**
+ * setUpBeforeClass
+ *
+ * @return void
+ */
+ public static function setUpBeforeClass():void
+ {
+ global $conf,$user,$langs,$db;
+
+ $db->begin(); // This is to have all actions inside a transaction even if test launched without suite.
+
+ print __METHOD__."\n";
+ }
+
+ /**
+ * tearDownAfterClass
+ *
+ * @return void
+ */
+ public static function tearDownAfterClass():void
+ {
+ global $conf,$user,$langs,$db;
+ $db->rollback();
+
+ print __METHOD__."\n";
+ }
+
+ /**
+ * Init phpunit tests
+ *
+ * @return void
+ */
+ protected function setUp():void
+ {
+ global $conf,$user,$langs,$db;
+ $conf=$this->savconf;
+ $user=$this->savuser;
+ $langs=$this->savlangs;
+ $db=$this->savdb;
+
+ print __METHOD__."\n";
+ }
+
+ /**
+ * End phpunit tests
+ *
+ * @return void
+ */
+ protected function tearDown():void
+ {
+ print __METHOD__."\n";
+ }
+
+ /**
+ * testInventoryCreate
+ *
+ * @return int
+ */
+ public function testInventoryCreate()
+ {
+ global $conf,$user,$langs,$db;
+ $conf=$this->savconf;
+ $user=$this->savuser;
+ $langs=$this->savlangs;
+ $db=$this->savdb;
+
+ $localobject=new Inventory($db);
+ $localobject->initAsSpecimen();
+ $result=$localobject->create($user);
+ $this->assertLessThan($result, 0);
+ print __METHOD__." result=".$result."\n";
+ return $result;
+ }
+
+ /**
+ * testInventoryFetch
+ *
+ * @param int $id Id invoice
+ * @return int
+ *
+ * @depends testInventoryCreate
+ * The depends says test is run only if previous is ok
+ */
+ public function testInventoryFetch($id)
+ {
+ global $conf,$user,$langs,$db;
+ $conf=$this->savconf;
+ $user=$this->savuser;
+ $langs=$this->savlangs;
+ $db=$this->savdb;
+
+ $localobject=new Inventory($this->savdb);
+ $result=$localobject->fetch($id);
+
+ $this->assertLessThan($result, 0);
+ print __METHOD__." id=".$id." result=".$result."\n";
+ return $localobject;
+ }
+
+ /**
+ * testInventoryUpdate
+ *
+ * @param Inventory $localobject Invoice
+ * @return int
+ *
+ * @depends testInventoryFetch
+ * The depends says test is run only if previous is ok
+ */
+ public function testInventoryUpdate($localobject)
+ {
+ global $conf,$user,$langs,$db;
+ $conf=$this->savconf;
+ $user=$this->savuser;
+ $langs=$this->savlangs;
+ $db=$this->savdb;
+
+ $localobject->status = 9;
+ $localobject->title = 'test';
+ $result=$localobject->update($user, $user);
+ print __METHOD__." id=".$localobject->id." result=".$result."\n";
+ $this->assertLessThan($result, 0);
+ return $localobject;
+ }
+
+
+ /**
+ * testInventoryValidate
+ *
+ * @param Inventory $localobject Invoice
+ * @return void
+ *
+ * @depends testInventoryUpdate
+ * The depends says test is run only if previous is ok
+ */
+ public function testInventoryValidate($localobject)
+ {
+ global $conf,$user,$langs,$db;
+ $conf=$this->savconf;
+ $user=$this->savuser;
+ $langs=$this->savlangs;
+ $db=$this->savdb;
+
+ $result=$localobject->validate($user);
+ print __METHOD__." id=".$localobject->id." result=".$result."\n";
+
+ $this->assertLessThan($result, 0);
+ $this->assertEquals($localobject->status, '1');
+ return $localobject;
+ }
+
+ /**
+ * testInventorySetDraft
+ *
+ * @param Inventory $localobject Invoice
+ * @return void
+ *
+ * @depends testInventoryValidate
+ * The depends says test is run only if previous is ok
+ */
+ public function testInventorySetDraft($localobject)
+ {
+ global $conf,$user,$langs,$db;
+ $conf=$this->savconf;
+ $user=$this->savuser;
+ $langs=$this->savlangs;
+ $db=$this->savdb;
+
+ $result=$localobject->setDraft($user);
+ print __METHOD__." id=".$localobject->id." result=".$result."\n";
+
+ $this->assertLessThan($result, 0);
+ $this->assertEquals($localobject->status, '0');
+ return $localobject;
+ }
+
+ /**
+ * testInventorySetRecorded
+ *
+ * @param Inventory $localobject Invoice
+ * @return void
+ *
+ * @depends testInventorySetDraft
+ * The depends says test is run only if previous is ok
+ */
+ public function testInventorySetRecorded($localobject)
+ {
+ global $conf,$user,$langs,$db;
+ $conf=$this->savconf;
+ $user=$this->savuser;
+ $langs=$this->savlangs;
+ $db=$this->savdb;
+
+ $result=$localobject->setRecorded($user);
+ print __METHOD__." id=".$localobject->id." result=".$result."\n";
+
+ $this->assertLessThan($result, 0);
+ $this->assertEquals($localobject->status, '2');
+ return $localobject;
+ }
+
+ /**
+ * testInventorySetCanceled
+ *
+ * @param Inventory $localobject Invoice
+ * @return void
+ *
+ * @depends testInventorySetRecorded
+ * The depends says test is run only if previous is ok
+ */
+ public function testInventorySetCanceled($localobject)
+ {
+ global $conf,$user,$langs,$db;
+ $conf=$this->savconf;
+ $user=$this->savuser;
+ $langs=$this->savlangs;
+ $db=$this->savdb;
+
+ $result=$localobject->setCanceled($user);
+ print __METHOD__." id=".$localobject->id." result=".$result."\n";
+
+ $this->assertLessThan($result, 0);
+ $this->assertEquals($localobject->status, '9');
+ return $localobject;
+ }
+
+ /**
+ * testInventoryOther
+ *
+ * @param Inventory $localobject Invoice
+ * @return int
+ * @depends testInventorySetRecorded
+ * The depends says test is run only if previous is ok
+ */
+ public function testInventoryOther($localobject)
+ {
+ global $conf,$user,$langs,$db;
+ $conf=$this->savconf;
+ $user=$this->savuser;
+ $langs=$this->savlangs;
+ $db=$this->savdb;
+
+ $localobject->info($localobject->id);
+ print __METHOD__." localobject->date_creation=".$localobject->date_creation."\n";
+ $this->assertNotEquals($localobject->date_creation, '');
+ return $localobject->id;
+ }
+
+ /**
+ * testInventoryDelete
+ *
+ * @param int $id Id of invoice
+ * @return int
+ * @depends testInventoryOther
+ * The depends says test is run only if previous is ok
+ */
+ public function testInventoryDelete($id)
+ {
+ global $conf,$user,$langs,$db;
+ $conf=$this->savconf;
+ $user=$this->savuser;
+ $langs=$this->savlangs;
+ $db=$this->savdb;
+
+ $localobject=new Inventory($this->savdb);
+ $result=$localobject->fetch($id);
+ $result=$localobject->delete($user);
+ print __METHOD__." id=".$id." result=".$result."\n";
+ $this->assertLessThan($result, 0);
+
+ return $result;
+ }
+
+ /**
+ * Compare all public properties values of 2 objects
+ *
+ * @param Object $oA Object operand 1
+ * @param Object $oB Object operand 2
+ * @param boolean $ignoretype False will not report diff if type of value differs
+ * @param array $fieldstoignorearray Array of fields to ignore in diff
+ * @return array Array with differences
+ */
+ public function objCompare($oA, $oB, $ignoretype = true, $fieldstoignorearray = array('id'))
+ {
+ $retAr=array();
+
+ if (get_class($oA) !== get_class($oB)) {
+ $retAr[]="Supplied objects are not of same class.";
+ } else {
+ $oVarsA=get_object_vars($oA);
+ $oVarsB=get_object_vars($oB);
+ $aKeys=array_keys($oVarsA);
+ foreach ($aKeys as $sKey) {
+ if (in_array($sKey, $fieldstoignorearray)) {
+ continue;
+ }
+ if (! $ignoretype && ($oVarsA[$sKey] !== $oVarsB[$sKey])) {
+ $retAr[]=$sKey.' : '.(is_object($oVarsA[$sKey])?get_class($oVarsA[$sKey]):$oVarsA[$sKey]).' <> '.(is_object($oVarsB[$sKey])?get_class($oVarsB[$sKey]):$oVarsB[$sKey]);
+ }
+ if ($ignoretype && ($oVarsA[$sKey] != $oVarsB[$sKey])) {
+ $retAr[]=$sKey.' : '.(is_object($oVarsA[$sKey])?get_class($oVarsA[$sKey]):$oVarsA[$sKey]).' <> '.(is_object($oVarsB[$sKey])?get_class($oVarsB[$sKey]):$oVarsB[$sKey]);
+ }
+ }
+ }
+ return $retAr;
+ }
+}