<?php

declare(strict_types=1);

/*
 * This file is part of the Sonata Project package.
 *
 * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sonata\DoctrineORMAdminBundle\Datagrid;

use Doctrine\ORM\Query\AST\Functions\IdentityFunction;
use Doctrine\ORM\Query\AST\OrderByClause;
use Doctrine\ORM\Query\AST\PathExpression;
use Doctrine\ORM\Query\AST\SelectExpression;
use Doctrine\ORM\Query\AST\SelectStatement;
use Doctrine\ORM\Query\TreeWalkerAdapter;

/**
 * Finds all PathExpressions in an AST's OrderByClause, and ensures that
 * the referenced fields are present in the SelectClause of the passed AST.
 *
 * Inspired by Doctrine\ORM\Tools\Pagination classes.
 *
 * @author Dariusz Markowicz <dmarkowicz77@gmail.com>
 */
final class OrderByToSelectWalker extends TreeWalkerAdapter
{
    public function walkSelectStatement(SelectStatement $AST)
    {
        if (!$AST->orderByClause instanceof OrderByClause) {
            return;
        }

        // Get a map of referenced identifiers to field names.
        $selects = [];
        foreach ($AST->orderByClause->orderByItems as $item) {
            if (!$item->expression instanceof PathExpression) {
                continue;
            }

            $pathExpression = $item->expression;
            $idVar = $pathExpression->identificationVariable;
            $field = $pathExpression->field;
            if (!isset($selects[$idVar])) {
                $selects[$idVar] = [];
            }
            $selects[$idVar][$field] = $pathExpression;
        }

        // Loop the select clause of the AST and exclude items from $selects
        // that are already being selected in the query.
        foreach ($AST->selectClause->selectExpressions as $selectExpression) {
            if ($selectExpression instanceof SelectExpression) {
                $idVar = $selectExpression->expression;
                if ($idVar instanceof IdentityFunction) {
                    $idVar = $idVar->pathExpression->identificationVariable;
                }
                if (!\is_string($idVar)) {
                    continue;
                }
                $field = $selectExpression->fieldIdentificationVariable;
                if (null === $field) {
                    // No need to add this select, as we're already fetching the whole object.
                    unset($selects[$idVar]);
                } else {
                    unset($selects[$idVar][$field]);
                }
            }
        }

        // Add select items which were not excluded to the AST's select clause.
        foreach ($selects as $idVar => $fields) {
            foreach ($fields as $field => $expression) {
                $AST->selectClause->selectExpressions[] = new SelectExpression(
                    $this->createSelectExpressionItem($expression), null, true
                );
            }
        }
    }

    /**
     * Retrieve either an IdentityFunction (IDENTITY(u.assoc)) or a state field (u.name).
     *
     * @return IdentityFunction|PathExpression
     */
    private function createSelectExpressionItem(PathExpression $pathExpression)
    {
        if (PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION === $pathExpression->type) {
            $identity = new IdentityFunction('identity');

            $identity->pathExpression = clone $pathExpression;

            return $identity;
        }

        return clone $pathExpression;
    }
}


<?php

declare(strict_types=1);

/*
 * This file is part of the Sonata Project package.
 *
 * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sonata\DoctrineORMAdminBundle\Filter;

use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
use Sonata\AdminBundle\Form\Type\Filter\NumberType;

class NumberFilter extends Filter
{
    public function filter(ProxyQueryInterface $queryBuilder, $alias, $field, $data)
    {
        if (!$data || !\is_array($data) || !\array_key_exists('value', $data) || !is_numeric($data['value'])) {
            return;
        }

        $type = $data['type'] ?? false;

        $operator = $this->getOperator($type);

        if (!$operator) {
            $operator = '=';
        }

        // c.name > '1' => c.name OPERATOR :FIELDNAME
        $parameterName = $this->getNewParameterName($queryBuilder);
        $this->applyWhere($queryBuilder, sprintf('%s.%s %s :%s', $alias, $field, $operator, $parameterName));
        $queryBuilder->setParameter($parameterName, $data['value']);
    }

    public function getDefaultOptions()
    {
        return [];
    }

    public function getRenderSettings()
    {
        return [NumberType::class, [
            'field_type' => $this->getFieldType(),
            'field_options' => $this->getFieldOptions(),
            'label' => $this->getLabel(),
        ]];
    }

    /**
     * @param string $type
     *
     * @return bool
     */
    private function getOperator($type)
    {
        $choices = [
            NumberType::TYPE_EQUAL => '=',
            NumberType::TYPE_GREATER_EQUAL => '>=',
            NumberType::TYPE_GREATER_THAN => '>',
            NumberType::TYPE_LESS_EQUAL => '<=',
            NumberType::TYPE_LESS_THAN => '<',
        ];

        return $choices[$type] ?? false;
    }
}


<?php

declare(strict_types=1);

/*
 * This file is part of the Sonata Project package.
 *
 * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sonata\DoctrineORMAdminBundle\Builder;

use Doctrine\ORM\Mapping\ClassMetadata;
use Sonata\AdminBundle\Admin\AdminInterface;
use Sonata\AdminBundle\Admin\FieldDescriptionInterface;
use Sonata\AdminBundle\Builder\DatagridBuilderInterface;
use Sonata\AdminBundle\Datagrid\Datagrid;
use Sonata\AdminBundle\Datagrid\DatagridInterface;
use Sonata\AdminBundle\Datagrid\PagerInterface;
use Sonata\AdminBundle\Datagrid\SimplePager;
use Sonata\AdminBundle\Filter\FilterFactoryInterface;
use Sonata\AdminBundle\Guesser\TypeGuesserInterface;
use Sonata\DoctrineORMAdminBundle\Datagrid\Pager;
use Sonata\DoctrineORMAdminBundle\Filter\ModelAutocompleteFilter;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormFactoryInterface;

class DatagridBuilder implements DatagridBuilderInterface
{
    /**
     * @var FilterFactoryInterface
     */
    protected $filterFactory;

    /**
     * @var FormFactoryInterface
     */
    protected $formFactory;

    /**
     * @var TypeGuesserInterface
     */
    protected $guesser;

    /**
     * @var bool
     */
    protected $csrfTokenEnabled;

    /**
     * @param bool $csrfTokenEnabled
     */
    public function __construct(
        FormFactoryInterface $formFactory,
        FilterFactoryInterface $filterFactory,
        TypeGuesserInterface $guesser,
        $csrfTokenEnabled = true
    ) {
        $this->formFactory = $formFactory;
        $this->filterFactory = $filterFactory;
        $this->guesser = $guesser;
        $this->csrfTokenEnabled = $csrfTokenEnabled;
    }

    public function fixFieldDescription(AdminInterface $admin, FieldDescriptionInterface $fieldDescription)
    {
        // set default values
        $fieldDescription->setAdmin($admin);

        if ($admin->getModelManager()->hasMetadata($admin->getClass())) {
            list($metadata, $lastPropertyName, $parentAssociationMappings) = $admin->getModelManager()
                ->getParentMetadataForProperty($admin->getClass(), $fieldDescription->getName());

            // set the default field mapping
            if (isset($metadata->fieldMappings[$lastPropertyName])) {
                $fieldDescription->setOption(
                    'field_mapping',
                    $fieldDescription->getOption(
                        'field_mapping',
                        $fieldMapping = $metadata->fieldMappings[$lastPropertyName]
                    )
                );

                if ('string' === $fieldMapping['type']) {
                    $fieldDescription->setOption('global_search', $fieldDescription->getOption('global_search', true)); // always search on string field only
                }

                if (!empty($embeddedClasses = $metadata->embeddedClasses)
                    && isset($fieldMapping['declaredField'])
                    && \array_key_exists($fieldMapping['declaredField'], $embeddedClasses)
                ) {
                    $fieldDescription->setOption(
                        'field_name',
                        $fieldMapping['fieldName']
                    );
                }
            }

            // set the default association mapping
            if (isset($metadata->associationMappings[$lastPropertyName])) {
                $fieldDescription->setOption(
                    'association_mapping',
                    $fieldDescription->getOption(
                        'association_mapping',
                        $metadata->associationMappings[$lastPropertyName]
                    )
                );
            }

            $fieldDescription->setOption(
                'parent_association_mappings',
                $fieldDescription->getOption('parent_association_mappings', $parentAssociationMappings)
            );
        }

        $fieldDescription->setOption('code', $fieldDescription->getOption('code', $fieldDescription->getName()));
        $fieldDescription->setOption('name', $fieldDescription->getOption('name', $fieldDescription->getName()));

        if (\in_array($fieldDescription->getMappingType(), [
            ClassMetadata::ONE_TO_MANY,
            ClassMetadata::MANY_TO_MANY,
            ClassMetadata::MANY_TO_ONE,
            ClassMetadata::ONE_TO_ONE,
        ], true)) {
            $admin->attachAdminClass($fieldDescription);
        }
    }

    public function addFilter(DatagridInterface $datagrid, $type, FieldDescriptionInterface $fieldDescription, AdminInterface $admin)
    {
        if (null === $type) {
            $guessType = $this->guesser->guessType($admin->getClass(), $fieldDescription->getName(), $admin->getModelManager());

            $type = $guessType->getType();

            $fieldDescription->setType($type);

            $options = $guessType->getOptions();

            foreach ($options as $name => $value) {
                if (\is_array($value)) {
                    $fieldDescription->setOption($name, array_merge($value, $fieldDescription->getOption($name, [])));
                } else {
                    $fieldDescription->setOption($name, $fieldDescription->getOption($name, $value));
                }
            }
        } else {
            $fieldDescription->setType($type);
        }

        $this->fixFieldDescription($admin, $fieldDescription);
        $admin->addFilterFieldDescription($fieldDescription->getName(), $fieldDescription);

        $fieldDescription->mergeOption('field_options', ['required' => false]);

        // NEXT_MAJOR: Check only against FQCNs when dropping support for Symfony 2.8
        if ('doctrine_orm_model_autocomplete' === $type || ModelAutocompleteFilter::class === $type) {
            $fieldDescription->mergeOption('field_options', [
                'class' => $fieldDescription->getTargetEntity(),
                'model_manager' => $fieldDescription->getAdmin()->getModelManager(),
                'admin_code' => $admin->getCode(),
                'context' => 'filter',
            ]);
        }

        $filter = $this->filterFactory->create($fieldDescription->getName(), $type, $fieldDescription->getOptions());

        if (false !== $filter->getLabel() && !$filter->getLabel()) {
            $filter->setLabel($admin->getLabelTranslatorStrategy()->getLabel($fieldDescription->getName(), 'filter', 'label'));
        }

        $datagrid->addFilter($filter);
    }

    public function getBaseDatagrid(AdminInterface $admin, array $values = [])
    {
        $pager = $this->getPager($admin->getPagerType());

        $pager->setCountColumn($admin->getModelManager()->getIdentifierFieldNames($admin->getClass()));

        $defaultOptions = [];
        if ($this->csrfTokenEnabled) {
            $defaultOptions['csrf_protection'] = false;
        }

        $formBuilder = $this->formFactory->createNamedBuilder('filter', FormType::class, [], $defaultOptions);

        return new Datagrid($admin->createQuery(), $admin->getList(), $pager, $formBuilder, $values);
    }

    /**
     * Get pager by pagerType.
     *
     * @param string $pagerType
     *
     * @throws \RuntimeException If invalid pager type is set
     *
     * @return PagerInterface
     */
    protected function getPager($pagerType)
    {
        switch ($pagerType) {
            case Pager::TYPE_DEFAULT:
                return new Pager();

            case Pager::TYPE_SIMPLE:
                return new SimplePager();

            default:
                throw new \RuntimeException(sprintf('Unknown pager type "%s".', $pagerType));
        }
    }
}


<?php

declare(strict_types=1);

/*
 * This file is part of the Sonata Project package.
 *
 * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sonata\DoctrineORMAdminBundle\DependencyInjection;

use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

/**
 * This class contains the configuration information for the bundle.
 *
 * This information is solely responsible for how the different configuration
 * sections are normalized, and merged.
 *
 * @author Michael Williams <mtotheikle@gmail.com>
 */
class Configuration implements ConfigurationInterface
{
    /**
     * Generates the configuration tree.
     *
     * @return TreeBuilder
     */
    public function getConfigTreeBuilder()
    {
        $treeBuilder = new TreeBuilder('sonata_doctrine_orm_admin');

        // Keep compatibility with symfony/config < 4.2
        if (!method_exists($treeBuilder, 'getRootNode')) {
            $rootNode = $treeBuilder->root('sonata_doctrine_orm_admin');
        } else {
            $rootNode = $treeBuilder->getRootNode();
        }

        $rootNode
            ->children()
                ->scalarNode('entity_manager')->defaultNull()->end()
                ->arrayNode('audit')
                    ->addDefaultsIfNotSet()
                    ->children()
                        ->booleanNode('force')->defaultTrue()->end()
                    ->end()
                ->end()
                ->arrayNode('templates')
                    ->addDefaultsIfNotSet()
                    ->children()
                        ->arrayNode('form')
                            ->prototype('scalar')->end()
                            ->defaultValue(['@SonataDoctrineORMAdmin/Form/form_admin_fields.html.twig'])
                        ->end()
                        ->arrayNode('filter')
                            ->prototype('scalar')->end()
                            ->defaultValue(['@SonataDoctrineORMAdmin/Form/filter_admin_fields.html.twig'])
                        ->end()
                        ->arrayNode('types')
                            ->children()
                                ->arrayNode('list')
                                    ->useAttributeAsKey('name')
                                    ->prototype('scalar')->end()
                                ->end()
                                ->arrayNode('show')
                                    ->useAttributeAsKey('name')
                                    ->prototype('scalar')->end()
                                ->end()
                            ->end()
                        ->end()
                    ->end()
                ->end()
            ->end()
        ;

        return $treeBuilder;
    }
}


<?php

declare(strict_types=1);

/*
 * This file is part of the Sonata Project package.
 *
 * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sonata\DoctrineORMAdminBundle\Filter;

use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\QueryBuilder;
use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
use Sonata\AdminBundle\Form\Type\Filter\DefaultType;
use Sonata\AdminBundle\Form\Type\ModelAutocompleteType;
use Sonata\Form\Type\EqualType;

class ModelAutocompleteFilter extends Filter
{
    public function filter(ProxyQueryInterface $queryBuilder, $alias, $field, $data)
    {
        if (!$data || !\is_array($data) || !\array_key_exists('value', $data)) {
            return;
        }

        if ($data['value'] instanceof Collection) {
            $data['value'] = $data['value']->toArray();
        }

        if (\is_array($data['value'])) {
            $this->handleMultiple($queryBuilder, $alias, $data);
        } else {
            $this->handleModel($queryBuilder, $alias, $data);
        }
    }

    public function getDefaultOptions()
    {
        return [
            'field_name' => false,
            'field_type' => ModelAutocompleteType::class,
            'field_options' => [],
            'operator_type' => EqualType::class,
            'operator_options' => [],
        ];
    }

    public function getRenderSettings()
    {
        return [DefaultType::class, [
            'field_type' => $this->getFieldType(),
            'field_options' => $this->getFieldOptions(),
            'operator_type' => $this->getOption('operator_type'),
            'operator_options' => $this->getOption('operator_options'),
            'label' => $this->getLabel(),
        ]];
    }

    /**
     * For the record, the $alias value is provided by the association method (and the entity join method)
     *  so the field value is not used here.
     *
     * @param ProxyQueryInterface|QueryBuilder $queryBuilder
     * @param string                           $alias
     * @param mixed                            $data
     *
     * @return mixed
     */
    protected function handleMultiple(ProxyQueryInterface $queryBuilder, $alias, $data)
    {
        if (0 === \count($data['value'])) {
            return;
        }

        $parameterName = $this->getNewParameterName($queryBuilder);

        if (isset($data['type']) && EqualType::TYPE_IS_NOT_EQUAL === $data['type']) {
            $this->applyWhere($queryBuilder, $queryBuilder->expr()->notIn($alias, ':'.$parameterName));
        } else {
            $this->applyWhere($queryBuilder, $queryBuilder->expr()->in($alias, ':'.$parameterName));
        }

        $queryBuilder->setParameter($parameterName, $data['value']);
    }

    /**
     * @param ProxyQueryInterface|QueryBuilder $queryBuilder
     * @param string                           $alias
     * @param mixed                            $data
     *
     * @return mixed
     */
    protected function handleModel(ProxyQueryInterface $queryBuilder, $alias, $data)
    {
        if (empty($data['value'])) {
            return;
        }

        $parameterName = $this->getNewParameterName($queryBuilder);

        if (isset($data['type']) && EqualType::TYPE_IS_NOT_EQUAL === $data['type']) {
            $this->applyWhere($queryBuilder, sprintf('%s != :%s', $alias, $parameterName));
        } else {
            $this->applyWhere($queryBuilder, sprintf('%s = :%s', $alias, $parameterName));
        }

        $queryBuilder->setParameter($parameterName, $data['value']);
    }

    protected function association(ProxyQueryInterface $queryBuilder, $data)
    {
        $associationMappings = $this->getParentAssociationMappings();
        $associationMappings[] = $this->getAssociationMapping();
        $alias = $queryBuilder->entityJoin($associationMappings);

        return [$alias, false];
    }
}


<?php

declare(strict_types=1);

/*
 * This file is part of the Sonata Project package.
 *
 * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sonata\DoctrineORMAdminBundle\Guesser;

use Doctrine\ORM\Mapping\MappingException;
use Sonata\AdminBundle\Guesser\TypeGuesserInterface;
use Sonata\AdminBundle\Model\ModelManagerInterface;

abstract class AbstractTypeGuesser implements TypeGuesserInterface
{
    /**
     * @param string $baseClass
     * @param string $propertyFullName
     *
     * @return array|null
     */
    protected function getParentMetadataForProperty($baseClass, $propertyFullName, ModelManagerInterface $modelManager)
    {
        try {
            return $modelManager->getParentMetadataForProperty($baseClass, $propertyFullName);
        } catch (MappingException $e) {
            // no metadata not found.
            return;
        }
    }
}


<?php

declare(strict_types=1);

/*
 * This file is part of the Sonata Project package.
 *
 * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sonata\DoctrineORMAdminBundle\Filter;

use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping\ClassMetadata;
use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
use Sonata\AdminBundle\Form\Type\Filter\DefaultType;
use Sonata\Form\Type\EqualType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;

class ModelFilter extends Filter
{
    public function filter(ProxyQueryInterface $queryBuilder, $alias, $field, $data)
    {
        if (!$data || !\is_array($data) || !\array_key_exists('value', $data) || empty($data['value'])) {
            return;
        }

        if ($data['value'] instanceof Collection) {
            $data['value'] = $data['value']->toArray();
        }

        if (!\is_array($data['value'])) {
            $data['value'] = [$data['value']];
        }

        $this->handleMultiple($queryBuilder, $alias, $data);
    }

    public function getDefaultOptions()
    {
        return [
            'mapping_type' => false,
            'field_name' => false,
            'field_type' => EntityType::class,
            'field_options' => [],
            'operator_type' => EqualType::class,
            'operator_options' => [],
        ];
    }

    public function getRenderSettings()
    {
        return [DefaultType::class, [
            'field_type' => $this->getFieldType(),
            'field_options' => $this->getFieldOptions(),
            'operator_type' => $this->getOption('operator_type'),
            'operator_options' => $this->getOption('operator_options'),
            'label' => $this->getLabel(),
        ]];
    }

    /**
     * For the record, the $alias value is provided by the association method (and the entity join method)
     *  so the field value is not used here.
     *
     * @param string $alias
     * @param mixed  $data
     *
     * @return mixed
     */
    protected function handleMultiple(ProxyQueryInterface $queryBuilder, $alias, $data)
    {
        if (0 === \count($data['value'])) {
            return;
        }

        $parameterName = $this->getNewParameterName($queryBuilder);

        if (isset($data['type']) && EqualType::TYPE_IS_NOT_EQUAL === $data['type']) {
            $or = $queryBuilder->expr()->orX();

            $or->add($queryBuilder->expr()->notIn($alias, ':'.$parameterName));

            if (ClassMetadata::MANY_TO_MANY === $this->getOption('mapping_type')) {
                $or->add(
                    sprintf('%s.%s IS EMPTY', $this->getParentAlias($queryBuilder, $alias), $this->getFieldName())
                );
            } else {
                $or->add($queryBuilder->expr()->isNull(
                    sprintf('IDENTITY(%s.%s)', $this->getParentAlias($queryBuilder, $alias), $this->getFieldName())
                ));
            }

            $this->applyWhere($queryBuilder, $or);
        } else {
            $this->applyWhere($queryBuilder, $queryBuilder->expr()->in($alias, ':'.$parameterName));
        }

        $queryBuilder->setParameter($parameterName, $data['value']);
    }

    protected function association(ProxyQueryInterface $queryBuilder, $data)
    {
        $types = [
            ClassMetadata::ONE_TO_ONE,
            ClassMetadata::ONE_TO_MANY,
            ClassMetadata::MANY_TO_MANY,
            ClassMetadata::MANY_TO_ONE,
        ];

        if (!\in_array($this->getOption('mapping_type'), $types, true)) {
            throw new \RuntimeException('Invalid mapping type');
        }

        $associationMappings = $this->getParentAssociationMappings();
        $associationMappings[] = $this->getAssociationMapping();
        $alias = $queryBuilder->entityJoin($associationMappings);

        return [$alias, false];
    }

    /**
     * Retrieve the parent alias for given alias.
     * Root alias for direct association or entity joined alias for association depth >= 2.
     *
     * @param string $alias
     *
     * @return string
     */
    private function getParentAlias(ProxyQueryInterface $queryBuilder, $alias)
    {
        $parentAlias = $rootAlias = current($queryBuilder->getRootAliases());
        $joins = $queryBuilder->getDQLPart('join');
        if (isset($joins[$rootAlias])) {
            foreach ($joins[$rootAlias] as $join) {
                if ($join->getAlias() === $alias) {
                    $parts = explode('.', $join->getJoin());
                    $parentAlias = $parts[0];

                    break;
                }
            }
        }

        return $parentAlias;
    }
}


<?php

declare(strict_types=1);

/*
 * This file is part of the Sonata Project package.
 *
 * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sonata\DoctrineORMAdminBundle\Filter;

use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
use Sonata\AdminBundle\Form\Type\Filter\DefaultType;
use Sonata\Form\Type\EqualType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormTypeInterface;

class ClassFilter extends Filter
{
    public function filter(ProxyQueryInterface $queryBuilder, $alias, $field, $data)
    {
        if (!$data || !\is_array($data) || !\array_key_exists('value', $data)) {
            return;
        }

        if (0 === \strlen($data['value'])) {
            return;
        }

        $data['type'] = !isset($data['type']) ? EqualType::TYPE_IS_EQUAL : $data['type'];

        $operator = $this->getOperator((int) $data['type']);

        if (!$operator) {
            $operator = 'INSTANCE OF';
        }

        $this->applyWhere($queryBuilder, sprintf('%s %s %s', $alias, $operator, $data['value']));
    }

    public function getDefaultOptions()
    {
        return [];
    }

    public function getFieldType()
    {
        return $this->getOption('field_type', ChoiceType::class);
    }

    public function getFieldOptions()
    {
        $choiceOptions = [
            'required' => false,
            'choices' => $this->getOption('sub_classes'),
        ];

        if (method_exists(FormTypeInterface::class, 'setDefaultOptions')) {
            $choiceOptions['choices_as_values'] = true;
        }

        return $this->getOption('choices', $choiceOptions);
    }

    public function getRenderSettings()
    {
        return [DefaultType::class, [
            'operator_type' => EqualType::class,
            'field_type' => $this->getFieldType(),
            'field_options' => $this->getFieldOptions(),
            'label' => $this->getLabel(),
        ]];
    }

    /**
     * @param int $type
     *
     * @return mixed
     */
    private function getOperator($type)
    {
        $choices = [
            EqualType::TYPE_IS_EQUAL => 'INSTANCE OF',
            EqualType::TYPE_IS_NOT_EQUAL => 'NOT INSTANCE OF',
        ];

        return $choices[$type] ?? false;
    }
}


<?php

declare(strict_types=1);

/*
 * This file is part of the Sonata Project package.
 *
 * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sonata\DoctrineORMAdminBundle\Filter;

use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
use Sonata\AdminBundle\Form\Type\Filter\ChoiceType;

class StringFilter extends Filter
{
    public function filter(ProxyQueryInterface $queryBuilder, $alias, $field, $data)
    {
        if (!$data || !\is_array($data) || !\array_key_exists('value', $data) || null === $data['value']) {
            return;
        }

        $data['value'] = trim($data['value']);

        if (0 === \strlen($data['value'])) {
            return;
        }

        $data['type'] = !isset($data['type']) ? ChoiceType::TYPE_CONTAINS : $data['type'];

        $operator = $this->getOperator((int) $data['type']);

        if (!$operator) {
            $operator = 'LIKE';
        }

        // c.name > '1' => c.name OPERATOR :FIELDNAME
        $parameterName = $this->getNewParameterName($queryBuilder);

        $or = $queryBuilder->expr()->orX();

        if ($this->getOption('case_sensitive')) {
            $or->add(sprintf('%s.%s %s :%s', $alias, $field, $operator, $parameterName));
        } else {
            $or->add(sprintf('LOWER(%s.%s) %s :%s', $alias, $field, $operator, $parameterName));
        }

        if (ChoiceType::TYPE_NOT_CONTAINS === $data['type']) {
            $or->add($queryBuilder->expr()->isNull(sprintf('%s.%s', $alias, $field)));
        }

        $this->applyWhere($queryBuilder, $or);

        if (ChoiceType::TYPE_EQUAL === $data['type']) {
            $queryBuilder->setParameter($parameterName, $data['value']);
        } else {
            $queryBuilder->setParameter($parameterName,
                sprintf(
                    $this->getOption('format'),
                    $this->getOption('case_sensitive') ? $data['value'] : mb_strtolower($data['value'])
                )
            );
        }
    }

    public function getDefaultOptions()
    {
        return [
            'format' => '%%%s%%',
            'case_sensitive' => true,
        ];
    }

    public function getRenderSettings()
    {
        return [ChoiceType::class, [
            'field_type' => $this->getFieldType(),
            'field_options' => $this->getFieldOptions(),
            'label' => $this->getLabel(),
        ]];
    }

    /**
     * @param string $type
     *
     * @return bool
     */
    private function getOperator($type)
    {
        $choices = [
            ChoiceType::TYPE_CONTAINS => 'LIKE',
            ChoiceType::TYPE_NOT_CONTAINS => 'NOT LIKE',
            ChoiceType::TYPE_EQUAL => '=',
        ];

        return $choices[$type] ?? false;
    }
}


<?php

/**
 *
 *
 * Copyright (c) 2010 Keith Palmer / ConsoliBYTE, LLC.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.opensource.org/licenses/eclipse-1.0.php
 *
 *
 * @package QuickBooks
 * @subpackage IPP
 */

/**
 *
 *
 *
 */
abstract class QuickBooks_IPP_Service
{
	/**
	 * The last raw XML request
	 * @var string
	 */
	protected $_last_request;

	/**
	 * The last raw XML response
	 * @var string
	 */
	protected $_last_response;

	/**
	 *
	 * @var unknown_type
	 */
	protected $_last_debug;

	/**
	 *
	 */
	protected $_flavor;

	/**
	 * The last error code
	 * @var string
	 */
	protected $_errcode;

	/**
	 * The last error message
	 * @var string
	 */
	protected $_errtext;

	/**
	 * The last error detail
	 * @var string
	 */
	protected $_errdetail;

	/**
	 *
	 *
	 */
	public function __construct()
	{
		$this->_errcode = QuickBooks_IPP::ERROR_OK;

		$this->_last_request = null;
		$this->_last_response = null;
		$this->_last_debug = array();

		$this->_flavor = null;		// auto-detect
	}

	public function useIDSParser($Context, $true_or_false)
	{
		$IPP = $Context->IPP();

		return $IPP->useIDSParser((boolean) $true_or_false);
	}

	protected function _entitlements($Context, $realmID)
	{
		$IPP = $Context->IPP();

		// Send the data to IPP
		//                  $Context, $realm, $resource, $optype, $xml = '', $ID = null
		$return = $IPP->IDS($Context, $realmID, null, QuickBooks_IPP_IDS::OPTYPE_ENTITLEMENTS);

		$this->_setLastRequestResponse($Context->lastRequest(), $Context->lastResponse());
		$this->_setLastDebug($Context->lastDebug());

		if ($IPP->errorCode() != QuickBooks_IPP::ERROR_OK)
		{
			$this->_setError(
				$IPP->errorCode(),
				$IPP->errorText(),
				$IPP->errorDetail());

			return false;
		}

		return $return;
	}

	protected function _cdc($Context, $realmID, $entities, $timestamp, $page, $size)
	{
		$IPP = $Context->IPP();

		// Send the data to IPP
		//                  $Context, $realm, $resource, $optype, $xml = '', $ID = null
		$return = $IPP->IDS($Context, $realmID, null, QuickBooks_IPP_IDS::OPTYPE_CDC, array(
			$entities,
			$timestamp
			));

		$this->_setLastRequestResponse($Context->lastRequest(), $Context->lastResponse());
		$this->_setLastDebug($Context->lastDebug());

		if ($IPP->errorCode() != QuickBooks_IPP::ERROR_OK)
		{
			$this->_setError(
				$IPP->errorCode(),
				$IPP->errorText(),
				$IPP->errorDetail());

			return false;
		}

		return $return;
	}

	protected function _syncStatus($Context, $realmID, $resource, $IDType)
	{
		$IPP = $Context->IPP();

		switch ($IPP->version())
		{
			case QuickBooks_IPP_IDS::VERSION_2:
				return $this->_syncStatus_v2($Context, $realmID, $resource, $IDType);
				break;
			return false;
		}
	}

	protected function _syncStatus_v2($Context, $realmID, $resource, $IDType)
	{
		$IPP = $Context->IPP();

		$xml = '<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<SyncStatusRequest xmlns="http://www.intuit.com/sb/cdm/v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.intuit.com/sb/cdm/xmlrequest RestDataFilter.xsd">
	<OfferingId>ipp</OfferingId>
	<SyncStatusParam>
		<IdSet>';

		//foreach ($arr as $IDType)
		//{
			$parse = QuickBooks_IPP_IDS::parseIDType($IDType);
			$xml .= '<Id idDomain="' . $parse['domain'] . '">' . $parse['ID'] . '</Id>' . QUICKBOOKS_CRLF;
		//}

		$xml .= '
		<IdSet>
		<ObjectType>' . $resource . '</ObjectType>
	</SyncStatusParam>
</SyncStatusRequest>';

		$return = $IPP->IDS($Context, $realmID, $resource, QuickBooks_IPP_IDS::OPTYPE_SYNCSTATUS, $xml);
		$this->_setLastRequestResponse($Context->lastRequest(), $Context->lastResponse());
		$this->_setLastDebug($Context->lastDebug());

		return $return;
	}

	protected function _report($Context, $realmID, $resource, $xml = '')
	{
		$IPP = $Context->IPP();

		if (!$xml)
		{
			$xml = '';
			$xml .= '<?xml version="1.0" encoding="UTF-8"?>' . QUICKBOOKS_CRLF;
			$xml .= '<' . $resource . ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.intuit.com/sb/cdm/' . $IPP->version() . '"></' . $resource . '>';
		}

		$return = $IPP->IDS($Context, $realmID, $resource, QuickBooks_IPP_IDS::OPTYPE_REPORT, $xml);
		$this->_setLastRequestResponse($Context->lastRequest(), $Context->lastResponse());
		$this->_setLastDebug($Context->lastDebug());

		return $return;
	}

	/*
	protected function _delete($Context, $realmID, $resource, $IDType, $xml = '')
	{
		$IPP = $Context->IPP();

		if (!$xml)
		{
			$parse = QuickBooks_IPP_IDS::parseIDType($IDType);

			$xml = '';
			$xml .= '<?xml version="1.0" encoding="UTF-8"?>' . QUICKBOOKS_CRLF;
			$xml .= '<' . $resource . 'Query xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.intuit.com/sb/cdm/' . $IPP->version() . '">' . QUICKBOOKS_CRLF;
			$xml .= '	<TransactionIdSet>' . QUICKBOOKS_CRLF;
			$xml .= '		<Id idDomain="' . $parse[0] . '">' . $parse[1] . '</Id>' . QUICKBOOKS_CRLF;
			$xml .= '	</TransactionIdSet>' . QUICKBOOKS_CRLF;
			$xml .= '</' . $resource . 'Query>';
		}

		$return = $IPP->IDS($Context, $realmID, $resource, QuickBooks_IPP_IDS::OPTYPE_DELETE, $xml);
		$this->_setLastRequestResponse($Context->lastRequest(), $Context->lastResponse());
		$this->_setLastDebug($Context->lastDebug());

		if (count($return))
		{
			return $return[0];
		}

		return null;
	}
	*/

	/**
	 *
	 *
	 */
	protected function _guessResource($xml, $optype)
	{
		$tmp = explode('_', get_class($this));
		return end($tmp);
	}

	/**
	 *
	 *
	 */
	public function rawQuery($Context, $realmID, $xml, $resource = null)
	{
		$IPP = $Context->IPP();

		if (!$resource)
		{
			$resource = $this->_guessResource($xml, QuickBooks_IPP_IDS::OPTYPE_QUERY);
		}

		return $this->_findAll($Context, $realmID, $resource, null, null, null, null, $xml);
	}

	public function rawAdd($Context, $realmID, $xml, $resource = null)
	{
		$IPP = $Context->IPP();

		if (!$resource)
		{
			$resource = $this->_guessResource($xml, QuickBooks_IPP_IDS::OPTYPE_ADD);
		}

		// Send the data to IPP
		$return = $IPP->IDS($Context, $realmID, $resource, QuickBooks_IPP_IDS::OPTYPE_ADD, $xml);
		$this->_setLastRequestResponse($Context->lastRequest(), $Context->lastResponse());
		$this->_setLastDebug($Context->lastDebug());

		if ($IPP->errorCode() != QuickBooks_IPP::ERROR_OK)
		{
			$this->_setError(
				$IPP->errorCode(),
				$IPP->errorText(),
				$IPP->errorDetail());

			return false;
		}

		return $return;
	}

	protected function _map($list, $key, $value)
	{
		$map = array();
		foreach ($list as $Object)
		{
			$map[$Object->get($key)] = $Object->get($value);
		}

		return $map;
	}

	protected function _find()
	{

	}

	/**
	 *
	 * Returns false on error, and sets $IPP->errorCode, $IPP->errorText, and $IPP->errorDetail
	 *
	 * Added $options array in 09/2012:
	 *   Supported array keys for QuickBooks Desktop are:
	 *     ActiveOnly       => true/false (False by default. May not be used with DeletedObjects)
	 *     DeletedObjects   => true/false (False by default. May not be used with ActiveOnly)
	 *   Supported array keys for QuickBooks Online are:
	 *     (none yet)
	 */
	protected function _findAll($Context, $realmID, $resource, $query = null, $sort = null, $page = 1, $size = 50, $xml = '', $options = array())
	{
		$IPP = $Context->IPP();
		$flavor = $IPP->flavor();

		//print('flavor [' . $flavor . ']');
		//exit;

		if ($flavor == QuickBooks_IPP_IDS::FLAVOR_DESKTOP)
		{
			if (!$xml)
			{
				$options_string = '';
				//$options_string = ' ErroredObjectsOnly="true" ';

				/*if (!empty($options['ActiveOnly']))
				{
					$options_string = 'ActiveOnly="false" ';
				}
				else if (!empty($options['DeletedObjects']))
				{
					$options_string = 'DeletedObjects="true" ';
				}*/

				$xml = '';
				$xml .= '<?xml version="1.0" encoding="UTF-8"?>' . QUICKBOOKS_CRLF;
				$xml .= '<' . $resource . 'Query ' . $options_string . 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.intuit.com/sb/cdm/' . $IPP->version() . '">' . QUICKBOOKS_CRLF;

				if ($size)
				{
					$xml .= '	<StartPage>' . (int) $page . '</StartPage>' . QUICKBOOKS_CRLF;
					$xml .= '	<ChunkSize>' . (int) $size . '</ChunkSize>' . QUICKBOOKS_CRLF;
				}

				$xml .= $query;

				$xml .= '</' . $resource . 'Query>';
			}
		}
		else if ($flavor == QuickBooks_IPP_IDS::FLAVOR_ONLINE)
		{
		    if (!$xml)
			{
				if (is_array($query) and count($query) > 0)
				{
					$xml = http_build_query(array_merge(array(
						'PageNum' => (int) $page,
						'ResultsPerPage' => (int) $size,
						), (array) $query));
				}
				else
				{
					$xml = http_build_query(array_merge(array(
						'PageNum' => (int) $page,
						'ResultsPerPage' => (int) $size,
						)));

					$xml .= $query;
				}
			}
		}

		$return = $IPP->IDS($Context, $realmID, $resource, QuickBooks_IPP_IDS::OPTYPE_QUERY, $xml);
		$this->_setLastRequestResponse($Context->lastRequest(), $Context->lastResponse());
		$this->_setLastDebug($Context->lastDebug());

		if ($IPP->errorCode() != QuickBooks_IPP::ERROR_OK)
		{
			$this->_setError(
				$IPP->errorCode(),
				$IPP->errorText(),
				$IPP->errorDetail());

			return false;
		}

		return $return;
	}

	/**
	 * Get an IDS object by Name (i.e. get a customer by the QuickBooks Name field)
	 *
	 * @param QuickBooks_IPP_Context $Context
	 * @param integer $realmID
	 * @param string $resource
	 * @param string $xml
	 * @return QuickBooks_IPP_Object
	 */
	protected function _findByName($Context, $realmID, $resource, $name, $xml = '')
	{
		$IPP = $Context->IPP();

		if ($IPP->flavor() == QuickBooks_IPP_IDS::FLAVOR_DESKTOP)
		{
			if (!$xml)
			{
				$xml = '';
				$xml .= '<?xml version="1.0" encoding="UTF-8"?>' . QUICKBOOKS_CRLF;
				$xml .= '<' . $resource . 'Query xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.intuit.com/sb/cdm/' . $IPP->version() . '">' . QUICKBOOKS_CRLF;
				$xml .= '	<FirstLastInside>' . QuickBooks_XML::encode($name) . '</FirstLastInside>' . QUICKBOOKS_CRLF;
				$xml .= '</' . $resource . 'Query>';
			}
		}
		else
		{
			$xml = http_build_query(array( 'Filter' => 'Name :EQUALS: ' . $name ));
		}

		$return = $IPP->IDS($Context, $realmID, $resource, QuickBooks_IPP_IDS::OPTYPE_QUERY, $xml);
		$this->_setLastRequestResponse($Context->lastRequest(), $Context->lastResponse());
		$this->_setLastDebug($Context->lastDebug());

		if (count($return))
		{
			return $return[0];
		}

		return null;
	}

	/**
	 * Add an IDS object via IPP
	 *
	 * @param QuickBooks_IPP_Context $Context
	 * @param integer $realmID
	 * @param string $resource
	 * @param object $Object
	 * @return integer
	 */
	protected function _add($Context, $realmID, $resource, $Object)
	{
		$IPP = $Context->IPP();

		switch ($IPP->version())
		{
			case QuickBooks_IPP_IDS::VERSION_2:
				return $this->_add_v2($Context, $realmID, $resource, $Object);
			case QuickBooks_IPP_IDS::VERSION_3:
				return $this->_add_v3($Context, $realmID, $resource, $Object);
		}
	}

	protected function _add_v3($Context, $realmID, $resource, $Object)
	{
		$IPP = $Context->IPP();

		$unsets = array(
			'Id',
			);

		foreach ($unsets as $unset)
		{
			$Object->remove($unset);
		}

		// Generate the XML
		$xml = $Object->asXML(0, null, null, null, QuickBooks_IPP_IDS::VERSION_3);

		// Send the data to IPP
		$return = $IPP->IDS($Context, $realmID, $resource, QuickBooks_IPP_IDS::OPTYPE_ADD, $xml);
		$this->_setLastRequestResponse($Context->lastRequest(), $Context->lastResponse());
		$this->_setLastDebug($Context->lastDebug());

		if ($IPP->errorCode() != QuickBooks_IPP::ERROR_OK)
		{
			$this->_setError(
				$IPP->errorCode(),
				$IPP->errorText(),
				$IPP->errorDetail());

			return false;
		}

		return $return;
	}

	protected function _add_v2($Context, $realmID, $resource, $Object)
	{
		$IPP = $Context->IPP();

		//

		//$Object->unsetAddress();
		//$Object->unsetPhone();
		//$Object->unsetDBAName();

		$unsets = array(
			'Id',
			'SyncToken',
			'MetaData',
			'ExternalKey',
			'Synchronized',
			'PartyReferenceId',
			'SalesTaxCodeId', 		// @todo These are customer/vendor specific and probably shouldn't be here
			'SalesTaxCodeName',
			'OpenBalanceDate',
			'OpenBalance',
			);

		foreach ($unsets as $unset)
		{
			$Object->remove($unset);
		}

		if ($IPP->flavor() == QuickBooks_IPP_IDS::FLAVOR_DESKTOP)
		{
			// Build the XML request
			$xml = '';
			$xml .= '<?xml version="1.0" encoding="UTF-8"?>' . QUICKBOOKS_CRLF;
			$xml .= '<Add xmlns="http://www.intuit.com/sb/cdm/' . $IPP->version() . '" ' . QUICKBOOKS_CRLF;
			$xml .= '	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' . QUICKBOOKS_CRLF;
			$xml .= '	RequestId="' . md5(mt_rand() . microtime()) . '" ' . QUICKBOOKS_CRLF;
			$xml .= '	xsi:schemaLocation="http://www.intuit.com/sb/cdm/' . $IPP->version() . ' ./RestDataFilter.xsd ">' . QUICKBOOKS_CRLF;
			$xml .= '	<OfferingId>ipp</OfferingId>' . QUICKBOOKS_CRLF;
			$xml .= '	<ExternalRealmId>' . $realmID . '</ExternalRealmId>' . QUICKBOOKS_CRLF;
			$xml .= '' . $Object->asIDSXML(1, null, QuickBooks_IPP_IDS::OPTYPE_ADD);
			$xml .= '</Add>';
		}
		else
		{
			$xml = '';
			$xml .= '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . QUICKBOOKS_CRLF;
			$xml .= $Object->asIDSXML(0, null, QuickBooks_IPP_IDS::OPTYPE_ADD, $IPP->flavor());
		}

		// Send the data to IPP
		$return = $IPP->IDS($Context, $realmID, $resource, QuickBooks_IPP_IDS::OPTYPE_ADD, $xml);
		$this->_setLastRequestResponse($Context->lastRequest(), $Context->lastResponse());
		$this->_setLastDebug($Context->lastDebug());

		if ($IPP->errorCode() != QuickBooks_IPP::ERROR_OK)
		{
			$this->_setError(
				$IPP->errorCode(),
				$IPP->errorText(),
				$IPP->errorDetail());

			return false;
		}

		return $return;
	}

	protected function _delete($Context, $realmID, $resource, $ID)
	{
		// v3 only
		$IPP = $Context->IPP();

		// Get the object first... because IDS is stupid and wants us to send
		//	the entire object even though we know the Id of the object that we
		//	want to delete... *sigh*
		$objects = $this->_query($Context, $realmID, "SELECT * FROM " . $resource . " WHERE Id = '" . QuickBooks_IPP_IDS::usableIDType($ID) . "' ");

		if (isset($objects[0]) and
			is_object($objects[0]))
		{
			$Object = $objects[0];

			$unsets = array();

			foreach ($unsets as $unset)
			{
				$Object->remove($unset);
			}

			// Generate the XML
			$xml = $Object->asXML(0, null, null, null, QuickBooks_IPP_IDS::VERSION_3);

			//die($xml);

			// Send the data to IPP
			$return = $IPP->IDS($Context, $realmID, $resource, QuickBooks_IPP_IDS::OPTYPE_DELETE, $xml);
			$this->_setLastRequestResponse($Context->lastRequest(), $Context->lastResponse());
			$this->_setLastDebug($Context->lastDebug());

			//print('erro code: [' . $IPP->errorCode() . ']' . "\n");
			//print("\n\n\n\n\n" . $Context->lastResponse() . "\n\n\n\n\n");

			if ($IPP->errorCode() != QuickBooks_IPP::ERROR_OK)
			{
				$this->_setError(
					$IPP->errorCode(),
					$IPP->errorText(),
					$IPP->errorDetail());

				return false;
			}

			return true;
		}

		$this->_setError(
			QuickBooks_IPP::ERROR_INTERNAL,
			'Could not find ' . $resource . ' ' . QuickBooks_IPP_IDS::usableIDType($ID) . ' to delete.',
			'Could not find ' . $resource . ' ' . QuickBooks_IPP_IDS::usableIDType($ID) . ' to delete.');

		return false;
	}

	protected function _void($Context, $realmID, $resource, $ID)
	{
		// v3 only
		$IPP = $Context->IPP();

		// Get the object first... because IDS is stupid and wants us to send
		//	the entire object even though we know the Id of the object that we
		//	want to delete... *sigh*
		$objects = $this->_query($Context, $realmID, "SELECT * FROM " . $resource . " WHERE Id = '" . QuickBooks_IPP_IDS::usableIDType($ID) . "' ");

		if (isset($objects[0]) and
			is_object($objects[0]))
		{
			$Object = $objects[0];

			$unsets = array();

			foreach ($unsets as $unset)
			{
				$Object->remove($unset);
			}

			// Generate the XML
			$xml = $Object->asXML(0, null, null, null, QuickBooks_IPP_IDS::VERSION_3);

			//die($xml);

			// Send the data to IPP
			$return = $IPP->IDS($Context, $realmID, $resource, QuickBooks_IPP_IDS::OPTYPE_VOID, $xml);
			$this->_setLastRequestResponse($Context->lastRequest(), $Context->lastResponse());
			$this->_setLastDebug($Context->lastDebug());

			//print('erro code: [' . $IPP->errorCode() . ']' . "\n");
			//print("\n\n\n\n\n" . $Context->lastResponse() . "\n\n\n\n\n");

			if ($IPP->errorCode() != QuickBooks_IPP::ERROR_OK)
			{
				$this->_setError(
					$IPP->errorCode(),
					$IPP->errorText(),
					$IPP->errorDetail());

				return false;
			}

			return true;
		}

		$this->_setError(
			QuickBooks_IPP::ERROR_INTERNAL,
			'Could not find ' . $resource . ' ' . QuickBooks_IPP_IDS::usableIDType($ID) . ' to void.',
			'Could not find ' . $resource . ' ' . QuickBooks_IPP_IDS::usableIDType($ID) . ' to void.');

		return false;
	}

	protected function _pdf($Context, $realmID, $resource, $ID)
	{
		// v3 only
		$IPP = $Context->IPP();
		$IPP->useIDSParser(false); // We want raw pdf output

		return $IPP->IDS($Context, $realmID, $resource, QuickBooks_IPP_IDS::OPTYPE_PDF, null, $ID);
	}

	protected function _download($Context, $realmID, $resource, $ID)
	{
		// v3 only
		$IPP = $Context->IPP();
		$IPP->useIDSParser(false); // We want raw pdf output

		return $IPP->IDS($Context, $realmID, $resource, QuickBooks_IPP_IDS::OPTYPE_DOWNLOAD, null, $ID);
	}

	protected function _send($Context, $realmID, $resource, $ID)
	{
		// v3 only
		$IPP = $Context->IPP();

		$return = $IPP->IDS($Context, $realmID, $resource, QuickBooks_IPP_IDS::OPTYPE_SEND, null, $ID);

      $this->_setLastRequestResponse($Context->lastRequest(), $Context->lastResponse());
      $this->_setLastDebug($Context->lastDebug());

      if ($IPP->errorCode() != QuickBooks_IPP::ERROR_OK)
      {
         $this->_setError(
            $IPP->errorCode(),
            $IPP->errorText(),
            $IPP->errorDetail());

         return false;
      }

      return $return;
	}

	/**
	 * @deprecated 			Use _update() instead
	 */
	protected function _modify($Context, $realmID, $resource, $Object, $ID)
	{
		return $this->_update($Context, $realmID, $resource, $Object, $ID);
	}

	/**
	 * Update an object within IDS (QuickBooks)
	 *
	 * @param object $Context
	 * @param string $realmID
	 * @param string $resource
	 * @param object $Object
	 * @return boolean
	 */
	protected function _update($Context, $realmID, $resource, $Object, $ID)
	{
		$IPP = $Context->IPP();

		switch ($IPP->version())
		{
			case QuickBooks_IPP_IDS::VERSION_2:
				return $this->_update_v2($Context, $realmID, $resource, $Object, $ID);
			case QuickBooks_IPP_IDS::VERSION_3:
				return $this->_update_v3($Context, $realmID, $resource, $Object, $ID);
		}

		return false;
	}

	protected function _update_v3($Context, $realmID, $resource, $Object, $ID)
	{
		$IPP = $Context->IPP();

		$unsets = array();

		foreach ($unsets as $unset)
		{
			$Object->remove($unset);
		}

		// Generate the XML
		$xml = $Object->asXML(0, null, null, null, QuickBooks_IPP_IDS::VERSION_3);

		//die($xml);

		// Send the data to IPP
		$return = $IPP->IDS($Context, $realmID, $resource, QuickBooks_IPP_IDS::OPTYPE_MOD, $xml);
		$this->_setLastRequestResponse($Context->lastRequest(), $Context->lastResponse());
		$this->_setLastDebug($Context->lastDebug());

		if ($IPP->errorCode() != QuickBooks_IPP::ERROR_OK)
		{
			$this->_setError(
				$IPP->errorCode(),
				$IPP->errorText(),
				$IPP->errorDetail());

			return false;
		}

		return $return;
	}

	protected function _update_v2($Context, $realmID, $resource, $Object, $ID)
	{
		$IPP = $Context->IPP();

		// Remove crap that we don't want to send to QuickBooks
		$unsets = array(
		//	'Id',
		//	'SyncToken',
			'MetaData',
			'ExternalKey',
			'Synchronized',
			'CustomField',
		//	'PartyReferenceId',
		//	'SalesTaxCodeId', 		// @todo These are customer/vendor specific and probably shouldn't be here
		//	'SalesTaxCodeName',
		//	'OpenBalanceDate',
		//	'OpenBalance',
			);

		foreach ($unsets as $unset)
		{
			$Object->remove($unset);
		}

		$Object->set('Synchronized', 'false');

		if ($IPP->flavor() == QuickBooks_IPP_IDS::FLAVOR_DESKTOP)
		{
			// Build the XML request
			$xml = '';
			$xml .= '<?xml version="1.0" encoding="UTF-8"?>' . QUICKBOOKS_CRLF;
			$xml .= '<Mod xmlns="http://www.intuit.com/sb/cdm/' . $IPP->version() . '" ' . QUICKBOOKS_CRLF;
			$xml .= '	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' . QUICKBOOKS_CRLF;
			$xml .= '	RequestId="' . md5(mt_rand() . microtime()) . '" ' . QUICKBOOKS_CRLF;
			$xml .= '	xsi:schemaLocation="http://www.intuit.com/sb/cdm/' . $IPP->version() . ' ./RestDataFilter.xsd ">' . QUICKBOOKS_CRLF;
			//$xml .= '	<OfferingId>ipp</OfferingId>' . QUICKBOOKS_CRLF;
			$xml .= '	<ExternalRealmId>' . $realmID . '</ExternalRealmId>' . QUICKBOOKS_CRLF;
			$xml .= '' . $Object->asIDSXML(1, null, QuickBooks_IPP_IDS::OPTYPE_MOD);
			$xml .= '</Mod>';
		}
		else
		{
			$xml = '';
			$xml .= '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . QUICKBOOKS_CRLF;
			$xml .= $Object->asIDSXML(0, null, QuickBooks_IPP_IDS::OPTYPE_MOD, $IPP->flavor());
		}

		// Send the data to IPP
		$return = $IPP->IDS($Context, $realmID, $resource, QuickBooks_IPP_IDS::OPTYPE_MOD, $xml, $ID);
		$this->_setLastRequestResponse($Context->lastRequest(), $Context->lastResponse());
		$this->_setLastDebug($Context->lastDebug());

		// Check for errors
		if ($IPP->errorCode() != QuickBooks_IPP::ERROR_OK)
		{
			$this->_setError(
				$IPP->errorCode(),
				$IPP->errorText(),
				$IPP->errorDetail());

			return false;
		}

		return $return;
	}

	/**
	 * Find a record by ID number
	 *
	 *
	 */
	protected function _findById($Context, $realmID, $resource, $IDType, $xml_or_IDType = '', $query = null)
	{
		$IPP = $Context->IPP();

		$flavor = $IPP->flavor();

		if (!$xml_or_IDType)
		{
			if ($flavor == QuickBooks_IPP_IDS::FLAVOR_DESKTOP)
			{
				$parse = QuickBooks_IPP_IDS::parseIDType($IDType);

				$xml_or_IDType = '';
				$xml_or_IDType .= '<?xml version="1.0" encoding="UTF-8"?>' . QUICKBOOKS_CRLF;
				$xml_or_IDType .= '<' . $resource . 'Query xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.intuit.com/sb/cdm/' . $IPP->version() . '">' . QUICKBOOKS_CRLF;

				if ($resource == QuickBooks_IPP_IDS::RESOURCE_CUSTOMER)
				{
					$xml_or_IDType .= '<CustomFieldEnable>true</CustomFieldEnable>';
				}

				if ($query)
				{
					$xml_or_IDType .= $query;
				}

				$xml_or_IDType .= '	<' . QuickBooks_IPP_IDS::resourceToKeyType($resource) . 'Set>' . QUICKBOOKS_CRLF;
				$xml_or_IDType .= '		<Id idDomain="' . $parse['domain'] . '">' . $parse['ID'] . '</Id>' . QUICKBOOKS_CRLF;
				$xml_or_IDType .= '	</' . QuickBooks_IPP_IDS::resourceToKeyType($resource) . 'Set>' . QUICKBOOKS_CRLF;
				$xml_or_IDType .= '</' . $resource . 'Query>';
			}
			else if ($flavor == QuickBooks_IPP_IDS::FLAVOR_ONLINE)
			{
				$xml_or_IDType = $IDType;
			}
		}

		$return = $IPP->IDS($Context, $realmID, $resource, QuickBooks_IPP_IDS::OPTYPE_FINDBYID, $xml_or_IDType);
		$this->_setLastRequestResponse($Context->lastRequest(), $Context->lastResponse());
		$this->_setLastDebug($Context->lastDebug());

		if (count($return))
		{
			return $return[0];
		}

		return null;
	}

	protected function _query($Context, $realmID, $query)
	{
		$IPP = $Context->IPP();

		// Send the data to IPP
		//$return = $IPP->IDS($Context, $realmID, null, QuickBooks_IPP_IDS::OPTYPE_QUERY, str_replace('=', '%3D', $query));
		$return = $IPP->IDS($Context, $realmID, null, QuickBooks_IPP_IDS::OPTYPE_QUERY, urlencode($query));
		$this->_setLastRequestResponse($Context->lastRequest(), $Context->lastResponse());
		$this->_setLastDebug($Context->lastDebug());

		if ($IPP->errorCode() != QuickBooks_IPP::ERROR_OK)
		{
			$this->_setError(
				$IPP->errorCode(),
				$IPP->errorText(),
				$IPP->errorDetail());

			return false;
		}

		return $return;
	}

	/**
	 * Set the last XML request and XML response that was made by this service
	 *
	 * @param string $request		The last XML request that was made
	 * @param string $response		The last XML response that was made
	 * @return void
	 */
	protected function _setLastRequestResponse($request, $response)
	{
		$this->_last_request = $request;
		$this->_last_response = $response;
	}

	protected function _setLastDebug($debug)
	{
		$this->_last_debug = $debug;
	}

	/**
	 * Get the last XML request that was made
	 *
	 * @param object $Context
	 * @return string
	 */
	public function lastRequest($Context = null)
	{
		if ($Context)
		{
			return $Context->lastRequest();
		}

		return $this->_last_request;
	}

	/**
	 * Get the last raw XML response that was returned
	 *
	 * @param object $Context		If you provide a specific context, this will return the last response using that particular context, otherwise it will return the last response from this service
	 * @return string				The last raw XML response
	 */
	public function lastResponse($Context = null)
	{
		if ($Context)
		{
			return $Context->lastResponse();
		}

		return $this->_last_response;
	}

	public function lastError($Context = null)
	{
		if ($Context)
		{
			return $Context->lastError();
		}

		return $this->_errcode . ': [' . $this->_errtext . ', ' . $this->_errdetail . ']';
	}

	public function lastDebug($Context = null)
	{
		if ($Context)
		{
			return $Context->lastDebug();
		}

		return $this->_last_debug;
	}

	/**
	 * Get the error number of the last error that occured
	 *
	 * @return mixed		The error number (or error code, some QuickBooks error codes are hex strings)
	 */
	public function errorCode()
	{
		return $this->_errcode;
	}

	/**
	 * Alias if ->errorCode()   (here for consistency with rest of framework)
	 */
	public function errorNumber()
	{
		return $this->errorCode();
	}

	/**
	 * Get the last error message that was reported
	 *
	 * Remember that issuing new commands may cause previous unchecked errors
	 * to be *cleared*, so make sure you check for errors if you expect an
	 * error might occur!
	 *
	 * @return string
	 */
	public function errorText()
	{
		return $this->_errtext;
	}

	/**
	 * Alias of ->errorText()   (here for consistency with rest of framework)
	 */
	public function errorMessage()
	{
		return $this->errorText();
	}

	/**
	 *  Get the error detail message from the response
	 *
	 * The error detail node sometimes contains additional information about
	 * the error that occurred. You should make sure to also check the result
	 * of ->errorCode() and ->errorMessage() too.
	 *
	 * @return string
	 */
	public function errorDetail()
	{
		return $this->_errdetail;
	}

	/**
	 * Tell whether or not an error occurred
	 *
	 * @return boolean
	 */
	public function hasErrors()
	{
		return $this->_errcode != QuickBooks_IPP::ERROR_OK;
	}

	/**
	 * Set an error message
	 *
	 * @param integer $errnum	The error number/code
	 * @param string $errmsg	The text error message
	 * @return void
	 */
	protected function _setError($errcode, $errtext = '', $errdetail = '')
	{
		$this->_errcode = $errcode;
		$this->_errtext = $errtext;
		$this->_errdetail = $errdetail;
	}
}


<?php

/**
 *
 *
 * Copyright (c) 2010 Keith Palmer / ConsoliBYTE, LLC.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.opensource.org/licenses/eclipse-1.0.php
 *
 * @author Keith Palmer <keith@consolibyte.com>
 * @license LICENSE.txt
 *
 * @package QuickBooks
 * @subpackage Adapter
 */

/**
 *
 */
QuickBooks_Loader::load('/QuickBooks/Adapter/Client.php');

/**
 *
 */
class QuickBooks_Adapter_Client_Php extends SoapClient implements QuickBooks_Adapter_Client
{
	public function __construct($endpoint, $wsdl = QUICKBOOKS_WSDL, $trace = true)
	{
		ini_set('soap.wsdl_cache_enabled', '0');

		$options['location'] = $endpoint;

		if ($trace)
		{
			$options['trace'] = 1;
		}

		parent::__construct($wsdl, $options);
	}

	/**
	 * Authenticate against a QuickBooks SOAP server
	 *
	 * @param string $user
	 * @param string $pass
	 * @return array
	 */
	public function authenticate($user, $pass)
	{
		$req = new QuickBooks_Request_Authenticate($user, $pass);

		$resp = parent::__soapCall('authenticate', array( $req ));
		$tmp = current($resp);

		return current($tmp);
	}

	public function sendRequestXML($ticket, $hcpresponse, $companyfile, $country, $majorversion, $minorversion)
	{
		$req = new QuickBooks_Request_SendRequestXML($ticket, $hcpresponse, $companyfile, $country, $majorversion, $minorversion);

		//print("SENDING:<pre>");
		//print_r($req);
		//print('</pre>');

		$resp = parent::__soapCall('sendRequestXML', array( $req ));
		$tmp = current($resp);

		return $tmp;
	}

	public function receiveResponseXML($ticket, $response, $hresult, $message)
	{
		$req = new QuickBooks_Request_ReceiveResponseXML($ticket, $response, $hresult, $message);

		$resp = parent::__soapCall('receiveResponseXML', array( $req ));
		$tmp = current($resp);

		return $tmp;
	}

	public function getLastRequest()
	{
		return parent::__getLastRequest();
	}

	public function getLastResponse()
	{
		return parent::__getLastResponse();
	}
}


<?php

/**
 * QuickBooks NonInventoryItem object container
 *
 * NOTE: By default, NonInventoryItems are created as SalesOrPurchase items, and are
 * thus *NOT* created as SalesAndPurchase items. If you want to create an item
 * that is sold *and* purchased, you'll need to set the type with the method:
 * 	-> {@link QuickBooks_Object_NonInventoryItem::isSalesAndPurchase()}
 *
 * @todo Verify the get/set methods on this one... it was copied from ServiceItem
 * @todo Add isActive(), getIsActive(), etc. methods
 *
 * @author Keith Palmer <keith@consolibyte.com>
 * @license LICENSE.txt
 *
 * @package QuickBooks
 * @subpackage Object
 */

/**
 *
 */
QuickBooks_Loader::load('/QuickBooks/QBXML/Object.php');

/**
 *
 */
class QuickBooks_QBXML_Object_NonInventoryItem extends QuickBooks_QBXML_Object
{
	protected $_is_sales_and_purchase;

	public function __construct($arr = array(), $is_sales_and_purchase = false)
	{
		parent::__construct($arr);

		if (count($this->getArray('SalesAndPurchase')) > 0)
		{
			$is_sales_and_purchase = true;
		}

		$this->_is_sales_and_purchase = $is_sales_and_purchase;
	}

	/**
	 * Set the ListID for this item
	 *
	 * @param string $ListID
	 * @return boolean
	 */
	public function setListID($ListID)
	{
		return $this->set('ListID', $ListID);
	}

	/**
	 * Get the ListID for this item
	 *
	 * @return string
	 */
	public function getListID()
	{
		return $this->get('ListID');
	}

	/**
	 * Set the name for this item
	 *
	 * @param string $name
	 * @return boolean
	 */
	public function setName($name)
	{
		return $this->set('Name', $name);
	}

	/**
	 * Get the name for this item
	 *
	 * @return string
	 */
	public function getName()
	{
		return $this->get('Name');
	}

	public function getUnitOfMeasureSetListID()
	{
		return $this->get('UnitOfMeasureSetRef ListID');
	}

	public function getUnitOfMeasureSetFullName()
	{
		return $this->get('UnitOfMeasureSetRef FullName');
	}

	/**
	 * Tell (and optionally set) whether or not this item is currently for Sale *and* Purchase
	 *
	 * @param boolean $enable
	 * @return boolean
	 */
	public function isSalesAndPurchase($enable = null)
	{
		$current = $this->_is_sales_and_purchase;

		if (!is_null($enable))
		{
			$this->_is_sales_and_purchase = (boolean) $enable;
		}

		return $current;
	}

	/**
	 * Tell (and optionall set) whether or not this item is currently for Sale *or* Purchase
	 *
	 * @param boolean $enable
	 * @return boolean
	 */
	public function isSalesOrPurchase($enable = null)
	{
		$current = !$this->_is_sales_and_purchase;

		if (!is_null($enable))
		{
			$this->_is_sales_and_purchase = ! (boolean) $enable;
		}

		return $current;
	}

	// Sales OR Purchase

	/**
	 * Set the description of this item (Sales OR Purchase)
	 *
	 * @param string $descrip
	 * @return boolean
	 */
	public function setDescription($descrip)
	{
		return $this->set('SalesOrPurchase Desc', $descrip);
	}

	public function getDescription()
	{
		return $this->get('SalesOrPurchase Desc');
	}

	/**
	 * Set the price for this item (Sales OR Purchase)
	 *
	 * @param string $price
	 * @return boolean
	 */
	public function setPrice($price)
	{
		$this->remove('SalesOrPurchase PricePercent');

		return $this->set('SalesOrPurchase Price', sprintf('%01.2f', (float) $price));
	}

	/**
	 * Get the price for this item (Sales OR Purchase)
	 */
	public function getPrice()
	{
		return $this->get('SalesOrPurchase Price');
	}

	/**
	 * Set the price percent for this item (Sales OR Purchase)
	 */
	public function setPricePercent($percent)
	{
		$this->remove('SalesOrPurchase Price');

		return $this->set('SalesOrPurchase PricePercent', (float) $percent);
	}

	/**
	 * Get the price percent for this item (Sales OR Purchase)
	 */
	public function getPricePercent()
	{
		return $this->get('SalesOrPurchase PricePercent');
	}

	/**
	 * Set the account ListID for this item (Sales OR Purchase)
	 *
	 * @param string $ListID
	 * @return string
	 */
	public function setAccountListID($ListID)
	{
		return $this->set('SalesOrPurchase AccountRef ListID', $ListID);
	}

	/**
	 * (Sales OR Purchase)
	 */
	public function setAccountName($name)
	{
		return $this->set('SalesOrPurchase AccountRef FullName', $name);
	}

	/**
	 * (Sales OR Purchase)
	 */
	public function setAccountApplicationID($value)
	{
		return $this->set('SalesOrPurchase AccountRef ' . QUICKBOOKS_API_APPLICATIONID, $this->encodeApplicationID(QUICKBOOKS_OBJECT_ACCOUNT, QUICKBOOKS_LISTID, $value));
	}

	public function getAccountApplicationID()
	{
		return $this->get('SalesOrPurchase AccountRef ' . QUICKBOOKS_API_APPLICATIONID);
	}

	/**
	 * (Sales OR Purchase)
	 */
	public function getAccountListID()
	{
		return $this->get('SalesOrPurchase AccountRef ListID');
	}

	/**
	 * (Sales OR Purchase)
	 */
	public function getAccountName()
	{
		return $this->get('SalesOrPurchase AccountRef FullName');
	}

	// Sales AND Purchase

	public function setSalesDescription($descrip)
	{
		return $this->set('SalesAndPurchase SalesDesc', $descrip);
	}

	public function getSalesDescription()
	{
		return $this->get('SalesAndPurchase SalesDesc');
	}

	public function setSalesPrice($price)
	{
		return $this->set('SalesAndPurchase SalesPrice', sprintf('%01.2f', (float) $price));
	}

	public function getSalesPrice()
	{
		return $this->get('SalesAndPurchase SalesPrice');
	}

	public function setIncomeAccountListID($ListID)
	{
		return $this->set('SalesAndPurchase IncomeAccountRef ListID', $ListID);
	}

	public function getIncomeAccountListID()
	{
		return $this->get('SalesAndPurchase IncomeAccountRef ListID');
	}

	public function setIncomeAccountName($name)
	{
		return $this->set('SalesAndPurchase IncomeAccountRef FullName', $name);
	}

	public function getIncomeAccountName()
	{
		return $this->get('SalesAndPurchase IncomeAccountRef FullName');
	}

	public function setIncomeAccountApplicationID($value)
	{
		return $this->set('SalesAndPurchase IncomeAccountRef ' . QUICKBOOKS_API_APPLICATIONID, $this->encodeApplicationID(QUICKBOOKS_OBJECT_ACCOUNT, QUICKBOOKS_LISTID, $value));
	}

	public function getIncomeAccountApplicationID()
	{
		return $this->get('SalesAndPurchase IncomeAccountRef ' . QUICKBOOKS_API_APPLICATIONID);
	}

	public function setPurchaseDescription($descrip)
	{
		return $this->set('SalesAndPurchase PurchaseDesc', $descrip);
	}

	public function getPurchaseDescription()
	{
		return $this->get('SalesAndPurchase PurchaseDesc');
	}

	public function setPurchaseCost($cost)
	{
		return $this->set('SalesAndPurchase PurchaseCost', sprintf('%01.2f', (float) $cost));
	}

	public function getPurchaseCost()
	{
		return $this->get('SalesAndPurchase PurchaseCost');
	}

	public function setExpenseAccountListID($ListID)
	{
		return $this->set('SalesAndPurchase ExpenseAccountRef ListID', $ListID);
	}

	public function setExpenseAccountName($name)
	{
		return $this->set('SalesAndPurchase ExpenseAccountRef FullName', $name);
	}

	public function setExpenseAccountApplicationID($value)
	{
		return $this->set('SalesAndPurchase ExpenseAccountRef ' . QUICKBOOKS_API_APPLICATIONID, $this->encodeApplicationID(QUICKBOOKS_OBJECT_ACCOUNT, QUICKBOOKS_LISTID, $value));
	}

	public function getExpenseAccountApplicationID()
	{
		return $this->get('SalesAndPurchase ExpenseAccountRef ' . QUICKBOOKS_API_APPLICATIONID);
	}

	public function getExpenseAccountListID()
	{
		return $this->get('SalesAndPurchase ExpenseAccountRef ListID');
	}

	public function getExpenseAccountName()
	{
		return $this->get('SalesAndPurchase ExpenseAccountRef FullName');
	}

	public function setPreferredVendorListID($ListID)
	{
		return $this->set('SalesAndPurchase PrefVendorRef ListID', $ListID);
	}

	public function setPreferredVendorName($name)
	{
		return $this->set('SalesAndPurchase PrefVendorRef FullName', $name);
	}

	public function setPreferredVendorApplicationID($value)
	{
		return $this->set('SalesAndPurchase PrefVendorRef ' . QUICKBOOKS_API_APPLICATIONID, $this->encodeApplicationID(QUICKBOOKS_OBJECT_VENDOR, QUICKBOOKS_LISTID, $value));
	}

	public function getPreferredVendorApplicationID()
	{
		return $this->get('SalesAndPurchase PrefVendorRef ' . QUICKBOOKS_API_APPLICATIONID);
	}

	public function getPreferredVendorListID()
	{
		return $this->get('SalesAndPurchase PrefVendorRef ListID');
	}

	public function getPreferredVendorName()
	{
		return $this->get('SalesAndPurchase PrefVendorRef FullName');
	}

	/**
	 *
	 *
	 * @return boolean
	 */
	protected function _cleanup()
	{
		if ($this->isSalesAndPurchase())
		{
			// Remove any SalesOrPurchase keys

			foreach ($this->getArray('SalesOrPurchase*') as $key => $value)
			{
				$this->remove($key);
			}
		}
		else
		{
			foreach ($this->getArray('SalesAndPurchase*') as $key => $value)
			{
				$this->remove($key);
			}
		}

		return true;
	}

	/**
	 *
	 */
	public function asArray($request, $nest = true)
	{
		$this->_cleanup();

		return parent::asArray($request, $nest);
	}

	/**
	 * Convert this object to a valid qbXML request
	 *
	 * @param string $request					The type of request to convert this to (examples: CustomerAddRq, CustomerModRq, CustomerQueryRq)
	 * @param boolean $todo_for_empty_elements	A constant, one of: QUICKBOOKS_XML_XML_COMPRESS, QUICKBOOKS_XML_XML_DROP, QUICKBOOKS_XML_XML_PRESERVE
	 * @param string $indent
	 * @param string $root
	 * @return string
	 */
	public function asQBXML($request, $version = null, $locale = null, $root = null)
	{
		$this->_cleanup();

		return parent::asQBXML($request, $version, $locale, $root);
	}

	/**
	 * Tell what type of object this is
	 *
	 * @return string
	 */
	public function object()
	{
		return QUICKBOOKS_OBJECT_NONINVENTORYITEM;
	}
}


<?php

/**
 * QuickBooks Customer object container
 *
 * @author Keith Palmer <keith@consolibyte.com>
 * @license LICENSE.txt
 *
 * @package QuickBooks
 * @subpackage Object
 */

/**
 * Base object class
 */
QuickBooks_Loader::load('/QuickBooks/QBXML/Object.php');

/**
 * QuickBooks Customer object class
 */
class QuickBooks_QBXML_Object_Customer extends QuickBooks_QBXML_Object
{
	/**
	 * Create a new QuickBooks_Object_Customer object
	 *
	 * @param array $arr
	 */
	public function __construct($arr = array())
	{
		parent::__construct($arr);
	}

	/**
	 * Set the ListID of this customer record
	 *
	 * @param string $ListID
	 * @return boolean
	 */
	public function setListID($ListID)
	{
		return $this->set('ListID', $ListID);
	}

	/**
	 * Get the ListID of this customer record
	 *
	 * @return string
	 */
	public function getListID()
	{
		return $this->get('ListID');
	}

	/**
	 * Set the ListID of the parent client
	 *
	 * @param string $ListID
	 * @return boolean
	 */
	public function setParentListID($ListID)
	{
		return $this->set('ParentRef ListID', $ListID);
	}

	/**
	 * Set the FullName of the parent client
	 *
	 * @param string $name
	 * @return boolean
	 */
	public function setParentName($name)
	{
		return $this->set('ParentRef FullName', $name);
	}

	public function setParentFullName($FullName)
	{
		return $this->set('ParentRef FullName', $FullName);
	}

	/**
	 * Set the application id of the parent client
	 *
	 * @param string $id
	 * @return boolean
	 */
	public function setParentApplicationID($id)
	{
		return $this->set('ParentRef ' . QUICKBOOKS_API_APPLICATIONID, $this->encodeApplicationID(QUICKBOOKS_OBJECT_CUSTOMER, QUICKBOOKS_LISTID, $id));
	}

	/**
	 * Get the ListID of the parent client (if exists)
	 *
	 * @return string
	 */
	public function getParentListID()
	{
		return $this->get('ParentRef ListID');
	}

	/**
	 * Get the FullName of the parent client (if exists)
	 *
	 * @return string
	 */
	public function getParentName()
	{
		return $this->get('ParentRef FullName');
	}

	/**
	 * Get the application id of the parent client
	 *
	 * @return string
	 */
	public function getParentApplicationID()
	{
		return $this->get('ParentRef ' . QUICKBOOKS_API_APPLICATIONID);
	}

	public function setCurrencyListID($lid)
	{
		return $this->set('CurrencyRef ListID', $lid);
	}

	public function setCurrencyFullName($FullName)
	{
		return $this->setFullNameType('CurrencyRef FullName', null, null, $FullName);
	}

	/**
	 * Set the customer type list id
	 */
	public function setCustomerTypeListID($lid)
	{
		return $this->set('CustomerTypeRef ListID', $lid);
	}

	public function setCustomerTypeFullName($FullName)
	{
		return $this->setFullNameType('CustomerTypeRef FullName', null, null, $FullName);
	}

	public function setCustomerTypeName($name)
	{
		return $this->set('CustomerTypeRef FullName', $name);
	}

	public function setCustomerTypeApplicationID($value)
	{
		return $this->set('CustomerTypeRef ' . QUICKBOOKS_API_APPLICATIONID, $this->encodeApplicationID(QUICKBOOKS_OBJECT_CUSTOMERTYPE, QUICKBOOKS_LISTID, $value));
	}

	/**
	 * @deprecated
	 */
	public function setTermsName($name)
	{
		return $this->set('TermsRef FullName', $name);
	}

	public function setTermsFullName($name)
	{
		return $this->set('TermsRef FullName', $name);
	}

	public function setTermsListID($ListID)
	{
		return $this->set('TermsRef ListID', $ListID);
	}

	/*
	public function setTermsApplicationID($value)
	{
		return $this->set('TermsRef ' . QUICKBOOKS_API_APPLICATIONID, $this->encodeApplicationID(QUICKBOOKS_OBJECT_TERMS, QUICKBOOKS_LISTID, $value));
	}

	public function getTermsApplicationID()
	{
		return $this->get('TermsRef ' . QUICKBOOKS_API_APPLICATIONID);
	}*/

	public function getTermsName()
	{
		return $this->get('TermsRef FullName');
	}

	public function getTermsListID()
	{
		return $this->get('TermsRef ListID');
	}

	public function setSalesRepName($name)
	{
		return $this->set('SalesRepRef FullName', $name);
	}

	public function setSalesRepFullName($name)
	{
		return $this->set('SalesRepRef FullName', $name);
	}

	public function setSalesRepListID($ListID)
	{
		return $this->set('SalesRepRef ListID', $ListID);
	}

	/*
	public function setSalesRepApplicationID($value)
	{
		return $this->set('SalesRepRef ' . QUICKBOOKS_API_APPLICATIONID, $this->encodeApplicationID(QUICKBOOKS_OBJECT_SALESREP, QUICKBOOKS_LISTID, $value));
	}

	public function getSalesRepApplicationID()
	{
		return $this->get('SalesRepRef ' . QUICKBOOKS_API_APPLICATIONID);
	}*/

	public function getSalesRepName()
	{
		return $this->get('SalesRepRef FullName');
	}

	public function getSalesRepListID()
	{
		return $this->get('SalesRepRef ListID');
	}

	/**
	 * Set the delivery method
	 *
	 * @see QUICKBOOKS_CUSTOMER_DELIVERYMETHOD_PRINT, QUICKBOOKS_CUSTOMER_DELIVERYMETHOD_EMAIL, QUICKBOOKS_CUSTOMER_DELIVERYMETHOD_FAX
	 *
	 * Only supported by QuickBooks Online Edition as of qbXML version 7.0.
	 * QuickBooks Online Edition has a bug where if you do not provide a
	 * DeliveryMethod when issueing CustomerAdd or CustomerMod requests, the
	 * CustomerAdd or CustomerMod request will take a very long time to
	 * process (2+ minutes sometimes). The fix is to simply provide this tag,
	 * after which requests process very quickly (2 seconds or less).
	 *
	 * @param string $value
	 * @return boolean
	 */
	public function setDeliveryMethod($value)
	{
		return $this->set('DeliveryMethod', $value);
	}

	/**
	 * Get the delivery method
	 *
	 * @see QUICKBOOKS_CUSTOMER_DELIVERYMETHOD_PRINT, QUICKBOOKS_CUSTOMER_DELIVERYMETHOD_EMAIL, QUICKBOOKS_CUSTOMER_DELIVERYMETHOD_FAX
	 *
	 * @return string
	 */
	public function getDeliveryMethod()
	{
		return $this->get('DeliveryMethod');
	}

	/**
	 * Set the name of this customer
	 *
	 * NOTE: This will be auto-set to ->getFirstName() ->getLastName() if you
	 * don't set it explicitly.
	 *
	 * @param string $name
	 * @return boolean
	 */
	public function setName($name)
	{
		return $this->set('Name', $name);
	}

	/**
	 * Get the name of this customer
	 *
	 * @TODO What should the behavior of this be if "Name" is not set...?
	 *
	 * @return string
	 */
	public function getName()
	{
		return $this->get('Name');
	}

	/**
	 * Set the full name of this customer (full name)
	 *
	 * NOTE: This will be auto-set to ->getName() if you don't set it
	 * explicitly.
	 *
	 * @param string $name
	 * @return boolean
	 */
	public function setFullName($name)
	{
		return $this->setFullNameType('FullName', 'Name', 'ParentRef FullName', $name);
	}

	/**
	 * Get the name of this customer (full name)
	 *
	 * @return string
	 */
	public function getFullName()
	{
		return $this->getFullNameType('FullName', 'Name', 'ParentRef FullName');
	}

	/**
	 * Set the company name of this customer
	 *
	 * @param string $name
	 * @return boolean
	 */
	public function setCompanyName($name)
	{
		return $this->set('CompanyName', $name);
	}

	/**
	 * Get the company name of this customer
	 *
	 * @return string
	 */
	public function getCompanyName()
	{
		return $this->get('CompanyName');
	}

	/**
	 * Set the first name of this customer
	 *
	 * @param string $name
	 * @return boolean
	 */
	public function setFirstName($fname)
	{
		return $this->set('FirstName', $fname);
	}

	/**
	 * Get the first name of this customer
	 *
	 * @return string
	 */
	public function getFirstName()
	{
		return $this->get('FirstName');
	}

	/**
	 * Set the last name of this customer
	 *
	 * @param string $lname
	 * @return boolean
	 */
	public function setLastName($lname)
	{
		return $this->set('LastName', $lname);
	}

	/**
	 * Get the last name of this customer
	 *
	 * @return string
	 */
	public function getLastName()
	{
		return $this->get('LastName');
	}

	public function setMiddleName($mname)
	{
		return $this->set('MiddleName', $mname);
	}

	public function getMiddleName()
	{
		return $this->get('MiddleName');
	}

	public function getShipAddress($part = null, $defaults = array())
	{
		return $this->_getXYZAddress('Ship', '', $part, $defaults);
	}

	public function setShipAddress($addr1, $addr2 = '', $addr3 = '', $addr4 = '', $addr5 = '', $city = '', $state = '', $province = '', $postalcode = '', $country = '', $note = '')
	{
		return $this->_setXYZAddress('Ship', '', $addr1, $addr2, $addr3, $addr4, $addr5, $city, $state, $province, $postalcode, $country, $note);
	}

	public function getBillAddress($part = null, $defaults = array())
	{
		return $this->_getXYZAddress('Bill', '', $part, $defaults);
	}

	public function setBillAddress(
		$addr1,
		$addr2 = '',
		$addr3 = '',
		$addr4 = '',
		$addr5 = '',
		$city = '',
		$state = '',
		$province = '',
		$postalcode = '',
		$country = '',
		$note = '')
	{
		return $this->_setXYZAddress('Bill', '', $addr1, $addr2, $addr3, $addr4, $addr5, $city, $state, $province, $postalcode, $country, $note);
	}

	public function getShipAddressBlock($part = null, $defaults = array())
	{
		return $this->_getXYZAddress('Ship', 'Block', $part, $defaults);
	}

	public function getBillAddressBlock($part = null, $defaults = array())
	{
		return $this->_getXYZAddress('Bill', 'Block', $part, $defaults);
	}

	protected function _setXYZAddress($pre, $post, $addr1, $addr2, $addr3, $addr4, $addr5, $city, $state, $province, $postalcode, $country, $note)
	{
		$b = FALSE;
		for ($i = 1; $i <= 5; $i++)
		{
			$b = $this->set($pre . 'Address' . $post . ' Addr' . $i, ${'addr' . $i});
		}

		$b = $this->set($pre . 'Address' . $post . ' City', $city);
		$b = $this->set($pre . 'Address' . $post . ' State', $state);
		$b = $this->set($pre . 'Address' . $post . ' Province', $province);
		$b = $this->set($pre . 'Address' . $post . ' PostalCode', $postalcode);
		$b = $this->set($pre . 'Address' . $post . ' Country', $country);
		$b = $this->set($pre . 'Address' . $post . ' Note', $note);

		return $b;
	}

	protected function _getXYZAddress($pre, $post, $part = null, $defaults = array())
	{
		if (!is_null($part))
		{
			return $this->get($pre . 'Address' . $post . ' ' . $part);
		}

		return $this->getArray($pre . 'Address' . $post . ' *', $defaults);
	}

	/**
	 *
	 *
	 * @param string $phone
	 * @return boolean
	 */
	public function setPhone($phone)
	{
		return $this->set('Phone', $phone);
	}

	public function getPhone()
	{
		return $this->get('Phone');
	}

	/**
	 * Set the alternate phone number for this customer
	 *
	 * @param string $phone
	 * @return boolean
	 */
	public function setAltPhone($phone)
	{
		return $this->set('AltPhone', $phone);
	}

	public function getAltPhone()
	{
		return $this->get('AltPhone');
	}

	/**
	 * Set the fax number for this customer
	 *
	 * @param string $fax
	 * @return boolean
	 */
	public function setFax($fax)
	{
		return $this->set('Fax', $fax);
	}

	public function getFax()
	{
		return $this->get('Fax');
	}

	/**
	 * Set the e-mail address for this customer
	 *
	 * @param string $email
	 * @return boolean
	 */
	public function setEmail($email)
	{
		return $this->set('Email', $email);
	}

	public function getEmail()
	{
		return $this->get('Email');
	}

	public function setAccountNumber($num)
	{
		return $this->set('AccountNumber', $num);
	}

	public function getAccountNumber()
	{
		return $this->get('AccountNumber');
	}

	/**
	 * Set the contact person for this customer
	 *
	 * @param string $contact
	 * @return boolean
	 */
	public function setContact($contact)
	{
		return $this->set('Contact', $contact);
	}

	/**
	 * Get the name of the contact at this company/customer
	 *
	 * @return string
	 */
	public function getContact()
	{
		return $this->get('Contact');
	}

	/**
	 * Set the alternate contact for this customer
	 *
	 * @param string $contact
	 * @return boolean
	 */
	public function setAltContact($contact)
	{
		return $this->set('AltContact', $contact);
	}

	/**
	 * Get the name of the alternate contact for this customer/company
	 *
	 * @return string
	 */
	public function getAltContact()
	{
		return $this->get('AltContact');
	}

	/**
	 * Set the salutation for this customer
	 *
	 * @param string $salut
	 * @return boolean
	 */
	public function setSalutation($salut)
	{
		return $this->set('Salutation', $salut);
	}

	/**
	 *
	 *
	 * @return string
	 */
	public function getSalutation()
	{
		return $this->get('Salutation');
	}

	public function getCustomerTypeApplicationID()
	{
		return $this->get('CustomerTypeRef ' . QUICKBOOKS_API_APPLICATIONID);
	}

	public function getCustomerTypeListID()
	{
		return $this->get('CustomerTypeRef ListID');
	}

	public function getCustomerTypeName()
	{
		return $this->get('CustomerTypeRef FullName');
	}

	public function setOpenBalance($balance)
	{
		return $this->set('OpenBalance', (float) $balance);
	}

	public function getOpenBalance()
	{
		return $this->get('OpenBalance');
	}

	public function setOpenBalanceDate($date)
	{
		return $this->setDateType('OpenBalanceDate', $date);
	}

	public function getOpenBalanceDate($format = 'Y-m-d')
	{
		return $this->getDateType('OpenBalanceDate', $format);
	}

	/**
	 * Get the balance for this customer (not including sub-customers or jobs)
	 *
	 * @return float
	 */
	public function getBalance()
	{
		return $this->get('Balance');
	}

	public function setBalance($value)
	{
		return $this->set('Balance', $value);
	}

	/**
	 * Get the total balance for this customer and all of this customer's sub-customers/jobs
	 *
	 * @return float
	 */
	public function getTotalBalance()
	{
		return $this->get('TotalBalance');
	}

	public function setTotalBalance($value)
	{
		return $this->set('TotalBalance', $value);
	}

	public function setSalesTaxCodeName($name)
	{
		return $this->set('SalesTaxCodeRef FullName', $name);
	}

	public function setSalesTaxCodeListID($ListID)
	{
		return $this->set('SalesTaxCodeRef ListID', $ListID);
	}

	public function setSalesTaxCodeApplicationID($value)
	{
		return $this->set('SalesTaxCodeRef ' . QUICKBOOKS_API_APPLICATIONID, $this->encodeApplicationID(QUICKBOOKS_OBJECT_SALESTAXCODE, QUICKBOOKS_LISTID, $value));
	}

	public function getSalesTaxCodeApplicationID()
	{
		return $this->get('SalesTaxCodeRef ' . QUICKBOOKS_API_APPLICATIONID);
	}

	public function getSalesTaxCodeName()
	{
		return $this->get('SalesTaxCodeRef FullName');
	}

	public function getSalesTaxCodeListID()
	{
		return $this->get('SalesTaxCodeRef ListID');
	}

	/**
	 * Set the credit card information for this customer
	 *
	 * @param string $cardno		The credit card number
	 * @param integer $expmonth		The expiration month (1 is January, 2 is February, etc.)
	 * @param integer $expyear		The expiration year
	 * @param string $name			The name on the credit card
	 * @param string $address		The billing address for the credit card
	 * @param string $postalcode	The postal code for the credit card
	 * @return boolean
	 */
	public function setCreditCardInfo($cardno, $expmonth, $expyear, $name, $address, $postalcode)
	{
		// should probably do better checking here for failed sets.
		$b = FALSE;
		$b = $this->set('CreditCardInfo CreditCardNumber', $cardno);
		$b = $this->set('CreditCardInfo ExpirationMonth', $expmonth);
		$b = $this->set('CreditCardInfo ExpirationYear', $expyear);
		$b = $this->set('CreditCardInfo NameOnCard', $name);
		$b = $this->set('CreditCardInfo CreditCardAddress', $address);
		$b = $this->set('CreditCardInfo CreditCardPostalCode', $postalcode);

		return $b;
	}

	/**
	 * Get credit card information for this customer
	 *
	 * @param string $part		If you just want a specific part of the card info, specify it here
	 * @param array $defaults	Defaults for the card data if you want the entire array
	 * @return mixed			If you specify a part, a string part is returned, otherwise an array of card data
	 */
	public function getCreditCardInfo($part = null, $defaults = array())
	{
		if (!is_null($part))
		{
			return $this->get('CreditCardInfo ' . $part);
		}

		return $this->getArray('CreditCardInfo *', $defaults);
	}

	public function setNotes($notes)
	{
		return $this->set('Notes', $notes);
	}

	public function getNotes()
	{
		return $this->get('Notes');
	}

	public function setPriceLevelName($name)
	{
		return $this->set('PriceLevelRef FullName', $name);
	}

	public function setPriceLevelListID($ListID)
	{
		return $this->set('PriceLevelRef ListID', $ListID);
	}

	public function setPriceLevelApplicationID($value)
	{
		return $this->set('PriceLevelRef ' . QUICKBOOKS_API_APPLICATIONID, $this->encodeApplicationID(QUICKBOOKS_OBJECT_PRICELEVEL, QUICKBOOKS_LISTID, $value));
	}

	public function getPriceLevelApplicationID()
	{
		return $this->get('PriceLevelRef ' . QUICKBOOKS_API_APPLICATIONID);
	}

	public function getPriceLevelName()
	{
		return $this->get('PriceLevelRef FullName');
	}

	public function setPreferredDeliveryMethod($value)
	{
		return $this->set('PreferredDeliveryMethod', $value);
	}

	public function getPreferredDeliveryMethod()
	{
		return $this->get('PreferredDeliveryMethod');
	}

	/**
	 * Get the price level list id.
	 *
	 * @return string
	 */
	public function getPriceLevelListID()
	{
		return $this->get('PriceLevelRef ListID');
	}

	/**
	 * Initializes the default name if one hasn't been set.
	 *
	 * @return boolean
	 */
	protected function _cleanup()
	{
		//return $this->setNameAsFirstLast();
	}

	/**
	 * Returns the customer object as an array
	 *
	 * @return array
	 */
	public function asArray($request, $nest = true)
	{
		$this->_cleanup();

		return parent::asArray($request, $nest);
	}

	/**
	 * Convert this object to a valid qbXML request
	 *
	 * @param string $request The type of request to convert this to (examples: CustomerAddRq, CustomerModRq, CustomerQueryRq)
	 * @param boolean $todo_for_empty_elements A constant, one of: QUICKBOOKS_XML_XML_COMPRESS, QUICKBOOKS_XML_XML_DROP, QUICKBOOKS_XML_XML_PRESERVE
	 * @param string $indent
	 * @param string $root
	 * @return string
	 */
	public function asQBXML($request, $version = null, $locale = null, $root = null)
	{
		$this->_cleanup();

		return parent::asQBXML($request, $version, $locale, $root);
	}

	/**
	 * Tell what type of object this is
	 *
	 * @return string
	 */
	public function object()
	{
		return QUICKBOOKS_OBJECT_CUSTOMER;
	}
}


<?php

/**
 *
 *
 * Copyright (c) 2010 Keith Palmer / ConsoliBYTE, LLC.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.opensource.org/licenses/eclipse-1.0.php
 *
 * @license LICENSE.txt
 * @author Keith Palmer <Keith@ConsoliBYTE.com>
 *
 * @package QuickBooks
 * @subpackage IPP
 */

QuickBooks_Loader::load('/QuickBooks/IPP/Service.php');

class QuickBooks_IPP_Service_SalesReceipt extends QuickBooks_IPP_Service
{
	public function update($Context, $realmID, $IDType, $Object)
	{
		return parent::_update($Context, $realmID, QuickBooks_IPP_IDS::RESOURCE_SALESRECEIPT, $Object, $IDType);
	}

	/**
	 * Add a new sales receipt to IDS/QuickBooks
	 *
	 * @param QuickBooks_IPP_Context $Context
	 * @param string $realmID
	 * @param QuickBooks_IPP_Object_SalesReceipt $Object		The sales receipt to add
	 * @return string											The Id value of the new sales receipt
	 */
	public function add($Context, $realmID, $Object)
	{
		return parent::_add($Context, $realmID, QuickBooks_IPP_IDS::RESOURCE_SALESRECEIPT, $Object);
	}

	public function query($Context, $realm, $query)
	{
		return parent::_query($Context, $realm, $query);
	}

	public function findById($Context, $realmID, $ID, $domain = null)
	{
		$xml = null;
		return parent::_findById($Context, $realmID, QuickBooks_IPP_IDS::RESOURCE_SALESRECEIPT, $ID, $domain, $xml);
	}

	public function delete($Context, $realmID, $IDType)
	{
		return parent::_delete($Context, $realmID, QuickBooks_IPP_IDS::RESOURCE_SALESRECEIPT, $IDType);
	}

	public function void($Context, $realmID, $IDType)
	{
		return parent::_void($Context, $realmID, QuickBooks_IPP_IDS::RESOURCE_SALESRECEIPT, $IDType);
	}

	public function pdf($Context, $realmID, $IDType)
	{
		return parent::_pdf($Context, $realmID, QuickBooks_IPP_IDS::RESOURCE_SALESRECEIPT, $IDType);
	}

	public function send($Context, $realmID, $IDType)
	{
		return parent::_send($Context, $realmID, QuickBooks_IPP_IDS::RESOURCE_SALESRECEIPT, $IDType);
	}
}


<?php

/**
 *
 *
 * Copyright (c) 2010 Keith Palmer / ConsoliBYTE, LLC.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.opensource.org/licenses/eclipse-1.0.php
 *
 * @license LICENSE.txt
 * @author Keith Palmer <Keith@ConsoliBYTE.com>
 *
 * @package QuickBooks
 * @subpackage IPP
 */

QuickBooks_Loader::load('/QuickBooks/IPP/Service.php');

class QuickBooks_IPP_Service_Class extends QuickBooks_IPP_Service
{
	public function findAll($Context, $realmID)
	{
		$xml = null;
		return parent::_findAll($Context, $realmID, QuickBooks_IPP_IDS::RESOURCE_CLASS, $xml);
	}

	/**
	 * Get a customer by ID
	 *
	 * @param QuickBooks_IPP_Context $Context
	 * @param string $realmID
	 * @param string $ID						The ID of the customer (this expects an IdType, which includes the domain)
	 * @return QuickBooks_IPP_Object_Customer	The customer object
	 */
	public function findById($Context, $realmID, $IDType)
	{
		$xml = null;
		return parent::_findById($Context, $realmID, QuickBooks_IPP_IDS::RESOURCE_CLASS, $IDType, $xml);
	}

	/**
	 * Add a new class to QuickBooks
	 *
	 * @param QuickBooks_IPP_Context $Context
	 * @param string $realmID
	 * @param QuickBooks_IPP_Object_Class
	 * @return string								The new ID of the created class
	 */
	public function add($Context, $realmID, $Object)
	{
		return parent::_add($Context, $realmID, QuickBooks_IPP_IDS::RESOURCE_CLASS, $Object);
	}

	public function query($Context, $realm, $query)
	{
		return parent::_query($Context, $realm, $query);
	}
}


<?php

/**
 * Check class for QuickBooks
 *
 * @author Keith Palmer Jr. <keith@ConsoliBYTE.com>
 * @license LICENSE.txt
 *
 * @package QuickBooks
 * @subpackage Object
 */

/**
 * QuickBooks object base class
 */
QuickBooks_Loader::load('/QuickBooks/QBXML/Object.php');

/**
 *
 */
class QuickBooks_QBXML_Object_Check_ItemGroupLine extends QuickBooks_QBXML_Object
{
	/**
	 * Create a new QuickBooks_Object_Check_ItemGroupLine object
	 *
	 * @param array $arr
	 */
	public function __construct($arr = array())
	{
		parent::__construct($arr);
	}

	// Path: ItemGroupRef ListID, datatype:

	/**
	 * Set the ItemGroupRef ListID for the Check
	 *
	 * @param string $ListID		The ListID of the record to reference
	 * @return boolean
	 */
	public function setItemGroupListID($ListID)
	{
		return $this->set('ItemGroupRef ListID', $ListID);
	}

	/**
	 * Get the ItemGroupRef ListID for the Check
	 *
	 * @return string
	 */
	public function getItemGroupListID()
	{
		return $this->get('ItemGroupRef ListID');
	}

	/**
	 * Set the primary key for the related record within your own application for the Check
	 *
	 * @param mixed $value			The primary key within your own application
	 * @return string
	 */
	public function setItemGroupApplicationID($value)
	{
		return $this->set('ItemGroupRef ' . QUICKBOOKS_API_APPLICATIONID, $this->encodeApplicationID(QUICKBOOKS_OBJECT_ITEMGROUP, QUICKBOOKS_LISTID, $value));
	}

	// Path: ItemGroupRef FullName, datatype:

	/**
	 * Set the ItemGroupRef FullName for the Check
	 *
	 * @param string $FullName		The FullName of the record to reference
	 * @return boolean
	 */
	public function setItemGroupName($FullName)
	{
		return $this->set('ItemGroupRef FullName', $FullName);
	}

	/**
	 * Get the ItemGroupRef FullName for the Check
	 *
	 * @return string
	 */
	public function getItemGroupName()
	{
		return $this->get('ItemGroupRef FullName');
	}

	// Path: Desc, datatype:

	/**
	 * Set the Desc for the Check
	 *
	 * @param string $value
	 * @return boolean
	 */
	public function setDesc($value)
	{
		return $this->set('Desc', $value);
	}

	/**
	 * Get the Desc for the Check
	 *
	 * @return string
	 */
	public function getDesc()
	{
		return $this->get('Desc');
	}

	/**
	 * @see QuickBooks_Object_Check_ItemGroupLineAdd::setDesc()
	 */
	public function setDescription($value)
	{
		$this->setDesc($value);
	}

	/**
	 * @see QuickBooks_Object_Check_ItemGroupLineAdd::getDesc()
	 */
	public function getDescription()
	{
		$this->getDesc();
	}
	// Path: Quantity, datatype:

	/**
	 * Set the Quantity for the Check
	 *
	 * @param string $value
	 * @return boolean
	 */
	public function setQuantity($value)
	{
		return $this->set('Quantity', (float) $value);
	}

	/**
	 * Get the Quantity for the Check
	 *
	 * @return string
	 */
	public function getQuantity()
	{
		return $this->get('Quantity');
	}

	// Path: UnitOfMeasure, datatype:

	/**
	 * Set the UnitOfMeasure for the Check
	 *
	 * @param string $value
	 * @return boolean
	 */
	public function setUnitOfMeasure($value)
	{
		return $this->set('UnitOfMeasure', $value);
	}

	/**
	 * Get the UnitOfMeasure for the Check
	 *
	 * @return string
	 */
	public function getUnitOfMeasure()
	{
		return $this->get('UnitOfMeasure');
	}

	public function object()
	{
		return 'ItemGroupLine';
	}
}