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

Booking : fix ne pas permettre de réserver des shifts déjà pris #717

Merged
merged 3 commits into from
Jan 26, 2023
Merged
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
59 changes: 28 additions & 31 deletions app/Resources/views/booking/_partial/modal.html.twig
Original file line number Diff line number Diff line change
@@ -1,41 +1,37 @@
{% set nbShifts = bucket.shifts | length %}
{% set nbBookableShifts = shift_service.bookableShifts(bucket, beneficiary) | length %}
{% set availableShifts = shift_service.bookableShifts(bucket, beneficiary) %}
{% set bookableShifts = shift_service.bookableShifts(bucket, beneficiary) %}
{% set nbBookableShifts = bookableShifts | length %}
{% set firstBookable = shift_service.firstBookable(bucket, beneficiary) %}
<div id="book{{ firstBookable.id }}" class="modal">
<div class="modal-content">
<h5>Réserver ce créneau / <span class="{{ firstBookable.job.color }}-text">{{ firstBookable.job.name }}</span></h5>
<h6>{{ bucket.start|date_fr_long }} de {{ bucket.start|date('G\\hi') }} à {{ bucket.end|date('G\\hi') }}</h6>

{% if not shift_service.canBookDuration(beneficiary, bucket.getFirst.getDuration, cycle) %}
<div class="alert card red lighten-4 red-text text-darken-4">
<div class="card-content">
{% if not shift_service.canBookDuration(beneficiary, shift_service.getMinimalShiftDuration, cycle) %}
<span class="card-title">Attention, tu as déjà fait ton quota d'heures sur ce cycle.</span>
<p>Ce créneau ne sera pas comptabilisé dans ton calcul d'heures car seul un maximum de {{ due_duration_by_cycle | duration_from_minutes }} est comptabilisé par cycle.</p>
{% else %}
<span class="card-title">Attention, avec ce créneau tu dépasseras les {{ due_duration_by_cycle | duration_from_minutes }} de bénévolat sur ton cycle.</span>
<p>Ce créneau ne sera qu'en partie comptabilisé dans ton calcul d'heures, car seul un maximum de {{ due_duration_by_cycle | duration_from_minutes }} est comptabilisé par cycle.</p>
{% endif %}
<div class="alert card red lighten-4 red-text text-darken-4">
<div class="card-content">
{% if not shift_service.canBookDuration(beneficiary, shift_service.getMinimalShiftDuration, cycle) %}
<span class="card-title">Attention, tu as déjà fait ton quota d'heures sur ce cycle.</span>
<p>Ce créneau ne sera pas comptabilisé dans ton calcul d'heures car seul un maximum de {{ due_duration_by_cycle | duration_from_minutes }} est comptabilisé par cycle.</p>
{% else %}
<span class="card-title">Attention, avec ce créneau tu dépasseras les {{ due_duration_by_cycle | duration_from_minutes }} de bénévolat sur ton cycle.</span>
<p>Ce créneau ne sera qu'en partie comptabilisé dans ton calcul d'heures, car seul un maximum de {{ due_duration_by_cycle | duration_from_minutes }} est comptabilisé par cycle.</p>
{% endif %}
</div>
</div>
</div>
{% endif %}

{% if availableShifts | length == 1 %}
{% if bookableShifts | length == 1 %}
{% if not firstBookable.formation and beneficiary.formations | length %}
<div class="row">
<div class="col s12">
<div class="card-panel teal warning">
<span class="white-text">
<i class="material-icons">warning</i>
Ce créneau n'est <b>pas</b> qualifié.
<br>
{{ beneficiary.firstname }} a {% if beneficiary.formations | length == 1 %}une formation{% else %}des formations{% endif %} ({{ beneficiary.formations | join(', ')}}).
<br>
Tes compétences sont précieuses, pense si possible à les valoriser sur <b>un créneau qualifié</b>.
</span>
</div>
</div>
<div class="card-panel teal warning">
<span class="white-text">
<i class="material-icons">warning</i>
Ce créneau n'est <b>pas</b> qualifié.
<br>
{{ beneficiary.firstname }} a {% if beneficiary.formations | length == 1 %}une formation{% else %}des formations{% endif %} ({{ beneficiary.formations | join(', ')}}).
<br>
Tes compétences sont précieuses, pense si possible à les valoriser sur <b>un créneau qualifié</b>.
</span>
</div>
{% endif %}
{% endif %}
Expand All @@ -61,12 +57,13 @@
{% endif %}
{% endif %}
<p>Nombre de places restantes : {{ nbBookableShifts }}/{{ nbShifts }}</p>
{% for availableShift in availableShifts %}
{% for bookableShift in bookableShifts %}
<div>
<label for="{{ availableShift.id }}" style="color: #5f5a5a; font-weight: 600;">
<input type="radio" id="{{ availableShift.id }}" class="checkedFormation" name="formation" value="{{ availableShift.id }}" />
{% if availableShift.formation %}
<span>{{ availableShift.formation.name }}</span>
{{ bookableShift.getShifter() }}
<label for="{{ bookableShift.id }}" style="color: #5f5a5a; font-weight: 600;">
<input type="radio" id="{{ bookableShift.id }}" class="checkedFormation" name="formation" value="{{ bookableShift.id }}" />
{% if bookableShift.formation %}
<span>{{ bookableShift.formation.name }}</span>
{% else %}
<span>sans formation particulière</span>
{% endif %}
Expand Down
23 changes: 10 additions & 13 deletions app/Resources/views/booking/_partial/shift.html.twig
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
<div
{% set nbShifter = bucket.shifterCount() %}
{% set nbShifts = bucket.shifts | length %}
{% set nbBookableShifts = shift_service.getBookableShiftsCount(bucket) %}
{% set nbBookedShifts = nbShifts - nbBookableShifts %}
{% set firstBookableShift = shift_service.firstBookable(bucket, beneficiary) %}
data-offset="{{ (((bucket.start|date('G')-start)*60 + bucket.start|date('i'))/60) }}"
data-length="{{ (100/(end-start+1)) }}"
style="width:{{ (bucket.duration / 60) * (100/(end-start+1)) }}%;
left:{{ (((bucket.start|date('G')-start)*60 + bucket.start|date('i'))/60)*(100/(end-start+1)) }}%;
top: {{ line*10 }}px;" class="shift-bucket">
{% set nbShifter = bucket.shifterCount() %}
{% set nbShifts = bucket.shifts | length %}
{% set nbBookableShifts = shift_service.getBookableShiftsCount(bucket) %}
{% set nbBookedShifts = nbShifts - nbBookableShifts %}
{% set firstBookableShift = shift_service.firstBookable(bucket, beneficiary) %}
<div class="shift-bucket"
data-offset="{{ (((bucket.start|date('G')-start)*60 + bucket.start|date('i'))/60) }}" data-length="{{ (100/(end-start+1)) }}"
style="width:{{ (bucket.duration / 60) * (100/(end-start+1)) }}%;left:{{ (((bucket.start|date('G')-start)*60 + bucket.start|date('i'))/60)*(100/(end-start+1)) }}%;top: {{ line*10 }}px;">
<div style="height:2px; width: 100%;position: absolute;top:-2px;">
{% if nbBookableShifts < nbShifts %}
{% for shifter in 1..(nbShifts - nbBookableShifts) %}
{% if nbBookedShifts > 0 %}
{% for shift in 1..nbBookedShifts %}
<div class="green lighten-3 left" style="height:100%; width: {{ 100/(nbShifts) }}%"></div>
{% endfor %}
{% endif %}
Expand Down
8 changes: 6 additions & 2 deletions src/AppBundle/Entity/ShiftBucket.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,13 @@ public function getDuration()
return $this->shifts->first()->getDuration();
}

public function canBookInterval(Beneficiary $beneficiary) // check if none of the shifts belong to the beneficiary ?
/**
* - check that none of the shifts belong to the beneficiary
* - check that the beneficiary doesn't already have a shift in the same interval
*/
public function canBookInterval(Beneficiary $beneficiary)
{
$alreadyBooked = $beneficiary->getShifts()->exists(function ($key, Shift $shift) {
$alreadyBooked = $beneficiary->getShifts()->exists(function ($key, Shift $shift) {
return $shift->getStart() == $this->getStart() && $shift->getEnd() == $this->getEnd();
});
$alreadyReserved = $beneficiary->getReservedShifts()->exists(function ($key, Shift $shift) {
Expand Down
44 changes: 24 additions & 20 deletions src/AppBundle/Service/ShiftService.php
Original file line number Diff line number Diff line change
Expand Up @@ -193,42 +193,42 @@ public function getBeneficiariesWhoCanBookForCycle(Membership $member, $cycle =

public function isShiftBookable(Shift $shift, Beneficiary $beneficiary = null)
{
// Do not book old or locked shifts
if ($shift->getIsPast() || $shift->isLocked()) {
if (!$beneficiary) {
Copy link
Member Author

@raphodn raphodn Jan 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pour le if !beneficiary, dans quel cas ca pourrait se produire en fait ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Si un salarié utilise le lien direct pour réserver un créneau (oui, c'est un peu tordu car il n'a pas le lien dans sa vue)

return true;
}

// Do not book old or locked or booked shifts
if ($shift->getIsPast() || $shift->isLocked() || $shift->getShifter()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Attention, tu es sur pour le $shift->getShifter() ?

Je me demande si on n'appelle pas cette methode avec un shift mais ca pourrait aussi concerner un bucket ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

je viens de re-vérifier, c'est toujours un $shift qui est le premier paramètre lorsque la fonction isShiftBookable() est appelée. mais ca me parait bizarre en effet que ca n'ait pas été fix plus tôt

return false;
}
// Do not book pre-booked shift
if ($shift->getLastShifter() && $beneficiary->getId() != $shift->getLastShifter()->getId()) {
return false;
}
if (!$beneficiary) {
return true;
}
// Do not book shift i do not know how to handle (formation)
// Do not book shift the beneficiary cannot handle (formation)
if ($shift->getFormation() && !$beneficiary->getFormations()->contains($shift->getFormation())) {
return false;
}

$member = $beneficiary->getMembership();
if ($member->isCurrentlyExemptedFromShifts($shift->getStart()))
return false;

if ($member->isWithdrawn())
return false;

if ($member->getFirstShiftDate() > $shift->getStart())
return false;

// First shift ever of the beneficiary, check he or she is not the first one to book the bucket
if ($this->isBeginner($beneficiary) && $this->isShiftEmpty($shift)) {
return false;
}

// Check that beneficiary did not book a shift that overlaps the current
if (!$this->canBookShift($beneficiary, $shift)) {
return false;
}

// membership rules (exemption, withdrawn, frozen)
$member = $beneficiary->getMembership();
if ($member->isCurrentlyExemptedFromShifts($shift->getStart())) {
return false;
}
if ($member->isWithdrawn()) {
return false;
}
if ($member->getFirstShiftDate() > $shift->getStart()) {
return false;
}
if ($member->getFrozen()) {
$cycle_end = $this->membershipService->getEndOfCycle($member);
//current cycle : cannot book when frozen
Expand Down Expand Up @@ -295,8 +295,9 @@ public function isShiftEmpty($shift)
public function getBookableShifts(ShiftBucket $bucket, Beneficiary $beneficiary = null)
{
if (!$beneficiary) {
// return all free shifts
$bookableShifts = $bucket->getShifts()->filter(function (Shift $shift) {
return !$shift->getShifter(); // free
return !$shift->getShifter();
});
} else {
if ($bucket->canBookInterval($beneficiary)) {
Expand Down Expand Up @@ -340,8 +341,11 @@ public function getFirstBookable(ShiftBucket $bucket, Beneficiary $beneficiary =
public function getBookableShiftsCount(ShiftBucket $bucket, Beneficiary $beneficiary = null)
{
$bookableShifts = $this->getBookableShifts($bucket, $beneficiary);
if (!$beneficiary)

if (!$beneficiary) {
return count($bookableShifts);
}

return count(ShiftBucket::filterByFormations($bookableShifts, $beneficiary->getFormations()));
}

Expand Down