Problem/Motivation

Whenever the current/next day is displayed, things are not correct when Exception days are used.

Steps to reproduce

- Add an Office Hours field to a content type
- In field settings, Allow exception days (and seasonal days)
- choose the 'current day' formatter, or the select-list formatter with included open/closed status.
- create, edit a node with regular/season hours and exception day in the near feature (today+tomorrow)
- Save, display the node.
The node should display the exception day, with corresponiding open/closed status.

Proposed resolution

Below code shows the relevant formatter option. 'show next' and 'show current' are valid here

    $element['show_closed'] = [
      '#title' => $this->t('Number of days to show'),
      '#type' => 'select',
      '#options' => [
        'all' => $this->t('Show all days'),
        'open' => $this->t('Show only open days'),
        'next' => $this->t('Show next open day'),
        'none' => $this->t('Hide all days'),
        'current' => $this->t('Show only current day'),
      ],
      '#default_value' => $settings['show_closed'],
      '#description' => $this->t('The days to show in the formatter. Useful in combination with the Current Status block.'),
    ];

The code in OfficeHoursFormatterTrait~getRows() must be updated.
When looping over the data, the days for the current week must be overwritten by the data from exception days.
Upon doing so, be aware that the current season must be taken (but this can be added later)
Perhaps also changes in:
- OfficeHoursItemList~getCurrentSlot();
- $items->getCacheTime();

Note: when testing/developing, make sure that initially the formatter options 'schema.org' is disabled, and 'show open/closed status' is set properly, to not to be confused that some code is called multiple times.

Remaining tasks

...

User interface changes

The internal 'View' processing of the API is changed (in Model/View/Controller model).
The 'external' View of the data stay identical.
But perhaps it will change for all-days formatter option.

API changes

None, only the internal 'View' processing of the API is changed (in Model/View/Controller model).

Data model changes

None, only the internal 'View' processing of the API is changed. The Model is already enhanced in previous issues (in Model/View/Controller model).

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

johnv created an issue. See original summary.

johnv’s picture

Issue summary: View changes

  • johnv committed 698efbd on 8.x-1.x
    Issue #3307517: Add Exception Day support for 'Current day' - Remove...
johnv’s picture

The above patch removes error messages in version 1.7 when a current/next status is displayed and Exception Days are maintained.

johnv’s picture

Title: Add Exception Day support for 'Current day' » Add Exception Day support for 'Current day' formatter
luksak’s picture

I'm interested in fixing this. What needs to happen here? I have trouble to wrap my head around this...

johnv’s picture

Issue summary: View changes

Hi Lukas,
glad you step in.
In fact, this is a special case of the (newer) issue #3355643: Season Formatter: update weekly timetable depending on the season.
Since the latest versions, we now have 3 different sets of office hours:
- regular hours
- seasonal hours
- exceptions

There are other requests:
- #3355646: Season Formatter: Each season week as individual table
- do not show all seasons, only current season
But they can be dealt with later.

I updated the issue description. I hope it will help you.

johnv’s picture

Assigned: johnv » Unassigned

Raveen Thakur made their first commit to this issue’s fork.

luksak’s picture

Ok, that is a bit confusing to be tbh. AFAIK that's a different case than I have. I only have exceptions, not seasons. Or is seasons going to replace exceptions alltogether?

I have been using an old path from #1998266: Add "Exception day" feature. In that patch we maintained a separate list of exception days and compare those to the days of the current and next week. This was woughly what we did back then:

  /**
    * @param array $office_hours
    * @return array
    */
   protected function handleClosedDates($office_hours) {
     $time = \Drupal::time()->getRequestTime();
     $today = (int) idate('w', $time);
     $special_opening_hours = $this->getSpecialOpeningHours();
     foreach ($office_hours as $key => $item) {
       $day = (int) $item['startday'];
       if($day == 0) {
         $day = 7;
       }
       if($day >= $today) {
         $week = 'this';
       }
       else {
         $week = 'next';
       }
       $datetime = new DrupalDateTime($day - 1 . ' day ' . $week . ' week');
       $date = $datetime->format('Y-m-d');
       if (isset($special_opening_hours[$date])) {
         if ($special_opening_hours[$date]['closed'] === '1') {
           $office_hours[$key]['closed'] = TRUE;
           $office_hours[$key]['slots'] = [];
         }
         else if (isset($special_opening_hours[$date]['from']) && isset($special_opening_hours[$date]['to'])) {
           $office_hours[$key]['slots'][0] = [
             'start' => $special_opening_hours[$date]['from'],
             'end' => $special_opening_hours[$date]['to'],
           ];
         }
       }
     }
     return $office_hours;
   }

   /**
    * @return array
    */
   protected function getSpecialOpeningHours() {
     $special_opening_hours = [];
     $field_list = $this->parent->get('field_special_opening_hours');
     foreach ($field_list as $item) {
       $entity = $item->entity;
       $special_opening_hours[$entity->get('field_date')->value] = [
         'closed' => $entity->get('field_closed')->value,
         'from' => $entity->get('field_time_from')->value,
         'to' => $entity->get('field_time_to')->value,
       ];
     }
     return $special_opening_hours;
   }

I'd take a similar approach except you know a better one.

@Raveen Thakur sorry, I don't get your MR. The commit is unrelated to this issue.

johnv’s picture

Hi Lukas,
No, seasons is not going to replace exceptions. You can forget seasons for your use case. I will adapt later on.

luksak’s picture

Status: Active » Needs review

There is one remaining issue. The labels of the exception days break for me, since they rely on a timestamp. Du to the changes of my MR, the timestamp is converted to a int representing the weekday. In my opinion this is a flaw of the design per se:

The OfficeHoursItem stores the weekday as an int in the day attribute
The OfficeHoursExceptionsItem stores the timestamp of the days as an int in the same day attribute

That leads to this issue. Why don't we move the timestamp to a different attribute and use the day attribute consistently? Otherwise we have to write weird code to handle the differences between OfficeHoursItem and OfficeHoursExceptionsItem.

johnv’s picture

When testing with multiple slots per day, this part $exception_days[$date] = $item;overwrites the first slot:

  public function getExceptionDays() {
    $exception_days = [];
    $date_formatter = \Drupal::service('date.formatter');

    foreach ($this->list as $item) {
      if ($item->isException()) {
        $slot = $item->getValue();
        $date = $date_formatter->format($slot['day'], 'custom', OfficeHoursItem::EXCEPTION_DAY_LOOKUP_DATE_FORMAT);
        $exception_days[$date] = $item;
      }
    }

    return $exception_days;
  }

johnv’s picture

The attached patch:
- overrides all values of a day, by adding $day_delta. Still a problem when weekday has less slots then exception day.
- getExceptiondays is now static. Still to test with multiple nodes on a page.
- has no problem with labels, due to not copying objects, but only object values.

I renamed $slot to $value, for better code comparison.
There is still duplicate code.
Docu is not updated.

johnv’s picture

Removed the static variable. Must be tested.

luksak’s picture

@johnv Thank you for your feedback and your patch! Here is a quick first review;

Your approach makes sense. I have only tested my particular use case.
There is a lot of calls to dpm()
There is uncommented code in the patch.
Is there a reason for not using the issue fork? I find it a had to review your changes without having a diff.

Also this probably needs tests.

johnv’s picture

Hi Lukas, truth is i have't been able to include MR 's in my workflow, (bluntly: i did not succeed in using them until know) hence, the patch.
indeed, a diff would make things easier. Sorry for that.
I meant to remove the dpms but i needed to wrap things up before Day end.

About your use case: What Will you so with the remai ing exceptional days? Will you not show them at all? Or Just show this weeks exception in the weedkays AND in the exception?

luksak’s picture

Using MRs is actually a lot easier than using patches. I highly recommend you to try them. You can get started using the instructions right below th IS of this issue by clicking on "Show commands".

I am actually only showing the next or current open day. So yes, I don't need them and that's why I didn't experience the issues you are trying to fix with your patch.

johnv’s picture

StatusFileSize
new3.97 KB

OK, I am trying to work with MR's.
I tried to rebase the MR23, since there are some other commits in main branch. Did not succeed , yet.
MR24 can be discarded.

At least, please see attached patch, with less impact, doing exactly the same as the previous one.

  • johnv committed 1ce41635 on 8.x-1.x
    Issue #3307517 by johnv, Lukas von Blarer, Raveen Thakur: Add Exception...
johnv’s picture

Version: 8.x-1.x-dev » 8.x-1.11
Status: Needs review » Fixed

Wow,
a presumably small request with a big patch!

Somewhere in this issue, I decided that the complete formatter should replace the exceptions. But that would require some additional settings.
For the CURRENT formatter, it is always OK to replace with the exception (or with the actual seasonal hours).

For housekeeping, the following have been changed in the interfaces:

$item->IsInRange() --> new
$item->IsOpen() --> new
$item->IsSeasonDay() --> new
$item->isException() --> $item->isExceptionDay()

$itemlist->getNextDay() --> new
$itemlist->getCurrent() --> $itemlist->getCurrentSlot()

OfficeHoursItem::isExceptionDay() --> OfficeHoursDateHelper::isExceptionDay()
OfficeHoursItem::isSeasonDay() --> OfficeHoursDateHelper::isSeasonDay()

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.