Questions around the TYPO3 integration and plugins as well as Flow and NeosCMS
Forum rules: Always add your TYPO3/Flow, Aimeos and PHP version as well as your environment (Linux/Mac/Win)
#5885 by tenkraD
24 Mar 2018, 14:20
Working Solution on the End Goto https://aimeos.org/help/post6012.html#p6012
---------------------------------------------------------------------

Hello Guys,

i would like to extend the stock of products with a optional field order time.
the order time is a string, and describes how long it takes for us to reorder if the product is selled out - 1 weeks/ 1 month / a few days...

-------------------------------------------------------------------------------------------------------------
1. My Problem is i cant save the values in db and dont know what im doing wrong. Also i dont get any error response, im a bit lost.
It would be great if you can help me.

I created an extension named "mhaimeos" with our extensionbuilder.
Then i extended the stock shema, this is working good.


Path: /typo3conf/ext/mhaimeos/Resources/Private/Extensions/mhaimeos/lib/custom/config/stock.php
Code: Select all<?php

/**
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
 *
 * Extend Database Table mshop_stock in /typo3conf/ext/aimeos/Resources/Libraries/aimeos/aimeos-core/lib/mshoplib/setup/default/schema/stock.php
 *
 * https://aimeos.org/docs/Developers/Library/Setup_tasks/Schema_update#Modify_existing_tables
 */


return array(
   'table' => array(
         
      'mshop_stock' => function ( \Doctrine\DBAL\Schema\Schema $schema ) {

         $table = $schema->getTable( 'mshop_stock' );
         
         $table->addColumn( 'ordertime', 'string', array( 'length' => 32 ) );
         
         $table->addIndex( array( 'siteid', 'ordertime' ), 'idx_mssto_sid_ordertime' );
         
         return $schema;
      },
   ),
);


After that i created the Item
Path: /typo3conf/ext/mhaimeos/Resources/Private/Extensions/mhaimeos/lib/custom/src/MShop/Stock/Item/Mystandard.php
Code: Select all<?php

/**
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
 * @package MShop
 * @subpackage Stock
 */

namespace Aimeos\MShop\Stock\Item;
//namespace Mhaimeos\MShop\Stock\Item;
 
class Mystandard extends Standard
{
    private $values;
 
    public function __construct( array $values )
    {
        parent::__construct( 'stock.', $values );
        $this->values = $values;
    }
 
    /**
     * Returns ordertime for the product stock
     *
     * @return string|null stock ordertime of the product
     */
    public function getOrderTime()
    {
        if( isset( $this->values['stock.ordertime'] ) ) {
            return (string) $this->values['stock.ordertime'];
        }
        return '';
    }
 
    public function setOrderTime( $ordertime )
    {
        if( (string) $ordertime !== $this->getOrderTime() )
        {
            $this->values['stock.ordertime'] = (string) $ordertime;
            $this->setModified();
        }
        return $this;
    }
 
    public function fromArray( array $list )
    {
        $unknown = [];
        $list = parent::fromArray( $list );
 
        foreach( $list as $key => $value )
        {
            switch( $key )
            {
                case 'stock.ordertime': $this->setOrderTime( $value ); break;
                default: $unknown[$key] = $value;
            }
        }
 
        return $unknown;
    }
 
    public function toArray( $private = false )
    {
        $list = parent::toArray( $private );
 
        if( $private === true ) {
            $list['stock.ordertime'] = $this->getOrderTime();
        }
 
        return $list;
    }
}


I created the manager
Path: /typo3conf/ext/mhaimeos/Resources/Private/Extensions/mhaimeos/lib/custom/src/MShop/Stock/Manager/Mystandard.php
Code: Select all<?php

/**
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
 * @package MShop
 * @subpackage Stock
 */

namespace Aimeos\MShop\Stock\Manager;
//namespace Mhaimeos\MShop\Stock\Manager;
 
class Mystandard extends Standard
{
    private $searchConfig = array(
        'stock.ordertime'=> array(
            'code'=>'stock.ordertime',
            'internalcode'=>'msto."ordertime"',
            'label'=>'Product Ordertime',
            'type'=> 'string', // integer, float, etc.
            'internaltype'=> \Aimeos\MW\DB\Statement\Base::PARAM_STR, // _INT, _FLOAT, etc.
        ),
    );
 
    public function saveItem( \Aimeos\MShop\Common\Item\Iface $item, $fetch = true )
    {
        // a modified copy of the code from the parent class
        // extended by a bind() call and updated bind positions (first parameter)
       $iface = '\\Mhaimeos\\MShop\\Stock\\Item\\Myiface';
       if( !( $item instanceof $iface ) ) {
          throw new \Aimeos\MShop\Stock\Exception( sprintf( 'Object is not of required type "%1$s"', $iface ) );
       }
       
       if( !$item->isModified() ) {
          return $item;
       }
       
       $context = $this->getContext();
       
       $dbm = $context->getDatabaseManager();
       $dbname = $this->getResourceName();
       $conn = $dbm->acquire( $dbname );
       
       try
       {
          $id = $item->getId();
          $date = date( 'Y-m-d H:i:s' );
       
          if( $id === null )
          {
             /** mshop/stock/manager/standard/insert/mysql
              * Inserts a new product stock record into the database table
              *
              * @see mshop/stock/manager/standard/insert/ansi
              */
       
             /** mshop/stock/manager/standard/insert/ansi
              * Inserts a new product stock record into the database table
              *
              * Items with no ID yet (i.e. the ID is NULL) will be created in
              * the database and the newly created ID retrieved afterwards
              * using the "newid" SQL statement.
              *
              * The SQL statement must be a string suitable for being used as
              * prepared statement. It must include question marks for binding
              * the values from the product stock item to the statement before they are
              * sent to the database server. The number of question marks must
              * be the same as the number of columns listed in the INSERT
              * statement. The order of the columns must correspond to the
              * order in the saveItems() method, so the correct values are
              * bound to the columns.
              *
              * The SQL statement should conform to the ANSI standard to be
              * compatible with most relational database systems. This also
              * includes using double quotes for table and column names.
              *
              * @param string SQL statement for inserting records
              * @since 2017.01
              * @category Developer
              * @see mshop/stock/manager/standard/update/ansi
              * @see mshop/stock/manager/standard/newid/ansi
              * @see mshop/stock/manager/standard/delete/ansi
              * @see mshop/stock/manager/standard/search/ansi
              * @see mshop/stock/manager/standard/count/ansi
              * @see mshop/stock/manager/standard/stocklevel
              */
             $path = 'mshop/stock/manager/standard/insert';
//             $path = 'mshop/stock/manager/mystandard/insert';
          }
          else
          {
             /** mshop/stock/manager/standard/update/mysql
              * Updates an existing product stock record in the database
              *
              * @see mshop/stock/manager/standard/update/ansi
              */
       
             /** mshop/stock/manager/standard/update/ansi
              * Updates an existing product stock record in the database
              *
              * Items which already have an ID (i.e. the ID is not NULL) will
              * be updated in the database.
              *
              * The SQL statement must be a string suitable for being used as
              * prepared statement. It must include question marks for binding
              * the values from the product stock item to the statement before they are
              * sent to the database server. The order of the columns must
              * correspond to the order in the saveItems() method, so the
              * correct values are bound to the columns.
              *
              * The SQL statement should conform to the ANSI standard to be
              * compatible with most relational database systems. This also
              * includes using double quotes for table and column names.
              *
              * @param string SQL statement for updating records
              * @since 2017.01
              * @category Developer
              * @see mshop/stock/manager/standard/insert/ansi
              * @see mshop/stock/manager/standard/newid/ansi
              * @see mshop/stock/manager/standard/delete/ansi
              * @see mshop/stock/manager/standard/search/ansi
              * @see mshop/stock/manager/standard/count/ansi
              * @see mshop/stock/manager/standard/stocklevel
              */
             $path = 'mshop/stock/manager/standard/update';
//             $path = 'mshop/stock/manager/mystandard/update';
          }
       
          $stmt = $this->getCachedStatement( $conn, $path );
       
          
          $stmt->bind( 1, $item->getProductCode() );
          $stmt->bind( 2, $item->getTypeId(), \Aimeos\MW\DB\Statement\Base::PARAM_INT );
          $stmt->bind( 3, $item->getStocklevel(), \Aimeos\MW\DB\Statement\Base::PARAM_INT );
          $stmt->bind( 4, $item->getDateBack() );
 //  GET the Order Time
          $stmt->bind( 5, $item->getOrderTime() );
          
          $stmt->bind( 6, $date ); //mtime
          $stmt->bind( 7, $context->getEditor() );
          $stmt->bind( 8, $context->getLocale()->getSiteId(), \Aimeos\MW\DB\Statement\Base::PARAM_INT );
       
          if( $id !== null ) {
             $stmt->bind( 9, $id, \Aimeos\MW\DB\Statement\Base::PARAM_INT );
             $item->setId( $id ); // modified false
          } else {
             $stmt->bind( 9, $date ); //ctime
          }
          
          
          $stmt->execute()->finish();
       
          if( $id === null && $fetch === true )
          {
             /** mshop/stock/manager/standard/newid/mysql
              * Retrieves the ID generated by the database when inserting a new record
              *
              * @see mshop/stock/manager/standard/newid/ansi
              */
       
             /** mshop/stock/manager/standard/newid/ansi
              * Retrieves the ID generated by the database when inserting a new record
              *
              * As soon as a new record is inserted into the database table,
              * the database server generates a new and unique identifier for
              * that record. This ID can be used for retrieving, updating and
              * deleting that specific record from the table again.
              *
              * For MySQL:
              *  SELECT LAST_INSERT_ID()
              * For PostgreSQL:
              *  SELECT currval('seq_msto_id')
              * For SQL Server:
              *  SELECT SCOPE_IDENTITY()
              * For Oracle:
              *  SELECT "seq_msto_id".CURRVAL FROM DUAL
              *
              * There's no way to retrive the new ID by a SQL statements that
              * fits for most database servers as they implement their own
              * specific way.
              *
              * @param string SQL statement for retrieving the last inserted record ID
              * @since 2017.01
              * @category Developer
              * @see mshop/stock/manager/standard/insert/ansi
              * @see mshop/stock/manager/standard/update/ansi
              * @see mshop/stock/manager/standard/delete/ansi
              * @see mshop/stock/manager/standard/search/ansi
              * @see mshop/stock/manager/standard/count/ansi
              * @see mshop/stock/manager/standard/stocklevel
              */
             $path = 'mshop/stock/manager/standard/newid';
             $item->setId( $this->newId( $conn, $path ) );
          }
       
          $dbm->release( $conn, $dbname );
       }
       catch( \Exception $e )
       {
          $dbm->release( $conn, $dbname );
          throw $e;
       }
       
       return $item;
    }
 
    public function getSearchAttributes( $withsub = true )
    {
        $list = parent::getSearchAttributes( $withsub );
        foreach( $this->searchConfig as $key => $fields ) {
            $list[$key] = new \Aimeos\MW\Criteria\Attribute\Standard( $fields );
        }
        return $list;
    }
 
    protected function createItemBase( array $values = [] /* , ... */ )
    {
        return new \Mhaimeos\MShop\Stock\Item\Standard( $values /* , ... */ );
    }
}



I created the Config
Its a copy from the existing one. I just change the name, the insert and update.
Path: /typo3conf/ext/mhaimeos/Resources/Private/Extensions/mhaimeos/lib/custom/config/stock.php
Code: Select all<?php

/**
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
 * @copyright Aimeos (aimeos.org), 2015-2017
 */


return array(
   'manager' => array(
      'name' => 'Mystandard',
         
      'type' => array(
         'standard' => array(
            'delete' => array(
               'ansi' => '
                  DELETE FROM "mshop_stock_type"
                  WHERE :cond AND siteid = ?
               '
            ),
            'insert' => array(
               'ansi' => '
                  INSERT INTO "mshop_stock_type" (
                     "code", "domain", "label", "status",
                     "mtime", "editor", "siteid", "ctime"
                  ) VALUES (
                     ?, ?, ?, ?, ?, ?, ?, ?
                  )
               '
            ),
            'update' => array(
               'ansi' => '
                  UPDATE "mshop_stock_type"
                  SET "code" = ?, "domain" = ?, "label" = ?,
                     "status" = ?, "mtime" = ?, "editor" = ?
                  WHERE "siteid" = ? AND "id" = ?
               '
            ),
            'search' => array(
               'ansi' => '
                  SELECT mstoty."id" AS "stock.type.id", mstoty."siteid" AS "stock.type.siteid",
                     mstoty."code" AS "stock.type.code", mstoty."domain" AS "stock.type.domain",
                     mstoty."label" AS "stock.type.label", mstoty."status" AS "stock.type.status",
                     mstoty."mtime" AS "stock.type.mtime", mstoty."editor" AS "stock.type.editor",
                     mstoty."ctime" AS "stock.type.ctime"
                  FROM "mshop_stock_type" mstoty
                  :joins
                  WHERE :cond
                  GROUP BY mstoty."id", mstoty."siteid", mstoty."code", mstoty."domain",
                     mstoty."label", mstoty."status", mstoty."mtime", mstoty."editor",
                     mstoty."ctime" /*-columns*/ , :columns /*columns-*/
                  /*-orderby*/ ORDER BY :order /*orderby-*/
                  LIMIT :size OFFSET :start
               '
            ),
            'count' => array(
               'ansi' => '
                  SELECT COUNT(*) AS "count"
                  FROM (
                     SELECT DISTINCT mstoty."id"
                     FROM "mshop_stock_type" mstoty
                     :joins
                     WHERE :cond
                     LIMIT 10000 OFFSET 0
                  ) AS list
               '
            ),
            'newid' => array(
               'db2' => 'SELECT IDENTITY_VAL_LOCAL()',
               'mysql' => 'SELECT LAST_INSERT_ID()',
               'oracle' => 'SELECT mshop_stock_type_seq.CURRVAL FROM DUAL',
               'pgsql' => 'SELECT lastval()',
               'sqlite' => 'SELECT last_insert_rowid()',
               'sqlsrv' => 'SELECT SCOPE_IDENTITY()',
               'sqlanywhere' => 'SELECT @@IDENTITY',
            ),
         ),
      ),
      'standard' => array(
         'delete' => array(
            'ansi' => '
               DELETE FROM "mshop_stock"
               WHERE :cond AND siteid = ?
            '
         ),
         'insert' => array(
            'ansi' => '
               INSERT INTO "mshop_stock" (
                  "productcode", "typeid", "stocklevel", "backdate", "ordertime"
                  "mtime", "editor", "siteid", "ctime"
               ) VALUES (
                  ?, ?, ?, ?, ?, ?, ?, ?, ?
               )
            '
         ),
         'update' => array(
            'ansi' => '
               UPDATE "mshop_stock"
               SET "productcode" = ?, "typeid" = ?, "stocklevel" = ?,
                  "backdate" = ?, "ordertime" = ?, "mtime" = ?, "editor" = ?
               WHERE "siteid" = ? AND "id" = ?
            '
         ),
         'search' => array(
            'ansi' => '
               SELECT msto."id" AS "stock.id", msto."productcode" AS "stock.productcode",
                  msto."siteid" AS "stock.siteid", msto."typeid" AS "stock.typeid",
                  msto."stocklevel" AS "stock.stocklevel", msto."backdate" AS "stock.backdate",
                   msto."ordertime" AS "stock.ordertime",
                  msto."mtime" AS "stock.mtime", msto."editor" AS "stock.editor",
                  msto."ctime" AS "stock.ctime"
               FROM "mshop_stock" AS msto
               :joins
               WHERE :cond
               GROUP BY msto."id", msto."productcode", msto."siteid", msto."typeid",
                  msto."stocklevel", msto."backdate", msto."ordertime", msto."mtime", msto."editor",
                  msto."ctime" /*-columns*/ , :columns /*columns-*/
               /*-orderby*/ ORDER BY :order /*orderby-*/
               LIMIT :size OFFSET :start
            '
         ),
         'count' => array(
            'ansi' => '
               SELECT COUNT(*) AS "count"
               FROM (
                  SELECT DISTINCT msto."id"
                  FROM "mshop_stock" AS msto
                  :joins
                  WHERE :cond
                  LIMIT 10000 OFFSET 0
               ) AS list
            '
         ),
         'stocklevel' => array(
            'ansi' => '
               UPDATE "mshop_stock"
               SET "stocklevel" = "stocklevel" + ?, "mtime" = ?, "editor" = ?
               WHERE :cond
            '
         ),
         'newid' => array(
            'db2' => 'SELECT IDENTITY_VAL_LOCAL()',
            'mysql' => 'SELECT LAST_INSERT_ID()',
            'oracle' => 'SELECT mshop_stock_seq.CURRVAL FROM DUAL',
            'pgsql' => 'SELECT lastval()',
            'sqlite' => 'SELECT last_insert_rowid()',
            'sqlsrv' => 'SELECT SCOPE_IDENTITY()',
            'sqlanywhere' => 'SELECT @@IDENTITY',
         ),
      ),
   ),
);


I edited the template
Path: /typo3conf/ext/aimeos/Resources/Private/Extensions/ai-admin-jqadm/admin/jqadm/templates/product/item-stock-default.php
Code: Select all<?php

/**
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
 * @copyright Aimeos (aimeos.org), 2015-2017
 */

$enc = $this->encoder();
$stockTypes = $this->get( 'stockTypes', [] );


?>
<div id="stock" class="item-stock content-block tab-pane fade" role="tabpanel" aria-labelledby="stock">
   <table class="stock-list table table-default">
      <thead>
         <tr>
            <?php if( count( $stockTypes ) > 1 ) : ?>
               <th class="stock-type">
                  <span class="help"><?= $enc->html( $this->translate( 'admin', 'Type' ) ); ?></span>
                  <div class="form-text text-muted help-text">
                     <?= $enc->html( $this->translate( 'admin', 'Warehouse or local store if your articles are available at several locations' ) ); ?>
                  </div>
               </th>
            <?php endif; ?>
            <th class="stock-stocklevel">
               <span class="help"><?= $enc->html( $this->translate( 'admin', 'Stock level' ) ); ?></span>
               <div class="form-text text-muted help-text">
                  <?= $enc->html( $this->translate( 'admin', 'Number of articles currently in stock, leave empty for an unlimited quantity' ) ); ?>
               </div>
            </th>
            <th class="stock-databack">
               <span class="help"><?= $enc->html( $this->translate( 'admin', 'Back in stock' ) ); ?></span>
               <div class="form-text text-muted help-text">
                  <?= $enc->html( $this->translate( 'admin', 'Shown if the article reached a stock level of zero' ) ); ?>
               </div>
            </th>
            <th class="stock-ordertime">
               <span class="help"><?= $enc->html( $this->translate( 'admin', 'Stock Item Order Time' ) ); ?></span>
               <div class="form-text text-muted help-text">
                  <?= $enc->html( $this->translate( 'admin', 'Shown if the article reached a stock level of zero' ) ); ?>
               </div>
            </th>
            <th class="actions">
               <div class="btn act-add fa" tabindex="<?= $this->get( 'tabindex' ); ?>"
                  title="<?= $enc->attr( $this->translate( 'admin', 'Insert new entry (Ctrl+I)') ); ?>">
               </div>
            </th>
         </tr>
      </thead>
      <tbody>

         <?php foreach( $this->get( 'stockData/stock.id', [] ) as $idx => $id ) : ?>
            <tr class="<?= $this->site()->readonly( $this->get( 'stockData/stock.siteid/' . $idx ) ); ?>">
               <?php if( count( $stockTypes ) > 1 ) : ?>
                  <td class="stock-type mandatory">
                     <select class="form-control custom-select item-typeid" required="required" tabindex="<?= $this->get( 'tabindex' ); ?>"
                        name="<?= $enc->attr( $this->formparam( array( 'stock', 'stock.typeid', '' ) ) ); ?>"
                        <?= $this->site()->readonly( $this->get( 'stockData/stock.siteid/' . $idx ) ); ?> >
                        <option value="">
                           <?= $enc->html( $this->translate( 'admin', 'Please select' ) ); ?>
                        </option>

                        <?php foreach( $this->get( 'stockTypes', [] ) as $typeId => $typeItem ) : ?>
                           <?php if( $typeId == $this->get( 'stockData/stock.typeid/' . $idx ) ) : ?>
                              <option value="<?= $enc->attr( $typeId ); ?>" selected="selected"><?= $enc->html( $typeItem->getLabel() ) ?></option>
                           <?php else : ?>
                              <option value="<?= $enc->attr( $typeId ); ?>"><?= $enc->html( $typeItem->getLabel() ) ?></option>
                           <?php endif; ?>
                        <?php endforeach; ?>

                     </select>
                  </td>
               <?php else : $stockType = reset( $stockTypes ); ?>
                  <input class="item-typeid" type="hidden"
                     name="<?= $enc->attr( $this->formparam( array( 'stock', 'stock.typeid', '' ) ) ); ?>"
                     value="<?= $enc->attr( $stockType ? $stockType->getId() : '' ); ?>" />
               <?php endif; ?>
               <td class="stock-stocklevel optional">
                  <input class="form-control item-stocklevel" type="number" step="1" min="0" tabindex="<?= $this->get( 'tabindex' ); ?>"
                     name="<?= $enc->attr( $this->formparam( array( 'stock', 'stock.stocklevel', '' ) ) ); ?>"
                     value="<?= $enc->attr( $this->get( 'stockData/stock.stocklevel/' . $idx ) ); ?>"
                     <?= $this->site()->readonly( $this->get( 'stockData/stock.siteid/' . $idx ) ); ?> />
               </td>
               <td class="stock-databack optional">
                  <input class="form-control item-dateback" type="datetime-local" tabindex="<?= $this->get( 'tabindex' ); ?>"
                     name="<?= $enc->attr( $this->formparam( array( 'stock', 'stock.dateback', '' ) ) ); ?>"
                     value="<?= $enc->attr( $this->get( 'stockData/stock.dateback/' . $idx ) ); ?>"
                     placeholder="<?= $enc->attr( $this->translate( 'admin', 'YYYY-MM-DD hh:mm:ss (optional)' ) ); ?>"
                     <?= $this->site()->readonly( $this->get( 'stockData/stock.siteid/' . $idx ) ); ?> />
               </td>
               <td class="stock-ordertime optional">
                  <input class="form-control item-ordertime" type="string" tabindex="<?= $this->get( 'tabindex' ); ?>"
                     name="<?= $enc->attr( $this->formparam( array( 'stock', 'stock.ordertime', '' ) ) ); ?>"
                     value="<?= $enc->attr( $this->get( 'stockData/stock.ordertime/' . $idx ) ); ?>"
                     placeholder="<?= $enc->attr( $this->translate( 'admin', 'Order Time' ) ); ?>"
                     <?= $this->site()->readonly( $this->get( 'stockData/stock.siteid/' . $idx ) ); ?> />
               </td>
               <td class="actions">
                  <input class="item-id" type="hidden" value="<?= $enc->attr( $id ); ?>"
                     name="<?= $enc->attr( $this->formparam( array( 'stock', 'stock.id', '' ) ) ); ?>" />
                  <?php if( !$this->site()->readonly( $this->get( 'stockData/stock.siteid/' . $idx ) ) ) : ?>
                     <div class="btn act-delete fa" tabindex="<?= $this->get( 'tabindex' ); ?>"
                        title="<?= $enc->attr( $this->translate( 'admin', 'Delete this entry') ); ?>">
                     </div>
                  <?php endif; ?>
               </td>
            </tr>
         <?php endforeach; ?>

         <tr class="prototype">
            <?php if( count( $stockTypes ) > 1 ) : ?>
               <td class="stock-type">
                  <select class="form-control custom-select item-typeid" required="required" tabindex="<?= $this->get( 'tabindex' ); ?>" disabled="disabled"
                     name="<?= $enc->attr( $this->formparam( array( 'stock', 'stock.typeid', '' ) ) ); ?>">
                     <option value="">
                        <?= $enc->attr( $this->translate( 'admin', 'Please select' ) ); ?>
                     </option>

                     <?php foreach( $stockTypes as $typeId => $typeItem ) : ?>
                        <option value="<?= $enc->attr( $typeId ); ?>"><?= $enc->html( $typeItem->getLabel() ) ?></option>
                     <?php endforeach; ?>
                  </select>
               </td>
            <?php else : $stockType = reset( $stockTypes ); ?>
               <input class="item-typeid" type="hidden"
                  name="<?= $enc->attr( $this->formparam( array( 'stock', 'stock.typeid', '' ) ) ); ?>"
                  value="<?= $enc->attr( $stockType ? $stockType->getId() : '' ); ?>" />
            <?php endif; ?>
            <td class="stock-stocklevel optional">
               <input class="form-control item-stocklevel" type="number" step="1" min="0" tabindex="<?= $this->get( 'tabindex' ); ?>" disabled="disabled"
               name="<?= $enc->attr( $this->formparam( array( 'stock', 'stock.stocklevel', '' ) ) ); ?>" />
            </td>
            <td class="stock-databack optional">
               <input class="form-control item-dateback" type="datetime-local" tabindex="<?= $this->get( 'tabindex' ); ?>" disabled="disabled"
                  name="<?= $enc->attr( $this->formparam( array( 'stock', 'stock.dateback', '' ) ) ); ?>"
                  placeholder="<?= $enc->attr( $this->translate( 'admin', 'YYYY-MM-DD hh:mm:ss (optional)' ) ); ?>" />
            </td>
            <td class="stock-ordertime optional">
               <input class="form-control item-orderdate" type="string" tabindex="<?= $this->get( 'tabindex' ); ?>"
                  name="<?= $enc->attr( $this->formparam( array( 'stock', 'stock.ordertime', '' ) ) ); ?>"
                  placeholder="<?= $enc->attr( $this->translate( 'admin', 'Order Time' ) ); ?>"
            </td>
            
            <td class="actions">
               <input class="item-id" type="hidden" value="" disabled="disabled"
                  name="<?= $enc->attr( $this->formparam( array( 'stock', 'stock.id', '' ) ) ); ?>" />
               <div class="btn act-delete fa" tabindex="<?= $this->get( 'tabindex' ); ?>"
                  title="<?= $enc->attr( $this->translate( 'admin', 'Delete this entry') ); ?>">
               </div>
            </td>
         </tr>
      </tbody>
   </table>

   <?= $this->get( 'stockBody' ); ?>
</div>


-----------------------------------------------------------------------------------------------------
2. And my second Question is do i have to change the iface for order with my new function getOrderTime and setOrderTime and if yes where do i have to register the iface? I tryed it in the save function of my manager.

Path: /typo3conf/ext/mhaimeos/Resources/Private/Extensions/mhaimeos/lib/custom/src/MShop/Stock/Item/Myiface.php
Code: Select all<?php

/**
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
 * @copyright Metaways Infosystems GmbH, 2011
 * @copyright Aimeos (aimeos.org), 2015-2017
 * @package MShop
 * @subpackage Stock
 */


namespace Aimeos\MShop\Stock\Item;
//namespace Mhaimeos\MShop\Stock\Item;


/**
 * Extends Default stock item interface.
 *
 * @package MShop
 * @subpackage Stock
 */
interface Myiface extends Iface
{
   /**
    * Returns the ordertime of the stock item.
    *
    * @return string Order Time
    */
   public function getOrderTime();

   /**
    * Sets a new Ordertime of the stock item.
    *
    * @param string $ordertime
    * @return \Aimeos\MShop\Stock\Item\Iface Stock item for chaining method calls
    */
   public function setOrderTime( $ordertime );

}


----------------------------------------------------------------------------------------------------

3. Third Question, are my namespaces correct?

----------------------------------------------------------------------------------------------------

4. And my last question is how do i change the templates for the extadmin, is there anything documented?

I tryed to change the ItemUI.js and ListUI.js but got this error
ext-all.js:21 Uncaught TypeError: g is not a constructor
at constructor.setConfig (ext-all.js:21)
at new constructor (ext-all.js:21)
at S.initComponent (ext-all.js:21)
at S.Ext.Component [as constructor] (ext-all.js:21)
at S [as constructor] (ext-base.js:21)
at S [as constructor] (ext-base.js:21)
at S [as constructor] (ext-base.js:21)
at new S (ext-base.js:21)
at S.initComponent (index.php?M=web_AimeosTxAimeosAdmin&moduleToken=60ae9e4e8faae802e56a6d72d2902be966dd9fed&tx_aimeos_web_aimeostxaimeosadmin[action]=file&tx_aimeos_web_aimeostxaimeosadmin[controller]=Extadm:3347)
at S.initComponent (index.php?M=web_AimeosTxAimeosAdmin&moduleToken=60ae9e4e8faae802e56a6d72d2902be966dd9fed&tx_aimeos_web_aimeostxaimeosadmin[action]=file&tx_aimeos_web_aimeostxaimeosadmin[controller]=Extadm:9500)



Path: /typo3conf/ext/aimeos/Resources/Private/Extensions/ai-admin-extadm/admin/extjs/src/panel/product/stock/ItemUi.js
Code: Select all/*!
 * Copyright (c) Metaways Infosystems GmbH, 2011
 * LGPLv3, http://opensource.org/licenses/LGPL-3.0
 */

Ext.ns('MShop.panel.product.stock');

/**
 * Concrete ItemUi
 *
 * @extends Mshop.panel.AbstractListItemUi
 */
MShop.panel.product.stock.ItemUi = Ext.extend(MShop.panel.AbstractItemUi, {

    siteidProperty : 'stock.siteid',

    initComponent : function() {

        this.title = MShop.I18n.dt('admin', 'Product stock');

        MShop.panel.AbstractItemUi.prototype.setSiteCheck(this);

        this.items = [{
            xtype : 'tabpanel',
            activeTab : 0,
            border : false,
            itemId : 'MShop.panel.product.stock.ItemUi',
            plugins : ['ux.itemregistry'],
            items : [{
                xtype : 'panel',
                title : MShop.I18n.dt('admin', 'Basic'),
                border : false,
                layout : 'hbox',
                layoutConfig : {
                    align : 'stretch'
                },
                itemId : 'MShop.panel.product.stock.ItemUi.BasicPanel',

                plugins : ['ux.itemregistry'],
                defaults : {
                    bodyCssClass : this.readOnlyClass
                },
                items : [{
                    xtype : 'form',
                    title : MShop.I18n.dt('admin', 'Details'),
                    flex : 1,
                    ref : '../../mainForm',
                    autoScroll : true,
                    items : [{
                        xtype : 'fieldset',
                        style : 'padding-right: 25px;',
                        border : false,
                        labelAlign : 'top',
                        defaults : {
                            readOnly : this.fieldsReadOnly,
                            anchor : '100%'
                        },
                        items : [{
                            xtype : 'hidden',
                            name : 'stock.productcode'
                        }, {
                            xtype : 'displayfield',
                            fieldLabel : MShop.I18n.dt('admin', 'ID'),
                            name : 'stock.id'
                        }, {
                            xtype : 'combo',
                            fieldLabel : MShop.I18n.dt('admin', 'Type'),
                            name : 'stock.typeid',
                            mode : 'local',
                            store : MShop.GlobalStoreMgr.get('Stock_Type', this.domain),
                            displayField : 'stock.type.label',
                            valueField : 'stock.type.id',
                            forceSelection : true,
                            triggerAction : 'all',
                            typeAhead : true,
                            listeners : {
                                'render' : {
                                    fn : function() {
                                        var record, index = this.store.find('stock.type.code', 'default');
                                        if((record = this.store.getAt(index))) {
                                            this.setValue(record.id);
                                        }
                                    }
                                }
                            }
                        }, {
                            xtype : 'numberfield',
                            fieldLabel : MShop.I18n.dt('admin', 'Stock level'),
                            name : 'stock.stocklevel',
                            emptyText : MShop.I18n.dt('admin', 'Quantity or empty if unlimited (optional)')
                        }, {
                            xtype : 'datefield',
                            fieldLabel : MShop.I18n.dt('admin', 'Back in stock'),
                            name : 'stock.dateback',
                            format : 'Y-m-d H:i:s',
                            emptyText : MShop.I18n.dt('admin', 'YYYY-MM-DD hh:mm:ss (optional)')
                        }, {
                            xtype : 'ordertime',
                            fieldLabel : MShop.I18n.dt('admin', 'Stock Order Time'),
                            name : 'stock.ordertime',
                            emptyText : MShop.I18n.dt('admin', 'Stock Order Time')
                        }, {
                            xtype : 'displayfield',
                            fieldLabel : MShop.I18n.dt('admin', 'Created'),
                            name : 'stock.ctime'
                        }, {
                            xtype : 'displayfield',
                            fieldLabel : MShop.I18n.dt('admin', 'Last modified'),
                            name : 'stock.mtime'
                        }, {
                            xtype : 'displayfield',
                            fieldLabel : MShop.I18n.dt('admin', 'Editor'),
                            name : 'stock.editor'
                        }]
                    }]
                }]
            }]
        }];

        MShop.panel.product.stock.ItemUi.superclass.initComponent.call(this);
    },

    onSaveItem : function() {
        // validate data
        if(!this.mainForm.getForm().isValid() && this.fireEvent('validate', this) !== false) {
            Ext.Msg.alert(MShop.I18n.dt('admin', 'Invalid data'), MShop.I18n.dt('admin',
                'Please recheck your data'));
            return;
        }

        this.saveMask.show();
        this.isSaveing = true;

        // force record to be saved!
        this.record.dirty = true;

        if(this.fireEvent('beforesave', this, this.record) === false) {
            this.isSaveing = false;
            this.saveMask.hide();
        }

        this.mainForm.getForm().updateRecord(this.record);
        this.record.data['stock.productcode'] = this.listUI.itemUi.record.data['product.code'];

        if(this.action == 'add' || this.action == 'copy') {
            this.store.add(this.record);
        }

        // store async action is triggered. {@see onStoreWrite/onStoreException}
        if(!this.store.autoSave) {
            this.onAfterSave();
        }
    },

    onStoreException : function() {
        this.store.remove(this.record);
        MShop.panel.product.stock.ItemUi.superclass.onStoreException.apply(this, arguments);
    }
});

Ext.reg('MShop.panel.product.stock.itemui', MShop.panel.product.stock.ItemUi);


Path: /typo3conf/ext/aimeos/Resources/Private/Extensions/ai-admin-extadm/admin/extjs/src/panel/product/stock/ListUi.js
Code: Select all/*!
 * LGPLv3, http://opensource.org/licenses/LGPL-3.0
 * Copyright (c) Metaways Infosystems GmbH, 2011
 * Copyright (c) Aimeos (aimeos.org), 2015-2017
 */

Ext.ns('MShop.panel.product.stock');

MShop.panel.product.stock.ListUi = Ext.extend(MShop.panel.AbstractListUi, {

    recordName : 'Stock',
    idProperty : 'stock.id',
    siteidProperty : 'stock.siteid',
    itemUiXType : 'MShop.panel.product.stock.itemui',

    autoExpandColumn : 'product-stock-stocktype',

    filterConfig : {
        filters : [{
            dataIndex : 'stock.type.label',
            operator : '=~',
            value : ''
        }]
    },

    initComponent : function() {
        this.title = MShop.I18n.dt('admin', 'Stock');

        MShop.panel.AbstractListUi.prototype.initActions.call(this);
        MShop.panel.AbstractListUi.prototype.initToolbar.call(this);

        MShop.panel.product.stock.ListUi.superclass.initComponent.call(this);
    },

    afterRender : function() {
        this.itemUi = this.findParentBy(function(c) {
            return c.isXType(MShop.panel.AbstractItemUi, false);
        });

        MShop.panel.product.stock.ListUi.superclass.afterRender.apply(this, arguments);
    },

    onBeforeLoad : function(store, options) {
        this.setSiteParam(store);

        if(this.domain) {
            this.setDomainFilter(store, options);
        }

        options.params = options.params || {};
        options.params.condition = {
            '&&' : [{
                '==' : {
                    'stock.productcode' : this.itemUi.record ? this.itemUi.record.data['product.code'] : ''
                }
            }]
        };

    },

    getColumns : function() {
        this.typeStore = MShop.GlobalStoreMgr.get('Stock_Type');

        return [
            {
                xtype : 'gridcolumn',
                dataIndex : 'stock.id',
                header : MShop.I18n.dt('admin', 'ID'),
                sortable : true,
                width : 50,
                hidden : true
            },
            {
                xtype : 'gridcolumn',
                dataIndex : 'stock.productcode',
                header : MShop.I18n.dt('admin', 'Product code'),
                width : 50,
                hidden : true
            },
            {
                xtype : 'gridcolumn',
                dataIndex : 'stock.typeid',
                header : MShop.I18n.dt('admin', 'Type'),
                align : 'center',
                id : 'product-stock-stocktype',
                renderer : this.typeColumnRenderer.createDelegate(this, [
                    this.typeStore,
                    "stock.type.label"], true)
            },
            {
                xtype : 'gridcolumn',
                dataIndex : 'stock.stocklevel',
                header : MShop.I18n.dt('admin', 'Stock level'),
                sortable : true,
                align : 'center',
                width : 80
            },
            {
                xtype : 'datecolumn',
                dataIndex : 'stock.dateback',
                header : MShop.I18n.dt('admin', 'Date back'),
                format : 'Y-m-d H:i:s',
                sortable : true,
                width : 130
            },
            {
                xtype : 'ordertime',
                dataIndex : 'stock.ordertime',
                header : MShop.I18n.dt('admin', 'Order Time'),
                sortable : true,
                width : 130
            },
            {
                xtype : 'datecolumn',
                dataIndex : 'stock.ctime',
                header : MShop.I18n.dt('admin', 'Created'),
                format : 'Y-m-d H:i:s',
                sortable : true,
                width : 130,
                editable : false,
                hidden : true
            },
            {
                xtype : 'datecolumn',
                dataIndex : 'stock.mtime',
                header : MShop.I18n.dt('admin', 'Last modified'),
                format : 'Y-m-d H:i:s',
                sortable : true,
                width : 130,
                editable : false,
                hidden : true
            },
            {
                xtype : 'gridcolumn',
                dataIndex : 'stock.editor',
                header : MShop.I18n.dt('admin', 'Editor'),
                sortable : true,
                width : 130,
                editable : false,
                hidden : true
            }];
    }
});

Ext.reg('MShop.panel.product.stock.listui', MShop.panel.product.stock.ListUi);

Ext.ux.ItemRegistry.registerItem('MShop.panel.product.ItemUi', 'MShop.panel.product.stock.listui', MShop.panel.product.stock.ListUi, 2);




Many Thanks and sorry for the long post ;)
Typo3 7.6.x / Aimeos 17.10.0
Last edited by tenkraD on 11 Apr 2018, 20:36, edited 3 times in total.
#5889 by aimeos
26 Mar 2018, 09:01
The namespace/class name of your item in createItemBase() (manager class) is wrong. It must be \Aimeos\MShop\Stock\Item\MyStandard. The rest seems to be OK.

If you don't have a clue, add some debug output to see which code is executed.

Regarding the ExtJS interface: Forget about it!
In 2017.x it's deprecated and in the 2018.x version it's already removed.
#5924 by mohal_04
30 Mar 2018, 12:51
tenkraD wrote:Hello Guys,

i would like to extend the stock of products with a optional field order time.
the order time is a string, and describes how long it takes for us to reorder if the product is selled out - 1 weeks/ 1 month / a few days...

-------------------------------------------------------------------------------------------------------------
1. My Problem is i cant save the values in db and dont know what im doing wrong. Also i dont get any error response, im a bit lost.
It would be great if you can help me.

I created an extension named "mhaimeos" with our extensionbuilder.
Then i extended the stock shema, this is working good.


Path: /typo3conf/ext/mhaimeos/Resources/Private/Extensions/mhaimeos/lib/custom/config/stock.php
Code: Select all<?php

/**
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
 *
 * Extend Database Table mshop_stock in /typo3conf/ext/aimeos/Resources/Libraries/aimeos/aimeos-core/lib/mshoplib/setup/default/schema/stock.php
 *
 * https://aimeos.org/docs/Developers/Library/Setup_tasks/Schema_update#Modify_existing_tables
 */


return array(
   'table' => array(
         
      'mshop_stock' => function ( \Doctrine\DBAL\Schema\Schema $schema ) {

         $table = $schema->getTable( 'mshop_stock' );
         
         $table->addColumn( 'ordertime', 'string', array( 'length' => 32 ) );
         
         $table->addIndex( array( 'siteid', 'ordertime' ), 'idx_mssto_sid_ordertime' );
         
         return $schema;
      },
   ),
);


After that i created the Item
Path: /typo3conf/ext/mhaimeos/Resources/Private/Extensions/mhaimeos/lib/custom/src/MShop/Stock/Item/Mystandard.php
Code: Select all<?php

/**
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
 * @package MShop
 * @subpackage Stock
 */

namespace Aimeos\MShop\Stock\Item;
//namespace Mhaimeos\MShop\Stock\Item;
 
class Mystandard extends Standard
{
    private $values;
 
    public function __construct( array $values )
    {
        parent::__construct( 'stock.', $values );
        $this->values = $values;
    }
 
    /**
     * Returns ordertime for the product stock
     *
     * @return string|null stock ordertime of the product
     */
    public function getOrderTime()
    {
        if( isset( $this->values['stock.ordertime'] ) ) {
            return (string) $this->values['stock.ordertime'];
        }
        return '';
    }
 
    public function setOrderTime( $ordertime )
    {
        if( (string) $ordertime !== $this->getOrderTime() )
        {
            $this->values['stock.ordertime'] = (string) $ordertime;
            $this->setModified();
        }
        return $this;
    }
 
    public function fromArray( array $list )
    {
        $unknown = [];
        $list = parent::fromArray( $list );
 
        foreach( $list as $key => $value )
        {
            switch( $key )
            {
                case 'stock.ordertime': $this->setOrderTime( $value ); break;
                default: $unknown[$key] = $value;
            }
        }
 
        return $unknown;
    }
 
    public function toArray( $private = false )
    {
        $list = parent::toArray( $private );
 
        if( $private === true ) {
            $list['stock.ordertime'] = $this->getOrderTime();
        }
 
        return $list;
    }
}


I created the manager
Path: /typo3conf/ext/mhaimeos/Resources/Private/Extensions/mhaimeos/lib/custom/src/MShop/Stock/Manager/Mystandard.php
Code: Select all<?php

/**
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
 * @package MShop
 * @subpackage Stock
 */

namespace Aimeos\MShop\Stock\Manager;
//namespace Mhaimeos\MShop\Stock\Manager;
 
class Mystandard extends Standard
{
    private $searchConfig = array(
        'stock.ordertime'=> array(
            'code'=>'stock.ordertime',
            'internalcode'=>'msto."ordertime"',
            'label'=>'Product Ordertime',
            'type'=> 'string', // integer, float, etc.
            'internaltype'=> \Aimeos\MW\DB\Statement\Base::PARAM_STR, // _INT, _FLOAT, etc.
        ),
    );
 
    public function saveItem( \Aimeos\MShop\Common\Item\Iface $item, $fetch = true )
    {
        // a modified copy of the code from the parent class
        // extended by a bind() call and updated bind positions (first parameter)
       $iface = '\\Mhaimeos\\MShop\\Stock\\Item\\Myiface';
       if( !( $item instanceof $iface ) ) {
          throw new \Aimeos\MShop\Stock\Exception( sprintf( 'Object is not of required type "%1$s"', $iface ) );
       }
       
       if( !$item->isModified() ) {
          return $item;
       }
       
       $context = $this->getContext();
       
       $dbm = $context->getDatabaseManager();
       $dbname = $this->getResourceName();
       $conn = $dbm->acquire( $dbname );
       
       try
       {
          $id = $item->getId();
          $date = date( 'Y-m-d H:i:s' );
       
          if( $id === null )
          {
             /** mshop/stock/manager/standard/insert/mysql
              * Inserts a new product stock record into the database table
              *
              * @see mshop/stock/manager/standard/insert/ansi
              */
       
             /** mshop/stock/manager/standard/insert/ansi
              * Inserts a new product stock record into the database table
              *
              * Items with no ID yet (i.e. the ID is NULL) will be created in
              * the database and the newly created ID retrieved afterwards
              * using the "newid" SQL statement.
              *
              * The SQL statement must be a string suitable for being used as
              * prepared statement. It must include question marks for binding
              * the values from the product stock item to the statement before they are
              * sent to the database server. The number of question marks must
              * be the same as the number of columns listed in the INSERT
              * statement. The order of the columns must correspond to the
              * order in the saveItems() method, so the correct values are
              * bound to the columns.
              *
              * The SQL statement should conform to the ANSI standard to be
              * compatible with most relational database systems. This also
              * includes using double quotes for table and column names.
              *
              * @param string SQL statement for inserting records
              * @since 2017.01
              * @category Developer
              * @see mshop/stock/manager/standard/update/ansi
              * @see mshop/stock/manager/standard/newid/ansi
              * @see mshop/stock/manager/standard/delete/ansi
              * @see mshop/stock/manager/standard/search/ansi
              * @see mshop/stock/manager/standard/count/ansi
              * @see mshop/stock/manager/standard/stocklevel
              */
             $path = 'mshop/stock/manager/standard/insert';
//             $path = 'mshop/stock/manager/mystandard/insert';
          }
          else
          {
             /** mshop/stock/manager/standard/update/mysql
              * Updates an existing product stock record in the database
              *
              * @see mshop/stock/manager/standard/update/ansi
              */
       
             /** mshop/stock/manager/standard/update/ansi
              * Updates an existing product stock record in the database
              *
              * Items which already have an ID (i.e. the ID is not NULL) will
              * be updated in the database.
              *
              * The SQL statement must be a string suitable for being used as
              * prepared statement. It must include question marks for binding
              * the values from the product stock item to the statement before they are
              * sent to the database server. The order of the columns must
              * correspond to the order in the saveItems() method, so the
              * correct values are bound to the columns.
              *
              * The SQL statement should conform to the ANSI standard to be
              * compatible with most relational database systems. This also
              * includes using double quotes for table and column names.
              *
              * @param string SQL statement for updating records
              * @since 2017.01
              * @category Developer
              * @see mshop/stock/manager/standard/insert/ansi
              * @see mshop/stock/manager/standard/newid/ansi
              * @see mshop/stock/manager/standard/delete/ansi
              * @see mshop/stock/manager/standard/search/ansi
              * @see mshop/stock/manager/standard/count/ansi
              * @see mshop/stock/manager/standard/stocklevel
              */
             $path = 'mshop/stock/manager/standard/update';
//             $path = 'mshop/stock/manager/mystandard/update';
          }
       
          $stmt = $this->getCachedStatement( $conn, $path );
       
          
          $stmt->bind( 1, $item->getProductCode() );
          $stmt->bind( 2, $item->getTypeId(), \Aimeos\MW\DB\Statement\Base::PARAM_INT );
          $stmt->bind( 3, $item->getStocklevel(), \Aimeos\MW\DB\Statement\Base::PARAM_INT );
          $stmt->bind( 4, $item->getDateBack() );
 //  GET the Order Time
          $stmt->bind( 5, $item->getOrderTime() );
          
          $stmt->bind( 6, $date ); //mtime
          $stmt->bind( 7, $context->getEditor() );
          $stmt->bind( 8, $context->getLocale()->getSiteId(), \Aimeos\MW\DB\Statement\Base::PARAM_INT );
       
          if( $id !== null ) {
             $stmt->bind( 9, $id, \Aimeos\MW\DB\Statement\Base::PARAM_INT );
             $item->setId( $id ); // modified false
          } else {
             $stmt->bind( 9, $date ); //ctime
          }
          
          
          $stmt->execute()->finish();
       
          if( $id === null && $fetch === true )
          {
             /** mshop/stock/manager/standard/newid/mysql
              * Retrieves the ID generated by the database when inserting a new record
              *
              * @see mshop/stock/manager/standard/newid/ansi
              */
       
             /** mshop/stock/manager/standard/newid/ansi
              * Retrieves the ID generated by the database when inserting a new record
              *
              * As soon as a new record is inserted into the database table,
              * the database server generates a new and unique identifier for
              * that record. This ID can be used for retrieving, updating and
              * deleting that specific record from the table again.
              *
              * For MySQL:
              *  SELECT LAST_INSERT_ID()
              * For PostgreSQL:
              *  SELECT currval('seq_msto_id')
              * For SQL Server:
              *  SELECT SCOPE_IDENTITY()
              * For Oracle:
              *  SELECT "seq_msto_id".CURRVAL FROM DUAL
              *
              * There's no way to retrive the new ID by a SQL statements that
              * fits for most database servers as they implement their own
              * specific way.
              *
              * @param string SQL statement for retrieving the last inserted record ID
              * @since 2017.01
              * @category Developer
              * @see mshop/stock/manager/standard/insert/ansi
              * @see mshop/stock/manager/standard/update/ansi
              * @see mshop/stock/manager/standard/delete/ansi
              * @see mshop/stock/manager/standard/search/ansi
              * @see mshop/stock/manager/standard/count/ansi
              * @see mshop/stock/manager/standard/stocklevel
              */
             $path = 'mshop/stock/manager/standard/newid';
             $item->setId( $this->newId( $conn, $path ) );
          }
       
          $dbm->release( $conn, $dbname );
       }
       catch( \Exception $e )
       {
          $dbm->release( $conn, $dbname );
          throw $e;
       }
       
       return $item;
    }
 
    public function getSearchAttributes( $withsub = true )
    {
        $list = parent::getSearchAttributes( $withsub );
        foreach( $this->searchConfig as $key => $fields ) {
            $list[$key] = new \Aimeos\MW\Criteria\Attribute\Standard( $fields );
        }
        return $list;
    }
 
    protected function createItemBase( array $values = [] /* , ... */ )
    {
        return new \Mhaimeos\MShop\Stock\Item\Standard( $values /* , ... */ );
    }
}



I created the Config
Its a copy from the existing one. I just change the name, the insert and update.
Path: /typo3conf/ext/mhaimeos/Resources/Private/Extensions/mhaimeos/lib/custom/config/stock.php
Code: Select all<?php

/**
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
 * @copyright Aimeos (aimeos.org), 2015-2017
 */


return array(
   'manager' => array(
      'name' => 'Mystandard',
         
      'type' => array(
         'standard' => array(
            'delete' => array(
               'ansi' => '
                  DELETE FROM "mshop_stock_type"
                  WHERE :cond AND siteid = ?
               '
            ),
            'insert' => array(
               'ansi' => '
                  INSERT INTO "mshop_stock_type" (
                     "code", "domain", "label", "status",
                     "mtime", "editor", "siteid", "ctime"
                  ) VALUES (
                     ?, ?, ?, ?, ?, ?, ?, ?
                  )
               '
            ),
            'update' => array(
               'ansi' => '
                  UPDATE "mshop_stock_type"
                  SET "code" = ?, "domain" = ?, "label" = ?,
                     "status" = ?, "mtime" = ?, "editor" = ?
                  WHERE "siteid" = ? AND "id" = ?
               '
            ),
            'search' => array(
               'ansi' => '
                  SELECT mstoty."id" AS "stock.type.id", mstoty."siteid" AS "stock.type.siteid",
                     mstoty."code" AS "stock.type.code", mstoty."domain" AS "stock.type.domain",
                     mstoty."label" AS "stock.type.label", mstoty."status" AS "stock.type.status",
                     mstoty."mtime" AS "stock.type.mtime", mstoty."editor" AS "stock.type.editor",
                     mstoty."ctime" AS "stock.type.ctime"
                  FROM "mshop_stock_type" mstoty
                  :joins
                  WHERE :cond
                  GROUP BY mstoty."id", mstoty."siteid", mstoty."code", mstoty."domain",
                     mstoty."label", mstoty."status", mstoty."mtime", mstoty."editor",
                     mstoty."ctime" /*-columns*/ , :columns /*columns-*/
                  /*-orderby*/ ORDER BY :order /*orderby-*/
                  LIMIT :size OFFSET :start
               '
            ),
            'count' => array(
               'ansi' => '
                  SELECT COUNT(*) AS "count"
                  FROM (
                     SELECT DISTINCT mstoty."id"
                     FROM "mshop_stock_type" mstoty
                     :joins
                     WHERE :cond
                     LIMIT 10000 OFFSET 0
                  ) AS list
               '
            ),
            'newid' => array(
               'db2' => 'SELECT IDENTITY_VAL_LOCAL()',
               'mysql' => 'SELECT LAST_INSERT_ID()',
               'oracle' => 'SELECT mshop_stock_type_seq.CURRVAL FROM DUAL',
               'pgsql' => 'SELECT lastval()',
               'sqlite' => 'SELECT last_insert_rowid()',
               'sqlsrv' => 'SELECT SCOPE_IDENTITY()',
               'sqlanywhere' => 'SELECT @@IDENTITY',
            ),
         ),
      ),
      'standard' => array(
         'delete' => array(
            'ansi' => '
               DELETE FROM "mshop_stock"
               WHERE :cond AND siteid = ?
            '
         ),
         'insert' => array(
            'ansi' => '
               INSERT INTO "mshop_stock" (
                  "productcode", "typeid", "stocklevel", "backdate", "ordertime"
                  "mtime", "editor", "siteid", "ctime"
               ) VALUES (
                  ?, ?, ?, ?, ?, ?, ?, ?, ?
               )
            '
         ),
         'update' => array(
            'ansi' => '
               UPDATE "mshop_stock"
               SET "productcode" = ?, "typeid" = ?, "stocklevel" = ?,
                  "backdate" = ?, "ordertime" = ?, "mtime" = ?, "editor" = ?
               WHERE "siteid" = ? AND "id" = ?
            '
         ),
         'search' => array(
            'ansi' => '
               SELECT msto."id" AS "stock.id", msto."productcode" AS "stock.productcode",
                  msto."siteid" AS "stock.siteid", msto."typeid" AS "stock.typeid",
                  msto."stocklevel" AS "stock.stocklevel", msto."backdate" AS "stock.backdate",
                   msto."ordertime" AS "stock.ordertime",
                  msto."mtime" AS "stock.mtime", msto."editor" AS "stock.editor",
                  msto."ctime" AS "stock.ctime"
               FROM "mshop_stock" AS msto
               :joins
               WHERE :cond
               GROUP BY msto."id", msto."productcode", msto."siteid", msto."typeid",
                  msto."stocklevel", msto."backdate", msto."ordertime", msto."mtime", msto."editor",
                  msto."ctime" /*-columns*/ , :columns /*columns-*/
               /*-orderby*/ ORDER BY :order /*orderby-*/
               LIMIT :size OFFSET :start
            '
         ),
         'count' => array(
            'ansi' => '
               SELECT COUNT(*) AS "count"
               FROM (
                  SELECT DISTINCT msto."id"
                  FROM "mshop_stock" AS msto
                  :joins
                  WHERE :cond
                  LIMIT 10000 OFFSET 0
               ) AS list
            '
         ),
         'stocklevel' => array(
            'ansi' => '
               UPDATE "mshop_stock"
               SET "stocklevel" = "stocklevel" + ?, "mtime" = ?, "editor" = ?
               WHERE :cond
            '
         ),
         'newid' => array(
            'db2' => 'SELECT IDENTITY_VAL_LOCAL()',
            'mysql' => 'SELECT LAST_INSERT_ID()',
            'oracle' => 'SELECT mshop_stock_seq.CURRVAL FROM DUAL',
            'pgsql' => 'SELECT lastval()',
            'sqlite' => 'SELECT last_insert_rowid()',
            'sqlsrv' => 'SELECT SCOPE_IDENTITY()',
            'sqlanywhere' => 'SELECT @@IDENTITY',
         ),
      ),
   ),
);


I edited the template
Path: /typo3conf/ext/aimeos/Resources/Private/Extensions/ai-admin-jqadm/admin/jqadm/templates/product/item-stock-default.php
Code: Select all<?php

/**
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
 * @copyright Aimeos (aimeos.org), 2015-2017
 */

$enc = $this->encoder();
$stockTypes = $this->get( 'stockTypes', [] );


?>
<div id="stock" class="item-stock content-block tab-pane fade" role="tabpanel" aria-labelledby="stock">
   <table class="stock-list table table-default">
      <thead>
         <tr>
            <?php if( count( $stockTypes ) > 1 ) : ?>
               <th class="stock-type">
                  <span class="help"><?= $enc->html( $this->translate( 'admin', 'Type' ) ); ?></span>
                  <div class="form-text text-muted help-text">
                     <?= $enc->html( $this->translate( 'admin', 'Warehouse or local store if your articles are available at several locations' ) ); ?>
                  </div>
               </th>
            <?php endif; ?>
            <th class="stock-stocklevel">
               <span class="help"><?= $enc->html( $this->translate( 'admin', 'Stock level' ) ); ?></span>
               <div class="form-text text-muted help-text">
                  <?= $enc->html( $this->translate( 'admin', 'Number of articles currently in stock, leave empty for an unlimited quantity' ) ); ?>
               </div>
            </th>
            <th class="stock-databack">
               <span class="help"><?= $enc->html( $this->translate( 'admin', 'Back in stock' ) ); ?></span>
               <div class="form-text text-muted help-text">
                  <?= $enc->html( $this->translate( 'admin', 'Shown if the article reached a stock level of zero' ) ); ?>
               </div>
            </th>
            <th class="stock-ordertime">
               <span class="help"><?= $enc->html( $this->translate( 'admin', 'Stock Item Order Time' ) ); ?></span>
               <div class="form-text text-muted help-text">
                  <?= $enc->html( $this->translate( 'admin', 'Shown if the article reached a stock level of zero' ) ); ?>
               </div>
            </th>
            <th class="actions">
               <div class="btn act-add fa" tabindex="<?= $this->get( 'tabindex' ); ?>"
                  title="<?= $enc->attr( $this->translate( 'admin', 'Insert new entry (Ctrl+I)') ); ?>">
               </div>
            </th>
         </tr>
      </thead>
      <tbody>

         <?php foreach( $this->get( 'stockData/stock.id', [] ) as $idx => $id ) : ?>
            <tr class="<?= $this->site()->readonly( $this->get( 'stockData/stock.siteid/' . $idx ) ); ?>">
               <?php if( count( $stockTypes ) > 1 ) : ?>
                  <td class="stock-type mandatory">
                     <select class="form-control custom-select item-typeid" required="required" tabindex="<?= $this->get( 'tabindex' ); ?>"
                        name="<?= $enc->attr( $this->formparam( array( 'stock', 'stock.typeid', '' ) ) ); ?>"
                        <?= $this->site()->readonly( $this->get( 'stockData/stock.siteid/' . $idx ) ); ?> >
                        <option value="">
                           <?= $enc->html( $this->translate( 'admin', 'Please select' ) ); ?>
                        </option>

                        <?php foreach( $this->get( 'stockTypes', [] ) as $typeId => $typeItem ) : ?>
                           <?php if( $typeId == $this->get( 'stockData/stock.typeid/' . $idx ) ) : ?>
                              <option value="<?= $enc->attr( $typeId ); ?>" selected="selected"><?= $enc->html( $typeItem->getLabel() ) ?></option>
                           <?php else : ?>
                              <option value="<?= $enc->attr( $typeId ); ?>"><?= $enc->html( $typeItem->getLabel() ) ?></option>
                           <?php endif; ?>
                        <?php endforeach; ?>

                     </select>
                  </td>
               <?php else : $stockType = reset( $stockTypes ); ?>
                  <input class="item-typeid" type="hidden"
                     name="<?= $enc->attr( $this->formparam( array( 'stock', 'stock.typeid', '' ) ) ); ?>"
                     value="<?= $enc->attr( $stockType ? $stockType->getId() : '' ); ?>" />
               <?php endif; ?>
               <td class="stock-stocklevel optional">
                  <input class="form-control item-stocklevel" type="number" step="1" min="0" tabindex="<?= $this->get( 'tabindex' ); ?>"
                     name="<?= $enc->attr( $this->formparam( array( 'stock', 'stock.stocklevel', '' ) ) ); ?>"
                     value="<?= $enc->attr( $this->get( 'stockData/stock.stocklevel/' . $idx ) ); ?>"
                     <?= $this->site()->readonly( $this->get( 'stockData/stock.siteid/' . $idx ) ); ?> />
               </td>
               <td class="stock-databack optional">
                  <input class="form-control item-dateback" type="datetime-local" tabindex="<?= $this->get( 'tabindex' ); ?>"
                     name="<?= $enc->attr( $this->formparam( array( 'stock', 'stock.dateback', '' ) ) ); ?>"
                     value="<?= $enc->attr( $this->get( 'stockData/stock.dateback/' . $idx ) ); ?>"
                     placeholder="<?= $enc->attr( $this->translate( 'admin', 'YYYY-MM-DD hh:mm:ss (optional)' ) ); ?>"
                     <?= $this->site()->readonly( $this->get( 'stockData/stock.siteid/' . $idx ) ); ?> />
               </td>
               <td class="stock-ordertime optional">
                  <input class="form-control item-ordertime" type="string" tabindex="<?= $this->get( 'tabindex' ); ?>"
                     name="<?= $enc->attr( $this->formparam( array( 'stock', 'stock.ordertime', '' ) ) ); ?>"
                     value="<?= $enc->attr( $this->get( 'stockData/stock.ordertime/' . $idx ) ); ?>"
                     placeholder="<?= $enc->attr( $this->translate( 'admin', 'Order Time' ) ); ?>"
                     <?= $this->site()->readonly( $this->get( 'stockData/stock.siteid/' . $idx ) ); ?> />
               </td>
               <td class="actions">
                  <input class="item-id" type="hidden" value="<?= $enc->attr( $id ); ?>"
                     name="<?= $enc->attr( $this->formparam( array( 'stock', 'stock.id', '' ) ) ); ?>" />
                  <?php if( !$this->site()->readonly( $this->get( 'stockData/stock.siteid/' . $idx ) ) ) : ?>
                     <div class="btn act-delete fa" tabindex="<?= $this->get( 'tabindex' ); ?>"
                        title="<?= $enc->attr( $this->translate( 'admin', 'Delete this entry') ); ?>">
                     </div>
                  <?php endif; ?>
               </td>
            </tr>
         <?php endforeach; ?>

         <tr class="prototype">
            <?php if( count( $stockTypes ) > 1 ) : ?>
               <td class="stock-type">
                  <select class="form-control custom-select item-typeid" required="required" tabindex="<?= $this->get( 'tabindex' ); ?>" disabled="disabled"
                     name="<?= $enc->attr( $this->formparam( array( 'stock', 'stock.typeid', '' ) ) ); ?>">
                     <option value="">
                        <?= $enc->attr( $this->translate( 'admin', 'Please select' ) ); ?>
                     </option>

                     <?php foreach( $stockTypes as $typeId => $typeItem ) : ?>
                        <option value="<?= $enc->attr( $typeId ); ?>"><?= $enc->html( $typeItem->getLabel() ) ?></option>
                     <?php endforeach; ?>
                  </select>
               </td>
            <?php else : $stockType = reset( $stockTypes ); ?>
               <input class="item-typeid" type="hidden"
                  name="<?= $enc->attr( $this->formparam( array( 'stock', 'stock.typeid', '' ) ) ); ?>"
                  value="<?= $enc->attr( $stockType ? $stockType->getId() : '' ); ?>" />
            <?php endif; ?>
            <td class="stock-stocklevel optional">
               <input class="form-control item-stocklevel" type="number" step="1" min="0" tabindex="<?= $this->get( 'tabindex' ); ?>" disabled="disabled"
               name="<?= $enc->attr( $this->formparam( array( 'stock', 'stock.stocklevel', '' ) ) ); ?>" />
            </td>
            <td class="stock-databack optional">
               <input class="form-control item-dateback" type="datetime-local" tabindex="<?= $this->get( 'tabindex' ); ?>" disabled="disabled"
                  name="<?= $enc->attr( $this->formparam( array( 'stock', 'stock.dateback', '' ) ) ); ?>"
                  placeholder="<?= $enc->attr( $this->translate( 'admin', 'YYYY-MM-DD hh:mm:ss (optional)' ) ); ?>" />
            </td>
            <td class="stock-ordertime optional">
               <input class="form-control item-orderdate" type="string" tabindex="<?= $this->get( 'tabindex' ); ?>"
                  name="<?= $enc->attr( $this->formparam( array( 'stock', 'stock.ordertime', '' ) ) ); ?>"
                  placeholder="<?= $enc->attr( $this->translate( 'admin', 'Order Time' ) ); ?>"
            </td>
            
            <td class="actions">
               <input class="item-id" type="hidden" value="" disabled="disabled"
                  name="<?= $enc->attr( $this->formparam( array( 'stock', 'stock.id', '' ) ) ); ?>" />
               <div class="btn act-delete fa" tabindex="<?= $this->get( 'tabindex' ); ?>"
                  title="<?= $enc->attr( $this->translate( 'admin', 'Delete this entry') ); ?>">
               </div>
            </td>
         </tr>
      </tbody>
   </table>

   <?= $this->get( 'stockBody' ); ?>
</div>


-----------------------------------------------------------------------------------------------------
2. And my second Question is do i have to change the iface for order with my new function getOrderTime and setOrderTime and if yes where do i have to register the iface? I tryed it in the save function of my manager.

Path: /typo3conf/ext/mhaimeos/Resources/Private/Extensions/mhaimeos/lib/custom/src/MShop/Stock/Item/Myiface.php
Code: Select all<?php

/**
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
 * @copyright Metaways Infosystems GmbH, 2011
 * @copyright Aimeos (aimeos.org), 2015-2017
 * @package MShop
 * @subpackage Stock
 */


namespace Aimeos\MShop\Stock\Item;
//namespace Mhaimeos\MShop\Stock\Item;


/**
 * Extends Default stock item interface.
 *
 * @package MShop
 * @subpackage Stock
 */
interface Myiface extends Iface
{
   /**
    * Returns the ordertime of the stock item.
    *
    * @return string Order Time
    */
   public function getOrderTime();

   /**
    * Sets a new Ordertime of the stock item.
    *
    * @param string $ordertime
    * @return \Aimeos\MShop\Stock\Item\Iface Stock item for chaining method calls
    */
   public function setOrderTime( $ordertime );

}


----------------------------------------------------------------------------------------------------

3. Third Question, are my namespaces correct?

----------------------------------------------------------------------------------------------------

4. And my last question is how do i change the templates for the extadmin, is there anything documented?

I tryed to change the ItemUI.js and ListUI.js but got this error
ext-all.js:21 Uncaught TypeError: g is not a constructor
at constructor.setConfig (ext-all.js:21)
at new constructor (ext-all.js:21)
at S.initComponent (ext-all.js:21)
at S.Ext.Component [as constructor] (ext-all.js:21)
at S [as constructor] (ext-base.js:21)
at S [as constructor] (ext-base.js:21)
at S [as constructor] (ext-base.js:21)
at new S (ext-base.js:21)
at S.initComponent (index.php?M=web_AimeosTxAimeosAdmin&moduleToken=60ae9e4e8faae802e56a6d72d2902be966dd9fed&tx_aimeos_web_aimeostxaimeosadmin[action]=file&tx_aimeos_web_aimeostxaimeosadmin[controller]=Extadm:3347)
at S.initComponent (index.php?M=web_AimeosTxAimeosAdmin&moduleToken=60ae9e4e8faae802e56a6d72d2902be966dd9fed&tx_aimeos_web_aimeostxaimeosadmin[action]=file&tx_aimeos_web_aimeostxaimeosadmin[controller]=Extadm:9500)



Path: /typo3conf/ext/aimeos/Resources/Private/Extensions/ai-admin-extadm/admin/extjs/src/panel/product/stock/ItemUi.js
Code: Select all/*!
 * Copyright (c) Metaways Infosystems GmbH, 2011
 * LGPLv3, http://opensource.org/licenses/LGPL-3.0
 */

Ext.ns('MShop.panel.product.stock');

/**
 * Concrete ItemUi
 *
 * @extends Mshop.panel.AbstractListItemUi
 */
MShop.panel.product.stock.ItemUi = Ext.extend(MShop.panel.AbstractItemUi, {

    siteidProperty : 'stock.siteid',

    initComponent : function() {

        this.title = MShop.I18n.dt('admin', 'Product stock');

        MShop.panel.AbstractItemUi.prototype.setSiteCheck(this);

        this.items = [{
            xtype : 'tabpanel',
            activeTab : 0,
            border : false,
            itemId : 'MShop.panel.product.stock.ItemUi',
            plugins : ['ux.itemregistry'],
            items : [{
                xtype : 'panel',
                title : MShop.I18n.dt('admin', 'Basic'),
                border : false,
                layout : 'hbox',
                layoutConfig : {
                    align : 'stretch'
                },
                itemId : 'MShop.panel.product.stock.ItemUi.BasicPanel',

                plugins : ['ux.itemregistry'],
                defaults : {
                    bodyCssClass : this.readOnlyClass
                },
                items : [{
                    xtype : 'form',
                    title : MShop.I18n.dt('admin', 'Details'),
                    flex : 1,
                    ref : '../../mainForm',
                    autoScroll : true,
                    items : [{
                        xtype : 'fieldset',
                        style : 'padding-right: 25px;',
                        border : false,
                        labelAlign : 'top',
                        defaults : {
                            readOnly : this.fieldsReadOnly,
                            anchor : '100%'
                        },
                        items : [{
                            xtype : 'hidden',
                            name : 'stock.productcode'
                        }, {
                            xtype : 'displayfield',
                            fieldLabel : MShop.I18n.dt('admin', 'ID'),
                            name : 'stock.id'
                        }, {
                            xtype : 'combo',
                            fieldLabel : MShop.I18n.dt('admin', 'Type'),
                            name : 'stock.typeid',
                            mode : 'local',
                            store : MShop.GlobalStoreMgr.get('Stock_Type', this.domain),
                            displayField : 'stock.type.label',
                            valueField : 'stock.type.id',
                            forceSelection : true,
                            triggerAction : 'all',
                            typeAhead : true,
                            listeners : {
                                'render' : {
                                    fn : function() {
                                        var record, index = this.store.find('stock.type.code', 'default');
                                        if((record = this.store.getAt(index))) {
                                            this.setValue(record.id);
                                        }
                                    }
                                }
                            }
                        }, {
                            xtype : 'numberfield',
                            fieldLabel : MShop.I18n.dt('admin', 'Stock level'),
                            name : 'stock.stocklevel',
                            emptyText : MShop.I18n.dt('admin', 'Quantity or empty if unlimited (optional)')
                        }, {
                            xtype : 'datefield',
                            fieldLabel : MShop.I18n.dt('admin', 'Back in stock'),
                            name : 'stock.dateback',
                            format : 'Y-m-d H:i:s',
                            emptyText : MShop.I18n.dt('admin', 'YYYY-MM-DD hh:mm:ss (optional)')
                        }, {
                            xtype : 'ordertime',
                            fieldLabel : MShop.I18n.dt('admin', 'Stock Order Time'),
                            name : 'stock.ordertime',
                            emptyText : MShop.I18n.dt('admin', 'Stock Order Time')
                        }, {
                            xtype : 'displayfield',
                            fieldLabel : MShop.I18n.dt('admin', 'Created'),
                            name : 'stock.ctime'
                        }, {
                            xtype : 'displayfield',
                            fieldLabel : MShop.I18n.dt('admin', 'Last modified'),
                            name : 'stock.mtime'
                        }, {
                            xtype : 'displayfield',
                            fieldLabel : MShop.I18n.dt('admin', 'Editor'),
                            name : 'stock.editor'
                        }]
                    }]
                }]
            }]
        }];

        MShop.panel.product.stock.ItemUi.superclass.initComponent.call(this);
    },

    onSaveItem : function() {
        // validate data
        if(!this.mainForm.getForm().isValid() && this.fireEvent('validate', this) !== false) {
            Ext.Msg.alert(MShop.I18n.dt('admin', 'Invalid data'), MShop.I18n.dt('admin',
                'Please recheck your data'));
            return;
        }

        this.saveMask.show();
        this.isSaveing = true;

        // force record to be saved!
        this.record.dirty = true;

        if(this.fireEvent('beforesave', this, this.record) === false) {
            this.isSaveing = false;
            this.saveMask.hide();
        }

        this.mainForm.getForm().updateRecord(this.record);
        this.record.data['stock.productcode'] = this.listUI.itemUi.record.data['product.code'];

        if(this.action == 'add' || this.action == 'copy') {
            this.store.add(this.record);
        }

        // store async action is triggered. {@see onStoreWrite/onStoreException}
        if(!this.store.autoSave) {
            this.onAfterSave();
        }
    },

    onStoreException : function() {
        this.store.remove(this.record);
        MShop.panel.product.stock.ItemUi.superclass.onStoreException.apply(this, arguments);
    }
});

Ext.reg('MShop.panel.product.stock.itemui', MShop.panel.product.stock.ItemUi);


Path: /typo3conf/ext/aimeos/Resources/Private/Extensions/ai-admin-extadm/admin/extjs/src/panel/product/stock/ListUi.js
Code: Select all/*!
 * LGPLv3, http://opensource.org/licenses/LGPL-3.0
 * Copyright (c) Metaways Infosystems GmbH, 2011
 * Copyright (c) Aimeos (aimeos.org), 2015-2017
 */

Ext.ns('MShop.panel.product.stock');

MShop.panel.product.stock.ListUi = Ext.extend(MShop.panel.AbstractListUi, {

    recordName : 'Stock',
    idProperty : 'stock.id',
    siteidProperty : 'stock.siteid',
    itemUiXType : 'MShop.panel.product.stock.itemui',

    autoExpandColumn : 'product-stock-stocktype',

    filterConfig : {
        filters : [{
            dataIndex : 'stock.type.label',
            operator : '=~',
            value : ''
        }]
    },

    initComponent : function() {
        this.title = MShop.I18n.dt('admin', 'Stock');

        MShop.panel.AbstractListUi.prototype.initActions.call(this);
        MShop.panel.AbstractListUi.prototype.initToolbar.call(this);

        MShop.panel.product.stock.ListUi.superclass.initComponent.call(this);
    },

    afterRender : function() {
        this.itemUi = this.findParentBy(function(c) {
            return c.isXType(MShop.panel.AbstractItemUi, false);
        });

        MShop.panel.product.stock.ListUi.superclass.afterRender.apply(this, arguments);
    },

    onBeforeLoad : function(store, options) {
        this.setSiteParam(store);

        if(this.domain) {
            this.setDomainFilter(store, options);
        }

        options.params = options.params || {};
        options.params.condition = {
            '&&' : [{
                '==' : {
                    'stock.productcode' : this.itemUi.record ? this.itemUi.record.data['product.code'] : ''
                }
            }]
        };

    },

    getColumns : function() {
        this.typeStore = MShop.GlobalStoreMgr.get('Stock_Type');

        return [
            {
                xtype : 'gridcolumn',
                dataIndex : 'stock.id',
                header : MShop.I18n.dt('admin', 'ID'),
                sortable : true,
                width : 50,
                hidden : true
            },
            {
                xtype : 'gridcolumn',
                dataIndex : 'stock.productcode',
                header : MShop.I18n.dt('admin', 'Product code'),
                width : 50,
                hidden : true
            },
            {
                xtype : 'gridcolumn',
                dataIndex : 'stock.typeid',
                header : MShop.I18n.dt('admin', 'Type'),
                align : 'center',
                id : 'product-stock-stocktype',
                renderer : this.typeColumnRenderer.createDelegate(this, [
                    this.typeStore,
                    "stock.type.label"], true)
            },
            {
                xtype : 'gridcolumn',
                dataIndex : 'stock.stocklevel',
                header : MShop.I18n.dt('admin', 'Stock level'),
                sortable : true,
                align : 'center',
                width : 80
            },
            {
                xtype : 'datecolumn',
                dataIndex : 'stock.dateback',
                header : MShop.I18n.dt('admin', 'Date back'),
                format : 'Y-m-d H:i:s',
                sortable : true,
                width : 130
            },
            {
                xtype : 'ordertime',
                dataIndex : 'stock.ordertime',
                header : MShop.I18n.dt('admin', 'Order Time'),
                sortable : true,
                width : 130
            },
            {
                xtype : 'datecolumn',
                dataIndex : 'stock.ctime',
                header : MShop.I18n.dt('admin', 'Created'),
                format : 'Y-m-d H:i:s',
                sortable : true,
                width : 130,
                editable : false,
                hidden : true
            },
            {
                xtype : 'datecolumn',
                dataIndex : 'stock.mtime',
                header : MShop.I18n.dt('admin', 'Last modified'),
                format : 'Y-m-d H:i:s',
                sortable : true,
                width : 130,
                editable : false,
                hidden : true
            },
            {
                xtype : 'gridcolumn',
                dataIndex : 'stock.editor',
                header : MShop.I18n.dt('admin', 'Editor'),
                sortable : true,
                width : 130,
                editable : false,
                hidden : true
            }];
    }
});

Ext.reg('MShop.panel.product.stock.listui', MShop.panel.product.stock.ListUi);

Ext.ux.ItemRegistry.registerItem('MShop.panel.product.ItemUi', 'MShop.panel.product.stock.listui', MShop.panel.product.stock.ListUi, 2);




Many Thanks and sorry for the long post ;)
Typo3 7.6.x / Aimeos 17.10.0

-----

Have you fixed your problem? I am facing an issue and have been struggling to fix it. Can you help me? I want to add a new field in Product form on Admin side.

Thanks!
#5936 by mohal_04
03 Apr 2018, 09:31
tenkraD wrote:was in hollidays and not here sorry. ya, its working now. where do u have problems


I have rewritten my code 3 times and now the situation is this... I am able to display value in my extra field but it is not saving in the DB and there are no errors :(. Please, look at my code and guide me!

./ext/myextension/lib/custom/setup/default/schema/product.php
Code: Select allreturn array(
   'table' => array(
      'mshop_product' => function ( \Doctrine\DBAL\Schema\Schema $schema ) {

         // $table = $schema->createTable( 'mshop_price' );
         $table = $schema->getTable( 'mshop_product' );

         $table->addColumn( 'oldcode', 'string', array( 'length' => 32 ) );

         $table->addIndex( array( 'siteid', 'oldcode' ), 'idx_msproty_sid_oldcode' );

         return $schema;
      }
   )
);


./ext/myextension/lib/custom/src/MShop/Product/Item/Myproject.php
Code: Select allnamespace Aimeos\MShop\Product\Item;
 
class Myproject extends Standard
{
    private $myvalues;
 
    public function __construct(array $values)
    {
        parent::__construct($values);
        $this->myvalues = $values;
    }
 
    public function getOldCode()
    {
        if( isset( $this->myvalues['product.oldcode'] ) ) {
            return (string) $this->myvalues['product.oldcode'];
        }
        return '';
    }
 
    public function setOldCode( $oldcode )
    {
        if ((string) $oldcode !== $this->getOldCode()) {
            // exit("<br />616");
            $this->myvalues['product.oldcode'] = (string) $oldcode;
            $this->setModified();
        }
        return $this;
    }
 
    public function fromArray(array $list)
    {
        $unknown = [];
        $list = parent::fromArray($list);

        foreach ($list as $key => $value) {
            switch ($key) {
                case 'product.oldcode':
                    $this->setOldCode($value);
                    break;
                default:
                    $unknown[$key] = $value;
            }
        }
        // exit("<br />617");
        return $unknown;
    }
 
    public function toArray( $private = false )
    {
        $list = parent::toArray( $private );
 
        if( $private === true ) {
            $list['product.oldcode'] = $this->getOldCode();
        }
 
        return $list;
    }
}


./ext/myextension/lib/custom/src/MShop/Product/Manager/Myproject.php
Code: Select allnamespace Aimeos\MShop\Product\Manager;
 
class Myproject extends Standard
{
    private $searchConfig = array(
        'product.oldcode'=> array(
            'code'=>'product.oldcode',
            'internalcode'=>'mpro."oldcode"',
            'label'=>'Product oldcode',
            'type'=> 'string', // integer, float, etc.
            'internaltype'=> \Aimeos\MW\DB\Statement\Base::PARAM_STR, // _INT, _FLOAT, etc.
        ),
    );
 
    public function saveItem(\Aimeos\MShop\Common\Item\Iface $item, $fetch = true)
    {
        $iface = '\\Aimeos\\MShop\\Product\\Item\\Iface';
        if (! ($item instanceof $iface)) {
            throw new \Aimeos\MShop\Product\Exception(sprintf('Object is not of required type "%1$s"', $iface));
        }

        if (!$item->isModified()) {
            return $item;
        }

        $context = $this->getContext();

        $dbm = $context->getDatabaseManager();
        $dbname = $this->getResourceName();
        $conn = $dbm->acquire( $dbname );

        try {
            $id = $item->getId();
            $date = date('Y-m-d H:i:s');
            if ($id === null) {
                $path = 'mshop/product/manager/standard/insert';
            } else {
                $path = 'mshop/product/manager/standard/update';
            }
            $stmt = $this->getCachedStatement($conn, $path);

            $stmt->bind(1, $item->getTypeId(), \Aimeos\MW\DB\Statement\Base::PARAM_INT);
            $stmt->bind(2, $context->getLocale()->getSiteId(), \Aimeos\MW\DB\Statement\Base::PARAM_INT);
            $stmt->bind(3, $item->getCode());
            $stmt->bind(4, $item->getLabel());
            $stmt->bind(5, json_encode($item->getConfig()));
            $stmt->bind(6, $item->getDateStart());
            $stmt->bind(7, $item->getDateEnd());
            $stmt->bind(8, $item->getStatus(), \Aimeos\MW\DB\Statement\Base::PARAM_INT);
            $stmt->bind(9, $date); // mtime
            $stmt->bind(10, $context->getEditor());
            $stmt->bind(11, $item->getTarget());
            $stmt->bind(12, $item->getOldCode());

            if( $id !== null ) {
                $stmt->bind( 13, $id, \Aimeos\MW\DB\Statement\Base::PARAM_INT );
                $item->setId( $id ); //so item is no longer modified
            } else {
                $stmt->bind( 13, $date ); // ctime
            }

            $stmt->execute()->finish();

            if( $id === null && $fetch === true )
            {
                $path = 'mshop/product/manager/standard/newid';
                $item->setId( $this->newId( $conn, $path ) );
            }

            $dbm->release( $conn, $dbname );

        } catch (\Exception $e) {
            $dbm->release( $conn, $dbname );
            throw $e;
        }
        return $item;
    }
 
    public function getSearchAttributes( $withsub = true )
    {
        $list = parent::getSearchAttributes( $withsub );
        foreach( $this->searchConfig as $key => $fields ) {
            $list[$key] = new \Aimeos\MW\Criteria\Attribute\Standard( $fields );
        }
        return $list;
    }
 
    protected function createItemBase(array $values = [], array $listitems = [],
        array $refItems = [], array $propertyItems = [])
    {
        return new \Aimeos\MShop\Product\Item\Myproject($values, $listitems, $refItems, $propertyItems);
    }
}


and

./ext/myextension/lib/custom/config/mshop.php
Code: Select allreturn [
    'product' => [
        'manager' => [
            'name' => 'Myproject',
            'standard' => [
            'insert' => array(
               'ansi' => '
                  INSERT INTO "mshop_product" (
                     "typeid", "siteid", "code", "label", "config", "start", "end",
                     "status", "mtime", "editor", "target", "ctime", "oldcode"
                  ) VALUES (
                     ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
                  )
               '
            ),
            'update' => array(
               'ansi' => '
                  UPDATE "mshop_product"
                  SET "typeid" = ?, "code" = ?, "label" = ?, "status" = ?,
                     "start" = ?, "end" = ?, "config" = ?, "mtime" = ?, "editor" = ?, "target" = ?, "oldcode" = ?
                  WHERE "siteid" = ? AND "id" = ?
               '
            ),
            'search' => array(
               'ansi' => '
                  SELECT mpro."id" AS "product.id", mpro."siteid" AS "product.siteid",
                     mpro."typeid" AS "product.typeid", mpro."code" AS "product.code",
                     mpro."label" AS "product.label", mpro."config" AS "product.config",
                     mpro."start" AS "product.datestart", mpro."end" AS "product.dateend",
                     mpro."status" AS "product.status", mpro."ctime" AS "product.ctime",
                     mpro."mtime" AS "product.mtime", mpro."editor" AS "product.editor",
                     mpro."target" AS "product.target",
                     mpro."oldcode" AS "product.oldcode"
                  FROM "mshop_product" AS mpro
                  :joins
                  WHERE :cond
                  GROUP BY mpro."id", mpro."siteid", mpro."typeid", mpro."code",
                     mpro."label", mpro."config", mpro."start", mpro."end",
                     mpro."status", mpro."ctime", mpro."mtime", mpro."editor",
                     mpro."target", mpro."oldcode"
                     /*-columns*/ , :columns /*columns-*/
                  /*-orderby*/ ORDER BY :order /*orderby-*/
                  LIMIT :size OFFSET :start
               '
            ),
            'count' => array(
               'ansi' => '
                  SELECT COUNT(*) AS "count"
                  FROM (
                     SELECT DISTINCT mpro."id"
                     FROM "mshop_product" AS mpro
                     :joins
                     WHERE :cond
                     LIMIT 10000 OFFSET 0
                  ) AS list
               '
            ),
            'newid' => array(
               'db2' => 'SELECT IDENTITY_VAL_LOCAL()',
               'mysql' => 'SELECT LAST_INSERT_ID()',
               'oracle' => 'SELECT mshop_product_seq.CURRVAL FROM DUAL',
               'pgsql' => 'SELECT lastval()',
               'sqlite' => 'SELECT last_insert_rowid()',
               'sqlsrv' => 'SELECT SCOPE_IDENTITY()',
               'sqlanywhere' => 'SELECT @@IDENTITY',
            ),
            ],
        ],
    ],
];


Everything seems fine but still I am unable to save value for my custom field, i.e. oldcode.

Thanks!
#5938 by tenkraD
03 Apr 2018, 11:42
Looks good but i see 2 differences,

1. I created my extension with the extensionbuilder https://aimeos.org/developer/extensions/ and got another directory structur as you. In the extensionbuilder i have select "Typo3 2018.x Extension" because im using the 2018 Aimeos Extension in Typo3. Perhaps this is making u some problems, dont really know.

2. I extended the standard Iface that my new functions from Item.php in Manager.php can be used.
Create src/MShop/Product/Item/Myiface.php

Code: Select all<?php

interface Myiface extends Iface
{
   public function getOldCode();

   public function setOldCode( $oldcode );
}


2b. In Manager in the saveItem function use ure new Iface "Myiface"
src/MShop/Product/Manager/Myproject.php
Code: Select allpublic function saveItem(\Aimeos\MShop\Common\Item\Iface $item, $fetch = true)
    {
        $iface = '\\myextension\\lib\\custom\\src\\MShop\\Product\\Item\\Myiface';
        .......
        .......
    }
Last edited by tenkraD on 03 Apr 2018, 12:03, edited 2 times in total.
#5944 by mohal_04
04 Apr 2018, 06:25
tenkraD wrote:Nice


Yes, it was nice for moments until I see that it has somehow stopped saving some other data for product. For example, it is not saving Product images. I wonder what is the connection? I guess I have to extend those classes also, which is too dumb.

Can you guide me with this?

Thanks!
#5949 by tenkraD
04 Apr 2018, 15:57
Im a bit in hurry and have not much time to look at ure code. But the master jedi alias "Administrator" surely knows some help.

I saw just one thing: In ure manager in createItemBase change $listitems to $listItems.

Cya and good luck