diff --git a/src/Marello/Bundle/InventoryBundle/Entity/Repository/BalancedInventoryRepository.php b/src/Marello/Bundle/InventoryBundle/Entity/Repository/BalancedInventoryRepository.php index 0fb3f3d42..921768774 100644 --- a/src/Marello/Bundle/InventoryBundle/Entity/Repository/BalancedInventoryRepository.php +++ b/src/Marello/Bundle/InventoryBundle/Entity/Repository/BalancedInventoryRepository.php @@ -30,7 +30,7 @@ public function setAclHelper(AclHelper $aclHelper) * * @param ProductInterface $product * @param SalesChannelGroup $group - * @return BalancedInventoryLevel + * @return null|BalancedInventoryLevel */ public function findExistingBalancedInventory(ProductInterface $product, SalesChannelGroup $group) { diff --git a/src/Marello/Bundle/OrderBundle/Entity/Order.php b/src/Marello/Bundle/OrderBundle/Entity/Order.php index 23319d23b..e17e5bd69 100644 --- a/src/Marello/Bundle/OrderBundle/Entity/Order.php +++ b/src/Marello/Bundle/OrderBundle/Entity/Order.php @@ -489,6 +489,48 @@ class Order extends ExtendOrder implements */ protected $data = []; + /** + * @var \DateTime + * + * @ORM\Column(name="delivery_date", type="datetime", nullable=true) + * @Oro\ConfigField( + * defaultValues={ + * "dataaudit"={ + * "auditable"=true + * } + * } + * ) + */ + protected $deliveryDate; + + /** + * @var string + * + * @ORM\Column(name="order_note",type="text", nullable=true) + * @Oro\ConfigField( + * defaultValues={ + * "dataaudit"={ + * "auditable"=true + * } + * } + * ) + */ + protected $orderNote; + + /** + * @var string + * + * @ORM\Column(name="po_number",type="string", nullable=true, length=255) + * @Oro\ConfigField( + * defaultValues={ + * "dataaudit"={ + * "auditable"=true + * } + * } + * ) + */ + protected $poNumber; + /** * @param AbstractAddress|null $billingAddress * @param AbstractAddress|null $shippingAddress @@ -1227,4 +1269,61 @@ public function getData() { return $this->data; } + + /** + * @return \DateTime + */ + public function getDeliveryDate() + { + return $this->deliveryDate; + } + + /** + * @param \DateTime $deliveryDate + * @return $this + */ + public function setDeliveryDate(\DateTime $deliveryDate = null) + { + $this->deliveryDate = $deliveryDate; + + return $this; + } + + /** + * @return string + */ + public function getOrderNote() + { + return $this->orderNote; + } + + /** + * @param string $orderNote + * @return $this + */ + public function setOrderNote(string $orderNote) + { + $this->orderNote = $orderNote; + + return $this; + } + + /** + * @return string + */ + public function getPoNumber() + { + return $this->poNumber; + } + + /** + * @param string $poNumber + * @return $this + */ + public function setPoNumber(string $poNumber) + { + $this->poNumber = $poNumber; + + return $this; + } } diff --git a/src/Marello/Bundle/OrderBundle/Entity/OrderItem.php b/src/Marello/Bundle/OrderBundle/Entity/OrderItem.php index d25d2fec3..5103e5bb1 100644 --- a/src/Marello/Bundle/OrderBundle/Entity/OrderItem.php +++ b/src/Marello/Bundle/OrderBundle/Entity/OrderItem.php @@ -337,6 +337,22 @@ class OrderItem extends ExtendOrderItem implements */ protected $status; + + /** + * @var \Extend\Entity\EV_Marello_Product_Unit + * @Oro\ConfigField( + * defaultValues={ + * "dataaudit"={ + * "auditable"=true + * }, + * "importexport"={ + * "excluded"=true + * } + * } + * ) + */ + protected $productUnit; + /** * OrderItem constructor. */ @@ -742,4 +758,23 @@ public function setStatus($status) return $this; } + + /** + * @return \Extend\Entity\EV_Marello_Product_Unit + */ + public function getProductUnit() + { + return $this->productUnit; + } + + /** + * @param string $productUnit + * @return $this + */ + public function setProductUnit($productUnit) + { + $this->productUnit = $productUnit; + + return $this; + } } diff --git a/src/Marello/Bundle/OrderBundle/EventListener/Doctrine/OrderItemProductUnitListener.php b/src/Marello/Bundle/OrderBundle/EventListener/Doctrine/OrderItemProductUnitListener.php new file mode 100644 index 000000000..f96cad92d --- /dev/null +++ b/src/Marello/Bundle/OrderBundle/EventListener/Doctrine/OrderItemProductUnitListener.php @@ -0,0 +1,55 @@ +doctrineHelper = $doctrineHelper; + } + + + /** + * @param LifecycleEventArgs $args + */ + public function postPersist(LifecycleEventArgs $args) + { + $entity = $args->getEntity(); + if ($entity instanceof OrderItem && $entity->getProductUnit() === null) { + $entity->setProductUnit($this->findDefaultProductUnit()); + } + } + + /** + * @return null|object + */ + private function findDefaultProductUnit() + { + $productUnitClass = ExtendHelper::buildEnumValueClassName(LoadProductUnitData::PRODUCT_UNIT_ENUM_CLASS); + $productUnit = $this->doctrineHelper + ->getEntityManagerForClass($productUnitClass) + ->getRepository($productUnitClass) + ->findOneByDefault(true); + + if ($productUnit) { + return $productUnit; + } + + return null; + } +} diff --git a/src/Marello/Bundle/OrderBundle/Form/Type/OrderType.php b/src/Marello/Bundle/OrderBundle/Form/Type/OrderType.php index 53df8d48d..7c01ca594 100644 --- a/src/Marello/Bundle/OrderBundle/Form/Type/OrderType.php +++ b/src/Marello/Bundle/OrderBundle/Form/Type/OrderType.php @@ -16,6 +16,7 @@ use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\CallbackTransformer; use Symfony\Component\Form\Extension\Core\Type\HiddenType; +use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormEvent; @@ -98,6 +99,28 @@ public function buildForm(FormBuilderInterface $builder, array $options) 'required' => false ] ) + ->add( + 'deliveryDate', + OroDateTimeType::class, + [ + 'required' => false + ] + ) + ->add( + 'poNumber', + TextType::class, + [ + 'label' => 'marello.order.po_number.label', + 'required' => false + ] + ) + ->add( + 'orderNote', + TextareaType::class, + [ + 'required' => false + ] + ) ->add('items', OrderItemCollectionType::class); $this->addPaymentFields($builder); $this->addShippingFields($builder, $options['data']); diff --git a/src/Marello/Bundle/OrderBundle/Migrations/Schema/MarelloOrderBundleInstaller.php b/src/Marello/Bundle/OrderBundle/Migrations/Schema/MarelloOrderBundleInstaller.php index fc0368f31..0f5990365 100644 --- a/src/Marello/Bundle/OrderBundle/Migrations/Schema/MarelloOrderBundleInstaller.php +++ b/src/Marello/Bundle/OrderBundle/Migrations/Schema/MarelloOrderBundleInstaller.php @@ -43,7 +43,7 @@ class MarelloOrderBundleInstaller implements */ public function getMigrationVersion() { - return 'v1_11'; + return 'v1_12'; } /** @@ -145,6 +145,9 @@ protected function createMarelloOrderOrderTable(Schema $schema) $table->addIndex(['billing_address_id'], 'IDX_A619DD6443656FE6', []); $table->addIndex(['shipping_address_id'], 'IDX_A619DD64B1835C8F', []); $table->addIndex(['salesChannel_id'], 'IDX_A619DD644C7A5B2E', []); + $table->addColumn('delivery_date', 'datetime', ['notnull' => false]); + $table->addColumn('order_note', 'text', ['notnull' => false]); + $table->addColumn('po_number', 'string', ['length' => 255, 'notnull' => false]); $table->addIndex(['organization_id']); $this->activityExtension->addActivityAssociation($schema, 'marello_notification', $table->getName()); @@ -224,6 +227,17 @@ protected function createMarelloOrderOrderItemTable(Schema $schema) 'extend' => ['owner' => ExtendScope::OWNER_SYSTEM], ] ); + $this->extendExtension->addEnumField( + $schema, + $table, + 'productUnit', + 'marello_product_unit', + false, + false, + [ + 'extend' => ['owner' => ExtendScope::OWNER_SYSTEM], + ] + ); $table->setPrimaryKey(['id']); $table->addIndex(['organization_id']); } diff --git a/src/Marello/Bundle/OrderBundle/Migrations/Schema/v1_12/MarelloOrderBundle.php b/src/Marello/Bundle/OrderBundle/Migrations/Schema/v1_12/MarelloOrderBundle.php new file mode 100644 index 000000000..b200c3819 --- /dev/null +++ b/src/Marello/Bundle/OrderBundle/Migrations/Schema/v1_12/MarelloOrderBundle.php @@ -0,0 +1,67 @@ +updateOrderTable($schema); + $this->updateOrderItemTable($schema); + } + + private function updateOrderTable(Schema $schema) + { + $table = $schema->getTable('marello_order_order'); + $table->addColumn('delivery_date', 'datetime', ['notnull' => false]); + $table->addColumn('order_note', 'text', ['notnull' => false]); + $table->addColumn('po_number', 'string', ['length' => 255, 'notnull' => false]); + } + + private function updateOrderItemTable(Schema $schema) + { + $tableName = $this->extendExtension->getNameGenerator()->generateEnumTableName('marello_product_unit'); + // enum table is already available and created... + if ($schema->hasTable($tableName)) { + return; + } + + $table = $schema->getTable('marello_order_order_item'); + $this->extendExtension->addEnumField( + $schema, + $table, + 'productUnit', + 'marello_product_unit', + false, + false, + [ + 'extend' => ['owner' => ExtendScope::OWNER_SYSTEM], + ] + ); + } + + /** + * Sets the ExtendExtension + * + * @param ExtendExtension $extendExtension + */ + public function setExtendExtension(ExtendExtension $extendExtension) + { + $this->extendExtension = $extendExtension; + } +} diff --git a/src/Marello/Bundle/OrderBundle/Resources/config/oro/datagrids.yml b/src/Marello/Bundle/OrderBundle/Resources/config/oro/datagrids.yml index 4081a19ae..ddb6f695c 100644 --- a/src/Marello/Bundle/OrderBundle/Resources/config/oro/datagrids.yml +++ b/src/Marello/Bundle/OrderBundle/Resources/config/oro/datagrids.yml @@ -48,10 +48,18 @@ datagrids: method: formatCurrency context_resolver: Marello\Bundle\DataGridBundle\Grid\FormatterContextResolver::getResolverCurrencyClosure align: right + poNumber: + label: marello.order.po_number.label + frontend_type: string + renderable: false purchaseDate: frontend_type: datetime label: marello.order.purchase_date.label renderable: false + deliveryDate: + frontend_type: datetime + label: marello.order.delivery_date.label + renderable: false createdAt: label: oro.ui.created_at frontend_type: datetime @@ -74,8 +82,12 @@ datagrids: data_name: shippingName grandTotal: data_name: o.grandTotal + poNumber: + data_name: o.poNumber purchaseDate: data_name: o.purchaseDate + deliveryDate: + data_name: o.deliveryDate createdAt: data_name: o.createdAt updatedAt: @@ -102,10 +114,18 @@ datagrids: shippingAddress: type: string data_name: shippingName # This alias is generated using OrderGrdiAddressFilterListener + poNumber: + type: string + data_name: o.poNumber + enabled: false purchaseDate: type: datetime data_name: o.purchaseDate enabled: false + deliveryDate: + type: datetime + data_name: o.deliveryDate + enabled: false createdAt: type: datetime data_name: o.createdAt @@ -305,6 +325,10 @@ datagrids: quantity: label: marello.order.orderitem.quantity.label frontend_type: number + productUnit: + label: marello.order.orderitem.product_unit.label + frontend_type: string + data_name: productUnit price: label: marello.order.orderitem.price.per_unit.label type: localized_number diff --git a/src/Marello/Bundle/OrderBundle/Resources/config/services.yml b/src/Marello/Bundle/OrderBundle/Resources/config/services.yml index f6763b33e..7766ad381 100644 --- a/src/Marello/Bundle/OrderBundle/Resources/config/services.yml +++ b/src/Marello/Bundle/OrderBundle/Resources/config/services.yml @@ -32,6 +32,13 @@ services: tags: - { name: doctrine.event_listener, event: postPersist } + marello_order.listener.doctrine.order_item_product_unit: + class: Marello\Bundle\OrderBundle\EventListener\Doctrine\OrderItemProductUnitListener + arguments: + - '@oro_entity.doctrine_helper' + tags: + - { name: doctrine.event_listener, event: postPersist } + marello_order.listener.doctrine.order_item_status: class: Marello\Bundle\OrderBundle\EventListener\Doctrine\OrderItemStatusListener arguments: diff --git a/src/Marello/Bundle/OrderBundle/Resources/translations/messages.en.yml b/src/Marello/Bundle/OrderBundle/Resources/translations/messages.en.yml index 3a324340c..21a65badf 100644 --- a/src/Marello/Bundle/OrderBundle/Resources/translations/messages.en.yml +++ b/src/Marello/Bundle/OrderBundle/Resources/translations/messages.en.yml @@ -35,6 +35,9 @@ marello: shipping_method_reference.label: Shipping Method Reference shipment.label: Shipment data.label: Data + delivery_date.label: Delivery Date + order_note.label: Note + po_number.label: PO Number shipping_amount_incl_tax.label: Shipping Costs Incl. tax shipping_amount_excl_tax.label: Shipping Costs Excl. tax estimated_shipping_cost_amount.label: Estimated Shipping Costs @@ -63,6 +66,7 @@ marello: packing_slips: Packing Slips invoices: Invoices source_references_information: Source References + optional: Optional # OrderItem entity orderitem: entity_label: Ordered Item @@ -78,6 +82,7 @@ marello: label: Price per_unit.label: Price Per Unit quantity.label: Quantity + product_unit.label: Product Unit original_price_excl_tax.label: Original Price Excl. Tax original_price_incl_tax.label: Original Price Incl. Tax total_price_excl_tax.label: Total Price Excl. Tax diff --git a/src/Marello/Bundle/OrderBundle/Resources/views/Order/create.html.twig b/src/Marello/Bundle/OrderBundle/Resources/views/Order/create.html.twig index 1dc57c96a..e4f1d0a0c 100644 --- a/src/Marello/Bundle/OrderBundle/Resources/views/Order/create.html.twig +++ b/src/Marello/Bundle/OrderBundle/Resources/views/Order/create.html.twig @@ -62,6 +62,9 @@ {{ form_row(form.couponCode) }} {{ form_row(form.localization) }} {{ form_row(form.purchaseDate) }} + {{ form_row(form.deliveryDate) }} + {{ form_row(form.poNumber) }} + {{ form_row(form.orderNote) }} diff --git a/src/Marello/Bundle/OrderBundle/Resources/views/Order/view.html.twig b/src/Marello/Bundle/OrderBundle/Resources/views/Order/view.html.twig index 3112935e3..283a8a66a 100644 --- a/src/Marello/Bundle/OrderBundle/Resources/views/Order/view.html.twig +++ b/src/Marello/Bundle/OrderBundle/Resources/views/Order/view.html.twig @@ -179,6 +179,18 @@ {{ dataGrid.renderGrid('marello-order-invoices-grid', {'orderId': entity.id}) }} {% endset %} + {% set optionalWidget %} +
+
+
+ {{ UI.renderProperty('marello.order.delivery_date.label'|trans, entity.deliveryDate|oro_format_datetime) }} + {{ UI.renderProperty('marello.order.po_number.label'|trans, entity.poNumber) }} + {{ UI.renderProperty('marello.order.order_note.label'|trans, entity.orderNote) }} +
+
+
+ {% endset %} + {% set dataBlocks = [ { 'title': 'marello.order.datablock.general'|trans, @@ -215,6 +227,13 @@ 'subblocks': [ { 'data' : [packingSlips] } ] + }, + { + 'title': 'marello.order.datablock.optional'|trans, + 'class': 'active', + 'subblocks': [ + { 'data' : [optionalWidget] } + ] } ] %} diff --git a/src/Marello/Bundle/PricingBundle/Entity/AssembledChannelPriceList.php b/src/Marello/Bundle/PricingBundle/Entity/AssembledChannelPriceList.php index 95bd60447..70404ac73 100644 --- a/src/Marello/Bundle/PricingBundle/Entity/AssembledChannelPriceList.php +++ b/src/Marello/Bundle/PricingBundle/Entity/AssembledChannelPriceList.php @@ -24,7 +24,7 @@ * ) * @ORM\Table(name="marello_assembled_ch_pr_list") */ -class AssembledChannelPriceList extends ExtendAssembledChannelPriceList +class AssembledChannelPriceList extends ExtendAssembledChannelPriceList implements PriceListInterface { use EntityCreatedUpdatedAtTrait; diff --git a/src/Marello/Bundle/PricingBundle/Entity/AssembledPriceList.php b/src/Marello/Bundle/PricingBundle/Entity/AssembledPriceList.php index 030494686..776f4f45d 100644 --- a/src/Marello/Bundle/PricingBundle/Entity/AssembledPriceList.php +++ b/src/Marello/Bundle/PricingBundle/Entity/AssembledPriceList.php @@ -23,7 +23,7 @@ * ) * @ORM\Table(name="marello_assembled_price_list") */ -class AssembledPriceList extends ExtendAssembledPriceList +class AssembledPriceList extends ExtendAssembledPriceList implements PriceListInterface { use EntityCreatedUpdatedAtTrait; diff --git a/src/Marello/Bundle/PricingBundle/Entity/PriceListInterface.php b/src/Marello/Bundle/PricingBundle/Entity/PriceListInterface.php new file mode 100644 index 000000000..f8fce55bc --- /dev/null +++ b/src/Marello/Bundle/PricingBundle/Entity/PriceListInterface.php @@ -0,0 +1,28 @@ + $options['allowed_empty_value'] === false ? new NotNull() : null, 'label' => 'marello.pricing.productprice.value.label', ]); + + $builder->get('value')->addModelTransformer(new MoneyValueTransformer()); } /** diff --git a/src/Marello/Bundle/PricingBundle/Form/Type/ProductPriceType.php b/src/Marello/Bundle/PricingBundle/Form/Type/ProductPriceType.php index 990efd708..b472d0d29 100644 --- a/src/Marello/Bundle/PricingBundle/Form/Type/ProductPriceType.php +++ b/src/Marello/Bundle/PricingBundle/Form/Type/ProductPriceType.php @@ -3,6 +3,7 @@ namespace Marello\Bundle\PricingBundle\Form\Type; use Marello\Bundle\PricingBundle\Entity\ProductPrice; +use Oro\Bundle\CurrencyBundle\Form\DataTransformer\MoneyValueTransformer; use Oro\Bundle\FormBundle\Form\Type\OroMoneyType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\HiddenType; @@ -37,6 +38,8 @@ public function buildForm(FormBuilderInterface $builder, array $options) 'label' => 'marello.pricing.productprice.value.label', ]); } + + $builder->get('value')->addModelTransformer(new MoneyValueTransformer()); } /** diff --git a/src/Marello/Bundle/ProductBundle/Entity/Product.php b/src/Marello/Bundle/ProductBundle/Entity/Product.php index ea6ec22d8..1ab9af4b8 100644 --- a/src/Marello/Bundle/ProductBundle/Entity/Product.php +++ b/src/Marello/Bundle/ProductBundle/Entity/Product.php @@ -10,7 +10,7 @@ use Marello\Bundle\InventoryBundle\Model\InventoryItemAwareInterface; use Marello\Bundle\PricingBundle\Entity\AssembledChannelPriceList; use Marello\Bundle\PricingBundle\Entity\AssembledPriceList; -use Marello\Bundle\PricingBundle\Entity\ProductChannelPrice; +use Marello\Bundle\PricingBundle\Entity\PriceListInterface; use Marello\Bundle\PricingBundle\Model\PricingAwareInterface; use Marello\Bundle\ProductBundle\Model\ExtendProduct; use Marello\Bundle\SalesBundle\Entity\SalesChannel; @@ -1245,14 +1245,14 @@ public function getSalesChannelTaxCode(SalesChannel $salesChannel) /** * @param SalesChannel $salesChannel - * @return AssembledChannelPriceList|null + * @return PriceListInterface|null */ public function getSalesChannelPrice(SalesChannel $salesChannel) { - /** @var ProductChannelPrice $productChannelPrice */ + /** @var AssembledChannelPriceList $productChannelPrice */ $productChannelPrice = $this->getChannelPrices() ->filter(function ($productChannelPrice) use ($salesChannel) { - /** @var ProductChannelPrice $productChannelPrice */ + /** @var AssembledChannelPriceList $productChannelPrice */ return $productChannelPrice->getChannel() === $salesChannel; }) ->first(); diff --git a/src/Marello/Bundle/ProductBundle/Entity/Repository/ProductRepository.php b/src/Marello/Bundle/ProductBundle/Entity/Repository/ProductRepository.php index 5f1e51c5a..d46be6ff7 100644 --- a/src/Marello/Bundle/ProductBundle/Entity/Repository/ProductRepository.php +++ b/src/Marello/Bundle/ProductBundle/Entity/Repository/ProductRepository.php @@ -56,6 +56,56 @@ public function findByChannel(SalesChannel $salesChannel) return $this->aclHelper->apply($qb->getQuery())->getResult(); } + /** + * @param int $productId + * @return array + */ + public function getSalesChannelIdsByProductId(int $productId): array + { + $qb = $this->createQueryBuilder('product'); + $qb + ->select('sc.id') + ->innerJoin('product.channels', 'sc') + ->where($qb->expr()->eq('product.id', ':productId')) + ->setParameter('productId', $productId); + + $result = $qb->getQuery()->getArrayResult(); + + return array_column($result, 'id'); + } + + /** + * @param array $salesChannelIds + * @return int[] + */ + public function getProductIdsBySalesChannelIds(array $salesChannelIds): array + { + if (empty($salesChannelIds)) { + return []; + } + + $subQb = $this->createQueryBuilder('innerProduct'); + $subQb + ->select('1') + ->innerJoin('innerProduct.channels', 'sc') + ->where( + $subQb->expr()->andX( + $subQb->expr()->in('sc.id', ':salesChannelIds'), + $subQb->expr()->eq('innerProduct.id', 'product.id') + ) + ); + + $qb = $this->createQueryBuilder('product'); + $qb + ->select('product.id') + ->where($qb->expr()->exists($subQb)) + ->setParameter('salesChannelIds', $salesChannelIds); + + $result = $this->aclHelper->apply($qb->getQuery())->getResult(); + + return \array_column($result, 'id'); + } + /** * Return products for specified price list and product IDs * diff --git a/src/Marello/Bundle/ProductBundle/Migrations/Data/ORM/LoadProductUnitData.php b/src/Marello/Bundle/ProductBundle/Migrations/Data/ORM/LoadProductUnitData.php new file mode 100644 index 000000000..381db1d21 --- /dev/null +++ b/src/Marello/Bundle/ProductBundle/Migrations/Data/ORM/LoadProductUnitData.php @@ -0,0 +1,43 @@ + true, + self::PU_BOX => false, + self::PU_PALLET => false + ]; + + /** + * @param ObjectManager $manager + */ + public function load(ObjectManager $manager) + { + $className = ExtendHelper::buildEnumValueClassName(self::PRODUCT_UNIT_ENUM_CLASS); + + /** @var EnumValueRepository $enumRepo */ + $enumRepo = $manager->getRepository($className); + + $priority = 1; + foreach ($this->data as $name => $isDefault) { + $enumOption = $enumRepo->createEnumValue($name, $priority++, $isDefault); + $manager->persist($enumOption); + } + + $manager->flush(); + } +} diff --git a/src/Marello/Bundle/SalesBundle/Acl/Voter/SalesChannelGroupVoter.php b/src/Marello/Bundle/SalesBundle/Acl/Voter/SalesChannelGroupVoter.php new file mode 100644 index 000000000..b20cce1d7 --- /dev/null +++ b/src/Marello/Bundle/SalesBundle/Acl/Voter/SalesChannelGroupVoter.php @@ -0,0 +1,32 @@ +doctrineHelper->getEntityRepository(SalesChannelGroup::class); + + if ($scgRepository->hasAttachedIntegration($identifier)) { + return self::ACCESS_DENIED; + } + + return self::ACCESS_ABSTAIN; + } +} diff --git a/src/Marello/Bundle/SalesBundle/Autocomplete/GroupSalesChannelHandler.php b/src/Marello/Bundle/SalesBundle/Autocomplete/GroupSalesChannelHandler.php new file mode 100644 index 000000000..e1ac4eb22 --- /dev/null +++ b/src/Marello/Bundle/SalesBundle/Autocomplete/GroupSalesChannelHandler.php @@ -0,0 +1,66 @@ +entityRepository || !$this->idFieldName) { + throw new \RuntimeException('Search handler is not fully configured'); + } + } + + /** + * @param string $searchTerm + * @param int $firstResult + * @param int $maxResults + * @return QueryBuilder + */ + protected function prepareQueryBuilder($searchTerm, $firstResult, $maxResults) + { + $queryBuilder = $this->entityRepository->createQueryBuilder('scg'); + $queryBuilder + ->where($queryBuilder->expr()->like('LOWER(scg.name)', ':searchTerm')) + ->andWhere(($queryBuilder->expr()->eq('scg.system', $queryBuilder->expr()->literal(false)))) + ->setParameter('searchTerm', '%' . mb_strtolower($searchTerm) . '%') + ->orderBy('scg.name', 'ASC') + ->setFirstResult($firstResult) + ->setMaxResults($maxResults); + + return $queryBuilder; + } + + /** + * {@inheritdoc} + */ + protected function searchEntities($search, $firstResult, $maxResults) + { + $queryBuilder = $this->prepareQueryBuilder($search, $firstResult, $maxResults); + $query = $this->aclHelper->apply($queryBuilder, 'VIEW'); + + return $query->getResult(); + } + + /** + * {@inheritdoc} + */ + protected function findById($query) + { + $parts = explode(';', $query); + $id = $parts[0]; + + $criteria = [$this->idFieldName => $id]; + + return [$this->entityRepository->findOneBy($criteria, null)]; + } +} diff --git a/src/Marello/Bundle/SalesBundle/Entity/Repository/SalesChannelGroupRepository.php b/src/Marello/Bundle/SalesBundle/Entity/Repository/SalesChannelGroupRepository.php index 22b946013..463fd70aa 100644 --- a/src/Marello/Bundle/SalesBundle/Entity/Repository/SalesChannelGroupRepository.php +++ b/src/Marello/Bundle/SalesBundle/Entity/Repository/SalesChannelGroupRepository.php @@ -4,6 +4,7 @@ use Doctrine\ORM\EntityRepository; use Marello\Bundle\SalesBundle\Entity\SalesChannelGroup; +use Oro\Bundle\IntegrationBundle\Entity\Channel as IntegrationChannel; use Oro\Bundle\SecurityBundle\ORM\Walker\AclHelper; class SalesChannelGroupRepository extends EntityRepository @@ -33,4 +34,30 @@ public function findSystemChannelGroup() return reset($result); } + + /** + * @param IntegrationChannel $integrationChannel + * @return SalesChannelGroup|null + */ + public function findSalesChannelGroupAttachedToIntegration( + IntegrationChannel $integrationChannel + ): ?SalesChannelGroup { + return $this->findOneBy(['integrationChannel' => $integrationChannel]); + } + + /** + * @param int $salesChannelGroupId + * @return bool + */ + public function hasAttachedIntegration(int $salesChannelGroupId): bool + { + $qb = $this->createQueryBuilder('scg'); + $qb + ->select('1') + ->where($qb->expr()->eq('scg.id', ':salesChannelGroupId')) + ->andWhere($qb->expr()->isNotNull('scg.integrationChannel')) + ->setParameter('salesChannelGroupId', $salesChannelGroupId); + + return (bool) $qb->getQuery()->getResult(); + } } diff --git a/src/Marello/Bundle/SalesBundle/Entity/SalesChannel.php b/src/Marello/Bundle/SalesBundle/Entity/SalesChannel.php index e8e10c9ac..0575137e8 100644 --- a/src/Marello/Bundle/SalesBundle/Entity/SalesChannel.php +++ b/src/Marello/Bundle/SalesBundle/Entity/SalesChannel.php @@ -86,6 +86,9 @@ class SalesChannel extends ExtendSalesChannel implements * defaultValues={ * "dataaudit"={ * "auditable"=true + * }, + * "importexport"={ + * "identity"=true * } * } * ) diff --git a/src/Marello/Bundle/SalesBundle/Entity/SalesChannelGroup.php b/src/Marello/Bundle/SalesBundle/Entity/SalesChannelGroup.php index 6db0b29a8..b4993f49f 100644 --- a/src/Marello/Bundle/SalesBundle/Entity/SalesChannelGroup.php +++ b/src/Marello/Bundle/SalesBundle/Entity/SalesChannelGroup.php @@ -9,12 +9,21 @@ use Marello\Bundle\SalesBundle\Model\ExtendSalesChannelGroup; use Oro\Bundle\EntityConfigBundle\Metadata\Annotation\Config; use Oro\Bundle\EntityConfigBundle\Metadata\Annotation\ConfigField; +use Oro\Bundle\IntegrationBundle\Entity\Channel; use Oro\Bundle\OrganizationBundle\Entity\Ownership\AuditableOrganizationAwareTrait; /** * @ORM\Entity(repositoryClass="Marello\Bundle\SalesBundle\Entity\Repository\SalesChannelGroupRepository") * @ORM\HasLifecycleCallbacks() - * @ORM\Table(name="marello_sales_channel_group") + * @ORM\Table( + * name="marello_sales_channel_group", + * uniqueConstraints={ + * @ORM\UniqueConstraint( + * name="UNIQ_759DCFAB3D6A9E29", + * columns={"integration_channel_id"} + * ) + * } + * ) * @Config( * routeName="marello_sales_saleschannelgroup_index", * routeView="marello_sales_saleschannelgroup_view", @@ -101,6 +110,8 @@ class SalesChannelGroup extends ExtendSalesChannelGroup protected $system = false; /** + * @todo Add restriction on removing sales channel from the group in case when it attached to the integration + * * @var SalesChannel[] * * @ORM\OneToMany(targetEntity="SalesChannel", mappedBy="group", cascade={"persist"}, fetch="EAGER") @@ -114,6 +125,21 @@ class SalesChannelGroup extends ExtendSalesChannelGroup */ protected $salesChannels; + /** + * @ORM\OneToOne(targetEntity="Oro\Bundle\IntegrationBundle\Entity\Channel") + * @ORM\JoinColumn(name="integration_channel_id", nullable=true, onDelete="SET NULL", unique=true) + * @ConfigField( + * defaultValues={ + * "dataaudit"={ + * "auditable"=true + * } + * } + * ) + * + * @var Channel + */ + protected $integrationChannel; + public function __construct() { parent::__construct(); @@ -223,6 +249,25 @@ public function removeSalesChannel(SalesChannel $salesChannel) return $this; } + /** + * @return Channel|null + */ + public function getIntegrationChannel() + { + return $this->integrationChannel; + } + + /** + * @param Channel|null $integrationChannel + * @return $this + */ + public function setIntegrationChannel(Channel $integrationChannel = null) + { + $this->integrationChannel = $integrationChannel; + + return $this; + } + /** * @return string */ diff --git a/src/Marello/Bundle/SalesBundle/Form/Type/SalesChannelGroupSelectType.php b/src/Marello/Bundle/SalesBundle/Form/Type/SalesChannelGroupSelectType.php new file mode 100644 index 000000000..fb3711807 --- /dev/null +++ b/src/Marello/Bundle/SalesBundle/Form/Type/SalesChannelGroupSelectType.php @@ -0,0 +1,44 @@ +setDefaults( + [ + 'autocomplete_alias' => 'group_saleschannels', + 'entity_class' => SalesChannelGroup::class, + 'grid_name' => 'marello-sales-channel-groups', + 'configs' => [ + 'placeholder' => 'marello.sales.form.choose.sales_channel_group' + ], + ] + ); + } + + /** + * {@inheritDoc} + */ + public function getParent() + { + return OroEntitySelectOrCreateInlineType::class; + } + + /** + * {@inheritDoc} + */ + public function getBlockPrefix() + { + return 'marello_sales_saleschannel_group_select'; + } +} diff --git a/src/Marello/Bundle/SalesBundle/Form/Type/SalesChannelType.php b/src/Marello/Bundle/SalesBundle/Form/Type/SalesChannelType.php index b44f0cec5..f313eddbb 100644 --- a/src/Marello/Bundle/SalesBundle/Form/Type/SalesChannelType.php +++ b/src/Marello/Bundle/SalesBundle/Form/Type/SalesChannelType.php @@ -5,6 +5,7 @@ use Marello\Bundle\PricingBundle\Form\EventListener\CurrencySubscriber; use Marello\Bundle\SalesBundle\Entity\SalesChannel; use Oro\Bundle\CurrencyBundle\Form\Type\CurrencyType; +use Oro\Bundle\FormBundle\Utils\FormUtils; use Oro\Bundle\LocaleBundle\Form\Type\LocalizationSelectType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; diff --git a/src/Marello/Bundle/SalesBundle/Migrations/Schema/MarelloSalesBundleInstaller.php b/src/Marello/Bundle/SalesBundle/Migrations/Schema/MarelloSalesBundleInstaller.php index 4edbc9d0c..557bd34f2 100644 --- a/src/Marello/Bundle/SalesBundle/Migrations/Schema/MarelloSalesBundleInstaller.php +++ b/src/Marello/Bundle/SalesBundle/Migrations/Schema/MarelloSalesBundleInstaller.php @@ -45,9 +45,11 @@ protected function createMarelloSalesChannelGroupTable(Schema $schema) $table->addColumn('description', 'text', ['notnull' => false]); $table->addColumn('is_system', 'boolean', ['default' => false]); $table->addColumn('organization_id', 'integer', ['notnull' => false]); + $table->addColumn('integration_channel_id', 'integer', ['notnull' => false]); $table->addColumn('created_at', 'datetime', []); $table->addColumn('updated_at', 'datetime', ['notnull' => false]); $table->setPrimaryKey(['id']); + $table->addUniqueIndex(['integration_channel_id'], 'UNIQ_759DCFAB3D6A9E29'); } /** @@ -88,6 +90,13 @@ protected function addMarelloSalesChannelGroupForeignKeys(Schema $schema) ['id'], ['onDelete' => 'SET NULL', 'onUpdate' => null] ); + + $table->addForeignKeyConstraint( + $schema->getTable('oro_integration_channel'), + ['integration_channel_id'], + ['id'], + ['onDelete' => 'SET NULL', 'onUpdate' => null] + ); } /** diff --git a/src/Marello/Bundle/SalesBundle/Migrations/Schema/v1_3/MarelloSalesBundle.php b/src/Marello/Bundle/SalesBundle/Migrations/Schema/v1_3/MarelloSalesBundle.php index f668a56ef..21aa9cb45 100644 --- a/src/Marello/Bundle/SalesBundle/Migrations/Schema/v1_3/MarelloSalesBundle.php +++ b/src/Marello/Bundle/SalesBundle/Migrations/Schema/v1_3/MarelloSalesBundle.php @@ -19,6 +19,7 @@ public function up(Schema $schema, QueryBag $queries) { /** Tables generation **/ $this->modifyMarelloSalesSalesChannelTable($schema); + $this->modifyMarelloSalesSalesChannelGroupTable($schema); } /** @@ -32,4 +33,20 @@ protected function modifyMarelloSalesSalesChannelTable(Schema $schema) } } + /** + * {@inheritDoc} + */ + protected function modifyMarelloSalesSalesChannelGroupTable(Schema $schema) + { + $table = $schema->getTable('marello_sales_channel_group'); + $table->addColumn('integration_channel_id', 'integer', ['notnull' => false]); + $table->addUniqueIndex(['integration_channel_id'], 'UNIQ_759DCFAB3D6A9E29'); + + $table->addForeignKeyConstraint( + $schema->getTable('oro_integration_channel'), + ['integration_channel_id'], + ['id'], + ['onDelete' => 'SET NULL', 'onUpdate' => null] + ); + } } diff --git a/src/Marello/Bundle/SalesBundle/Resources/config/services.yml b/src/Marello/Bundle/SalesBundle/Resources/config/services.yml index 0ab8431a1..413301c39 100644 --- a/src/Marello/Bundle/SalesBundle/Resources/config/services.yml +++ b/src/Marello/Bundle/SalesBundle/Resources/config/services.yml @@ -37,6 +37,18 @@ services: tags: - { name: oro_form.autocomplete.search_handler, alias: saleschannels, acl_resource: marello_product_view } + marello_sales.group_saleschannels.form.autocomplete.search_handler: + class: 'Marello\Bundle\SalesBundle\Autocomplete\GroupSalesChannelHandler' + calls: + - [initDoctrinePropertiesByManagerRegistry, ["@doctrine"]] + - [setAclHelper,["@oro_security.acl_helper"]] + - [setLogger,["@logger"]] + arguments: + - 'Marello\Bundle\SalesBundle\Entity\SalesChannelGroup' + - ['name'] + tags: + - { name: oro_form.autocomplete.search_handler, alias: group_saleschannels } + marello_sales.active_saleschannels.form.autocomplete.search_handler: class: 'Marello\Bundle\SalesBundle\Autocomplete\ActiveSalesChannelHandler' parent: oro_form.autocomplete.search_handler @@ -152,3 +164,12 @@ services: marello_sales.placeholder.filter: public: true class: 'Marello\Bundle\SalesBundle\Placeholder\PlaceholderFilter' + + marello_sales.security.acl.voter.sales_channel_group: + class: 'Marello\Bundle\SalesBundle\Acl\Voter\SalesChannelGroupVoter' + arguments: + - "@oro_entity.doctrine_helper" + calls: + - [setClassName, ['Marello\Bundle\SalesBundle\Entity\SalesChannelGroup']] + tags: + - { name: security.voter } diff --git a/src/Marello/Bundle/SalesBundle/Resources/translations/messages.en.yml b/src/Marello/Bundle/SalesBundle/Resources/translations/messages.en.yml index c183d717d..30ec50f8c 100644 --- a/src/Marello/Bundle/SalesBundle/Resources/translations/messages.en.yml +++ b/src/Marello/Bundle/SalesBundle/Resources/translations/messages.en.yml @@ -52,6 +52,7 @@ marello: system.label: System organization.label: Organization sales_channels.label: Sales Channels + integration_channel.label: Integration Channel datablock: general: General @@ -61,3 +62,6 @@ marello: saved: Sales Channel Group has been saved successfully warning: is_system_update_attempt: System Sales Channel Group can't be updated + + form: + choose.sales_channel_group: 'Choose Sales Channel Group' diff --git a/src/Marello/Bundle/SalesBundle/Resources/views/SalesChannelGroup/searchResult.html.twig b/src/Marello/Bundle/SalesBundle/Resources/views/SalesChannelGroup/searchResult.html.twig new file mode 100644 index 000000000..c0ec92a2d --- /dev/null +++ b/src/Marello/Bundle/SalesBundle/Resources/views/SalesChannelGroup/searchResult.html.twig @@ -0,0 +1,18 @@ +{# + Available variables: + * entity - user entity Marello\Bundle\SalesBundle\Entity\SalesChannelGroup or null + * indexer_item - indexer item Oro\Bundle\SearchBundle\Query\Result\Item +#} +{% extends 'OroSearchBundle:Search:searchResultItem.html.twig' %} + +{% set showImage = false %} + +{% set recordUrl = indexer_item.recordUrl %} +{% set title = indexer_item.recordTitle %} + +{% set entityType = 'marello.sales.saleschannelgroup.entity_label'|trans %} + +{% set entityInfo = [ + {'title': 'oro.ui.created_at'|trans, 'value': entity.createdAt ? entity.createdAt|oro_format_datetime : 'N/A'}, + {'title': 'oro.ui.updated_at'|trans, 'value': entity.updatedAt ? entity.updatedAt|oro_format_datetime : 'N/A'}, +] %} diff --git a/src/Marello/Bundle/TaxBundle/Entity/TaxCode.php b/src/Marello/Bundle/TaxBundle/Entity/TaxCode.php index 3e2f3c01c..4515a1d36 100644 --- a/src/Marello/Bundle/TaxBundle/Entity/TaxCode.php +++ b/src/Marello/Bundle/TaxBundle/Entity/TaxCode.php @@ -3,6 +3,7 @@ namespace Marello\Bundle\TaxBundle\Entity; use Doctrine\ORM\Mapping as ORM; +use Marello\Bundle\TaxBundle\Model\ExtendTaxCode; use Oro\Bundle\EntityConfigBundle\Metadata\Annotation as Oro; /** @@ -28,7 +29,7 @@ * } * ) */ -class TaxCode +class TaxCode extends ExtendTaxCode { /** * @var integer @@ -42,11 +43,14 @@ class TaxCode /** * @var string * - * @ORM\Column(name="code", type="string", length=32, unique=true, nullable=false) + * @ORM\Column(name="code", type="string", length=255, unique=true, nullable=false) * @Oro\ConfigField( * defaultValues={ * "dataaudit"={ * "auditable"=true + * }, + * "importexport"={ + * "identity"=true * } * } * ) diff --git a/src/Marello/Bundle/TaxBundle/Migrations/Schema/MarelloTaxBundleInstaller.php b/src/Marello/Bundle/TaxBundle/Migrations/Schema/MarelloTaxBundleInstaller.php index 692ab6cab..8f2f5f6f1 100644 --- a/src/Marello/Bundle/TaxBundle/Migrations/Schema/MarelloTaxBundleInstaller.php +++ b/src/Marello/Bundle/TaxBundle/Migrations/Schema/MarelloTaxBundleInstaller.php @@ -18,7 +18,7 @@ class MarelloTaxBundleInstaller implements Installation */ public function getMigrationVersion() { - return 'v1_3_1'; + return 'v1_4'; } /** @@ -48,7 +48,7 @@ protected function createMarelloTaxTaxCodeTable(Schema $schema) { $table = $schema->createTable('marello_tax_tax_code'); $table->addColumn('id', 'integer', ['autoincrement' => true]); - $table->addColumn('code', 'string', ['notnull' => true, 'length' => 32]); + $table->addColumn('code', 'string', ['notnull' => true, 'length' => 255]); $table->addColumn('description', 'string', ['notnull' => false, 'length' => 255]); $table->addColumn('data', 'json_array', ['notnull' => false, 'comment' => '(DC2Type:json_array)']); $table->setPrimaryKey(['id']); diff --git a/src/Marello/Bundle/TaxBundle/Migrations/Schema/v1_4/MarelloTaxBundle.php b/src/Marello/Bundle/TaxBundle/Migrations/Schema/v1_4/MarelloTaxBundle.php new file mode 100644 index 000000000..bea351e6f --- /dev/null +++ b/src/Marello/Bundle/TaxBundle/Migrations/Schema/v1_4/MarelloTaxBundle.php @@ -0,0 +1,29 @@ +updateMarelloTaxCodeTable($schema); + } + + /** + * {@inheritDoc} + */ + protected function updateMarelloTaxCodeTable(Schema $schema) + { + $table = $schema->getTable('marello_tax_tax_code'); + $table->changeColumn('code', ['length' => 255]); + } +} diff --git a/src/Marello/Bundle/TaxBundle/Model/ExtendTaxCode.php b/src/Marello/Bundle/TaxBundle/Model/ExtendTaxCode.php new file mode 100644 index 000000000..a86fce041 --- /dev/null +++ b/src/Marello/Bundle/TaxBundle/Model/ExtendTaxCode.php @@ -0,0 +1,17 @@ +