Skip to content

Commit

Permalink
Merge pull request #10 from monsieurbiz/feature/product-preparation-d…
Browse files Browse the repository at this point in the history
…elay

Add preparation delay in product variants
  • Loading branch information
maximehuran authored Aug 13, 2021
2 parents 372a64f + 3126e59 commit 8dc55e2
Show file tree
Hide file tree
Showing 14 changed files with 330 additions and 5 deletions.
23 changes: 23 additions & 0 deletions src/Entity/ProductVariantInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

/*
* This file is part of Monsieur Biz' Shipping Slot plugin for Sylius.
*
* (c) Monsieur Biz <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace MonsieurBiz\SyliusShippingSlotPlugin\Entity;

use Sylius\Component\Core\Model\ProductVariantInterface as SyliusProductVariantInterface;

interface ProductVariantInterface extends SyliusProductVariantInterface
{
public function getPreparationDelay(): ?int;

public function setPreparationDelay(?int $preparationDelay): void;
}
34 changes: 34 additions & 0 deletions src/Entity/ProductVariantTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

/*
* This file is part of Monsieur Biz' Shipping Slot plugin for Sylius.
*
* (c) Monsieur Biz <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace MonsieurBiz\SyliusShippingSlotPlugin\Entity;

use Doctrine\ORM\Mapping as ORM;

trait ProductVariantTrait
{
/**
* @ORM\Column(name="preparation_delay", type="integer", nullable=true)
*/
private ?int $preparationDelay = null;

public function getPreparationDelay(): ?int
{
return $this->preparationDelay;
}

public function setPreparationDelay(?int $preparationDelay): void
{
$this->preparationDelay = $preparationDelay;
}
}
12 changes: 10 additions & 2 deletions src/Entity/ShippingSlotConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,19 @@ public function getSlotDelay(): int
*/
public function getRecurrences(
?DateTimeInterface $startDate = null,
?DateTimeInterface $endDate = null
?DateTimeInterface $endDate = null,
?int $customPreparationDelay = null
): array {
$recurrences = [];

// Use greater preparation delay, for example a product can make some time to be produced
$slotDelay = $this->getSlotDelay();
if (null !== $customPreparationDelay && $customPreparationDelay > $slotDelay) {
$slotDelay = $customPreparationDelay;
}

$minDate = (new DateTime())
->add(new DateInterval(sprintf('PT%dM', $this->getSlotDelay())))
->add(new DateInterval(sprintf('PT%dM', $slotDelay)))
->setTimezone(new DateTimeZone($this->getTimezone() ?? 'UTC'))
;

Expand Down
3 changes: 2 additions & 1 deletion src/Entity/ShippingSlotConfigInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public function getSlotDelay(): int;
*/
public function getRecurrences(
?DateTimeInterface $startDate = null,
?DateTimeInterface $endDate = null
?DateTimeInterface $endDate = null,
?int $customPreparationDelay = null
): array;
}
80 changes: 80 additions & 0 deletions src/Fixture/ProductVariantShippingSlotFixture.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

/*
* This file is part of Monsieur Biz' Shipping Slot plugin for Sylius.
*
* (c) Monsieur Biz <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace MonsieurBiz\SyliusShippingSlotPlugin\Fixture;

use Doctrine\ORM\EntityManagerInterface;
use MonsieurBiz\SyliusShippingSlotPlugin\Entity\ProductVariantInterface;
use Sylius\Bundle\FixturesBundle\Fixture\AbstractFixture;
use Sylius\Bundle\FixturesBundle\Fixture\FixtureInterface;
use Sylius\Component\Core\Repository\ProductVariantRepositoryInterface;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;

final class ProductVariantShippingSlotFixture extends AbstractFixture implements FixtureInterface
{
private ProductVariantRepositoryInterface $productVariantRepository;
private EntityManagerInterface $productVariantManager;

public function __construct(
ProductVariantRepositoryInterface $productVariantRepository,
EntityManagerInterface $productVariantManager
) {
$this->productVariantRepository = $productVariantRepository;
$this->productVariantManager = $productVariantManager;
}

/**
* @param array $options
*/
public function load(array $options): void
{
foreach ($options['product_variants'] ?? [] as $option) {
/** @var ProductVariantInterface $productVariant */
if (null === ($productVariant = $this->productVariantRepository->findOneBy(['code' => $option['code'] ?? '']))) {
continue;
}

$productVariant->setPreparationDelay($option['preparationDelay'] ?? null);
$this->productVariantManager->persist($productVariant);
}

$this->productVariantManager->flush();
}

/**
* @return string
*/
public function getName(): string
{
return 'monsieurbiz_shipping_slot_product_variant';
}

/**
* @param ArrayNodeDefinition $optionsNode
*/
protected function configureOptionsNode(ArrayNodeDefinition $optionsNode): void
{
/** @phpstan-ignore-next-line */
$optionsNode
->children()
->arrayNode('product_variants')
->arrayPrototype()
->children()
->scalarNode('code')->cannotBeEmpty()->end()
->integerNode('preparationDelay')->end()
->end()
->end()
->end()
;
}
}
44 changes: 44 additions & 0 deletions src/Form/Extension/ProductVariantTypeExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

/*
* This file is part of Monsieur Biz' Shipping Slot plugin for Sylius.
*
* (c) Monsieur Biz <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace MonsieurBiz\SyliusShippingSlotPlugin\Form\Extension;

use Sylius\Bundle\ProductBundle\Form\Type\ProductVariantType;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints as Assert;

class ProductVariantTypeExtension extends AbstractTypeExtension
{
public static function getExtendedTypes(): iterable
{
return [ProductVariantType::class];
}

/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('preparationDelay', IntegerType::class, [
'required' => false,
'label' => 'monsieurbiz_shipping_slot.ui.form.preparation_delay',
'constraints' => [
new Assert\PositiveOrZero(),
],
])
;
}
}
27 changes: 26 additions & 1 deletion src/Generator/SlotGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@
use DateTimeZone;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use MonsieurBiz\SyliusShippingSlotPlugin\Entity\ProductVariantInterface;
use MonsieurBiz\SyliusShippingSlotPlugin\Entity\ShipmentInterface;
use MonsieurBiz\SyliusShippingSlotPlugin\Entity\ShippingMethodInterface;
use MonsieurBiz\SyliusShippingSlotPlugin\Entity\SlotInterface;
use MonsieurBiz\SyliusShippingSlotPlugin\Event\RecurrenceGenerationEvent;
use MonsieurBiz\SyliusShippingSlotPlugin\Repository\SlotRepositoryInterface;
use Recurr\Recurrence;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\OrderItemInterface;
use Sylius\Component\Core\Repository\ShippingMethodRepositoryInterface;
use Sylius\Component\Order\Context\CartContextInterface;
use Sylius\Component\Resource\Factory\FactoryInterface;
Expand Down Expand Up @@ -205,7 +207,7 @@ public function generateCalendarEvents(
if (null === $shippingSlotConfig) {
return [];
}
$recurrences = $shippingSlotConfig->getRecurrences($startDate, $endDate);
$recurrences = $shippingSlotConfig->getRecurrences($startDate, $endDate, $this->getCartPreparationDelay());
$event = new RecurrenceGenerationEvent($recurrences);
$this->eventDispatcher->dispatch($event);
$recurrences = $event->getRecurrences();
Expand Down Expand Up @@ -243,4 +245,27 @@ private function isUnaivalableRecurrence(Recurrence $recurrence, array $unavaila

return false;
}

/**
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
private function getCartPreparationDelay(): ?int
{
$maxPreparationDelay = null;
$cartItems = $this->cartContext->getCart()->getItems();

/** @var OrderItemInterface $cartItem */
foreach ($cartItems as $cartItem) {
if (
null !== ($variant = $cartItem->getVariant())
&& $variant instanceof ProductVariantInterface
&& null !== ($preparationDelay = $variant->getPreparationDelay())
&& (null === $maxPreparationDelay || $preparationDelay > $maxPreparationDelay)
) {
$maxPreparationDelay = $preparationDelay;
}
}

return $maxPreparationDelay;
}
}
40 changes: 40 additions & 0 deletions src/Migrations/Version20210813115752.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

/*
* This file is part of Monsieur Biz' Shipping Slot plugin for Sylius.
*
* (c) Monsieur Biz <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace MonsieurBiz\SyliusShippingSlotPlugin\Migrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20210813115752 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}

public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE sylius_product_variant ADD preparation_delay INT DEFAULT NULL');
}

public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE sylius_product_variant DROP preparation_delay');
}
}
8 changes: 7 additions & 1 deletion src/Resources/config/sylius/fixtures.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,10 @@ sylius_fixtures:
color: '#22B99A'
shipping_methods:
- 'courier_delivery'

monsieurbiz_shipping_slot_product_variant:
options:
product_variants:
- code: 'Knitted_wool_blend_green_cap-variant-0'
preparationDelay: 720
- code: 'Cashmere_blend_violet_beanie-variant-0'
preparationDelay: 1440
15 changes: 15 additions & 0 deletions src/Resources/config/sylius/ui.yaml
Original file line number Diff line number Diff line change
@@ -1,16 +1,29 @@
sylius_ui:
events:
# Admin shipping method form - Add shipping slot configuration fiel
sylius.admin.shipping_method.update.form:
blocks:
shipping_slot_configuration: '@MonsieurBizSyliusShippingSlotPlugin/Admin/Form/ShippingMethod/shipping_slot_configuration.html.twig'
sylius.admin.shipping_method.create.form:
blocks:
shipping_slot_configuration: '@MonsieurBizSyliusShippingSlotPlugin/Admin/Form/ShippingMethod/shipping_slot_configuration.html.twig'

# Admin product and product variant forms - Add preparation delay field
sylius.admin.product_variant.tab_details:
blocks:
preparation_delay: '@MonsieurBizSyliusShippingSlotPlugin/Admin/Form/ProductVariant/preparation_delay.html.twig'
sylius.admin.product.tab_details:
blocks:
preparation_delay: '@MonsieurBizSyliusShippingSlotPlugin/Admin/Form/Product/preparation_delay.html.twig'

# Admin order show - Display the selected slots for shipments
sylius.admin.order.show.sidebar:
blocks:
monsieurbiz_shipping_slot_order_slots:
template: '@MonsieurBizSyliusShippingSlotPlugin/Admin/Order/Show/_slots.html.twig'
priority: 50

# Shop assets - Setup the CSS and JS
sylius.shop.layout.javascripts:
blocks:
monsieurbiz_shipping_slot_init_app:
Expand All @@ -19,6 +32,8 @@ sylius_ui:
blocks:
monsieurbiz_shipping_slot_styles:
template: '@MonsieurBizSyliusShippingSlotPlugin/Shop/_styles.html.twig'

# Shop shipping method choice - Add calendar in shipping method choice
sylius.shop.checkout.select_shipping.after_method:
blocks:
monsieurbiz_shipping_slot_calendar:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{% if form.vars.value is defined and form.vars.value.simple %}
<div class="ui segment">
<h4 class="ui dividing header">{{ 'monsieurbiz_shipping_slot.ui.shipping_slot_config'|trans }}</h4>
<div id="shipping_slot_config">
{{ form_row(form.variant.preparationDelay) }}
</div>
</div>
{% endif %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

<div class="ui segment">
<h4 class="ui dividing header">{{ 'monsieurbiz_shipping_slot.ui.shipping_slot_config'|trans }}</h4>
<div id="shipping_slot_config">
{{ form_row(form.preparationDelay) }}
</div>
</div>
6 changes: 6 additions & 0 deletions tests/Application/config/packages/_sylius.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,9 @@ sylius_order:
order:
classes:
model: App\Entity\Order

sylius_product:
resources:
product_variant:
classes:
model: App\Entity\ProductVariant
Loading

0 comments on commit 8dc55e2

Please sign in to comment.