Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 1
EffectiveHours
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 13
306
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 fromDefault
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 fromDayOverride
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 fromHoliday
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 closed
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getOpenTime
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getCloseTime
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isClosed
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSource
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isDayOverride
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isHolidayOverride
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isDefault
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 toArray
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace BuyerKiosk\StoreConfig\DTOs;
4
5/**
6 * EffectiveHours Value Object
7 *
8 * Represents the resolved store hours for a specific date after applying
9 * the priority cascade: holiday > day override > default.
10 *
11 * This is an immutable value object - once created, it cannot be modified.
12 *
13 * @package BuyerKiosk\StoreConfig\DTOs
14 */
15class EffectiveHours
16{
17    public const SOURCE_DEFAULT = 'default';
18    public const SOURCE_DAY_OVERRIDE = 'day_override';
19    public const SOURCE_HOLIDAY = 'holiday';
20
21    private ?string $openTime;
22    private ?string $closeTime;
23    private bool $isClosed;
24    private string $source;
25
26    /**
27     * Create a new EffectiveHours instance
28     *
29     * @param string|null $openTime Time in HH:MM format (24-hour), null if closed
30     * @param string|null $closeTime Time in HH:MM format (24-hour), null if closed
31     * @param bool $isClosed Whether the store is closed
32     * @param string $source Source of these hours (default, day_override, holiday)
33     */
34    public function __construct(
35        ?string $openTime,
36        ?string $closeTime,
37        bool $isClosed,
38        string $source
39    ) {
40        $this->openTime = $openTime;
41        $this->closeTime = $closeTime;
42        $this->isClosed = $isClosed;
43        $this->source = $source;
44    }
45
46    /**
47     * Create EffectiveHours from default store hours
48     *
49     * @param string $openTime Default open time HH:MM
50     * @param string $closeTime Default close time HH:MM
51     * @return self
52     */
53    public static function fromDefault(string $openTime, string $closeTime): self
54    {
55        return new self($openTime, $closeTime, false, self::SOURCE_DEFAULT);
56    }
57
58    /**
59     * Create EffectiveHours from a day-of-week override
60     *
61     * @param string|null $openTime Open time HH:MM or null if closed
62     * @param string|null $closeTime Close time HH:MM or null if closed
63     * @param bool $isClosed Whether the day is marked closed
64     * @return self
65     */
66    public static function fromDayOverride(?string $openTime, ?string $closeTime, bool $isClosed): self
67    {
68        return new self(
69            $isClosed ? null : $openTime,
70            $isClosed ? null : $closeTime,
71            $isClosed,
72            self::SOURCE_DAY_OVERRIDE
73        );
74    }
75
76    /**
77     * Create EffectiveHours from a holiday override
78     *
79     * @param string|null $openTime Open time HH:MM or null if closed
80     * @param string|null $closeTime Close time HH:MM or null if closed
81     * @param bool $isClosed Whether the holiday is marked closed
82     * @return self
83     */
84    public static function fromHoliday(?string $openTime, ?string $closeTime, bool $isClosed): self
85    {
86        return new self(
87            $isClosed ? null : $openTime,
88            $isClosed ? null : $closeTime,
89            $isClosed,
90            self::SOURCE_HOLIDAY
91        );
92    }
93
94    /**
95     * Create a closed EffectiveHours instance
96     *
97     * @param string $source Source of the closure
98     * @return self
99     */
100    public static function closed(string $source): self
101    {
102        return new self(null, null, true, $source);
103    }
104
105    /**
106     * Get the opening time
107     *
108     * @return string|null Time in HH:MM format or null if closed
109     */
110    public function getOpenTime(): ?string
111    {
112        return $this->openTime;
113    }
114
115    /**
116     * Get the closing time
117     *
118     * @return string|null Time in HH:MM format or null if closed
119     */
120    public function getCloseTime(): ?string
121    {
122        return $this->closeTime;
123    }
124
125    /**
126     * Check if the store is closed
127     *
128     * @return bool
129     */
130    public function isClosed(): bool
131    {
132        return $this->isClosed;
133    }
134
135    /**
136     * Get the source of these hours
137     *
138     * @return string One of: default, day_override, holiday
139     */
140    public function getSource(): string
141    {
142        return $this->source;
143    }
144
145    /**
146     * Check if hours come from a day-of-week override
147     *
148     * @return bool
149     */
150    public function isDayOverride(): bool
151    {
152        return $this->source === self::SOURCE_DAY_OVERRIDE;
153    }
154
155    /**
156     * Check if hours come from a holiday override
157     *
158     * @return bool
159     */
160    public function isHolidayOverride(): bool
161    {
162        return $this->source === self::SOURCE_HOLIDAY;
163    }
164
165    /**
166     * Check if hours are using defaults (no overrides)
167     *
168     * @return bool
169     */
170    public function isDefault(): bool
171    {
172        return $this->source === self::SOURCE_DEFAULT;
173    }
174
175    /**
176     * Convert to array for JSON serialization
177     *
178     * @return array{openTime: string|null, closeTime: string|null, isClosed: bool, source: string}
179     */
180    public function toArray(): array
181    {
182        return [
183            'openTime' => $this->openTime,
184            'closeTime' => $this->closeTime,
185            'isClosed' => $this->isClosed,
186            'source' => $this->source,
187        ];
188    }
189}