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.
The holiday management system enables:
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;
| Field | Type | Description | Example |
|---|---|---|---|
| Name | Text | Name of the holiday | "Christmas" |
| Start Date | Date/Time | Start of closure | 2024-12-24 18:00 |
| End Date | Date/Time | End of closure | 2024-12-26 23:59 |
| Sales Channel | Dropdown | Select sales channel | "My Restaurant" |
| Repeat annually | Checkbox | Repeat holiday annually | ✓ |
| Description | Textarea | Optional description | "Christmas holidays - closed" |
GET /store-api/shopbite/holiday
| Parameter | Type | Description | Example |
|---|---|---|---|
sales-channel-id | UUID | Filter by Sales Channel | 019a36f224b0704fb6835914050392f4 |
from-date | Date | Filter by start date | 2024-01-01 |
to-date | Date | Filter by end date | 2024-12-31 |
include-recurring | Boolean | Include recurring holidays | true |
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"
{
"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
}
| Status Code | Meaning | Solution |
|---|---|---|
| 200 | Success | Data successfully retrieved |
| 401 | Unauthorized | Check API credentials |
| 403 | Forbidden | Check permissions |
| 404 | Not Found | Endpoint or Sales Channel not found |
| 500 | Internal Server Error | Check server logs |
Recurring holidays are automatically recreated for each year:
| Field | Type | Description | Example |
|---|---|---|---|
| Name | Text | Name of the holiday | "Christmas" |
| Month | Dropdown | Select month | December |
| Day | Number | Day in month | 25 |
| Start Time | Time | Start of closure | 18:00 |
| End Time | Time | End of closure | 23:59 |
| Sales Channel | Dropdown | Select sales channel | "My Restaurant" |
| Description | Textarea | Optional description | "Christmas 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"
-- 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`);
# In config/packages/cache.yaml
framework:
cache:
pools:
shopbite.holidays:
adapter: cache.adapter.redis
default_lifetime: 86400 # 24 hour cache
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);
}
// 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);
<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>
Symptoms:
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"
Symptoms:
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
Symptoms:
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