Documentation

Holiday Management

The ShopBite plugin offers a comprehensive holiday management system, specifically designed for gastronomy and retail businesses. This chapter explains the detailed configuration and management of holidays and closing days.

Overview

The holiday management system enables:

  • One-time holidays: Configuration of individual closing days
  • Recurring holidays: Annually repeating holidays
  • Period-based closures: Multi-day closures (e.g., Christmas)
  • Sales Channel-specific: Different holidays for different sales channels
  • API integration: Retrieval via the Shopware Store API

Database Structure

CREATE TABLE `shopbite_holiday` (
    `id` BINARY(16) NOT NULL,
    `start` DATETIME(3) NOT NULL,  -- Start of closure
    `end` DATETIME(3) NOT NULL,    -- End of closure
    `sales_channel_id` BINARY(16) NOT NULL,
    `name` VARCHAR(255) NULL,     -- Name of holiday
    `description` TEXT NULL,      -- Description
    `recurring` TINYINT(1) DEFAULT 0,  -- Annually repeating
    `created_at` DATETIME(3) NOT NULL,
    `updated_at` DATETIME(3) NULL,
    PRIMARY KEY (`id`),
    KEY `fk.shopbite_holiday.sales_channel_id` (`sales_channel_id`),
    CONSTRAINT `fk.shopbite_holiday.sales_channel_id` 
        FOREIGN KEY (`sales_channel_id`) 
        REFERENCES `sales_channel` (`id`) 
        ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

Administration Interface

Holiday List

  1. Navigate to ShopBite > Holidays
  2. Overview of all configured holidays
  3. Filtering by Sales Channel and date
  4. Bulk actions (Export, Import)

Add New Holiday

  1. Click Add new holiday
  2. Fill out the form:

Form Fields

FieldTypeDescriptionExample
NameTextName of the holiday"Christmas"
Start DateDate/TimeStart of closure2024-12-24 18:00
End DateDate/TimeEnd of closure2024-12-26 23:59
Sales ChannelDropdownSelect sales channel"My Restaurant"
Repeat annuallyCheckboxRepeat holiday annually
DescriptionTextareaOptional description"Christmas holidays - closed"

Edit Holidays

  1. Click Edit button
  2. Adjust fields
  3. Click Save

Delete Holidays

  1. Click Delete button
  2. Confirmation dialog
  3. Click Confirm

API Integration

API Endpoint

GET /store-api/shopbite/holiday

Request Parameters

ParameterTypeDescriptionExample
sales-channel-idUUIDFilter by Sales Channel019a36f224b0704fb6835914050392f4
from-dateDateFilter by start date2024-01-01
to-dateDateFilter by end date2024-12-31
include-recurringBooleanInclude recurring holidaystrue

Example Request

curl -X GET "https://your-shopware-domain.com/store-api/shopbite/holiday" \
  -H "Authorization: Bearer YourAPIToken" \
  -H "sw-access-key: YourAccessKey" \
  -H "Accept: application/json"

Example Response

{
  "apiAlias": "shopbite_holiday",
  "data": [
    {
      "id": "019a36f224b0704fb6835914050392f4",
      "start": "2024-12-24T18:00:00+01:00",
      "end": "2024-12-26T23:59:00+01:00",
      "salesChannelId": "019a36f224b0704fb6835914050392f4",
      "name": "Christmas",
      "description": "Christmas holidays - closed",
      "recurring": false,
      "createdAt": "2024-01-11T14:30:00+01:00",
      "updatedAt": "2024-01-11T14:30:00+01:00",
      "salesChannel": {
        "id": "019a36f224b0704fb6835914050392f4",
        "name": "My Restaurant",
        "typeId": "019a36f224b0704fb6835914050392f4"
      }
    },
    {
      "id": "019a36f224b0704fb6835914050392f5",
      "start": "2024-01-01T00:00:00+01:00",
      "end": "2024-01-01T23:59:00+01:00",
      "salesChannelId": "019a36f224b0704fb6835914050392f4",
      "name": "New Year",
      "description": "Closed",
      "recurring": true,
      "createdAt": "2024-01-11T14:30:00+01:00",
      "updatedAt": "2024-01-11T14:30:00+01:00",
      "salesChannel": {
        "id": "019a36f224b0704fb6835914050392f4",
        "name": "My Restaurant",
        "typeId": "019a36f224b0704fb6835914050392f4"
      }
    }
  ],
  "total": 10
}

Error Handling

Status CodeMeaningSolution
200SuccessData successfully retrieved
401UnauthorizedCheck API credentials
403ForbiddenCheck permissions
404Not FoundEndpoint or Sales Channel not found
500Internal Server ErrorCheck server logs

Recurring Holidays

How it works

Recurring holidays are automatically recreated for each year:

  • Annual creation: On January 1st of each year
  • Period: Based on the original date
  • Notification: Admin notification upon creation

Management

  1. Navigate to ShopBite > Holidays > Recurring Holidays
  2. Click Add new recurring holiday
  3. Fill out the form:

Form Fields

FieldTypeDescriptionExample
NameTextName of the holiday"Christmas"
MonthDropdownSelect monthDecember
DayNumberDay in month25
Start TimeTimeStart of closure18:00
End TimeTimeEnd of closure23:59
Sales ChannelDropdownSelect sales channel"My Restaurant"
DescriptionTextareaOptional description"Christmas holidays"

API Query for Recurring Holidays

curl -X GET "https://your-shopware-domain.com/store-api/shopbite/holiday?include-recurring=true" \
  -H "Authorization: Bearer YourAPIToken" \
  -H "sw-access-key: YourAccessKey"

Best Practices

1. Consistent Date Formats

  • Always use the ISO-8601 format (YYYY-MM-DD)
  • Time entries in 24-hour format (HH:MM:SS)
  • Consider time zones (Default: Europe/Berlin)

2. Avoid Overlapping Holidays

  • Check for overlaps with other holidays
  • Use the calendar in the administration
  • Utilize validation when saving

3. Performance Optimization

-- Indexes for better performance
CREATE INDEX `idx_sales_channel` ON `shopbite_holiday` (`sales_channel_id`);
CREATE INDEX `idx_date_range` ON `shopbite_holiday` (`start`, `end`);
CREATE INDEX `idx_recurring` ON `shopbite_holiday` (`recurring`);

4. Caching Strategy

# In config/packages/cache.yaml
framework:
    cache:
        pools:
            shopbite.holidays:
                adapter: cache.adapter.redis
                default_lifetime: 86400  # 24 hour cache

5. Automatic Notifications

Set up notifications for upcoming holidays:

// In a cron job or event listener
$upcomingHolidays = $holidayRepository->getUpcomingHolidays(7); // Next 7 days

foreach ($upcomingHolidays as $holiday) {
    $mailer->sendHolidayReminder($holiday);
}

Storefront Integration

JavaScript Example

// Fetch holidays
async function fetchHolidays(salesChannelId, fromDate, toDate) {
  const params = new URLSearchParams({
    'sales-channel-id': salesChannelId,
    'from-date': fromDate,
    'to-date': toDate
  });
  
  const response = await fetch(
    `/store-api/shopbite/holiday?${params.toString()}`,
    {
      headers: {
        'Authorization': `Bearer ${apiToken}`,
        'sw-access-key': accessKey
      }
    }
  );
  
  if (!response.ok) {
    throw new Error('Failed to fetch holidays');
  }
  
  return await response.json();
}

// Display holidays
function displayHolidays(holidays) {
  holidays.data.forEach(holiday => {
    const start = new Date(holiday.start);
    const end = new Date(holiday.end);
    
    console.log(`${holiday.name}: ${start.toLocaleDateString()} - ${end.toLocaleDateString()}`);
    console.log(`Description: ${holiday.description || 'No description'}`);
  });
}

// Example call
const today = new Date().toISOString().split('T')[0];
const nextYear = new Date();
nextYear.setFullYear(nextYear.getFullYear() + 1);
const nextYearEnd = nextYear.toISOString().split('T')[0];

fetchHolidays('019a36f224b0704fb6835914050392f4', today, nextYearEnd)
  .then(displayHolidays)
  .catch(console.error);

Vue.js Component

<template>
  <div class="holidays">
    <h3>Holidays and Closing Days</h3>
    <div v-for="holiday in holidays" :key="holiday.id" class="holiday-item">
      <div class="holiday-header">
        <span class="holiday-name">{{ holiday.name }}</span>
        <span v-if="holiday.recurring" class="badge recurring">Annual</span>
      </div>
      <div class="holiday-dates">
        {{ formatDate(holiday.start) }} - {{ formatDate(holiday.end) }}
      </div>
      <div v-if="holiday.description" class="holiday-description">
        {{ holiday.description }}
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      holidays: [],
      loading: false,
      error: null
    };
  },
  
  async mounted() {
    await this.fetchHolidays();
  },
  
  methods: {
    formatDate(dateString) {
      const date = new Date(dateString);
      return date.toLocaleDateString('en-GB', {
        weekday: 'short',
        day: 'numeric',
        month: 'short',
        year: 'numeric',
        hour: '2-digit',
        minute: '2-digit'
      });
    },
    
    async fetchHolidays() {
      this.loading = true;
      this.error = null;
      
      try {
        const response = await this.$axios.get('/store-api/shopbite/holiday');
        this.holidays = response.data.data;
      } catch (error) {
        console.error('Failed to fetch holidays:', error);
        this.error = 'Holidays could not be loaded.';
      } finally {
        this.loading = false;
      }
    }
  }
};
</script>

<style scoped>
.holidays {
  margin: 20px 0;
  padding: 15px;
  background: #fff5f5;
  border-radius: 8px;
  border: 1px solid #ffebee;
}

.holiday-item {
  padding: 12px 0;
  border-bottom: 1px solid #ffcdcd;
}

.holiday-item:last-child {
  border-bottom: none;
}

.holiday-header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
}

.holiday-name {
  font-weight: bold;
  color: #d32f2f;
}

.badge {
  padding: 2px 6px;
  border-radius: 4px;
  font-size: 12px;
  font-weight: bold;
}

.badge.recurring {
  background: #ffeb3b;
  color: #000;
}

.holiday-dates {
  color: #b71c1c;
  font-size: 14px;
  margin-bottom: 4px;
}

.holiday-description {
  color: #666;
  font-size: 13px;
  font-style: italic;
}
</style>

Troubleshooting and Debugging

Common Issues

Problem: Holidays are not taken into account

Symptoms:

  • Orders possible on holidays
  • Holidays not visible in API

Solutions:

# Check holiday data
mysql -u shopware -p shopware -e "SELECT * FROM shopbite_holiday;"

# Check time format
bin/console database:check-timezone

# Test API query
curl -X GET "https://your-domain.com/store-api/shopbite/holiday" -H "Authorization: Bearer YourToken"

Problem: Recurring holidays are not created

Symptoms:

  • Recurring holidays are missing
  • No automatic creation

Solutions:

# Check cron job
crontab -l

# Execute manually
bin/console shopbite:holidays:generate-recurring

# Check logs
tail -n 50 /var/log/shopware/prod.log | grep holiday

Problem: Time overlaps

Symptoms:

  • Holidays overlap
  • Incorrect closing times

Solutions:

# Check for overlaps
mysql -u shopware -p shopware -e "
  SELECT * FROM shopbite_holiday 
  WHERE start < (SELECT end FROM shopbite_holiday WHERE id != shopbite_holiday.id)
  AND end > (SELECT start FROM shopbite_holiday WHERE id != shopbite_holiday.id);"

# Enable validation
bin/console config:set ShopBitePlugin.config.validateHolidayOverlaps true

Next Steps