Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Task/improve location behavior #64

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,24 @@ Note: to test the plugin locally, see [CONTRIBUTING.md](CONTRIBUTING.md)
bin/console sylius:theme:assets:install

10. Add your products and stocks or [import them](https://github.com/coopTilleuls/CoopTilleulsSyliusQuickImportPlugin)
11. Configure the pick up locations, the available time slots, and how many people you can safely serve in parallel
11. Configure the pick up locations, the available time slots, closed periods, and how many people you can safely serve in parallel
12. Create a dedicated shipping method
13. Optionally, configure an online payment method from the admin (Stripe and PayPal are supported out of the box)
14. Manage range days in full calendar view. Edit templates/bundles/SyliusShopBundle/Checkout/SelectShipping/_shipment.html.twig and change config json
```javascript
<script type="application/json" id="calendar_config">{{ {
locale: app.request.locale,
shipmentID: form.vars.value.id,
defaultView: 'timeGridSevenDay',
minDay: 7,
unselectedBackgroundColor: 'rgb(55, 136, 216)',
selectedBackgroundColor: 'red',
messages: {
noSlotSelected: 'coop_tilleuls_click_n_collect.ui.noSlotSelected'|trans,
}
}|json_encode(65)|raw }}</script>
```
By default, defaultView is timeGridFourDay and minDay is 4.

**Export data**

Expand Down
15 changes: 15 additions & 0 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

namespace CoopTilleuls\SyliusClickNCollectPlugin\DependencyInjection;

use CoopTilleuls\SyliusClickNCollectPlugin\Entity\ClosedPeriod;
use CoopTilleuls\SyliusClickNCollectPlugin\Entity\ClosedPeriodInterface;
use CoopTilleuls\SyliusClickNCollectPlugin\Entity\Location;
use CoopTilleuls\SyliusClickNCollectPlugin\Entity\LocationInterface;
use CoopTilleuls\SyliusClickNCollectPlugin\Form\Type\LocationType;
Expand Down Expand Up @@ -73,6 +75,19 @@ private function addResourcesSection(ArrayNodeDefinition $node): void
->end()
->end()
->end()
->arrayNode('closedPeriod')
->addDefaultsIfNotSet()
->children()
->variableNode('options')->end()
->arrayNode('classes')
->addDefaultsIfNotSet()
->children()
->scalarNode('model')->defaultValue(ClosedPeriod::class)->cannotBeEmpty()->end()
->scalarNode('interface')->defaultValue(ClosedPeriodInterface::class)->cannotBeEmpty()->end()
->end()
->end()
->end()
->end()
->end()
->end()
->end();
Expand Down
53 changes: 53 additions & 0 deletions src/Entity/ClosedPeriod.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

namespace CoopTilleuls\SyliusClickNCollectPlugin\Entity;

class ClosedPeriod implements ClosedPeriodInterface
{
protected int $id;
protected \DateTimeInterface $startAt;
protected ?\DateTimeInterface $endAt = null;
protected LocationInterface $location;

public function getId(): int
{
return $this->id;
}

public function setId(int $id): void
{
$this->id = $id;
}

public function getStartAt(): \DateTimeInterface
{
return $this->startAt;
}

public function setStartAt(\DateTimeInterface $startAt): void
{
$this->startAt = $startAt;
}

public function getEndAt(): ?\DateTimeInterface
{
return $this->endAt;
}

public function setEndAt(?\DateTimeInterface $endAt): void
{
$this->endAt = $endAt;
}

public function getLocation(): LocationInterface
{
return $this->location;
}

public function setLocation(LocationInterface $location): void
{
$this->location = $location;
}
}
19 changes: 19 additions & 0 deletions src/Entity/ClosedPeriodInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace CoopTilleuls\SyliusClickNCollectPlugin\Entity;

use Sylius\Component\Resource\Model\ResourceInterface;

interface ClosedPeriodInterface extends ResourceInterface
{
public function getId(): int;
public function setId(int $id): void;
public function getStartAt(): \DateTimeInterface;
public function setStartAt(\DateTimeInterface $startAt): void;
public function getEndAt(): ?\DateTimeInterface;
public function setEndAt(?\DateTimeInterface $endAt): void;
public function getLocation(): LocationInterface;
public function setLocation(LocationInterface $location): void;
}
24 changes: 24 additions & 0 deletions src/Entity/Location.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,16 @@ class Location implements LocationInterface
*/
protected Collection $shippingMethods;

/**
* @var ClosedPeriodInterface[]|Collection
*/
protected Collection $closedPeriods;

public function __construct()
{
$this->createdAt = new \DateTimeImmutable();
$this->shippingMethods = new ArrayCollection();
$this->closedPeriods = new ArrayCollection();
}

public function getId()
Expand Down Expand Up @@ -206,4 +212,22 @@ public function removeShippingMethod(ClickNCollectShippingMethodInterface $shipp
$this->shippingMethods->removeElement($shippingMethod);
$shippingMethod->getLocations()->removeElement($this);
}

public function getClosedPeriods(): Collection
{
return $this->closedPeriods;
}

public function addClosedPeriod(ClosedPeriodInterface $closedPeriod): void
{
if (!$this->closedPeriods->contains($closedPeriod)) {
$this->closedPeriods->add($closedPeriod);
$closedPeriod->setLocation($this);
}
}

public function removeClosedPeriod(ClosedPeriodInterface $closedPeriod): void
{
$this->closedPeriods->removeElement($closedPeriod);
}
}
9 changes: 9 additions & 0 deletions src/Entity/LocationInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,13 @@ public function getShippingMethods(): Collection;
public function addShippingMethod(ClickNCollectShippingMethodInterface $shippingMethod): void;

public function removeShippingMethod(ClickNCollectShippingMethodInterface $shippingMethod): void;

/**
* @return ClosedPeriodInterface[]|Collection
*/
public function getClosedPeriods(): Collection;

public function addClosedPeriod(ClosedPeriodInterface $closedPeriod): void;

public function removeClosedPeriod(ClosedPeriodInterface $closedPeriod): void;
}
41 changes: 41 additions & 0 deletions src/Form/Type/ClosedPeriodType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

namespace CoopTilleuls\SyliusClickNCollectPlugin\Form\Type;

use CoopTilleuls\SyliusClickNCollectPlugin\Entity\ClosedPeriod;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\NotBlank;

class ClosedPeriodType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('startAt', DateType::class, [
'label' => 'sylius.ui.start_date',
'widget' => 'single_text',
'empty_data' => null,
'constraints' => [
new NotBlank(['groups' => ['sylius']]),
]
])
->add('endAt', DateType::class, [
'required' => false,
'label' => 'sylius.ui.end_date',
'widget' => 'single_text',
])
;
}

public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => ClosedPeriod::class,
]);
}
}
12 changes: 11 additions & 1 deletion src/Form/Type/LocationType.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Sylius\Bundle\ResourceBundle\Form\EventSubscriber\AddCodeFormSubscriber;
use Sylius\Bundle\ResourceBundle\Form\Type\AbstractResourceType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
Expand All @@ -38,6 +39,14 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
'empty_data' => '',
'label' => 'coop_tilleuls_click_n_collect.form.location.rrule',
])
->add('closedPeriods', CollectionType::class, [
'required' => false,
'entry_type' => ClosedPeriodType::class,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'label' => 'coop_tilleuls_click_n_collect.form.location.closed_periods',
])
->add('orderPreparationDelay', IntegerType::class, [
'attr' => ['min' => 0],
'label' => 'coop_tilleuls_click_n_collect.form.location.order_preparation_delay',
Expand Down Expand Up @@ -78,6 +87,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
->add('enabled', CheckboxType::class, [
'required' => false,
'label' => 'coop_tilleuls_click_n_collect.form.location.enabled',
]);
])
;
}
}
21 changes: 21 additions & 0 deletions src/Resources/config/doctrine/ClosedPeriod.orm.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>

<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">

<mapped-superclass name="CoopTilleuls\SyliusClickNCollectPlugin\Entity\ClosedPeriod" table="coop_tilleuls_click_n_collect_closed_period">
<id name="id" column="id" type="integer">
<generator />
</id>

<field name="startAt" column="start_at" type="datetime"></field>
<field name="endAt" column="end_at" type="datetime" nullable="true"></field>

<many-to-one field="location" target-entity="CoopTilleuls\SyliusClickNCollectPlugin\Entity\Location" inversed-by="closedPeriods">
<join-column name="location_id" nullable="false" />
</many-to-one>
</mapped-superclass>

</doctrine-mapping>
4 changes: 4 additions & 0 deletions src/Resources/config/doctrine/Location.orm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
<field name="generatePin" column="generate_pin" type="boolean" />

<many-to-many field="shippingMethods" target-entity="Sylius\Component\Shipping\Model\ShippingMethodInterface" mapped-by="locations" />

<one-to-many field="closedPeriods" target-entity="CoopTilleuls\SyliusClickNCollectPlugin\Entity\ClosedPeriod" orphan-removal="true" mapped-by="location">
<cascade><cascade-all /></cascade>
</one-to-many>
</mapped-superclass>

</doctrine-mapping>
48 changes: 43 additions & 5 deletions src/Resources/public/click_n_collect.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
$(function () {
var config = JSON.parse($('#calendar_config').text());
var locale = config.locale.includes('_') ? config.locale.split('_')[0] : config.locale;
// eslint-disable-next-line prefer-destructuring
var excludeDays = [];
var defaultView = config.defaultView ?? 'timeGridFourDay';
var minDay = config.minDay ?? 4;
$('input.click_n_collect_location').each(function () {
var n = $(this).attr('id').match(/sylius_checkout_select_shipping_shipments_([0-9]+)_location/)[1];

Expand All @@ -15,16 +19,20 @@ $(function () {
var calendar = new FullCalendar.Calendar($calendar[0], {
nowIndicator: true,
plugins: [ 'timeGrid' ],
defaultView: 'timeGridFourDay',
defaultView: defaultView,
views: {
timeGridFourDay: {
[defaultView]: {
type: 'timeGrid',
duration: { days: 4 },
buttonText: '4 day'
duration: { days: minDay },
buttonText: minDay+' day'
}
},
eventColor: config.unselectedBackgroundColor,
eventRender: function (info) {
var eventStartString = info.event.start.toISOString().substring(0, 10);
if (excludeDays && $.inArray(eventStartString, excludeDays) > -1) {
$(info.el).hide()
}
if (info.event.id.slice(0, 19) !== $collectionTime.val().slice(0, 19)) return;
selectEvent(info.el);
},
Expand Down Expand Up @@ -88,12 +96,42 @@ $(function () {
if (currentValue) $locations.val(currentValue);
else currentValue = $locations.val();

//get good location
var currentLocation = findLocationByCode(currentValue);
//check if closed periods exist and populate excludeDays
if (currentLocation.closedPeriods && currentLocation.closedPeriods.length > 0) {
currentLocation.closedPeriods.forEach(function (period) {
var start = new Date(period.startAt.substring(0, 10));
if (period.endAt) {
var end = new Date(period.endAt.substring(0, 10));
var rangeDates = getDatesInRange(start, end);
for (var i = 0; i < rangeDates.length; i++) {
excludeDays.push(rangeDates[i].toISOString().substring(0, 10));
}
} else {
var startString = start.toISOString().substring(0, 10);
excludeDays.push(startString);
}
});
}

$locations.show();
populateCalendar(currentValue);
populateLocationAddress(findLocationByCode(currentValue));
populateLocationAddress(currentLocation);
});
}

function getDatesInRange(startDate, endDate) {
const date = new Date(startDate.getTime());
const dates = [];
while (date <= endDate) {
dates.push(new Date(date));
date.setDate(date.getDate() + 1);
}

return dates;
}

function populateLocationAddress(location) {
$locationAddress.text((location.street || '') + ' ' + (location.postcode || '') + ' ' + (location.city || '') + ' ' + (location.provinceCode || '') + ' ' + (location.provinceName || '') + ' ' + (location.countryCode || ''));
$locationAddress.show();
Expand Down
1 change: 1 addition & 0 deletions src/Resources/translations/messages.bg.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ coop_tilleuls_click_n_collect:
province_code: Код на област
province_name: име на област
enabled: Включено
closed_periods: Затворени периоди
shipping_method:
click_n_collect: Click 'N' Collect
locations: Локации за Click 'N' Collect
1 change: 1 addition & 0 deletions src/Resources/translations/messages.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ coop_tilleuls_click_n_collect:
province_code: Province code
province_name: Province name
enabled: Enabled
closed_periods: Closed periods
shipping_method:
click_n_collect: Click 'N' Collect
locations: Collection locations
1 change: 1 addition & 0 deletions src/Resources/translations/messages.es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ coop_tilleuls_click_n_collect:
province_code: Código de Provincia
province_name: Nombre de Provincia
enabled: Habilitado
closed_periods: Periodos cerrados
shipping_method:
click_n_collect: Click 'N' Collect
locations: Ubicaciones de recogida
Loading