Plugin Directory

Changeset 3729359


Ignore:
Timestamp:
10/05/2026 05:21:01 PM (3 weeks ago)
Author:
salesigniter
Message:

1.0.15: Booked by (days or nights, for the store and per product), Also charge the return day, the unified pricing engine, one date picker for grouped rentals, fixes

Location:
sales-igniter-rentals-bookings-lite/trunk
Files:
1 added
38 edited

Legend:

Unmodified
Added
Removed
  • sales-igniter-rentals-bookings-lite/trunk/Admin/DataRemovalSettings.php

    r3728745 r3729359  
    77/**
    88 * Rentals > Settings > Advanced > "Delete rental data on uninstall": the double opt-in
    9  * (review finding BOOT-05, Kevin's answer to §7 Q8). See Model\DataRemoval.
     9 * (review finding BOOT-05, the owner's answer to §7 Q8). See Model\DataRemoval.
    1010 *
    1111 * The block is an ACF `message` field whose message is empty; its text and its inputs
    … …  
    1313 * `.acf-input`), NOT put into the field's message: SCF prints a message through
    1414 * acf_esc_html() / wp_kses(), which strips every <input>, so the box and the phrase
    15  * never reached the page (found by Kevin on a real install, 2026-09-28).
     15 * never reached the page (found by the owner on a real install, 2026-09-28).
    1616 *
    1717 * The inputs are this block's own (not ACF's), so nothing about it is stored by ACF: the
  • sales-igniter-rentals-bookings-lite/trunk/Admin/Upgrade.php

    r3728745 r3729359  
    9090                [ __( 'Date and date-and-time rentals with turnover buffers, store hours and closed days', 'sales-igniter-rentals-bookings-lite' ), true, true, true ],
    9191                [ __( 'Stays by the night: check-in and check-out days, prices and turnover in nights', 'sales-igniter-rentals-bookings-lite' ), true, true, true ],
    92                 [ __( 'Extras for nightly stays: half-day cells for partly booked days, a Stay type bulk action, room-nights in the Usage Report', 'sales-igniter-rentals-bookings-lite' ), false, true, true ],
     92                [ __( 'Extras for nightly stays: half-day cells for partly booked days, a "Booked by" bulk action, room-nights in the Usage Report', 'sales-igniter-rentals-bookings-lite' ), false, true, true ],
    9393                [ __( 'Guests and party size: a single count, or adults, children, infants and pets with age notes and limits', 'sales-igniter-rentals-bookings-lite' ), true, true, true ],
    9494                [ __( 'Popup Range Picker with the Calendar Styler (four looks, your colours)', 'sales-igniter-rentals-bookings-lite' ), true, true, true ],
  • sales-igniter-rentals-bookings-lite/trunk/Ajax/Calendar.php

    r3728745 r3729359  
    9696     */
    9797    public function sibooking_calendar(){
    98         // Kevin's answer to review Q10: the shopper calls stay open to visitors, but carry
     98        // The owner's answer to review Q10: the shopper calls stay open to visitors, but carry
    9999        // the calendar's nonce (sibooking.data.nonce; the scripts renew it on a cached page
    100100        // through sibooking_calendar_fresh) and are rate limited per client
    … …  
    452452            // last day's 00:00, so that day is free to start on and a booked day can be an end
    453453            'returnDayFree' => $dated && \Sibooking\Model\Buffer::startOnReturnDay((int) $settingsId),
     454            // "Also charge the return day" (Model\Pricing\ConfigFactory::countsReturnDay()): a
     455            // date-only rental's length counts its return day too ("1st to 3rd: 3 days")
     456            'chargeReturnDay' => $productid && !$nightly && !\Sibooking\Model\RentalTypes::usesTime(\Sibooking\Model\RentalTypes::of((int) $settingsId)) && \Sibooking\Model\Pricing\ConfigFactory::countsReturnDay((int) $settingsId),
    454457            // date picker selection + styler settings (Model\CalendarStyle); read by
    455458            // sibookingCalendarUi.js, ignored by the previous picker
    … …  
    529532            'dated' => false,
    530533            'returnDayFree' => false,
     534            'chargeReturnDay' => false,
    531535            'turnoverBeforeMinutes' => 0,
    532536            'turnoverAfterMinutes' => 0,
  • sales-igniter-rentals-bookings-lite/trunk/Ajax/Price.php

    r3728745 r3729359  
    3333    public function rental_price() {
    3434        check_ajax_referer( 'sibooking-calendar', 'nonce' );
    35         // Kevin's answer to review Q10: open to visitors, rate limited per client
     35        // The owner's answer to review Q10: open to visitors, rate limited per client
    3636        if ( ! \Sibooking\Model\RateLimit::hit( 'quote' ) ) {
    3737            \Sibooking\Model\RateLimit::refuse();
    … …  
    222222        $total = (float) $line['total'];
    223223        $breakdown = self::breakdown( $product, $dates['start'], $dates['end'], $unit, $line['engine'] ?? null );
     224        // "Also charge the return day" (Model\Pricing\ConfigFactory): a date-only rental from the
     225        // 1st to the 3rd is charged, and so counted, as 3 days
     226        if ( ! $nightly && '' === $totime && $to > $from && \Sibooking\Model\Pricing\ConfigFactory::countsReturnDay( $product->get_id() ) ) {
     227            $breakdown['days'] = max( 1, \Sibooking\Model\Stay::nights( $from, $to ) ) + 1;
     228        }
    224229        $extra = [];
    225230        foreach ( $line['lines'] as $l ) {
  • sales-igniter-rentals-bookings-lite/trunk/Api.php

    r3728745 r3729359  
    405405    }
    406406
     407    /**
     408     * A product's "Booked by" as the shop set it (its own choice, else the store's "Date-only
     409     * rentals are booked by"): days (the return day held), days_free (can go out again on the
     410     * return day) or nights. Without a product: the store's. Not filtered (nightly() and
     411     * startOnReturnDay() are).
     412     *
     413     * @since api 1 (Lite 1.0.15)
     414     * @param int|null $productId
     415     * @return string days | days_free | nights
     416     */
     417    public static function bookedBy( $productId = null ) {
     418        return $productId ? Stay::rule( (int) $productId ) : Stay::storeRule();
     419    }
     420
     421    /**
     422     * "Also charge the return day" for a product (its own Yes / No, else the store's): a
     423     * date-only rental booked by the day from the 1st to the 3rd is then 3 days, not 2.
     424     * Stays of nights are priced per night whatever this says.
     425     *
     426     * @since api 1 (Lite 1.0.15)
     427     * @param int $productId
     428     * @return bool
     429     */
     430    public static function chargesReturnDay( $productId ) {
     431        return Model\Pricing\ConfigFactory::chargesReturnDay( $productId );
     432    }
     433
    407434    /** @since api 1 @return string a date as the reminder emails print it (Rentals > Settings > Date Formatting) */
    408435    public static function emailDate( $datetime ) {
    … …  
    639666
    640667    /**
    641      * Whether a date-only rental's return day is free for another rental to start on (the
    642      * store setting "Allow rentals to start on the same day as they are returned", filter
    643      * sibooking_start_on_return_day). Stays of nights and timed rentals never ask.
     668     * Whether a date-only rental's return day is free for another rental to start on: the
     669     * product is "Booked by" "Days: can go out again on the return day" (bookedBy() is
     670     * days_free; until Lite 1.0.15 the store yes/no "Allow rentals to start on the same day as
     671     * they are returned"), filter sibooking_start_on_return_day. Stays of nights and timed
     672     * rentals never ask.
    644673     *
    645674     * @since api 1 (Lite 1.0.13)
  • sales-igniter-rentals-bookings-lite/trunk/Assets/public/js/sibookingCalendarRules.js

    r3728745 r3729359  
    827827
    828828    /** Whole days between two dates, at least 1 (same day = 1); for a stay of nights, the nights. */
    829     function dayCount(start, end, nightly) {
     829    function dayCount(start, end, nightly, chargeReturnDay) {
    830830        var s = toMoment(start).startOf('day'), e = toMoment(end).startOf('day');
    831         return nightly ? Math.max(0, e.diff(s, 'days')) : Math.max(1, e.diff(s, 'days'));
     831        if (nightly) return Math.max(0, e.diff(s, 'days'));
     832        // "Also charge the return day" (data.chargeReturnDay): the 1st to the 3rd is 3 days
     833        if (chargeReturnDay && e.diff(s, 'days') > 0) return e.diff(s, 'days') + 1;
     834        return Math.max(1, e.diff(s, 'days'));
    832835    }
    833836
  • sales-igniter-rentals-bookings-lite/trunk/Assets/public/js/sibookingCalendarRules.min.js

    r3728745 r3729359  
    1 (function(c,s){typeof module=="object"&&module.exports?module.exports=s(typeof globalThis!="undefined"&&globalThis.moment?globalThis.moment:require("moment")):c.SibookingCalendarRules=s(c.moment)})(typeof self!="undefined"?self:this,function(c){"use strict";var s="YYYY-MM-DD",Y="23:59",H="00:00";function d(r){return c.isMoment(r)?r.clone():c(r)}function S(r){var e=parseInt(r,10);return isNaN(e)||e<1?1:e}function Q(r,e){var n=S(e),t=[];return(r.fullybooked_dates||[]).forEach(function(i){(r.isComposite&&!r.compositeUnits||n>r.inventoryQuantity-i.quantity)&&t.push(i.date)}),t}function ur(r,e){if(r.isComposite&&!r.compositeUnits||!r.inventoryQuantity||r.inventoryQuantity>=99999)return null;var n=0;return(r.fullybooked_dates||[]).some(function(t){return t.date===e?(n=t.quantity,!0):!1}),Math.max(0,r.inventoryQuantity-n)}function fr(r,e,n){if(r.isComposite&&!r.compositeUnits||r.inventoryQuantity===void 0||r.inventoryQuantity===null||r.inventoryQuantity>=99999)return null;var t=Math.max(0,parseInt(r.inventoryQuantity,10)||0);if(!e||!t)return t;var i=d(e).startOf("day"),o=d(n||e).startOf("day");o.isBefore(i)&&(o=i.clone());var u={};(r.fullybooked_dates||[]).forEach(function(l){u[l.date]=Math.min(u[l.date]===void 0?1/0:u[l.date],t-(parseInt(l.quantity,10)||0))});for(var f=0;!i.isAfter(o,"day")&&f<3660;f++){var a=i.format(s);u[a]!==void 0&&(t=Math.min(t,Math.max(0,u[a]))),i.add(1,"d")}return t}function _(r,e){var n=d(r);if(!e||e.quantity===void 0||e.quantity==="")return n;var t=parseFloat(e.quantity);switch(e.type){case"hours":case"h":n.add(t,"h");break;case"days":case"d":n.add(t,"d");break;case"w":n.add(t,"w");break;case"m":n.add(t,"M");break;case"y":n.add(t,"y");break}return n}function N(r){return r&&typeof r=="object"&&r.quantity!==void 0&&r.quantity!==""&&r.quantity!==null}function ar(r,e){var n=d(r);return n.isSameOrAfter(c(e.from))&&n.isSameOrBefore(c(e.to))}var F=typeof WeakMap=="function"?new WeakMap:null;function P(r){if(r=r||[],F&&F.has(r))return F.get(r);for(var e={},n=[],t=0;t<r.length;t++){var i=r[t];if(typeof i=="string")e[i]=!0;else if(i&&i.from!==void 0){var o=c(i.from),u=c(i.to);o.isValid()&&u.isValid()&&n.push({from:o.valueOf(),to:u.valueOf(),item:i})}}var f={days:e,ranges:n};return F&&F.set(r,f),f}function sr(r){var e=r.split("-"),n=new Date(+e[0],+e[1]-1,+e[2],0,0,0,0).getTime(),t=new Date(+e[0],+e[1]-1,+e[2],23,59,59,999).getTime();return[n,t]}function U(r,e){return W(r,e).length>0}function W(r,e){var n=P(e),t=[];if(n.days[r]&&t.push(r),n.ranges.length)for(var i=sr(r),o=0;o<n.ranges.length;o++){var u=n.ranges[o];u.from<=i[1]&&u.to>=i[0]&&t.push(u.item)}return t}function T(r,e){var n=[];if(!r||!N(e))return n;var t=d(r).startOf("day").add(1,"d"),i=_(d(r).startOf("day"),e);i.isAfter(t,"day")&&n.push(d(r).format(s));for(var o=t.clone();o.isBefore(i,"day");)n.push(o.format(s)),o.add(1,"d");return n}function z(r,e,n){var t=d(r).startOf("day");if(n){var i=t.clone().add(1,"d");if(N(e)){var o=_(t.clone(),e);o.isAfter(i)&&(i=o)}return i.format(s)}if(N(e)){var u=_(t.clone(),e);if(u.isAfter(t))return u.format(s)}return t.format(s)}function V(r,e){if(r){var n={};(e||[]).forEach(function(u){n[u]=!0});for(var t=d(r).startOf("day"),i=0;i<730;i++){var o=t.format(s);if(n[o])return o;t.add(1,"d")}}}function lr(r,e,n,t,i){var o=[];if(r.maxFutureDaysDate&&o.push(c(r.maxFutureDaysDate)),e&&N(r.maxLength)&&o.push(_(d(e),r.maxLength)),e&&r.dated){var u=ir(r,d(e).format(s),i);u&&o.push(c(u,s))}if(e&&t&&r.timed){var f=X(r,d(e).format(s)+" "+t,i);f&&o.push(c(f,s))}if(e){var a=V(e,n);if(a&&r.nightly)o.push(c(a,s).subtract(R(r.bufferAfterDays),"d"));else if(a&&r.dated&&r.returnDayFree){var l=Number(r.bufferAfterMinutes)||0;o.push(c(a,s).subtract(l>0?Math.ceil(l/1440):0,"d"))}else if(a){var v=Number(r.bufferAfterMinutes)||0,h=r.calendarType==="datetime",p=v>0?h?Math.floor(v/1440):Math.ceil(v/1440):0;o.push(p>0?c(a,s).subtract(1+p,"d"):c(a,s))}}if(!o.length)return null;var b=o.reduce(function(D,k){return D.isBefore(k)?D:k});return b.format(s)}function cr(r,e,n,t){if(r.nightly)return vr(r,e,n,t);var i=Q(r,e),o=i.concat(r.disabled_dates||[]),u=o.concat(t||[]).concat(r.disabled_dates_start||[]).concat(r.timed?G(r,e):[]).concat(r.dated?tr(r,e):[]),f=r.dated&&r.returnDayFree?r.disabled_dates||[]:o,a=f.concat(n?T(n,r.minLength):[]).concat(r.disabled_dates_end||[]);return{booked:i,both:o,start:u,end:a}}function R(r){var e=Math.ceil(Number(r)||0);return e>0?e:0}function w(r,e){return d(e).startOf("day").diff(d(r).startOf("day"),"days")}function q(r,e){var n={};return Q(r,e).forEach(function(t){n[t]=!0}),n}function vr(r,e,n,t){var i=q(r,e),o=Object.keys(i).sort(),u=R(r.bufferBeforeDays),f=R(r.bufferAfterDays),a=N(r.minLength)?Math.max(1,w(c("2000-01-01",s),_(c("2000-01-01",s),r.minLength))):1,l={};o.forEach(function(k){for(var M=c(k,s),A=-(a+f-1);A<=u;A++)l[M.clone().add(A,"d").format(s)]=!0});var v=Object.keys(l).sort().concat(r.disabled_dates||[]).concat(t||[]).concat(r.disabled_dates_start||[]);typeof r.inventoryQuantity=="number"&&S(e)>r.inventoryQuantity&&(v=v.concat([{from:"1900-01-01",to:"2999-12-31"}]));var h=[];if(n)for(var p=c(z(n,r.minLength,!0),s),b=d(n).startOf("day");b.isBefore(p,"day");b.add(1,"d"))h.push(b.format(s));var D=(r.disabled_dates||[]).concat(h).concat(r.disabled_dates_end||[]);return{booked:o,both:o.concat(r.disabled_dates||[]),start:v,end:D}}function dr(r,e,n){var t=q(r,e),i=!!t[c(n,s).subtract(1,"d").format(s)],o=!!t[n];return!i&&!o?"free":i&&!o?"checkin-only":!i&&o?"checkout-only":(r.turnover_dates||[]).indexOf(n)!==-1?"turnover":"booked"}var y=1440;function g(r){var e=/^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{1,2}):(\d{2}))?/.exec(String(r));return e?Math.round(Date.UTC(+e[1],+e[2]-1,+e[3])/6e4)+(e[4]?+e[4]*60+ +e[5]:0):NaN}function E(r){return new Date(Math.floor(r/y)*864e5).toISOString().slice(0,10)}function yr(r){var e=Math.floor(r/60),n=r%60;return(e<10?"0":"")+e+":"+(n<10?"0":"")+n}function O(r){var e=parseInt(r&&r.turnoverBeforeMinutes,10),n=parseInt(r&&r.turnoverAfterMinutes,10);return{before:e>0?e:0,after:n>0?n:0}}var I=typeof WeakMap=="function"?new WeakMap:null;function C(r){if(!I||!r||typeof r!="object")return null;var e=I.get(r);return(!e||e.list!==r.inventoryArray||e.before!==r.turnoverBeforeMinutes||e.after!==r.turnoverAfterMinutes||e.qty!==r.inventoryQuantity)&&(e={list:r.inventoryArray,before:r.turnoverBeforeMinutes,after:r.turnoverAfterMinutes,qty:r.inventoryQuantity},I.set(r,e)),e}function B(r,e){var n=S(e),t=C(r);if(t&&t["full"+n])return t["full"+n];var i=[];return(r.inventoryArray||[]).forEach(function(o){if(!(!o||!(n>r.inventoryQuantity-o.q))){var u=g(o.s),f=g(o.e);!isNaN(u)&&!isNaN(f)&&f>u&&i.push({s:u,e:f})}}),t&&(t["full"+n]=i),i}function hr(r,e,n){var t=S(n),i=d(e).startOf("day"),o=[];return(r.inventoryArray||[]).forEach(function(u){if(t>r.inventoryQuantity-u.q){var f=c(u.s),a=c(u.e),l=i.isSame(f,"day"),v=i.isSame(a,"day");l&&v?o.push([f.format("HH:mm"),a.format("HH:mm")]):l&&a.isAfter(i,"day")?o.push([f.format("HH:mm"),Y]):v&&f.isBefore(a,"day")?o.push([H,a.format("HH:mm")]):f.isBefore(i,"day")&&a.isAfter(i,"day")&&o.push([H,Y])}}),o}function x(r,e,n,t){for(var i=0;i<r.length;i++)if(n-e.before<r[i].e&&t+e.after>r[i].s)return!0;return!1}function J(r,e){if(!r.minDate||String(r.minDate).slice(0,10)!==e)return null;var n=/[ T](\d{1,2}):(\d{2})(?::(\d{2}))?/.exec(String(r.minDate));if(!n)return null;var t=+n[1]*60+ +n[2]+(n[3]&&+n[3]>0?1:0);return t>0?yr(Math.min(t,y)):null}function j(r,e,n,t,i){i=i||{};var o=d(e).format(s),u=g(o),f=Z(r.storeopen,r.storeclose,r.ui&&r.ui.slotInterval||r.timeIncrement),a={};if(t==="to"&&i.pickup&&String(i.pickup).slice(0,10)===o&&(a.notBefore=String(i.pickup).slice(11,16)),t!=="to"){var l=J(r,o);l&&(a.notEarlierThan=l)}var v=$(f,[],a);if(!f.length)return v;var h=O(r),p=B(r,n),b=m(f[0]),D=m(f[f.length-1]),k=g(t==="to"?i.pickup:i.ret);return v.forEach(function(M,A){if(!M.disabled){var or=u+m(M.time),L=k;isNaN(L)&&(t==="to"?L=A>0?u+m(f[A-1]):u-y+D:L=A<f.length-1?u+m(f[A+1]):u+y+b);var gr=t==="to"?x(p,h,L,or):x(p,h,or,L);gr&&(M.disabled=!0,M.reason="booked")}}),v}function G(r,e){var n=S(e),t=C(r);if(t&&t["np"+n]&&t.minDate===r.minDate&&t.hours===r.storeopen+"-"+r.storeclose)return t["np"+n];var i=O(r),o={},u={};if(B(r,n).forEach(function(l){var v=Math.floor((l.s-i.after)/y),h=Math.floor((l.e+i.before-1)/y);o[v-1]=o[v]=o[h]=o[h+1]=!0;for(var p=v+1,b=0;p<h&&b<3660;p++,b++)u[p]=!0}),r.minDate){var f=g(String(r.minDate).slice(0,10));isNaN(f)||(o[Math.floor(f/y)]=!0)}var a=[];return Object.keys(u).forEach(function(l){o[l]||a.push(E(l*y))}),Object.keys(o).forEach(function(l){var v=E(l*y),h=j(r,v,n,"from").some(function(p){return!p.disabled});h||a.push(v)}),a.sort(),t&&(t["np"+n]=a,t.minDate=r.minDate,t.hours=r.storeopen+"-"+r.storeclose),a}function K(r,e,n){var t=g(e);if(isNaN(t))return null;var i=O(r),o=null;return B(r,n).forEach(function(u){if(u.e>t-i.before){var f=u.s-i.after;(o===null||f<o)&&(o=f)}}),o}function X(r,e,n){var t=K(r,e,n);if(t===null)return null;var i=String(e).slice(0,10),o=E(t);if(o<i)return i;if(j(r,o,n,"to",{pickup:e}).some(function(f){return!f.disabled}))return o;var u=E(Math.floor(t/y)*y-y);return u<i?i:u}function m(r){var e=String(r).split(":");return parseInt(e[0],10)*60+parseInt(e[1]||"0",10)}function Z(r,e,n){var t=parseInt(n,10);(isNaN(t)||t<5)&&(t=30);for(var i=m(r||"08:00"),o=m(e||"18:00"),u=[],f=i;f<=o;f+=t){var a=Math.floor(f/60),l=f%60;u.push((a<10?"0":"")+a+":"+(l<10?"0":"")+l)}return u}function $(r,e,n){return n=n||{},(r||[]).map(function(t){var i=m(t),o=null;return n.notBefore&&i<=m(n.notBefore)?o="before-pickup":n.notAfter&&i>=m(n.notAfter)?o="after-return":n.notEarlierThan&&i<m(n.notEarlierThan)?o="too-early":(e||[]).some(function(u){return i>=m(u[0])&&i<m(u[1])?(o="booked",!0):!1}),{time:t,disabled:o!==null,reason:o}})}function pr(r){for(var e=0;e<r.length;e++)if(!r[e].disabled)return r[e].time;return null}function rr(r,e,n,t){var i=g(d(n).format(s)),o=g(d(t).format(s));return isNaN(i)||isNaN(o)?!1:x(B(r,e),O(r),i,er(r,i,o))}function er(r,e,n){return r.returnDayFree&&n>e?n:n+y-1}function nr(r,e){var n=(r.disabled_dates||[]).concat(r.disabled_dates_end||[]),t={};T(e,r.minLength).forEach(function(f){t[f]=!0});for(var i=c(d(e).format(s),s),o=0;o<400;o++,i.add(1,"d")){var u=i.format(s);if(!t[u]&&!(n.length&&U(u,n)))return u}return d(e).format(s)}function tr(r,e){var n=S(e),t=C(r),i="ns"+n+"|"+JSON.stringify([r.minLength||"",r.disabled_dates_end||[],(r.disabled_dates||[]).length]);if(t&&t[i])return t[i];var o=O(r),u=0;N(r.minLength)&&(u=Math.max(0,w(c("2000-01-01",s),_(c("2000-01-01",s),r.minLength))));var f=(r.disabled_dates||[]).length+(r.disabled_dates_end||[]).length?62:0,a={},l={};B(r,n).forEach(function(h){for(var p=Math.floor((h.s-o.after-(y-1))/y)+1,b=Math.floor((h.e+o.before-1)/y),D=p,k=0;D<=b&&k<3660;D++,k++)a[D]=!0;for(var M=p-u-f-1;M<p;M++)l[M]=!0});var v=Object.keys(a).map(function(h){return E(h*y)});return Object.keys(l).forEach(function(h){if(!a[h]){var p=E(h*y);rr(r,n,p,nr(r,p))&&v.push(p)}}),v.sort(),t&&(t[i]=v),v}function ir(r,e,n){var t=g(e);if(isNaN(t))return null;var i=O(r),o=null;if(B(r,n).forEach(function(a){a.e>t-i.before&&(o===null||a.s<o)&&(o=a.s)}),o===null)return null;var u=r.returnDayFree?0:y-1,f=E(Math.floor((o-i.after-u)/y)*y);return f<e?e:f}function mr(r){return r==="datetime"?["start","starttime","end","endtime"]:["start","end"]}function br(r,e,n){var t=d(r).startOf("day"),i=d(e).startOf("day");return Math.max(n?0:1,i.diff(t,"days"))}return{DAY:s,normalizeQuantity:S,bookedDatesForQuantity:Q,unitsLeft:ur,unitsCap:fr,addLength:_,hasLength:N,inRange:ar,isDayDisabled:U,disabledBy:W,compileDisabled:P,daysFromStartUntilMinDate:T,minEndDate:z,firstFullyBookedFrom:V,maxDate:lr,disabledLists:cr,disabledTimeRangesForDay:hr,turnoverMinutes:O,slotsFor:j,noPickupDays:G,returnCap:K,lastReturnDay:X,dateRentalRefused:rr,dateRentalEnd:er,firstEndDay:nr,noStartDays:tr,lastEndDay:ir,earliestPickupTime:J,absMinutes:g,timeSlots:Z,slotAvailability:$,firstAvailableSlot:pr,toMinutes:m,buildSteps:mr,dayCount:br,nightsBetween:w,dayState:dr,soldNights:q}});
     1(function(c,s){typeof module=="object"&&module.exports?module.exports=s(typeof globalThis!="undefined"&&globalThis.moment?globalThis.moment:require("moment")):c.SibookingCalendarRules=s(c.moment)})(typeof self!="undefined"?self:this,function(c){"use strict";var s="YYYY-MM-DD",Y="23:59",H="00:00";function d(r){return c.isMoment(r)?r.clone():c(r)}function S(r){var e=parseInt(r,10);return isNaN(e)||e<1?1:e}function Q(r,e){var n=S(e),t=[];return(r.fullybooked_dates||[]).forEach(function(i){(r.isComposite&&!r.compositeUnits||n>r.inventoryQuantity-i.quantity)&&t.push(i.date)}),t}function fr(r,e){if(r.isComposite&&!r.compositeUnits||!r.inventoryQuantity||r.inventoryQuantity>=99999)return null;var n=0;return(r.fullybooked_dates||[]).some(function(t){return t.date===e?(n=t.quantity,!0):!1}),Math.max(0,r.inventoryQuantity-n)}function ur(r,e,n){if(r.isComposite&&!r.compositeUnits||r.inventoryQuantity===void 0||r.inventoryQuantity===null||r.inventoryQuantity>=99999)return null;var t=Math.max(0,parseInt(r.inventoryQuantity,10)||0);if(!e||!t)return t;var i=d(e).startOf("day"),o=d(n||e).startOf("day");o.isBefore(i)&&(o=i.clone());var f={};(r.fullybooked_dates||[]).forEach(function(l){f[l.date]=Math.min(f[l.date]===void 0?1/0:f[l.date],t-(parseInt(l.quantity,10)||0))});for(var u=0;!i.isAfter(o,"day")&&u<3660;u++){var a=i.format(s);f[a]!==void 0&&(t=Math.min(t,Math.max(0,f[a]))),i.add(1,"d")}return t}function _(r,e){var n=d(r);if(!e||e.quantity===void 0||e.quantity==="")return n;var t=parseFloat(e.quantity);switch(e.type){case"hours":case"h":n.add(t,"h");break;case"days":case"d":n.add(t,"d");break;case"w":n.add(t,"w");break;case"m":n.add(t,"M");break;case"y":n.add(t,"y");break}return n}function N(r){return r&&typeof r=="object"&&r.quantity!==void 0&&r.quantity!==""&&r.quantity!==null}function ar(r,e){var n=d(r);return n.isSameOrAfter(c(e.from))&&n.isSameOrBefore(c(e.to))}var F=typeof WeakMap=="function"?new WeakMap:null;function P(r){if(r=r||[],F&&F.has(r))return F.get(r);for(var e={},n=[],t=0;t<r.length;t++){var i=r[t];if(typeof i=="string")e[i]=!0;else if(i&&i.from!==void 0){var o=c(i.from),f=c(i.to);o.isValid()&&f.isValid()&&n.push({from:o.valueOf(),to:f.valueOf(),item:i})}}var u={days:e,ranges:n};return F&&F.set(r,u),u}function sr(r){var e=r.split("-"),n=new Date(+e[0],+e[1]-1,+e[2],0,0,0,0).getTime(),t=new Date(+e[0],+e[1]-1,+e[2],23,59,59,999).getTime();return[n,t]}function U(r,e){return W(r,e).length>0}function W(r,e){var n=P(e),t=[];if(n.days[r]&&t.push(r),n.ranges.length)for(var i=sr(r),o=0;o<n.ranges.length;o++){var f=n.ranges[o];f.from<=i[1]&&f.to>=i[0]&&t.push(f.item)}return t}function T(r,e){var n=[];if(!r||!N(e))return n;var t=d(r).startOf("day").add(1,"d"),i=_(d(r).startOf("day"),e);i.isAfter(t,"day")&&n.push(d(r).format(s));for(var o=t.clone();o.isBefore(i,"day");)n.push(o.format(s)),o.add(1,"d");return n}function z(r,e,n){var t=d(r).startOf("day");if(n){var i=t.clone().add(1,"d");if(N(e)){var o=_(t.clone(),e);o.isAfter(i)&&(i=o)}return i.format(s)}if(N(e)){var f=_(t.clone(),e);if(f.isAfter(t))return f.format(s)}return t.format(s)}function V(r,e){if(r){var n={};(e||[]).forEach(function(f){n[f]=!0});for(var t=d(r).startOf("day"),i=0;i<730;i++){var o=t.format(s);if(n[o])return o;t.add(1,"d")}}}function lr(r,e,n,t,i){var o=[];if(r.maxFutureDaysDate&&o.push(c(r.maxFutureDaysDate)),e&&N(r.maxLength)&&o.push(_(d(e),r.maxLength)),e&&r.dated){var f=ir(r,d(e).format(s),i);f&&o.push(c(f,s))}if(e&&t&&r.timed){var u=X(r,d(e).format(s)+" "+t,i);u&&o.push(c(u,s))}if(e){var a=V(e,n);if(a&&r.nightly)o.push(c(a,s).subtract(R(r.bufferAfterDays),"d"));else if(a&&r.dated&&r.returnDayFree){var l=Number(r.bufferAfterMinutes)||0;o.push(c(a,s).subtract(l>0?Math.ceil(l/1440):0,"d"))}else if(a){var v=Number(r.bufferAfterMinutes)||0,h=r.calendarType==="datetime",p=v>0?h?Math.floor(v/1440):Math.ceil(v/1440):0;o.push(p>0?c(a,s).subtract(1+p,"d"):c(a,s))}}if(!o.length)return null;var b=o.reduce(function(D,k){return D.isBefore(k)?D:k});return b.format(s)}function cr(r,e,n,t){if(r.nightly)return vr(r,e,n,t);var i=Q(r,e),o=i.concat(r.disabled_dates||[]),f=o.concat(t||[]).concat(r.disabled_dates_start||[]).concat(r.timed?G(r,e):[]).concat(r.dated?tr(r,e):[]),u=r.dated&&r.returnDayFree?r.disabled_dates||[]:o,a=u.concat(n?T(n,r.minLength):[]).concat(r.disabled_dates_end||[]);return{booked:i,both:o,start:f,end:a}}function R(r){var e=Math.ceil(Number(r)||0);return e>0?e:0}function w(r,e){return d(e).startOf("day").diff(d(r).startOf("day"),"days")}function q(r,e){var n={};return Q(r,e).forEach(function(t){n[t]=!0}),n}function vr(r,e,n,t){var i=q(r,e),o=Object.keys(i).sort(),f=R(r.bufferBeforeDays),u=R(r.bufferAfterDays),a=N(r.minLength)?Math.max(1,w(c("2000-01-01",s),_(c("2000-01-01",s),r.minLength))):1,l={};o.forEach(function(k){for(var M=c(k,s),A=-(a+u-1);A<=f;A++)l[M.clone().add(A,"d").format(s)]=!0});var v=Object.keys(l).sort().concat(r.disabled_dates||[]).concat(t||[]).concat(r.disabled_dates_start||[]);typeof r.inventoryQuantity=="number"&&S(e)>r.inventoryQuantity&&(v=v.concat([{from:"1900-01-01",to:"2999-12-31"}]));var h=[];if(n)for(var p=c(z(n,r.minLength,!0),s),b=d(n).startOf("day");b.isBefore(p,"day");b.add(1,"d"))h.push(b.format(s));var D=(r.disabled_dates||[]).concat(h).concat(r.disabled_dates_end||[]);return{booked:o,both:o.concat(r.disabled_dates||[]),start:v,end:D}}function dr(r,e,n){var t=q(r,e),i=!!t[c(n,s).subtract(1,"d").format(s)],o=!!t[n];return!i&&!o?"free":i&&!o?"checkin-only":!i&&o?"checkout-only":(r.turnover_dates||[]).indexOf(n)!==-1?"turnover":"booked"}var y=1440;function g(r){var e=/^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{1,2}):(\d{2}))?/.exec(String(r));return e?Math.round(Date.UTC(+e[1],+e[2]-1,+e[3])/6e4)+(e[4]?+e[4]*60+ +e[5]:0):NaN}function E(r){return new Date(Math.floor(r/y)*864e5).toISOString().slice(0,10)}function yr(r){var e=Math.floor(r/60),n=r%60;return(e<10?"0":"")+e+":"+(n<10?"0":"")+n}function O(r){var e=parseInt(r&&r.turnoverBeforeMinutes,10),n=parseInt(r&&r.turnoverAfterMinutes,10);return{before:e>0?e:0,after:n>0?n:0}}var I=typeof WeakMap=="function"?new WeakMap:null;function C(r){if(!I||!r||typeof r!="object")return null;var e=I.get(r);return(!e||e.list!==r.inventoryArray||e.before!==r.turnoverBeforeMinutes||e.after!==r.turnoverAfterMinutes||e.qty!==r.inventoryQuantity)&&(e={list:r.inventoryArray,before:r.turnoverBeforeMinutes,after:r.turnoverAfterMinutes,qty:r.inventoryQuantity},I.set(r,e)),e}function B(r,e){var n=S(e),t=C(r);if(t&&t["full"+n])return t["full"+n];var i=[];return(r.inventoryArray||[]).forEach(function(o){if(!(!o||!(n>r.inventoryQuantity-o.q))){var f=g(o.s),u=g(o.e);!isNaN(f)&&!isNaN(u)&&u>f&&i.push({s:f,e:u})}}),t&&(t["full"+n]=i),i}function hr(r,e,n){var t=S(n),i=d(e).startOf("day"),o=[];return(r.inventoryArray||[]).forEach(function(f){if(t>r.inventoryQuantity-f.q){var u=c(f.s),a=c(f.e),l=i.isSame(u,"day"),v=i.isSame(a,"day");l&&v?o.push([u.format("HH:mm"),a.format("HH:mm")]):l&&a.isAfter(i,"day")?o.push([u.format("HH:mm"),Y]):v&&u.isBefore(a,"day")?o.push([H,a.format("HH:mm")]):u.isBefore(i,"day")&&a.isAfter(i,"day")&&o.push([H,Y])}}),o}function x(r,e,n,t){for(var i=0;i<r.length;i++)if(n-e.before<r[i].e&&t+e.after>r[i].s)return!0;return!1}function J(r,e){if(!r.minDate||String(r.minDate).slice(0,10)!==e)return null;var n=/[ T](\d{1,2}):(\d{2})(?::(\d{2}))?/.exec(String(r.minDate));if(!n)return null;var t=+n[1]*60+ +n[2]+(n[3]&&+n[3]>0?1:0);return t>0?yr(Math.min(t,y)):null}function j(r,e,n,t,i){i=i||{};var o=d(e).format(s),f=g(o),u=Z(r.storeopen,r.storeclose,r.ui&&r.ui.slotInterval||r.timeIncrement),a={};if(t==="to"&&i.pickup&&String(i.pickup).slice(0,10)===o&&(a.notBefore=String(i.pickup).slice(11,16)),t!=="to"){var l=J(r,o);l&&(a.notEarlierThan=l)}var v=$(u,[],a);if(!u.length)return v;var h=O(r),p=B(r,n),b=m(u[0]),D=m(u[u.length-1]),k=g(t==="to"?i.pickup:i.ret);return v.forEach(function(M,A){if(!M.disabled){var or=f+m(M.time),L=k;isNaN(L)&&(t==="to"?L=A>0?f+m(u[A-1]):f-y+D:L=A<u.length-1?f+m(u[A+1]):f+y+b);var gr=t==="to"?x(p,h,L,or):x(p,h,or,L);gr&&(M.disabled=!0,M.reason="booked")}}),v}function G(r,e){var n=S(e),t=C(r);if(t&&t["np"+n]&&t.minDate===r.minDate&&t.hours===r.storeopen+"-"+r.storeclose)return t["np"+n];var i=O(r),o={},f={};if(B(r,n).forEach(function(l){var v=Math.floor((l.s-i.after)/y),h=Math.floor((l.e+i.before-1)/y);o[v-1]=o[v]=o[h]=o[h+1]=!0;for(var p=v+1,b=0;p<h&&b<3660;p++,b++)f[p]=!0}),r.minDate){var u=g(String(r.minDate).slice(0,10));isNaN(u)||(o[Math.floor(u/y)]=!0)}var a=[];return Object.keys(f).forEach(function(l){o[l]||a.push(E(l*y))}),Object.keys(o).forEach(function(l){var v=E(l*y),h=j(r,v,n,"from").some(function(p){return!p.disabled});h||a.push(v)}),a.sort(),t&&(t["np"+n]=a,t.minDate=r.minDate,t.hours=r.storeopen+"-"+r.storeclose),a}function K(r,e,n){var t=g(e);if(isNaN(t))return null;var i=O(r),o=null;return B(r,n).forEach(function(f){if(f.e>t-i.before){var u=f.s-i.after;(o===null||u<o)&&(o=u)}}),o}function X(r,e,n){var t=K(r,e,n);if(t===null)return null;var i=String(e).slice(0,10),o=E(t);if(o<i)return i;if(j(r,o,n,"to",{pickup:e}).some(function(u){return!u.disabled}))return o;var f=E(Math.floor(t/y)*y-y);return f<i?i:f}function m(r){var e=String(r).split(":");return parseInt(e[0],10)*60+parseInt(e[1]||"0",10)}function Z(r,e,n){var t=parseInt(n,10);(isNaN(t)||t<5)&&(t=30);for(var i=m(r||"08:00"),o=m(e||"18:00"),f=[],u=i;u<=o;u+=t){var a=Math.floor(u/60),l=u%60;f.push((a<10?"0":"")+a+":"+(l<10?"0":"")+l)}return f}function $(r,e,n){return n=n||{},(r||[]).map(function(t){var i=m(t),o=null;return n.notBefore&&i<=m(n.notBefore)?o="before-pickup":n.notAfter&&i>=m(n.notAfter)?o="after-return":n.notEarlierThan&&i<m(n.notEarlierThan)?o="too-early":(e||[]).some(function(f){return i>=m(f[0])&&i<m(f[1])?(o="booked",!0):!1}),{time:t,disabled:o!==null,reason:o}})}function pr(r){for(var e=0;e<r.length;e++)if(!r[e].disabled)return r[e].time;return null}function rr(r,e,n,t){var i=g(d(n).format(s)),o=g(d(t).format(s));return isNaN(i)||isNaN(o)?!1:x(B(r,e),O(r),i,er(r,i,o))}function er(r,e,n){return r.returnDayFree&&n>e?n:n+y-1}function nr(r,e){var n=(r.disabled_dates||[]).concat(r.disabled_dates_end||[]),t={};T(e,r.minLength).forEach(function(u){t[u]=!0});for(var i=c(d(e).format(s),s),o=0;o<400;o++,i.add(1,"d")){var f=i.format(s);if(!t[f]&&!(n.length&&U(f,n)))return f}return d(e).format(s)}function tr(r,e){var n=S(e),t=C(r),i="ns"+n+"|"+JSON.stringify([r.minLength||"",r.disabled_dates_end||[],(r.disabled_dates||[]).length]);if(t&&t[i])return t[i];var o=O(r),f=0;N(r.minLength)&&(f=Math.max(0,w(c("2000-01-01",s),_(c("2000-01-01",s),r.minLength))));var u=(r.disabled_dates||[]).length+(r.disabled_dates_end||[]).length?62:0,a={},l={};B(r,n).forEach(function(h){for(var p=Math.floor((h.s-o.after-(y-1))/y)+1,b=Math.floor((h.e+o.before-1)/y),D=p,k=0;D<=b&&k<3660;D++,k++)a[D]=!0;for(var M=p-f-u-1;M<p;M++)l[M]=!0});var v=Object.keys(a).map(function(h){return E(h*y)});return Object.keys(l).forEach(function(h){if(!a[h]){var p=E(h*y);rr(r,n,p,nr(r,p))&&v.push(p)}}),v.sort(),t&&(t[i]=v),v}function ir(r,e,n){var t=g(e);if(isNaN(t))return null;var i=O(r),o=null;if(B(r,n).forEach(function(a){a.e>t-i.before&&(o===null||a.s<o)&&(o=a.s)}),o===null)return null;var f=r.returnDayFree?0:y-1,u=E(Math.floor((o-i.after-f)/y)*y);return u<e?e:u}function mr(r){return r==="datetime"?["start","starttime","end","endtime"]:["start","end"]}function br(r,e,n,t){var i=d(r).startOf("day"),o=d(e).startOf("day");return n?Math.max(0,o.diff(i,"days")):t&&o.diff(i,"days")>0?o.diff(i,"days")+1:Math.max(1,o.diff(i,"days"))}return{DAY:s,normalizeQuantity:S,bookedDatesForQuantity:Q,unitsLeft:fr,unitsCap:ur,addLength:_,hasLength:N,inRange:ar,isDayDisabled:U,disabledBy:W,compileDisabled:P,daysFromStartUntilMinDate:T,minEndDate:z,firstFullyBookedFrom:V,maxDate:lr,disabledLists:cr,disabledTimeRangesForDay:hr,turnoverMinutes:O,slotsFor:j,noPickupDays:G,returnCap:K,lastReturnDay:X,dateRentalRefused:rr,dateRentalEnd:er,firstEndDay:nr,noStartDays:tr,lastEndDay:ir,earliestPickupTime:J,absMinutes:g,timeSlots:Z,slotAvailability:$,firstAvailableSlot:pr,toMinutes:m,buildSteps:mr,dayCount:br,nightsBetween:w,dayState:dr,soldNights:q}});
  • sales-igniter-rentals-bookings-lite/trunk/Assets/public/js/sibookingCalendarUi.js

    r3728745 r3729359  
    12711271                var from = start.format(fmt) + (useTime && this.times.getTime('from') ? ' ' + this.times.label(this.times.getTime('from')) : '');
    12721272                var to = end.format(fmt) + (useTime && this.times.getTime('to') ? ' ' + this.times.label(this.times.getTime('to')) : '');
    1273                 var days = R.dayCount(start, end, !!data().nightly);
     1273                var days = R.dayCount(start, end, !!data().nightly, !!data().chargeReturnDay);
    12741274                var length;
    12751275                var fromT = useTime ? this.times.getTime('from') : '', toT = useTime ? this.times.getTime('to') : '';
  • sales-igniter-rentals-bookings-lite/trunk/Assets/public/js/sibookingCalendarUi.min.js

    r3728745 r3729359  
    1 (function(r){"use strict";var A=null;function _(){if(A===null){var n="ltr";try{n=window.getComputedStyle(document.body||document.documentElement).direction}catch(c){}A=n==="rtl"}return A}function M(){return _()?"←":"→"}function O(n){return n=String(n==null?"":n),n===""||!_()?n:"⁦"+n+"⁩"}r(window).on("load",function(){if(typeof sibooking=="undefined"||!sibooking.data||typeof flatpickr=="undefined")return;var n=window.SibookingCalendarRules,c=window.wp&&window.wp.i18n?window.wp.i18n:{__:function(k){return k},_n:function(k,t,e){return e===1?k:t},sprintf:function(k){return k}},l=c.__,T=r.extend({start:l("Start Date","sales-igniter-rentals-bookings-lite"),starttime:l("Start Time","sales-igniter-rentals-bookings-lite"),end:l("End Date","sales-igniter-rentals-bookings-lite"),endtime:l("End Time","sales-igniter-rentals-bookings-lite")},sibooking.data.ui&&sibooking.data.ui.labels||{}),E='<svg class="sib-ic" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="5" width="18" height="16" rx="3"></rect><path d="M3 10h18M8 3v4M16 3v4"></path></svg>',P='<svg class="sib-ic" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9"></circle><path d="M12 7v5l3 2"></path></svg>';function d(){return window.sibooking.data}function S(){return d().ui||{}}function I(){var k=parseInt(S().lowStock,10);return k>0?k:2}function m(k){return r("<div>").text(k==null?"":String(k)).html()}class j{constructor(t){this.calendar=t,this.timepickerfrom=r("#timepicker"),this.timepickerto=r("#timepicker2"),this.side="from",this.$grid=r(".sib-cal .sib-slots"),this.$head=r(".sib-cal .sib-pane--time .sib-pane-head"),this.onChange=null}enabled(){return this.timepickerfrom.length>0}getTime(t){var e=t==="to"?this.timepickerto:this.timepickerfrom;return e.length&&e.val()||""}setTime(t,e,i){var a=t==="to"?this.timepickerto:this.timepickerfrom;a.length&&(a.val(e||""),this.calendar.updateVisibleTime(t),i||a.trigger("change"))}dayFor(t){var e=t==="to"?this.calendar.siflatpickrend:this.calendar.siflatpickrstart;return!e||!e.selectedDates.length?null:moment(e.selectedDates[0])}momentFor(t){var e=this.dayFor(t),i=this.getTime(t);return e&&i?e.format(n.DAY)+" "+i:null}availability(t){var e=this.dayFor(t);return e?n.slotsFor(d(),e,this.calendar.inputQuantity,t,{pickup:t==="to"?this.momentFor("from"):null,ret:t==="from"?this.momentFor("to"):null}):[]}updateFromDisabledTimes(){this.ensureValid("from"),this.side==="from"&&this.render("from")}updateToDisabledTimes(){this.ensureValid("to"),this.side==="to"&&this.render("to")}ensureValid(t){var e=this.getTime(t);if(e){var i=this.availability(t);if(i.length){var a=i.some(function(s){return s.time===e&&!s.disabled});a||this.setTime(t,"",!0)}}}groupOf(t){var e=parseInt(t.split(":")[0],10);return e<12?l("Morning","sales-igniter-rentals-bookings-lite"):e<17?l("Afternoon","sales-igniter-rentals-bookings-lite"):l("Evening","sales-igniter-rentals-bookings-lite")}label(t){var e=d().inputTimeFormat||"HH:mm";return O(moment(t,"HH:mm").format(e))}render(t){this.side=t;var e=this.dayFor(t),i=this.$grid;i.empty();var a=this.$head,s=d();if(!e){a.html('<span class="sib-h">'+m(t==="to"?T.endtime:T.starttime)+"</span>"),i.append('<div class="sib-placeholder">'+m(l("Pick a date first","sales-igniter-rentals-bookings-lite"))+"</div>");return}var o=e.format(s.inputDateFormatMoment||"ddd D MMM");a.html('<span class="sib-h">'+m((t==="to"?T.endtime:T.starttime)+" · "+o)+'</span><span class="sib-hours">'+m(c.sprintf(l("Open %1$s – %2$s","sales-igniter-rentals-bookings-lite"),(_()?"⁦":"")+this.label(s.storeopen),this.label(s.storeclose)+(_()?"⁩":"")))+"</span>");var h=this.availability(t),u=this.getTime(t),p=t==="to"&&!u?this.getTime("from"):null,g={},y=[];h.forEach(function(b){var v=this.groupOf(b.time);g[v]||(g[v]=[],y.push(v)),g[v].push(b)}.bind(this));var f=this;y.forEach(function(b){var v=r('<div class="sib-slot-group"><span class="sib-h">'+m(b)+"</span></div>"),w=r('<div class="sib-slot-row"></div>');g[b].forEach(function(D){var x="sib-slot";D.disabled&&(x+=" is-off is-"+D.reason),D.time===u&&(x+=" is-sel"),!D.disabled&&D.time===p&&(x+=" is-suggested");var C=r('<button type="button" class="'+x+'"></button>').text(f.label(D.time)).attr("data-time",D.time);D.disabled&&C.attr("disabled","disabled").attr("aria-disabled","true"),D.reason==="before-pickup"&&C.append('<span class="sib-slot-sub">'+m(l("before pick-up","sales-igniter-rentals-bookings-lite"))+"</span>"),!D.disabled&&D.time===p&&C.append('<span class="sib-slot-sub">'+m(l("same as pick-up","sales-igniter-rentals-bookings-lite"))+"</span>"),w.append(C)}),v.append(w),i.append(v)}),h.some(function(b){return!b.disabled})||i.append('<div class="sib-placeholder">'+m(l("No times left on this day","sales-igniter-rentals-bookings-lite"))+"</div>"),i.append('<div class="sib-hint">'+m(l("Greyed times are booked or inside the turnover buffer.","sales-igniter-rentals-bookings-lite"))+"</div>"),i.off("click.sib").on("click.sib",".sib-slot:not([disabled])",function(){var b=r(this).attr("data-time");f.setTime(f.side,b),i.find(".sib-slot").removeClass("is-sel"),r(this).addClass("is-sel"),f.onChange&&f.onChange(f.side,b)})}setInitialTimes(){var t=d();if(this.enabled()&&t.sessionFromDate&&t.sessionToDate){var e=moment(t.sessionFromDate),i=moment(t.sessionToDate),a=function(h){return moment(h,"HH:mm:ss").format("HH:mm")},s=this.withinStoreHours(e)?e.format("HH:mm"):a(t.storeopen),o=this.withinStoreHours(i)?i.format("HH:mm"):a(t.storeclose);this.setTime("from",s,!0),this.setTime("to",o,!0)}}withinStoreHours(t){var e=d(),i=t.hours()*60+t.minutes();return i>=n.toMinutes(e.storeopen)&&i<=n.toMinutes(e.storeclose)}}class L{constructor(){this.$root=r(".sib-cal"),this.sibookingDateContainer=r(".sibooking-datecontainer"),this.inputQuantity=n.normalizeQuantity(r('input[name="quantity"]').val()),this.lists={booked:[],both:[],start:[],end:[]},this.steps=[],this.stepIndex=0,this.hoverEnd=null}get calendarType(){return d().calendarType}get dateFormat(){return d().inputDateFormat}get dateFormatMoment(){return d().inputDateFormatMoment}get timeFormat(){return d().inputTimeFormat}isdatepicker(){return r("#flatpickr").length>0||r("#flatpickr2").length>0}init(){!this.isdatepicker()||!this.$root.length||(this.times=new j(this),this.times.onChange=this.onTimePicked.bind(this),this.buildSteps(),this.updateDisabledDatesAll(),this.loadCalendar(),this.addEventHandlers(),this.render(),this.goTo(0))}$summary(){return this.$root.find(".sib-summary").first()}stepsFor(){return n.buildSteps(this.calendarType)}buildSteps(){var t=this.times&&this.times.enabled();this.steps=this.stepsFor().filter(function(e){return t||e!=="starttime"&&e!=="endtime"}),this.$root.toggleClass("has-time",this.steps.indexOf("starttime")!==-1)}startDate(){return this.siflatpickrstart&&this.siflatpickrstart.selectedDates.length?moment(this.siflatpickrstart.selectedDates[0]):null}endDate(){return this.siflatpickrend&&this.siflatpickrend.selectedDates.length?moment(this.siflatpickrend.selectedDates[0]):null}calendarOptions(){return{disableMobile:!0,mode:"single",dateFormat:"Y-m-d",allowInput:!1,monthSelectorType:"static",showMonths:1,locale:r.extend({firstDayOfWeek:parseInt(S().firstDay||0,10)},S().locale||{}),inline:!0,appendTo:this.$root.find(".sib-cal-mount")[0]}}loadCalendar(){var t=this,e=this.calendarOptions();this.siflatpickrstart=flatpickr("#flatpickr",r.extend({},e,{onDayCreate:this.onDayCreate.bind(this,"start"),onOpen:this.onStartOpen.bind(this),onChange:this.onChangeStart.bind(this),minDate:d().minDate,maxDate:this.getMaxDate()})),this.siflatpickrend=flatpickr("#flatpickr2",r.extend({},e,{allowInvalidPreload:!0,onDayCreate:this.onDayCreate.bind(this,"end"),onOpen:this.onOpenEnd.bind(this),onChange:this.onChangeEnd.bind(this),onClose:this.onCloseEnd.bind(this)})),this.siflatpickrstart.calendarContainer.classList.add("sib-fp","sib-fp--start"),this.siflatpickrend.calendarContainer.classList.add("sib-fp","sib-fp--end"),r(document).trigger("sibooking:calendars",[this.siflatpickrstart,this.siflatpickrend]),r(this.siflatpickrend.calendarContainer).on("mouseover",".flatpickr-day",function(){t.hoverEnd&&t.hoverEnd.getTime()===this.dateObj.getTime()||(t.hoverEnd=this.dateObj,t.paintRange())}).on("mouseleave",function(){t.hoverEnd=null,t.paintRange()}),this.applyDisable()}addEventHandlers(){var t=this;r('input[name="quantity"]').on("change",function(){n.normalizeQuantity(r(this).val())!==t.inputQuantity&&t.onQuantityChanged()}),r(document).on("sib:guests:change",function(e,i,a){a&&!r(a).closest("form").is(t.$root.closest("form"))||t.refreshPrice()}),r(".single_add_to_cart_button").not("#sibookingpurchase").on("click",function(e){var i=t.missingSelection();i?(e.preventDefault(),t._missingSaid=i,t.tell(i)):t._missingSaid&&(t.$root.find(".sib-note").text()===t._missingSaid&&t.say(""),t._missingSaid="")}),r("#sibookingpurchase").on("click",function(){r('input[name="isbookingpurchase"]').val(!0)}),r("#siwidget-update").on("click",function(e){e.preventDefault(),t.widgetUpdate()}),this.$root.find(".sib-panel").length&&(this.$root.on("click",".sib-trigger",function(){t.togglePanel()}),this.$root.on("click",".sib-panel-close, .sib-backdrop",function(){t.closePanel()}),r(document).on("keydown.sibpanel",function(e){e.key==="Escape"&&t.closePanel()}),r(document).on("mousedown.sibpanel touchstart.sibpanel",function(e){t.$root.hasClass("is-open")&&!r(e.target).closest("#sib-cal").length&&t.closePanel()})),this.$root.on("click keydown",".sib-step[data-index]",function(e){if(!(e.type==="keydown"&&e.key!=="Enter"&&e.key!==" ")){e.preventDefault();var i=parseInt(r(this).attr("data-index"),10);!isNaN(i)&&i<=t.furthestStep()&&t.changeStep(i)}}),this.$root.on("click",".sib-reset",function(e){e.preventDefault(),t.clearStartEndDate()})}missingSelection(){return!this.startDate()||!this.endDate()?l("Please first select your booking dates","sales-igniter-rentals-bookings-lite"):this.steps.indexOf("endtime")!==-1&&(!this.times.getTime("from")||!this.times.getTime("to"))?l("Please select your pick-up and return times","sales-igniter-rentals-bookings-lite"):""}onQuantityChanged(){var t=this.startDate(),e=this.endDate();if(this.updateDisabledDatesAll(),this.say(""),!t){this.render();return}if(this.rangeStillFree(t,e||t)){this.render();return}this.clearStartEndDate(),this.say(c.sprintf(c._n("There is not %d free on those dates. Please pick your dates again.","There are not %d free on those dates. Please pick your dates again.",this.inputQuantity,"sales-igniter-rentals-bookings-lite"),this.inputQuantity))}rangeStillFree(t,e){var i={};(this.lists.booked||[]).forEach(function(o){i[o]=!0});var a=t.clone().startOf("day"),s=e.clone().startOf("day");if(i[a.format(n.DAY)])return!1;for(a.add(1,"d");a.isBefore(s,"day");){if(i[a.format(n.DAY)])return!1;a.add(1,"d")}return!0}say(t){var e=this.$root.find(".sib-note");if(!e.length){if(!t)return;e=r('<p class="sib-note" aria-live="polite"></p>');var i=this.$summary();i.length?i.before(e):this.$root.append(e)}e.text(t||"").prop("hidden",!t)}tell(t){this.say(t);var e=this.$root.find(".sib-note").get(0);e&&e.scrollIntoView&&e.scrollIntoView({block:"nearest"})}guestsText(){var t=this.$root.closest("form").find(".sib-guests").get(0);return t&&t.sibGuests?t.sibGuests.summary():""}guestFields(){var t={};return this.$root.closest("form").find(".sib-guests-input[name], .sib-guests-total[name]").each(function(){this.value!==""&&(t[this.name]=this.value)}),t}blockedStarts(){return[]}updateDisabledDatesAll(){this.inputQuantity=n.normalizeQuantity(r('input[name="quantity"]').val()),this.lists=n.disabledLists(d(),this.inputQuantity,this.startDate(),this.blockedStarts()),this.applyDisable(),this.times&&(this.times.ensureValid("from"),this.times.ensureValid("to"))}applyDisable(){var t=d();this._closedFor!==t&&(this._closedFor=t,this._closedStart=(t.disabled_dates||[]).concat(t.disabled_dates_start||[]),this._closedEnd=(t.disabled_dates||[]).concat(t.disabled_dates_end||[]));var e=this.startDate(),i=this.getMaxDate();this.siflatpickrstart&&this.siflatpickrstart.set({disable:this.lists.start,maxDate:e?null:i}),this.siflatpickrend&&this.siflatpickrend.set({disable:this.lists.end,minDate:e?e.format(n.DAY):null,maxDate:i})}updateDisabledDatesEnd(){this.updateDisabledDatesAll()}getMaxDate(){var t=this.times&&this.times.enabled()?this.times.getTime("from"):"";return n.maxDate(d(),this.startDate(),this.lists.booked,t,this.inputQuantity)}getMinDateForEndDate(){return this.startDate()?n.minEndDate(this.startDate(),d().minLength,!!d().nightly):null}onDayCreate(t,e,i,a,s){var o=d(),h=moment(s.dateObj).format(n.DAY),u=null;if(o.nightly&&(u=n.dayState(o,this.inputQuantity,h),s.setAttribute("data-sib-day-state",u)),o.nightly?u==="booked"||u==="turnover":this.lists.booked.indexOf(h)!==-1)s.classList.add("sib-booked"),s.title=l("Fully booked","sales-igniter-rentals-bookings-lite");else{var p=n.disabledBy(h,t==="start"?this._closedStart||[]:this._closedEnd||[]);if(p.length){s.classList.add("sib-closed");var g=p.map(function(v){return v.reason}).filter(function(v,w,D){return v&&D.indexOf(v)===w});s.title=g.length?g.join(" · "):l("Disabled","sales-igniter-rentals-bookings-lite"),s.setAttribute("data-sib-reason",s.title)}}if(S().showPrices&&o.dayPrices&&o.dayPrices[h]&&!s.classList.contains("flatpickr-disabled")){var y=document.createElement("span");y.className="sib-price",y.textContent=o.dayPrices[h],s.appendChild(y)}if(S().showUnitsLeft&&!s.classList.contains("flatpickr-disabled")){var f=n.unitsLeft(o,h);if(f!==null){var b=document.createElement("span");b.className="sib-left"+(f<=I()?" is-low":""),b.textContent=c.sprintf(l("%d left","sales-igniter-rentals-bookings-lite"),f),s.appendChild(b)}}this.paintDay(s,t==="end"),r(document).trigger("sibooking:daycreate",[s,h,t,u])}paintDay(t,e){var i=this.startDate(),a=this.endDate()||(e&&this.hoverEnd?moment(this.hoverEnd):null);if(t.classList.remove("sib-range-start","sib-inrange","sib-range-end"),!!i){var s=moment(t.dateObj);s.isSame(i,"day")&&t.classList.add("sib-range-start"),a&&a.isAfter(i,"day")&&s.isAfter(i,"day")&&s.isBefore(a,"day")&&t.classList.add("sib-inrange"),a&&s.isSame(a,"day")&&!s.isSame(i,"day")&&t.classList.add("sib-range-end")}}paintRange(){var t=this;[[this.siflatpickrend,!0],[this.siflatpickrstart,!1]].forEach(function(e){var i=e[0];if(!(!i||!i.days))for(var a=i.days.querySelectorAll(".flatpickr-day"),s=0;s<a.length;s++)t.paintDay(a[s],e[1])})}onStartOpen(){this.updateDisabledDatesAll()}onChangeStart(){this.say("");var t=this.startDate(),e=t?t.format(n.DAY):"";if(e&&this._lastStart&&e!==this._lastStart&&(this.siflatpickrend&&this.siflatpickrend.clear(),this.times&&(this.times.setTime("from","",!0),this.times.setTime("to","",!0)),this.hoverEnd=null),this._lastStart=e,this.updateDisabledDatesAll(),this.startDate()){var i=this.endDate(),a=this.getMaxDate();i&&(i.isBefore(this.startDate(),"day")||a&&i.isAfter(moment(a,n.DAY),"day"))&&this.siflatpickrend.clear()}this.times&&this.times.updateFromDisabledTimes(),this.completeStep("start")}onOpenEnd(){if(!this.startDate()){this.siflatpickrend.close();return}this.applyDisable(),this.siflatpickrend.jumpToDate(this.startDate().toDate())}onChangeEnd(){this.updateInputValues(),this.paintRange(),this.times&&this.times.updateToDisabledTimes(),this.completeStep("end")}onCloseEnd(){this.updateInputValues(),this.render()}updateInputValues(){var t=r("#flatpickr2"),e=this.endDate();t.val(e?e.format(n.DAY):""),this.siflatpickrend&&this.siflatpickrend.altInput&&(this.siflatpickrend.altInput.value=e?this.siflatpickrend.formatDate(e.toDate(),this.dateFormat):"")}clearStartEndDate(){this._lastStart="",this.siflatpickrstart&&this.siflatpickrstart.clear(),this.siflatpickrend&&this.siflatpickrend.clear(),this.times&&(this.times.setTime("from","",!0),this.times.setTime("to","",!0)),this.hoverEnd=null,this.render(),this.goTo(0)}setInitialDates(){var t=d();if(!(!t.sessionFromDate||!t.sessionToDate||t.sessionFromDate==="Invalid Date"||t.sessionToDate==="Invalid Date")){var e=moment(t.sessionFromDate),i=moment(t.sessionToDate);!e.isValid()||!i.isValid()||t.minDate&&e.isBefore(moment(t.minDate),"day")||(this.siflatpickrstart.setDate(e.format(n.DAY),!1),this.updateDisabledDatesAll(),this.siflatpickrend.setDate(i.format(n.DAY),!1),this.updateInputValues(),this.times&&this.times.setInitialTimes(),this.afterInitialDates(e,i),this._lastStart=e.format(n.DAY),this._rememberedKey=this.startDate()&&this.endDate()?this.startDate().format(n.DAY)+" "+(this.times&&this.times.getTime("from")||"")+"|"+this.endDate().format(n.DAY)+" "+(this.times&&this.times.getTime("to")||""):"",this.siflatpickrstart.jumpToDate(e.toDate()),this.siflatpickrend.jumpToDate(e.toDate()),this.render(),this.goTo(this.steps.length),this.paintRange())}}afterInitialDates(t,e){}widgetUpdate(){if(!this.startDate()||!this.endDate()){this.tell(l("Please first select your booking dates","sales-igniter-rentals-bookings-lite"));return}var t=this,e=r("#sibooking-calendar");e.LoadingOverlay&&e.LoadingOverlay("show");var i={fromDate:this.startDate().format(n.DAY),toDate:this.endDate().format(n.DAY),fromTime:this.times?this.times.getTime("from"):"",toTime:this.times?this.times.getTime("to"):"",action:"sibooking_calendar_global",nonce:d().nonce},a=function(){e.LoadingOverlay&&e.LoadingOverlay("hide"),t.tell(l("Booking dates updated","sales-igniter-rentals-bookings-lite"))},s=function(o){o&&o.statusText==="abort"||(e.LoadingOverlay&&e.LoadingOverlay("hide"),t.tell(l("Your dates could not be saved. Check your connection and try again.","sales-igniter-rentals-bookings-lite")))};r.ajaxq&&r.ajaxq.isRunning("sibookingGlobalCalendar")&&r.ajaxq.abort("sibookingGlobalCalendar"),this.postDates(i,"sibookingGlobalCalendar").done(a).fail(s)}postDates(t,e){var i=d().ajaxurl,a=function(){return t.nonce=d().nonce,e&&r.ajaxq?r.ajaxq(e,{url:i,type:"POST",data:t}):r.post(i,t)},s=r.Deferred();return a().done(s.resolve).fail(function(o){if(o&&o.statusText!=="abort"&&window.sibAjax&&window.sibAjax.isNonceFailure(o)){window.sibAjax.renewNonce().then(function(){a().done(s.resolve).fail(s.reject)},function(){s.reject(o)});return}s.reject(o)}),s.promise()}furthestStep(){for(var t=0;t<this.steps.length&&this.isStepDone(this.steps[t]);t++);return Math.min(t,this.steps.length-1)}isStepDone(t){switch(t){case"start":return!!this.startDate();case"starttime":return!!(this.times&&this.times.getTime("from"));case"end":return!!this.endDate();case"endtime":return!!(this.times&&this.times.getTime("to"))}return!1}allDone(){var t=this;return this.steps.every(function(e){return t.isStepDone(e)})}stepValue(t){var e="ddd D MMM";switch(t){case"start":return this.startDate()?this.startDate().format(e):"";case"end":return this.endDate()?this.endDate().format(e):"";case"starttime":return this.times&&this.times.getTime("from")?this.times.label(this.times.getTime("from")):"";case"endtime":return this.times&&this.times.getTime("to")?this.times.label(this.times.getTime("to")):""}return""}completeStep(t){var e=this.steps.indexOf(t);if(this.render(),e!==-1){for(var i=e+1;i<this.steps.length&&this.isStepDone(this.steps[i]);)i++;if(this.goTo(i),this.allDone()&&this.$root.hasClass("is-open")){var a=this;setTimeout(function(){a.closePanel()},350)}}}onTimePicked(t){if(t==="from"&&this.times){this.updateDisabledDatesAll();var e=this.endDate(),i=this.getMaxDate();e&&i&&e.isAfter(moment(i,n.DAY),"day")&&(this.siflatpickrend.clear(),this.times.setTime("to","",!0),this.updateInputValues()),this.times.ensureValid("to")}this.completeStep(t==="to"?"endtime":"starttime")}changeStep(t){var e=this.steps[t],i=this.times;e==="start"?(this.siflatpickrend&&this.steps.indexOf("end")!==-1&&this.siflatpickrend.clear(),i&&(i.setTime("from","",!0),i.setTime("to","",!0)),this.hoverEnd=null,this.updateInputValues()):e==="starttime"?i&&i.setTime("to","",!0):e==="end"&&(i&&i.setTime("to","",!0),this.hoverEnd=null),this.render(),this.goTo(t)}goTo(t){this.stepIndex=Math.max(0,Math.min(t,this.steps.length));var e=this.steps[this.stepIndex],i=this.$root.find(".sib-stage"),a=e==="start"||!e&&!this.steps.length,s=e==="end"||e==="endtime"?"end":"start";a&&(s="start"),r(this.siflatpickrstart.calendarContainer).toggle(s==="start"),r(this.siflatpickrend.calendarContainer).toggle(s==="end"),s==="end"&&this.startDate()&&this.siflatpickrend.jumpToDate((this.endDate()||this.startDate()).toDate());var o=e==="endtime"?"to":"from";this.times&&this.times.enabled()&&this.times.render(o);var h=e==="starttime"||e==="endtime"?"time":"date";e===void 0&&(h="done"),i.attr("data-active",h);var u=e?T[e]:l("Your booking","sales-igniter-rentals-bookings-lite"),p="";if(s==="end"&&this.startDate()){var g=this.startDate().format("ddd D MMM")+(this.times&&this.times.getTime("from")?" · "+this.times.label(this.times.getTime("from")):"");p='<span class="sib-pane-ctx">'+m(d().nightly?T.start+" "+g:c.sprintf(l("Pick-up %s","sales-igniter-rentals-bookings-lite"),g))+"</span>"}this.$root.find(".sib-pane--date .sib-pane-head").html('<span class="sib-h">'+m(s==="end"?T.end:T.start)+"</span>"+p),this.$root.attr("data-step",e||"done"),this.renderSteps();var y=this.$root.find(".sib-stage-hint");if(y.text(e&&this.stepHints()[e]||""),e==="end"&&this.startDate()&&!this.endDate()){var f=n.minEndDate(this.startDate(),d().minLength,!!d().nightly);n.isDayDisabled(f,this.lists.end)||(this.hoverEnd=moment(f,n.DAY).toDate(),this.paintRange())}this.renderSummary(),r(document).trigger("sibooking:step",[e||"done",this.$root])}stepHints(){return{start:l("Tap a day to continue. Greyed days are booked, closed, or too close to the next booking.","sales-igniter-rentals-bookings-lite"),end:l("Days before pick-up and past the next booking cannot be picked.","sales-igniter-rentals-bookings-lite"),starttime:l("Choosing a time moves straight to the return date.","sales-igniter-rentals-bookings-lite"),endtime:""}}render(){this.renderSteps(),this.renderTrigger(),this.renderSummary(),this.rememberDates(),this.announceDates()}announceDates(){var t=this.startDate(),e=this.endDate(),i=(t?t.format(n.DAY):"")+"|"+(e?e.format(n.DAY):"");i!==this._datesKey&&(this._datesKey=i,r(document.body).trigger("sibooking:dates",[t,e]))}rememberDates(){var t=d();if(!(!t.ajaxurl||t.isComposite)&&!(this.steps.indexOf("end")===-1||!this.allDone())){var e=this.startDate(),i=this.endDate();if(!(!e||!i||i.isBefore(e,"day"))){var a=this.times&&this.times.getTime("from")||"",s=this.times&&this.times.getTime("to")||"",o=e.format(n.DAY)+" "+a+"|"+i.format(n.DAY)+" "+s;o!==this._rememberedKey&&(this._rememberedKey=o,this.postDates({action:"sibooking_calendar_global",fromDate:e.format(n.DAY),toDate:i.format(n.DAY),fromTime:a,toTime:s}))}}}renderSteps(){var t=this.$root.find(".sib-steps");if(this.$root.toggleClass("sib-cal--steps",t.length>0&&S().showSteps!==!1&&this.steps.length>0),!!t.length){if(S().showSteps===!1){t.empty().hide();return}t.show();var e=this,i=this.furthestStep();t.empty(),this.steps.forEach(function(a,s){var o=e.isStepDone(a),h="sib-step"+(o?" is-done":"")+(s===e.stepIndex?" is-on":"")+(s<=i?" is-reachable":""),u=o?e.stepValue(a):s===e.stepIndex?l("Choose","sales-igniter-rentals-bookings-lite"):"",p=o&&s!==e.stepIndex?' title="'+m(l("Change","sales-igniter-rentals-bookings-lite"))+'"':"";t.append('<div class="'+h+'" data-index="'+s+'" role="button" tabindex="0"'+p+' aria-label="'+m(T[a]+(o?": "+u:""))+'"><div class="sib-step-bar"></div><span class="sib-step-t">'+m(T[a])+'</span><span class="sib-step-v">'+m(u)+"</span></div>")})}}updateVisibleTime(t){}quotable(){var t=d();return!!(this.startDate()&&this.endDate()&&this.allDone()&&!t.isComposite&&t.ajaxurl)}totalBox(){var t=r(".sib-rental-total");if(!t.length){t=r('<div class="sib-rental-total" aria-live="polite"></div>');var e=this.$root.closest(".summary, .product"),i=".products, .related, .upsells, .up-sells, .cross-sells, .wc-block-product-template, .wp-block-woocommerce-product-collection, .wp-block-woocommerce-related-products",a=function(o){return o.filter(function(){return!r(this).closest(i).length})},s=a(e.find(".sib-pricing").not(".sib-pricing--loop")).first();s.length||(s=a(e.find("p.price")).first()),s.length||(s=a(e.find(".price")).first()),s.length||(s=r(".single-product .summary p.price").first()),s.length?s.after(t):this.$root.find(".sib-summary").append(t)}return t}showQuote(t){var e=this.totalBox(),i=t.quantity>1?' <span class="sib-rental-total-qty">× '+t.quantity+"</span>":"";e.html('<span class="sib-rental-total-label">'+m(l("Total for your dates","sales-igniter-rentals-bookings-lite"))+i+'</span><span class="sib-rental-total-amount">'+t.total_html+"</span>"),e.removeClass("is-in"),e[0].offsetWidth,e.addClass("is-in")}hideQuote(){this.totalBox().removeClass("is-in")}notesBox(){var t=r(".sib-quote-notes");return t.length||(t=r('<div class="sib-quote-notes" aria-live="polite"></div>'),this.totalBox().after(t)),t}showNotes(t){var e=Array.isArray(t)?t.filter(function(s){return s&&s.text}):[],i=r(".sib-quote-notes");if(!(!e.length&&!i.length)){i=this.notesBox();var a=e.map(function(s){var o=String(s.key||"").replace(/[^A-Za-z0-9_-]/g,"");return'<p class="sib-quote-note'+(o?" sib-quote-note--"+o:"")+'">'+m(s.text)+"</p>"}).join("");i.html()!==a&&i.html(a)}}quoteFields(){return this.guestFields()}showQuoteError(t){this.totalBox().html(t).addClass("is-in")}quoteAgain(t){}refreshPrice(){var t=this,e=d(),i=this.startDate(),a=this.endDate(),s=function(){t._priceKey="",t.showCardTotal(null),t.hideQuote(),t.showNotes(null)};if(!this.quotable()){s();return}var o=r('input[name="variation_id"]').val()||r('button[name="add-to-cart"]').val()||r('input[name="add-to-cart"]').val()||r(".single_add_to_cart_button").val()||"";if(!o){s();return}var h={action:"sibooking_rental_price",nonce:e.nonce,productid:o,variationid:r('input[name="variation_id"]').val()||0,from:i.format(n.DAY),to:a.format(n.DAY),fromtime:this.times?this.times.getTime("from"):"",totime:this.times?this.times.getTime("to"):"",quantity:this.inputQuantity};r.extend(h,this.quoteFields());var u=JSON.stringify(h);if(u===this._priceKey){this._lastQuote&&this._lastQuoteKey===u&&this.quoteAgain(this._lastQuote);return}this._priceKey=u,clearTimeout(this._priceTimer),this.notesBox();var p=function(){t._priceKey===u&&(s(),t.showQuoteError('<span class="sib-ajax-error" role="alert">'+m(l("The price for these dates could not be loaded.","sales-igniter-rentals-bookings-lite"))+' <a href="#" class="sib-ajax-retry">'+m(l("Try again","sales-igniter-rentals-bookings-lite"))+"</a></span>"))};this._retryBound||(this._retryBound=!0,r(document).on("click",".sib-ajax-retry",function(f){f.preventDefault(),t._priceKey="",t.refreshPrice()}));var g=function(f){if(t._priceKey===u){if(!f||!f.success){s();return}t._lastQuote=f.data,t._lastQuoteKey=u,t.showCardTotal(f.data),t.showQuote(f.data),t.showNotes(f.data.notes)}},y=function(f){h.nonce=d().nonce;var b=r.ajaxq?r.ajaxq("sibookingPrice",{url:e.ajaxurl,type:"POST",data:h}):r.post(e.ajaxurl,h);b.done(g).fail(function(v){if(!(t._priceKey!==u||v&&v.statusText==="abort")){if(f<1){p();return}var w=function(){y(f-1)};window.sibAjax&&window.sibAjax.isNonceFailure(v)?window.sibAjax.renewNonce().then(w,p):setTimeout(w,800)}})};this._priceTimer=setTimeout(function(){y(1)},120)}showCardTotal(t){var e=this.$root.closest(".product, .type-product, body").find(".sib-pricing").not(".sib-pricing--loop, .sib-pricing--custom").first();if(e.length){var i=e.find(".sib-pricing-head");if(i.find(".sib-pricing-total--live").remove(),!t||!t.total_html){e.removeClass("has-total has-live-total");return}var a=parseFloat(t.hours||0),s=parseInt(t.days||0,10),o,h=this.times&&this.times.enabled()&&this.times.getTime("from")&&this.times.getTime("to"),u=Math.floor(a/24),p=Math.ceil(Math.round((a-u*24)*100)/100);if(h&&a>0&&a<24){var g=Math.ceil(a);o=c.sprintf(c._n("for %d hour","for %d hours",g,"sales-igniter-rentals-bookings-lite"),g)}else h&&u>0&&p>0&&(t.length_unit||d().lengthUnit)!=="night"?o=c.sprintf(l("for %1$s %2$s","sales-igniter-rentals-bookings-lite"),c.sprintf(c._n("%d day","%d days",u,"sales-igniter-rentals-bookings-lite"),u),c.sprintf(c._n("%d hour","%d hours",p,"sales-igniter-rentals-bookings-lite"),p)):(t.length_unit||d().lengthUnit)==="night"?o=c.sprintf(c._n("for %d night","for %d nights",Math.max(1,s),"sales-igniter-rentals-bookings-lite"),Math.max(1,s)):o=c.sprintf(c._n("for %d day","for %d days",Math.max(1,s),"sales-igniter-rentals-bookings-lite"),Math.max(1,s));r('<span class="sib-pricing-total sib-pricing-total--live"><b class="sib-pricing-amt"></b> <span class="sib-pricing-total-label"></span></span>').find("b").html(t.total_html).end().find(".sib-pricing-total-label").text(o).end().insertAfter(i.find(".sib-pricing-from")),e.addClass("has-total has-live-total")}}lengthText(t){return d().lengthUnit==="night"?c.sprintf(c._n("%d night","%d nights",t,"sales-igniter-rentals-bookings-lite"),t):t===1?l("1 day","sales-igniter-rentals-bookings-lite"):c.sprintf(l("%d days","sales-igniter-rentals-bookings-lite"),t)}summaryDateFormat(){return this.dateFormatMoment||"ddd D MMM"}summaryParts(){var t=this.startDate(),e=this.endDate();if(!t||!e||!this.allDone())return null;var i=this.summaryDateFormat(),a=this.times&&this.times.enabled(),s=t.format(i)+(a&&this.times.getTime("from")?" "+this.times.label(this.times.getTime("from")):""),o=e.format(i)+(a&&this.times.getTime("to")?" "+this.times.label(this.times.getTime("to")):""),h=n.dayCount(t,e,!!d().nightly),u,p=a?this.times.getTime("from"):"",g=a?this.times.getTime("to"):"",y=!a||!p||!g||p==="00:00"&&(g==="00:00"||g==="23:59"),f=this,b=function(C){return f.lengthText(C)};if(y)u=b(h),s=t.format(i),o=e.format(i);else{var v=t.clone().set({hour:parseInt(p.split(":")[0],10),minute:parseInt(p.split(":")[1],10)}),w=e.clone().set({hour:parseInt(g.split(":")[0],10),minute:parseInt(g.split(":")[1],10)}),D=w.diff(v,"hours",!0);if(D<24){var x=Math.round(D*10)/10;u=x===1?l("1 hour","sales-igniter-rentals-bookings-lite"):c.sprintf(l("%s hours","sales-igniter-rentals-bookings-lite"),x)}else u=b(Math.ceil(D/24))}return{main:u,sub:s+" "+M()+" "+o}}resetButton(t){var e=this.$root.find(".sib-reset");return e.length||(e=r('<button type="button" class="sib-reset" title="'+m(l("Start over","sales-igniter-rentals-bookings-lite"))+'"><svg class="sib-ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M3 12a9 9 0 1 0 3-6.7"></path><path d="M3 4v5h5"></path></svg><span>'+m(l("Start over","sales-igniter-rentals-bookings-lite"))+"</span></button>"),t.after(e)),e}renderSummary(){this.refreshPrice();var t=this.$root.find(".sib-summary-text");if(t.length){this.resetButton(t).toggle(!!this.startDate());var e=this.summaryParts();if(!e){t.html('<span class="sib-summary-muted">'+m(l("Select your dates to see the booking summary","sales-igniter-rentals-bookings-lite"))+"</span>"),this.$root.removeClass("is-complete");return}t.html("<b>"+m(e.main)+"</b><small>"+m(e.sub)+"</small>"),this.$root.addClass("is-complete")}}togglePanel(){this.$root.hasClass("is-open")?this.closePanel():this.openPanel()}openPanel(){this.$root.addClass("is-open").find(".sib-trigger").attr("aria-expanded","true"),this.goTo(this.allDone()?0:this.furthestStep()),r("body").addClass("sib-panel-open")}closePanel(){this.$root.removeClass("is-open").find(".sib-trigger").attr("aria-expanded","false"),r("body").removeClass("sib-panel-open"),this.renderTrigger()}renderTrigger(){var t=this.$root.find(".sib-trigger-text");if(t.length){var e=this.startDate(),i=this.endDate(),a=this.dateFormatMoment||"ddd D MMM";if(!e){t.text(l("Select your dates","sales-igniter-rentals-bookings-lite")),this.$root.removeClass("has-dates");return}var s=this.times&&this.times.enabled(),o=e.format(a)+(s&&this.times.getTime("from")?" "+this.times.label(this.times.getTime("from")):""),h='<span class="sib-trigger-part">'+m(o)+"</span>";i&&this.steps.indexOf("end")!==-1&&(h+=' <span class="sib-trigger-part">'+M()+" "+m(i.format(a)+(s&&this.times.getTime("to")?" "+this.times.label(this.times.getTime("to")):""))+"</span>"),t.html(h),this.$root.addClass("has-dates")}}}var F={Calendar:L,TimeSlots:j,R:n,i18n:c,__:l,esc:m,data:d,ui:S,lowStock:I,onward:M,bidiLtr:O,STEP_LABELS:T,ICON_CAL:E,ICON_CLOCK:P};(window.sibookingCalendarExtensions||[]).forEach(function(k){if(typeof k=="function"){var t=k(F);typeof t=="function"&&(F.Calendar=t)}}),window.SibookingCalendar=new F.Calendar,window.SibookingCalendar.init(),window.SibookingTimepicker=window.SibookingCalendar.times||{timepickerfrom:r("#timepicker"),timepickerto:r("#timepicker2"),setInitialTimes:function(){},updateFromDisabledTimes:function(){},updateToDisabledTimes:function(){}},window.SibookingCalendar.isdatepicker()&&window.SibookingCalendar.$root.length&&window.SibookingCalendar.setInitialDates()})})(jQuery);
     1(function(r){"use strict";var A=null;function _(){if(A===null){var n="ltr";try{n=window.getComputedStyle(document.body||document.documentElement).direction}catch(c){}A=n==="rtl"}return A}function M(){return _()?"←":"→"}function O(n){return n=String(n==null?"":n),n===""||!_()?n:"⁦"+n+"⁩"}r(window).on("load",function(){if(typeof sibooking=="undefined"||!sibooking.data||typeof flatpickr=="undefined")return;var n=window.SibookingCalendarRules,c=window.wp&&window.wp.i18n?window.wp.i18n:{__:function(k){return k},_n:function(k,t,e){return e===1?k:t},sprintf:function(k){return k}},l=c.__,T=r.extend({start:l("Start Date","sales-igniter-rentals-bookings-lite"),starttime:l("Start Time","sales-igniter-rentals-bookings-lite"),end:l("End Date","sales-igniter-rentals-bookings-lite"),endtime:l("End Time","sales-igniter-rentals-bookings-lite")},sibooking.data.ui&&sibooking.data.ui.labels||{}),E='<svg class="sib-ic" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="5" width="18" height="16" rx="3"></rect><path d="M3 10h18M8 3v4M16 3v4"></path></svg>',P='<svg class="sib-ic" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9"></circle><path d="M12 7v5l3 2"></path></svg>';function d(){return window.sibooking.data}function S(){return d().ui||{}}function I(){var k=parseInt(S().lowStock,10);return k>0?k:2}function m(k){return r("<div>").text(k==null?"":String(k)).html()}class j{constructor(t){this.calendar=t,this.timepickerfrom=r("#timepicker"),this.timepickerto=r("#timepicker2"),this.side="from",this.$grid=r(".sib-cal .sib-slots"),this.$head=r(".sib-cal .sib-pane--time .sib-pane-head"),this.onChange=null}enabled(){return this.timepickerfrom.length>0}getTime(t){var e=t==="to"?this.timepickerto:this.timepickerfrom;return e.length&&e.val()||""}setTime(t,e,i){var a=t==="to"?this.timepickerto:this.timepickerfrom;a.length&&(a.val(e||""),this.calendar.updateVisibleTime(t),i||a.trigger("change"))}dayFor(t){var e=t==="to"?this.calendar.siflatpickrend:this.calendar.siflatpickrstart;return!e||!e.selectedDates.length?null:moment(e.selectedDates[0])}momentFor(t){var e=this.dayFor(t),i=this.getTime(t);return e&&i?e.format(n.DAY)+" "+i:null}availability(t){var e=this.dayFor(t);return e?n.slotsFor(d(),e,this.calendar.inputQuantity,t,{pickup:t==="to"?this.momentFor("from"):null,ret:t==="from"?this.momentFor("to"):null}):[]}updateFromDisabledTimes(){this.ensureValid("from"),this.side==="from"&&this.render("from")}updateToDisabledTimes(){this.ensureValid("to"),this.side==="to"&&this.render("to")}ensureValid(t){var e=this.getTime(t);if(e){var i=this.availability(t);if(i.length){var a=i.some(function(s){return s.time===e&&!s.disabled});a||this.setTime(t,"",!0)}}}groupOf(t){var e=parseInt(t.split(":")[0],10);return e<12?l("Morning","sales-igniter-rentals-bookings-lite"):e<17?l("Afternoon","sales-igniter-rentals-bookings-lite"):l("Evening","sales-igniter-rentals-bookings-lite")}label(t){var e=d().inputTimeFormat||"HH:mm";return O(moment(t,"HH:mm").format(e))}render(t){this.side=t;var e=this.dayFor(t),i=this.$grid;i.empty();var a=this.$head,s=d();if(!e){a.html('<span class="sib-h">'+m(t==="to"?T.endtime:T.starttime)+"</span>"),i.append('<div class="sib-placeholder">'+m(l("Pick a date first","sales-igniter-rentals-bookings-lite"))+"</div>");return}var o=e.format(s.inputDateFormatMoment||"ddd D MMM");a.html('<span class="sib-h">'+m((t==="to"?T.endtime:T.starttime)+" · "+o)+'</span><span class="sib-hours">'+m(c.sprintf(l("Open %1$s – %2$s","sales-igniter-rentals-bookings-lite"),(_()?"⁦":"")+this.label(s.storeopen),this.label(s.storeclose)+(_()?"⁩":"")))+"</span>");var h=this.availability(t),u=this.getTime(t),p=t==="to"&&!u?this.getTime("from"):null,g={},y=[];h.forEach(function(b){var v=this.groupOf(b.time);g[v]||(g[v]=[],y.push(v)),g[v].push(b)}.bind(this));var f=this;y.forEach(function(b){var v=r('<div class="sib-slot-group"><span class="sib-h">'+m(b)+"</span></div>"),w=r('<div class="sib-slot-row"></div>');g[b].forEach(function(D){var x="sib-slot";D.disabled&&(x+=" is-off is-"+D.reason),D.time===u&&(x+=" is-sel"),!D.disabled&&D.time===p&&(x+=" is-suggested");var C=r('<button type="button" class="'+x+'"></button>').text(f.label(D.time)).attr("data-time",D.time);D.disabled&&C.attr("disabled","disabled").attr("aria-disabled","true"),D.reason==="before-pickup"&&C.append('<span class="sib-slot-sub">'+m(l("before pick-up","sales-igniter-rentals-bookings-lite"))+"</span>"),!D.disabled&&D.time===p&&C.append('<span class="sib-slot-sub">'+m(l("same as pick-up","sales-igniter-rentals-bookings-lite"))+"</span>"),w.append(C)}),v.append(w),i.append(v)}),h.some(function(b){return!b.disabled})||i.append('<div class="sib-placeholder">'+m(l("No times left on this day","sales-igniter-rentals-bookings-lite"))+"</div>"),i.append('<div class="sib-hint">'+m(l("Greyed times are booked or inside the turnover buffer.","sales-igniter-rentals-bookings-lite"))+"</div>"),i.off("click.sib").on("click.sib",".sib-slot:not([disabled])",function(){var b=r(this).attr("data-time");f.setTime(f.side,b),i.find(".sib-slot").removeClass("is-sel"),r(this).addClass("is-sel"),f.onChange&&f.onChange(f.side,b)})}setInitialTimes(){var t=d();if(this.enabled()&&t.sessionFromDate&&t.sessionToDate){var e=moment(t.sessionFromDate),i=moment(t.sessionToDate),a=function(h){return moment(h,"HH:mm:ss").format("HH:mm")},s=this.withinStoreHours(e)?e.format("HH:mm"):a(t.storeopen),o=this.withinStoreHours(i)?i.format("HH:mm"):a(t.storeclose);this.setTime("from",s,!0),this.setTime("to",o,!0)}}withinStoreHours(t){var e=d(),i=t.hours()*60+t.minutes();return i>=n.toMinutes(e.storeopen)&&i<=n.toMinutes(e.storeclose)}}class L{constructor(){this.$root=r(".sib-cal"),this.sibookingDateContainer=r(".sibooking-datecontainer"),this.inputQuantity=n.normalizeQuantity(r('input[name="quantity"]').val()),this.lists={booked:[],both:[],start:[],end:[]},this.steps=[],this.stepIndex=0,this.hoverEnd=null}get calendarType(){return d().calendarType}get dateFormat(){return d().inputDateFormat}get dateFormatMoment(){return d().inputDateFormatMoment}get timeFormat(){return d().inputTimeFormat}isdatepicker(){return r("#flatpickr").length>0||r("#flatpickr2").length>0}init(){!this.isdatepicker()||!this.$root.length||(this.times=new j(this),this.times.onChange=this.onTimePicked.bind(this),this.buildSteps(),this.updateDisabledDatesAll(),this.loadCalendar(),this.addEventHandlers(),this.render(),this.goTo(0))}$summary(){return this.$root.find(".sib-summary").first()}stepsFor(){return n.buildSteps(this.calendarType)}buildSteps(){var t=this.times&&this.times.enabled();this.steps=this.stepsFor().filter(function(e){return t||e!=="starttime"&&e!=="endtime"}),this.$root.toggleClass("has-time",this.steps.indexOf("starttime")!==-1)}startDate(){return this.siflatpickrstart&&this.siflatpickrstart.selectedDates.length?moment(this.siflatpickrstart.selectedDates[0]):null}endDate(){return this.siflatpickrend&&this.siflatpickrend.selectedDates.length?moment(this.siflatpickrend.selectedDates[0]):null}calendarOptions(){return{disableMobile:!0,mode:"single",dateFormat:"Y-m-d",allowInput:!1,monthSelectorType:"static",showMonths:1,locale:r.extend({firstDayOfWeek:parseInt(S().firstDay||0,10)},S().locale||{}),inline:!0,appendTo:this.$root.find(".sib-cal-mount")[0]}}loadCalendar(){var t=this,e=this.calendarOptions();this.siflatpickrstart=flatpickr("#flatpickr",r.extend({},e,{onDayCreate:this.onDayCreate.bind(this,"start"),onOpen:this.onStartOpen.bind(this),onChange:this.onChangeStart.bind(this),minDate:d().minDate,maxDate:this.getMaxDate()})),this.siflatpickrend=flatpickr("#flatpickr2",r.extend({},e,{allowInvalidPreload:!0,onDayCreate:this.onDayCreate.bind(this,"end"),onOpen:this.onOpenEnd.bind(this),onChange:this.onChangeEnd.bind(this),onClose:this.onCloseEnd.bind(this)})),this.siflatpickrstart.calendarContainer.classList.add("sib-fp","sib-fp--start"),this.siflatpickrend.calendarContainer.classList.add("sib-fp","sib-fp--end"),r(document).trigger("sibooking:calendars",[this.siflatpickrstart,this.siflatpickrend]),r(this.siflatpickrend.calendarContainer).on("mouseover",".flatpickr-day",function(){t.hoverEnd&&t.hoverEnd.getTime()===this.dateObj.getTime()||(t.hoverEnd=this.dateObj,t.paintRange())}).on("mouseleave",function(){t.hoverEnd=null,t.paintRange()}),this.applyDisable()}addEventHandlers(){var t=this;r('input[name="quantity"]').on("change",function(){n.normalizeQuantity(r(this).val())!==t.inputQuantity&&t.onQuantityChanged()}),r(document).on("sib:guests:change",function(e,i,a){a&&!r(a).closest("form").is(t.$root.closest("form"))||t.refreshPrice()}),r(".single_add_to_cart_button").not("#sibookingpurchase").on("click",function(e){var i=t.missingSelection();i?(e.preventDefault(),t._missingSaid=i,t.tell(i)):t._missingSaid&&(t.$root.find(".sib-note").text()===t._missingSaid&&t.say(""),t._missingSaid="")}),r("#sibookingpurchase").on("click",function(){r('input[name="isbookingpurchase"]').val(!0)}),r("#siwidget-update").on("click",function(e){e.preventDefault(),t.widgetUpdate()}),this.$root.find(".sib-panel").length&&(this.$root.on("click",".sib-trigger",function(){t.togglePanel()}),this.$root.on("click",".sib-panel-close, .sib-backdrop",function(){t.closePanel()}),r(document).on("keydown.sibpanel",function(e){e.key==="Escape"&&t.closePanel()}),r(document).on("mousedown.sibpanel touchstart.sibpanel",function(e){t.$root.hasClass("is-open")&&!r(e.target).closest("#sib-cal").length&&t.closePanel()})),this.$root.on("click keydown",".sib-step[data-index]",function(e){if(!(e.type==="keydown"&&e.key!=="Enter"&&e.key!==" ")){e.preventDefault();var i=parseInt(r(this).attr("data-index"),10);!isNaN(i)&&i<=t.furthestStep()&&t.changeStep(i)}}),this.$root.on("click",".sib-reset",function(e){e.preventDefault(),t.clearStartEndDate()})}missingSelection(){return!this.startDate()||!this.endDate()?l("Please first select your booking dates","sales-igniter-rentals-bookings-lite"):this.steps.indexOf("endtime")!==-1&&(!this.times.getTime("from")||!this.times.getTime("to"))?l("Please select your pick-up and return times","sales-igniter-rentals-bookings-lite"):""}onQuantityChanged(){var t=this.startDate(),e=this.endDate();if(this.updateDisabledDatesAll(),this.say(""),!t){this.render();return}if(this.rangeStillFree(t,e||t)){this.render();return}this.clearStartEndDate(),this.say(c.sprintf(c._n("There is not %d free on those dates. Please pick your dates again.","There are not %d free on those dates. Please pick your dates again.",this.inputQuantity,"sales-igniter-rentals-bookings-lite"),this.inputQuantity))}rangeStillFree(t,e){var i={};(this.lists.booked||[]).forEach(function(o){i[o]=!0});var a=t.clone().startOf("day"),s=e.clone().startOf("day");if(i[a.format(n.DAY)])return!1;for(a.add(1,"d");a.isBefore(s,"day");){if(i[a.format(n.DAY)])return!1;a.add(1,"d")}return!0}say(t){var e=this.$root.find(".sib-note");if(!e.length){if(!t)return;e=r('<p class="sib-note" aria-live="polite"></p>');var i=this.$summary();i.length?i.before(e):this.$root.append(e)}e.text(t||"").prop("hidden",!t)}tell(t){this.say(t);var e=this.$root.find(".sib-note").get(0);e&&e.scrollIntoView&&e.scrollIntoView({block:"nearest"})}guestsText(){var t=this.$root.closest("form").find(".sib-guests").get(0);return t&&t.sibGuests?t.sibGuests.summary():""}guestFields(){var t={};return this.$root.closest("form").find(".sib-guests-input[name], .sib-guests-total[name]").each(function(){this.value!==""&&(t[this.name]=this.value)}),t}blockedStarts(){return[]}updateDisabledDatesAll(){this.inputQuantity=n.normalizeQuantity(r('input[name="quantity"]').val()),this.lists=n.disabledLists(d(),this.inputQuantity,this.startDate(),this.blockedStarts()),this.applyDisable(),this.times&&(this.times.ensureValid("from"),this.times.ensureValid("to"))}applyDisable(){var t=d();this._closedFor!==t&&(this._closedFor=t,this._closedStart=(t.disabled_dates||[]).concat(t.disabled_dates_start||[]),this._closedEnd=(t.disabled_dates||[]).concat(t.disabled_dates_end||[]));var e=this.startDate(),i=this.getMaxDate();this.siflatpickrstart&&this.siflatpickrstart.set({disable:this.lists.start,maxDate:e?null:i}),this.siflatpickrend&&this.siflatpickrend.set({disable:this.lists.end,minDate:e?e.format(n.DAY):null,maxDate:i})}updateDisabledDatesEnd(){this.updateDisabledDatesAll()}getMaxDate(){var t=this.times&&this.times.enabled()?this.times.getTime("from"):"";return n.maxDate(d(),this.startDate(),this.lists.booked,t,this.inputQuantity)}getMinDateForEndDate(){return this.startDate()?n.minEndDate(this.startDate(),d().minLength,!!d().nightly):null}onDayCreate(t,e,i,a,s){var o=d(),h=moment(s.dateObj).format(n.DAY),u=null;if(o.nightly&&(u=n.dayState(o,this.inputQuantity,h),s.setAttribute("data-sib-day-state",u)),o.nightly?u==="booked"||u==="turnover":this.lists.booked.indexOf(h)!==-1)s.classList.add("sib-booked"),s.title=l("Fully booked","sales-igniter-rentals-bookings-lite");else{var p=n.disabledBy(h,t==="start"?this._closedStart||[]:this._closedEnd||[]);if(p.length){s.classList.add("sib-closed");var g=p.map(function(v){return v.reason}).filter(function(v,w,D){return v&&D.indexOf(v)===w});s.title=g.length?g.join(" · "):l("Disabled","sales-igniter-rentals-bookings-lite"),s.setAttribute("data-sib-reason",s.title)}}if(S().showPrices&&o.dayPrices&&o.dayPrices[h]&&!s.classList.contains("flatpickr-disabled")){var y=document.createElement("span");y.className="sib-price",y.textContent=o.dayPrices[h],s.appendChild(y)}if(S().showUnitsLeft&&!s.classList.contains("flatpickr-disabled")){var f=n.unitsLeft(o,h);if(f!==null){var b=document.createElement("span");b.className="sib-left"+(f<=I()?" is-low":""),b.textContent=c.sprintf(l("%d left","sales-igniter-rentals-bookings-lite"),f),s.appendChild(b)}}this.paintDay(s,t==="end"),r(document).trigger("sibooking:daycreate",[s,h,t,u])}paintDay(t,e){var i=this.startDate(),a=this.endDate()||(e&&this.hoverEnd?moment(this.hoverEnd):null);if(t.classList.remove("sib-range-start","sib-inrange","sib-range-end"),!!i){var s=moment(t.dateObj);s.isSame(i,"day")&&t.classList.add("sib-range-start"),a&&a.isAfter(i,"day")&&s.isAfter(i,"day")&&s.isBefore(a,"day")&&t.classList.add("sib-inrange"),a&&s.isSame(a,"day")&&!s.isSame(i,"day")&&t.classList.add("sib-range-end")}}paintRange(){var t=this;[[this.siflatpickrend,!0],[this.siflatpickrstart,!1]].forEach(function(e){var i=e[0];if(!(!i||!i.days))for(var a=i.days.querySelectorAll(".flatpickr-day"),s=0;s<a.length;s++)t.paintDay(a[s],e[1])})}onStartOpen(){this.updateDisabledDatesAll()}onChangeStart(){this.say("");var t=this.startDate(),e=t?t.format(n.DAY):"";if(e&&this._lastStart&&e!==this._lastStart&&(this.siflatpickrend&&this.siflatpickrend.clear(),this.times&&(this.times.setTime("from","",!0),this.times.setTime("to","",!0)),this.hoverEnd=null),this._lastStart=e,this.updateDisabledDatesAll(),this.startDate()){var i=this.endDate(),a=this.getMaxDate();i&&(i.isBefore(this.startDate(),"day")||a&&i.isAfter(moment(a,n.DAY),"day"))&&this.siflatpickrend.clear()}this.times&&this.times.updateFromDisabledTimes(),this.completeStep("start")}onOpenEnd(){if(!this.startDate()){this.siflatpickrend.close();return}this.applyDisable(),this.siflatpickrend.jumpToDate(this.startDate().toDate())}onChangeEnd(){this.updateInputValues(),this.paintRange(),this.times&&this.times.updateToDisabledTimes(),this.completeStep("end")}onCloseEnd(){this.updateInputValues(),this.render()}updateInputValues(){var t=r("#flatpickr2"),e=this.endDate();t.val(e?e.format(n.DAY):""),this.siflatpickrend&&this.siflatpickrend.altInput&&(this.siflatpickrend.altInput.value=e?this.siflatpickrend.formatDate(e.toDate(),this.dateFormat):"")}clearStartEndDate(){this._lastStart="",this.siflatpickrstart&&this.siflatpickrstart.clear(),this.siflatpickrend&&this.siflatpickrend.clear(),this.times&&(this.times.setTime("from","",!0),this.times.setTime("to","",!0)),this.hoverEnd=null,this.render(),this.goTo(0)}setInitialDates(){var t=d();if(!(!t.sessionFromDate||!t.sessionToDate||t.sessionFromDate==="Invalid Date"||t.sessionToDate==="Invalid Date")){var e=moment(t.sessionFromDate),i=moment(t.sessionToDate);!e.isValid()||!i.isValid()||t.minDate&&e.isBefore(moment(t.minDate),"day")||(this.siflatpickrstart.setDate(e.format(n.DAY),!1),this.updateDisabledDatesAll(),this.siflatpickrend.setDate(i.format(n.DAY),!1),this.updateInputValues(),this.times&&this.times.setInitialTimes(),this.afterInitialDates(e,i),this._lastStart=e.format(n.DAY),this._rememberedKey=this.startDate()&&this.endDate()?this.startDate().format(n.DAY)+" "+(this.times&&this.times.getTime("from")||"")+"|"+this.endDate().format(n.DAY)+" "+(this.times&&this.times.getTime("to")||""):"",this.siflatpickrstart.jumpToDate(e.toDate()),this.siflatpickrend.jumpToDate(e.toDate()),this.render(),this.goTo(this.steps.length),this.paintRange())}}afterInitialDates(t,e){}widgetUpdate(){if(!this.startDate()||!this.endDate()){this.tell(l("Please first select your booking dates","sales-igniter-rentals-bookings-lite"));return}var t=this,e=r("#sibooking-calendar");e.LoadingOverlay&&e.LoadingOverlay("show");var i={fromDate:this.startDate().format(n.DAY),toDate:this.endDate().format(n.DAY),fromTime:this.times?this.times.getTime("from"):"",toTime:this.times?this.times.getTime("to"):"",action:"sibooking_calendar_global",nonce:d().nonce},a=function(){e.LoadingOverlay&&e.LoadingOverlay("hide"),t.tell(l("Booking dates updated","sales-igniter-rentals-bookings-lite"))},s=function(o){o&&o.statusText==="abort"||(e.LoadingOverlay&&e.LoadingOverlay("hide"),t.tell(l("Your dates could not be saved. Check your connection and try again.","sales-igniter-rentals-bookings-lite")))};r.ajaxq&&r.ajaxq.isRunning("sibookingGlobalCalendar")&&r.ajaxq.abort("sibookingGlobalCalendar"),this.postDates(i,"sibookingGlobalCalendar").done(a).fail(s)}postDates(t,e){var i=d().ajaxurl,a=function(){return t.nonce=d().nonce,e&&r.ajaxq?r.ajaxq(e,{url:i,type:"POST",data:t}):r.post(i,t)},s=r.Deferred();return a().done(s.resolve).fail(function(o){if(o&&o.statusText!=="abort"&&window.sibAjax&&window.sibAjax.isNonceFailure(o)){window.sibAjax.renewNonce().then(function(){a().done(s.resolve).fail(s.reject)},function(){s.reject(o)});return}s.reject(o)}),s.promise()}furthestStep(){for(var t=0;t<this.steps.length&&this.isStepDone(this.steps[t]);t++);return Math.min(t,this.steps.length-1)}isStepDone(t){switch(t){case"start":return!!this.startDate();case"starttime":return!!(this.times&&this.times.getTime("from"));case"end":return!!this.endDate();case"endtime":return!!(this.times&&this.times.getTime("to"))}return!1}allDone(){var t=this;return this.steps.every(function(e){return t.isStepDone(e)})}stepValue(t){var e="ddd D MMM";switch(t){case"start":return this.startDate()?this.startDate().format(e):"";case"end":return this.endDate()?this.endDate().format(e):"";case"starttime":return this.times&&this.times.getTime("from")?this.times.label(this.times.getTime("from")):"";case"endtime":return this.times&&this.times.getTime("to")?this.times.label(this.times.getTime("to")):""}return""}completeStep(t){var e=this.steps.indexOf(t);if(this.render(),e!==-1){for(var i=e+1;i<this.steps.length&&this.isStepDone(this.steps[i]);)i++;if(this.goTo(i),this.allDone()&&this.$root.hasClass("is-open")){var a=this;setTimeout(function(){a.closePanel()},350)}}}onTimePicked(t){if(t==="from"&&this.times){this.updateDisabledDatesAll();var e=this.endDate(),i=this.getMaxDate();e&&i&&e.isAfter(moment(i,n.DAY),"day")&&(this.siflatpickrend.clear(),this.times.setTime("to","",!0),this.updateInputValues()),this.times.ensureValid("to")}this.completeStep(t==="to"?"endtime":"starttime")}changeStep(t){var e=this.steps[t],i=this.times;e==="start"?(this.siflatpickrend&&this.steps.indexOf("end")!==-1&&this.siflatpickrend.clear(),i&&(i.setTime("from","",!0),i.setTime("to","",!0)),this.hoverEnd=null,this.updateInputValues()):e==="starttime"?i&&i.setTime("to","",!0):e==="end"&&(i&&i.setTime("to","",!0),this.hoverEnd=null),this.render(),this.goTo(t)}goTo(t){this.stepIndex=Math.max(0,Math.min(t,this.steps.length));var e=this.steps[this.stepIndex],i=this.$root.find(".sib-stage"),a=e==="start"||!e&&!this.steps.length,s=e==="end"||e==="endtime"?"end":"start";a&&(s="start"),r(this.siflatpickrstart.calendarContainer).toggle(s==="start"),r(this.siflatpickrend.calendarContainer).toggle(s==="end"),s==="end"&&this.startDate()&&this.siflatpickrend.jumpToDate((this.endDate()||this.startDate()).toDate());var o=e==="endtime"?"to":"from";this.times&&this.times.enabled()&&this.times.render(o);var h=e==="starttime"||e==="endtime"?"time":"date";e===void 0&&(h="done"),i.attr("data-active",h);var u=e?T[e]:l("Your booking","sales-igniter-rentals-bookings-lite"),p="";if(s==="end"&&this.startDate()){var g=this.startDate().format("ddd D MMM")+(this.times&&this.times.getTime("from")?" · "+this.times.label(this.times.getTime("from")):"");p='<span class="sib-pane-ctx">'+m(d().nightly?T.start+" "+g:c.sprintf(l("Pick-up %s","sales-igniter-rentals-bookings-lite"),g))+"</span>"}this.$root.find(".sib-pane--date .sib-pane-head").html('<span class="sib-h">'+m(s==="end"?T.end:T.start)+"</span>"+p),this.$root.attr("data-step",e||"done"),this.renderSteps();var y=this.$root.find(".sib-stage-hint");if(y.text(e&&this.stepHints()[e]||""),e==="end"&&this.startDate()&&!this.endDate()){var f=n.minEndDate(this.startDate(),d().minLength,!!d().nightly);n.isDayDisabled(f,this.lists.end)||(this.hoverEnd=moment(f,n.DAY).toDate(),this.paintRange())}this.renderSummary(),r(document).trigger("sibooking:step",[e||"done",this.$root])}stepHints(){return{start:l("Tap a day to continue. Greyed days are booked, closed, or too close to the next booking.","sales-igniter-rentals-bookings-lite"),end:l("Days before pick-up and past the next booking cannot be picked.","sales-igniter-rentals-bookings-lite"),starttime:l("Choosing a time moves straight to the return date.","sales-igniter-rentals-bookings-lite"),endtime:""}}render(){this.renderSteps(),this.renderTrigger(),this.renderSummary(),this.rememberDates(),this.announceDates()}announceDates(){var t=this.startDate(),e=this.endDate(),i=(t?t.format(n.DAY):"")+"|"+(e?e.format(n.DAY):"");i!==this._datesKey&&(this._datesKey=i,r(document.body).trigger("sibooking:dates",[t,e]))}rememberDates(){var t=d();if(!(!t.ajaxurl||t.isComposite)&&!(this.steps.indexOf("end")===-1||!this.allDone())){var e=this.startDate(),i=this.endDate();if(!(!e||!i||i.isBefore(e,"day"))){var a=this.times&&this.times.getTime("from")||"",s=this.times&&this.times.getTime("to")||"",o=e.format(n.DAY)+" "+a+"|"+i.format(n.DAY)+" "+s;o!==this._rememberedKey&&(this._rememberedKey=o,this.postDates({action:"sibooking_calendar_global",fromDate:e.format(n.DAY),toDate:i.format(n.DAY),fromTime:a,toTime:s}))}}}renderSteps(){var t=this.$root.find(".sib-steps");if(this.$root.toggleClass("sib-cal--steps",t.length>0&&S().showSteps!==!1&&this.steps.length>0),!!t.length){if(S().showSteps===!1){t.empty().hide();return}t.show();var e=this,i=this.furthestStep();t.empty(),this.steps.forEach(function(a,s){var o=e.isStepDone(a),h="sib-step"+(o?" is-done":"")+(s===e.stepIndex?" is-on":"")+(s<=i?" is-reachable":""),u=o?e.stepValue(a):s===e.stepIndex?l("Choose","sales-igniter-rentals-bookings-lite"):"",p=o&&s!==e.stepIndex?' title="'+m(l("Change","sales-igniter-rentals-bookings-lite"))+'"':"";t.append('<div class="'+h+'" data-index="'+s+'" role="button" tabindex="0"'+p+' aria-label="'+m(T[a]+(o?": "+u:""))+'"><div class="sib-step-bar"></div><span class="sib-step-t">'+m(T[a])+'</span><span class="sib-step-v">'+m(u)+"</span></div>")})}}updateVisibleTime(t){}quotable(){var t=d();return!!(this.startDate()&&this.endDate()&&this.allDone()&&!t.isComposite&&t.ajaxurl)}totalBox(){var t=r(".sib-rental-total");if(!t.length){t=r('<div class="sib-rental-total" aria-live="polite"></div>');var e=this.$root.closest(".summary, .product"),i=".products, .related, .upsells, .up-sells, .cross-sells, .wc-block-product-template, .wp-block-woocommerce-product-collection, .wp-block-woocommerce-related-products",a=function(o){return o.filter(function(){return!r(this).closest(i).length})},s=a(e.find(".sib-pricing").not(".sib-pricing--loop")).first();s.length||(s=a(e.find("p.price")).first()),s.length||(s=a(e.find(".price")).first()),s.length||(s=r(".single-product .summary p.price").first()),s.length?s.after(t):this.$root.find(".sib-summary").append(t)}return t}showQuote(t){var e=this.totalBox(),i=t.quantity>1?' <span class="sib-rental-total-qty">× '+t.quantity+"</span>":"";e.html('<span class="sib-rental-total-label">'+m(l("Total for your dates","sales-igniter-rentals-bookings-lite"))+i+'</span><span class="sib-rental-total-amount">'+t.total_html+"</span>"),e.removeClass("is-in"),e[0].offsetWidth,e.addClass("is-in")}hideQuote(){this.totalBox().removeClass("is-in")}notesBox(){var t=r(".sib-quote-notes");return t.length||(t=r('<div class="sib-quote-notes" aria-live="polite"></div>'),this.totalBox().after(t)),t}showNotes(t){var e=Array.isArray(t)?t.filter(function(s){return s&&s.text}):[],i=r(".sib-quote-notes");if(!(!e.length&&!i.length)){i=this.notesBox();var a=e.map(function(s){var o=String(s.key||"").replace(/[^A-Za-z0-9_-]/g,"");return'<p class="sib-quote-note'+(o?" sib-quote-note--"+o:"")+'">'+m(s.text)+"</p>"}).join("");i.html()!==a&&i.html(a)}}quoteFields(){return this.guestFields()}showQuoteError(t){this.totalBox().html(t).addClass("is-in")}quoteAgain(t){}refreshPrice(){var t=this,e=d(),i=this.startDate(),a=this.endDate(),s=function(){t._priceKey="",t.showCardTotal(null),t.hideQuote(),t.showNotes(null)};if(!this.quotable()){s();return}var o=r('input[name="variation_id"]').val()||r('button[name="add-to-cart"]').val()||r('input[name="add-to-cart"]').val()||r(".single_add_to_cart_button").val()||"";if(!o){s();return}var h={action:"sibooking_rental_price",nonce:e.nonce,productid:o,variationid:r('input[name="variation_id"]').val()||0,from:i.format(n.DAY),to:a.format(n.DAY),fromtime:this.times?this.times.getTime("from"):"",totime:this.times?this.times.getTime("to"):"",quantity:this.inputQuantity};r.extend(h,this.quoteFields());var u=JSON.stringify(h);if(u===this._priceKey){this._lastQuote&&this._lastQuoteKey===u&&this.quoteAgain(this._lastQuote);return}this._priceKey=u,clearTimeout(this._priceTimer),this.notesBox();var p=function(){t._priceKey===u&&(s(),t.showQuoteError('<span class="sib-ajax-error" role="alert">'+m(l("The price for these dates could not be loaded.","sales-igniter-rentals-bookings-lite"))+' <a href="#" class="sib-ajax-retry">'+m(l("Try again","sales-igniter-rentals-bookings-lite"))+"</a></span>"))};this._retryBound||(this._retryBound=!0,r(document).on("click",".sib-ajax-retry",function(f){f.preventDefault(),t._priceKey="",t.refreshPrice()}));var g=function(f){if(t._priceKey===u){if(!f||!f.success){s();return}t._lastQuote=f.data,t._lastQuoteKey=u,t.showCardTotal(f.data),t.showQuote(f.data),t.showNotes(f.data.notes)}},y=function(f){h.nonce=d().nonce;var b=r.ajaxq?r.ajaxq("sibookingPrice",{url:e.ajaxurl,type:"POST",data:h}):r.post(e.ajaxurl,h);b.done(g).fail(function(v){if(!(t._priceKey!==u||v&&v.statusText==="abort")){if(f<1){p();return}var w=function(){y(f-1)};window.sibAjax&&window.sibAjax.isNonceFailure(v)?window.sibAjax.renewNonce().then(w,p):setTimeout(w,800)}})};this._priceTimer=setTimeout(function(){y(1)},120)}showCardTotal(t){var e=this.$root.closest(".product, .type-product, body").find(".sib-pricing").not(".sib-pricing--loop, .sib-pricing--custom").first();if(e.length){var i=e.find(".sib-pricing-head");if(i.find(".sib-pricing-total--live").remove(),!t||!t.total_html){e.removeClass("has-total has-live-total");return}var a=parseFloat(t.hours||0),s=parseInt(t.days||0,10),o,h=this.times&&this.times.enabled()&&this.times.getTime("from")&&this.times.getTime("to"),u=Math.floor(a/24),p=Math.ceil(Math.round((a-u*24)*100)/100);if(h&&a>0&&a<24){var g=Math.ceil(a);o=c.sprintf(c._n("for %d hour","for %d hours",g,"sales-igniter-rentals-bookings-lite"),g)}else h&&u>0&&p>0&&(t.length_unit||d().lengthUnit)!=="night"?o=c.sprintf(l("for %1$s %2$s","sales-igniter-rentals-bookings-lite"),c.sprintf(c._n("%d day","%d days",u,"sales-igniter-rentals-bookings-lite"),u),c.sprintf(c._n("%d hour","%d hours",p,"sales-igniter-rentals-bookings-lite"),p)):(t.length_unit||d().lengthUnit)==="night"?o=c.sprintf(c._n("for %d night","for %d nights",Math.max(1,s),"sales-igniter-rentals-bookings-lite"),Math.max(1,s)):o=c.sprintf(c._n("for %d day","for %d days",Math.max(1,s),"sales-igniter-rentals-bookings-lite"),Math.max(1,s));r('<span class="sib-pricing-total sib-pricing-total--live"><b class="sib-pricing-amt"></b> <span class="sib-pricing-total-label"></span></span>').find("b").html(t.total_html).end().find(".sib-pricing-total-label").text(o).end().insertAfter(i.find(".sib-pricing-from")),e.addClass("has-total has-live-total")}}lengthText(t){return d().lengthUnit==="night"?c.sprintf(c._n("%d night","%d nights",t,"sales-igniter-rentals-bookings-lite"),t):t===1?l("1 day","sales-igniter-rentals-bookings-lite"):c.sprintf(l("%d days","sales-igniter-rentals-bookings-lite"),t)}summaryDateFormat(){return this.dateFormatMoment||"ddd D MMM"}summaryParts(){var t=this.startDate(),e=this.endDate();if(!t||!e||!this.allDone())return null;var i=this.summaryDateFormat(),a=this.times&&this.times.enabled(),s=t.format(i)+(a&&this.times.getTime("from")?" "+this.times.label(this.times.getTime("from")):""),o=e.format(i)+(a&&this.times.getTime("to")?" "+this.times.label(this.times.getTime("to")):""),h=n.dayCount(t,e,!!d().nightly,!!d().chargeReturnDay),u,p=a?this.times.getTime("from"):"",g=a?this.times.getTime("to"):"",y=!a||!p||!g||p==="00:00"&&(g==="00:00"||g==="23:59"),f=this,b=function(C){return f.lengthText(C)};if(y)u=b(h),s=t.format(i),o=e.format(i);else{var v=t.clone().set({hour:parseInt(p.split(":")[0],10),minute:parseInt(p.split(":")[1],10)}),w=e.clone().set({hour:parseInt(g.split(":")[0],10),minute:parseInt(g.split(":")[1],10)}),D=w.diff(v,"hours",!0);if(D<24){var x=Math.round(D*10)/10;u=x===1?l("1 hour","sales-igniter-rentals-bookings-lite"):c.sprintf(l("%s hours","sales-igniter-rentals-bookings-lite"),x)}else u=b(Math.ceil(D/24))}return{main:u,sub:s+" "+M()+" "+o}}resetButton(t){var e=this.$root.find(".sib-reset");return e.length||(e=r('<button type="button" class="sib-reset" title="'+m(l("Start over","sales-igniter-rentals-bookings-lite"))+'"><svg class="sib-ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M3 12a9 9 0 1 0 3-6.7"></path><path d="M3 4v5h5"></path></svg><span>'+m(l("Start over","sales-igniter-rentals-bookings-lite"))+"</span></button>"),t.after(e)),e}renderSummary(){this.refreshPrice();var t=this.$root.find(".sib-summary-text");if(t.length){this.resetButton(t).toggle(!!this.startDate());var e=this.summaryParts();if(!e){t.html('<span class="sib-summary-muted">'+m(l("Select your dates to see the booking summary","sales-igniter-rentals-bookings-lite"))+"</span>"),this.$root.removeClass("is-complete");return}t.html("<b>"+m(e.main)+"</b><small>"+m(e.sub)+"</small>"),this.$root.addClass("is-complete")}}togglePanel(){this.$root.hasClass("is-open")?this.closePanel():this.openPanel()}openPanel(){this.$root.addClass("is-open").find(".sib-trigger").attr("aria-expanded","true"),this.goTo(this.allDone()?0:this.furthestStep()),r("body").addClass("sib-panel-open")}closePanel(){this.$root.removeClass("is-open").find(".sib-trigger").attr("aria-expanded","false"),r("body").removeClass("sib-panel-open"),this.renderTrigger()}renderTrigger(){var t=this.$root.find(".sib-trigger-text");if(t.length){var e=this.startDate(),i=this.endDate(),a=this.dateFormatMoment||"ddd D MMM";if(!e){t.text(l("Select your dates","sales-igniter-rentals-bookings-lite")),this.$root.removeClass("has-dates");return}var s=this.times&&this.times.enabled(),o=e.format(a)+(s&&this.times.getTime("from")?" "+this.times.label(this.times.getTime("from")):""),h='<span class="sib-trigger-part">'+m(o)+"</span>";i&&this.steps.indexOf("end")!==-1&&(h+=' <span class="sib-trigger-part">'+M()+" "+m(i.format(a)+(s&&this.times.getTime("to")?" "+this.times.label(this.times.getTime("to")):""))+"</span>"),t.html(h),this.$root.addClass("has-dates")}}}var F={Calendar:L,TimeSlots:j,R:n,i18n:c,__:l,esc:m,data:d,ui:S,lowStock:I,onward:M,bidiLtr:O,STEP_LABELS:T,ICON_CAL:E,ICON_CLOCK:P};(window.sibookingCalendarExtensions||[]).forEach(function(k){if(typeof k=="function"){var t=k(F);typeof t=="function"&&(F.Calendar=t)}}),window.SibookingCalendar=new F.Calendar,window.SibookingCalendar.init(),window.SibookingTimepicker=window.SibookingCalendar.times||{timepickerfrom:r("#timepicker"),timepickerto:r("#timepicker2"),setInitialTimes:function(){},updateFromDisabledTimes:function(){},updateToDisabledTimes:function(){}},window.SibookingCalendar.isdatepicker()&&window.SibookingCalendar.$root.length&&window.SibookingCalendar.setInitialDates()})})(jQuery);
  • sales-igniter-rentals-bookings-lite/trunk/Includes/AdminOrderDates.php

    r3728745 r3729359  
    328328        }
    329329        $days = max( 1, (int) ceil( $seconds / DAY_IN_SECONDS ) );
     330        // "Also charge the return day": a date-only rental from the 1st to the 3rd is 3 days
     331        if ( ! $useTime && $productId && substr( (string) $to, 0, 10 ) > substr( (string) $from, 0, 10 ) && \Sibooking\Model\Pricing\ConfigFactory::countsReturnDay( $productId ) ) {
     332            $days = max( 1, \Sibooking\Model\Stay::nights( $from, $to ) ) + 1;
     333        }
    330334        return Price::lengthText( $days, $unit );
    331335    }
    … …  
    557561        $nightly = \Sibooking\Includes\Order::lineNightly( $item );
    558562        foreach ( \Sibooking\Model\Availability::check( $pid, $from, $to, $qty, [
    559             'source'  => 'admin',
    560             'skip'    => [ \Sibooking\Model\Availability::TOO_EARLY, \Sibooking\Model\Availability::STOCK ],
    561             'nightly' => $nightly,
    562             'line'    => $item,
     563            'source'        => 'admin',
     564            'skip'          => [ \Sibooking\Model\Availability::TOO_EARLY, \Sibooking\Model\Availability::STOCK ],
     565            'nightly'       => $nightly,
     566            'returnDayFree' => \Sibooking\Includes\Order::lineReturnDayFree( $item ),
     567            'line'          => $item,
    563568        ] ) as $reason => $message ) {
    564569            $errors[] = \Sibooking\Model\Availability::CLOSED === $reason ? __( 'The start or end date falls on a closed day', 'sales-igniter-rentals-bookings-lite' ) : $message;
    … …  
    572577        // units free for the buffered range, this order's own rows left out
    573578        $endForBuffer = apply_filters( 'sibooking_end_datetime_for_buffer', $to, $item );
    574         // the line's own rule (a stay of nights frees its check-out day, Model\Stay)
    575         $stay = [ 'nightly' => \Sibooking\Includes\Order::lineNightly( $item ) ];
     579        // the line's own rule (a stay of nights frees its check-out day, Model\Stay; "Days: can go
     580        // out again on the return day" frees its return day, Model\Buffer)
     581        $stay = [ 'nightly' => \Sibooking\Includes\Order::lineNightly( $item ), 'returnDayFree' => \Sibooking\Includes\Order::lineReturnDayFree( $item ) ];
    576582        $sb = $this->buffer->calculateBufferBeforeStartDate( $pid, $from, $stay );
    577583        $eb = $this->buffer->calculateBufferAfterEndDate( $pid, $endForBuffer, $stay + [ 'start' => $from ] );
    … …  
    686692        // 2. booking row -- in place, so the booking id and its send / return progress survive
    687693        $endForBuffer = apply_filters( 'sibooking_end_datetime_for_buffer', $to, $item );
    688         $stay = [ 'nightly' => \Sibooking\Includes\Order::lineNightly( $item ) ];
     694        $stay = [ 'nightly' => \Sibooking\Includes\Order::lineNightly( $item ), 'returnDayFree' => \Sibooking\Includes\Order::lineReturnDayFree( $item ) ];
    689695        $sb  = $this->buffer->calculateBufferBeforeStartDate( $pid, $from, $stay );
    690696        $eb  = $this->buffer->calculateBufferAfterEndDate( $pid, $endForBuffer, $stay + [ 'start' => $from ] );
    … …  
    13581364        $pid          = $part->get_product_id();
    13591365        $endForBuffer = apply_filters( 'sibooking_end_datetime_for_buffer', $to, $part );
    1360         $stay         = [ 'line' => $part, 'parent' => Order::kitParentId( $part ), 'nightly' => Order::lineNightly( $part ) ];
     1366        $stay         = [ 'line' => $part, 'parent' => Order::kitParentId( $part ), 'nightly' => Order::lineNightly( $part ), 'returnDayFree' => Order::lineReturnDayFree( $part ) ];
    13611367        return [
    13621368            $this->buffer->calculateBufferBeforeStartDate( $pid, $from, $stay ),
    … …  
    13741380        $errors = [];
    13751381        foreach ( \Sibooking\Model\Availability::check( $line->get_product_id(), $from, $to, max( 1, (int) $line->get_quantity() ), [
    1376             'source'  => 'admin',
    1377             'skip'    => [ \Sibooking\Model\Availability::TOO_EARLY, \Sibooking\Model\Availability::STOCK ],
    1378             'nightly' => Order::lineNightly( $line ),
    1379             'line'    => $line,
     1382            'source'        => 'admin',
     1383            'skip'          => [ \Sibooking\Model\Availability::TOO_EARLY, \Sibooking\Model\Availability::STOCK ],
     1384            'nightly'       => Order::lineNightly( $line ),
     1385            'returnDayFree' => Order::lineReturnDayFree( $line ),
     1386            'line'          => $line,
    13801387        ] ) as $reason => $message ) {
    13811388            $errors[] = \Sibooking\Model\Availability::CLOSED === $reason ? __( 'The start or end date falls on a closed day', 'sales-igniter-rentals-bookings-lite' ) : $message;
  • sales-igniter-rentals-bookings-lite/trunk/Includes/CartAvailability.php

    r3728745 r3729359  
    3232 * Deliberately not counted against the customer:
    3333 * - their own order still waiting for payment (Validation::ownPendingOrderIds());
    34  *   pending orders hold their units indefinitely (Kevin, §7 Q2), so a retried
     34 *   pending orders hold their units indefinitely (the owner, §7 Q2), so a retried
    3535 *   payment would otherwise be refused by its own reservation;
    3636 * - "buy" lines of a rentable product (`_sibooking-isbookingpurchase`), which carry no dates;
  • sales-igniter-rentals-bookings-lite/trunk/Includes/Diagnostics.php

    r3728745 r3729359  
    211211    }
    212212
    213     /** The double opt-in that deletes everything (Kevin's answer to review Q8). */
     213    /** The double opt-in that deletes everything (the owner's answer to review Q8). */
    214214    private static function dataRemovalRow() {
    215215        $warning = DataRemoval::warning();
  • sales-igniter-rentals-bookings-lite/trunk/Includes/Order.php

    r3728745 r3729359  
    4949
    5050    /**
     51     * Line meta: 1 = sold under "Booked by" "Days: can go out again on the return day" (its
     52     * return day free for the next rental), 0 = not (see lineReturnDayFree()).
     53     */
     54    const RETURN_DAY_META = '_sibooking_return_day_free';
     55
     56    /**
    5157     * Line meta this class writes for its own use, never for a person to read or edit.
    5258     * The admin order screen shows a line's meta keys raw unless they are hidden here, and
    … …  
    5561     * its price (`_sibooking_price`, Model\Pricing\Unified::SNAPSHOT_META).
    5662     */
    57     const HIDDEN_LINE_META = [ self::NIGHTLY_META, \Sibooking\Model\Pricing\Unified::SNAPSHOT_META ];
     63    const HIDDEN_LINE_META = [ self::NIGHTLY_META, self::RETURN_DAY_META, \Sibooking\Model\Pricing\Unified::SNAPSHOT_META ];
    5864
    5965    /**
    … …  
    235241                // a kit's children follow the parent's rule (a stay of nights frees the kayak
    236242                // on the cabin's check-out day too, Model\Stay)
    237                 $stayContext = [ 'line' => $order_item, 'parent' => self::kitParentId( $order_item ), 'nightly' => self::lineNightly( $order_item ) ];
     243                $stayContext = [ 'line' => $order_item, 'parent' => self::kitParentId( $order_item ), 'nightly' => self::lineNightly( $order_item ), 'returnDayFree' => self::lineReturnDayFree( $order_item ) ];
    238244                $startDateWithBuffer = $this->buffer->calculateBufferBeforeStartDate($order_item->get_product_id(),$order_item->get_meta('sibooking-from'), $stayContext);
    239245                // the start tells a date-only rental's end whether its return day is held (Model\Buffer)
    … …  
    300306        }
    301307        return $nightly;
     308    }
     309
     310    /**
     311     * Was this line sold with its return day free for the next rental ("Booked by" "Days: can
     312     * go out again on the return day", Model\Buffer::startOnReturnDay())? Recorded on the line
     313     * (`_sibooking_return_day_free`) the first time it is booked, like lineNightly(), so an
     314     * order edit after the product or the store changed "Booked by" keeps what was sold.
     315     *
     316     * A line booked before 1.0.15 has no record: its booking row tells (a row held to its return
     317     * day's 00:00 plus the turnover was written under the rule), else the current rule answers,
     318     * which is what such a line was always edited under.
     319     *
     320     * @param \WC_Order_Item_Product $item
     321     * @return bool
     322     */
     323    public static function lineReturnDayFree( $item ) {
     324        $stored = $item->get_meta( self::RETURN_DAY_META, true );
     325        if ( '' !== $stored && null !== $stored ) {
     326            return (bool) (int) $stored;
     327        }
     328        $parent = self::kitParentId( $item );
     329        $free   = self::returnDayFreeFromRow( $item, $parent );
     330        if ( null === $free ) {
     331            $ruleId = $parent ? $parent : (int) $item->get_product_id();
     332            $free   = \Sibooking\Model\Buffer::startOnReturnDay( \Sibooking\Model\PricingSubject::of( $ruleId ) );
     333        }
     334        if ( $item->get_id() ) {
     335            $item->update_meta_data( self::RETURN_DAY_META, $free ? '1' : '0' );
     336            $item->save_meta_data();
     337        }
     338        return $free;
     339    }
     340
     341    /**
     342     * The rule an older line's booking row was written under, from its buffered end: the end
     343     * worked out with the return day free, or held. Null when the row cannot tell (no row, a
     344     * stay of nights, a same-day rental, or a buffer changed since).
     345     *
     346     * @return bool|null
     347     */
     348    private static function returnDayFreeFromRow( $item, $parent ) {
     349        if ( ! $item->get_id() || ! $item->get_order_id() ) {
     350            return null;
     351        }
     352        $row = ( new \Sibooking\Model\Bookings() )->getBookingByOrderItemId( $item->get_order_id(), $item->get_id() );
     353        if ( ! $row || ! empty( $row->nightly ) || empty( $row->start_datetime ) || empty( $row->end_datetime ) || empty( $row->end_with_buffer_datetime ) ) {
     354            return null;
     355        }
     356        $buffer  = new \Sibooking\Model\Buffer();
     357        $context = [ 'nightly' => false, 'parent' => $parent, 'start' => (string) $row->start_datetime ];
     358        $free    = $buffer->calculateBufferAfterEndDate( $item->get_product_id(), (string) $row->end_datetime, $context + [ 'returnDayFree' => true ] );
     359        $held    = $buffer->calculateBufferAfterEndDate( $item->get_product_id(), (string) $row->end_datetime, $context + [ 'returnDayFree' => false ] );
     360        if ( $free === $held ) {
     361            return null;
     362        }
     363        $stored = substr( (string) $row->end_with_buffer_datetime, 0, 19 );
     364        if ( $stored === $free ) {
     365            return true;
     366        }
     367        return $stored === $held ? false : null;
    302368    }
    303369
    … …  
    360426     * before payment), so the order is kept -- the customer may already have paid -- and
    361427     * flagged: an order note, an admin email, and on-hold instead of processing when the
    362      * payment completes (Kevin's default for §7 Q1).
     428     * payment completes (the owner's default for §7 Q1).
    363429     *
    364430     * @param \WC_Order $order
  • sales-igniter-rentals-bookings-lite/trunk/Includes/PendingHold.php

    r3728745 r3729359  
    99 * How long an unpaid order holds its rental units (review finding DATA-08).
    1010 *
    11  * Kevin's answer to the review's Q2: "this is manual payments. It should hold stock
     11 * The owner's answer to the review's Q2: "this is manual payments. It should hold stock
    1212 * indefinitely for now." So by default nothing here ever runs: a pending or on-hold
    1313 * order keeps its units until it is cancelled, fails, is refunded, trashed or deleted,
    … …  
    4141        /**
    4242         * Minutes an unpaid (pending) order holds its rental units after checkout. 0, the
    43          * default, holds them until the order is cancelled, fails or is refunded (Kevin's
     43         * default, holds them until the order is cancelled, fails or is refunded (the owner's
    4444         * answer to the stability review's Q2: manual payments).
    4545         *
  • sales-igniter-rentals-bookings-lite/trunk/Includes/Price.php

    r3728745 r3729359  
    8585
    8686    /**
    87      * day | night: how the store words a daily rate (Rentals > Settings > Date
    88      * Formatting, "Daily rates are priced per"). The arithmetic never changes: a
    89      * stay from the 7th to the 10th is charged as 3 days and worded as 3 nights.
     87     * day | night: how the store words a daily rate of a product that is not booked by
     88     * Nights. Since 1.0.15 the word follows "Booked by" (Model\Stay: Nights says night, both
     89     * Days choices say day) and the store setting "Daily rates are priced per" (Settings >
     90     * Date Formatting) is gone from the page; a store that saved Night keeps it, hidden but
     91     * honoured, and is told once (Includes\BookedBy). The arithmetic never changes: a day
     92     * rental worded per night is still charged by the day.
    9093     */
    9194    public static function defaultPeriod() {
    … …  
    133136     * week | month | event | person.
    134137     *
    135      *   1. the product's own choice (Pricing > Price shown per), when not Automatic;
     138     *   1. the product's own choice (Pricing > Price shown per), when not Automatic (Day and
     139     *      Night are no longer offered, 1.0.15; a product saved with one keeps it);
    136140     *   2. person, for a product whose guests each take a place (Guests tab);
    137141     *   3. hour, when the product's shortest rate is an hourly one;
    138      *   4. the store's day or night.
     142     *   4. night for a product booked by Nights ("Booked by", Model\Stay);
     143     *   5. else day (or the old store wording, defaultPeriod()).
    139144     *
    140145     * @param int|string $productId
  • sales-igniter-rentals-bookings-lite/trunk/Includes/Stays.php

    r3728745 r3729359  
    1313 * and cleaning time counts in whole days. The rule is Model\Stay; this class is the rest:
    1414 *
    15  * - the settings, in this plugin's field groups (Model/ACF): "Stay type" on the store
    16  *   (Rentals > Settings > Calendar: Days | Nights) and on each date-only product (store
    17  *   default | Days | Nights), and the check-in / check-out times as text; a subscription
    18  *   product is never offered Nights;
     15 * - the settings, in this plugin's field groups (Model/ACF): "Date-only rentals are booked by"
     16 *   on the store (Rentals > Settings > Calendar: Days, return day booked | Days, out again on
     17 *   the return day | Nights) and "Booked by" on each date-only product (its Calendar Rules
     18 *   tab: store setting | the same three), and the check-in / check-out times as text, shown
     19 *   for Nights only (Includes\BookedBy has the rest of the screens); a subscription product
     20 *   is never offered Nights;
    1921 * - what the calendar is told: nights as the length unit, "Check-in" / "Check-out" as the
    2022 *   step labels unless the shop named them, the times; the day states are explained to
    … …  
    137139
    138140    /**
    139      * The Stay type shows for every date-only rental type (`sibooking_rental_types` entries
     141     * "Booked by" shows for every date-only rental type (`sibooking_rental_types` entries
    140142     * with no time of day), not only the plain Date type the field group names.
    141143     *
    … …  
    148150        }
    149151        $rows = [];
     152        foreach ( self::dateOnlyTypes() as $type ) {
     153            $rows[] = [ [ 'field' => 'field_61d28a385f491', 'operator' => '==', 'value' => $type ] ];
     154        }
     155        if ( $rows ) {
     156            $field['conditional_logic'] = $rows;
     157        }
     158        return $field;
     159    }
     160
     161    /** @return string[] the registered rental types with no time of day (`sibooking_rental_types`) */
     162    public static function dateOnlyTypes() {
     163        $types = [];
    150164        foreach ( \Sibooking\Model\RentalTypes::all() as $type => $def ) {
    151165            if ( empty( $def['time'] ) ) {
    152                 $rows[] = [ [ 'field' => 'field_61d28a385f491', 'operator' => '==', 'value' => $type ] ];
    153             }
    154         }
    155         if ( $rows ) {
    156             $field['conditional_logic'] = $rows;
    157         }
    158         return $field;
    159     }
    160 
    161     /** A product that may not be a stay (a subscription rental, `sibooking_product_allows_stay`): its Stay type offers no Nights. */
     166                $types[] = (string) $type;
     167            }
     168        }
     169        return $types;
     170    }
     171
     172    /** A product that may not be a stay (a subscription rental, `sibooking_product_allows_stay`): its Booked by offers no Nights. */
    162173    public function no_nights_for_subscriptions( $field ) {
    163174        $post = get_post(); // the product being edited
  • sales-igniter-rentals-bookings-lite/trunk/Includes/StaysSwitch.php

    r3728745 r3729359  
    165165            $n = (int) $result['done'];
    166166            /* translators: %d: number of bookings */
    167             echo '<div class="notice notice-success is-dismissible"><p>' . esc_html( sprintf( _n( '%d booking moved to the new stay type.', '%d bookings moved to the new stay type.', $n, 'sales-igniter-rentals-bookings-lite' ), $n ) ) . '</p></div>';
     167            echo '<div class="notice notice-success is-dismissible"><p>' . esc_html( sprintf( _n( '%d booking now follows the new "Booked by".', '%d bookings now follow the new "Booked by".', $n, 'sales-igniter-rentals-bookings-lite' ), $n ) ) . '</p></div>';
    168168        }
    169169        $conflictDays = isset( $result['conflicts'] ) ? implode( ', ', array_map( 'sanitize_text_field', (array) $result['conflicts'] ) ) : '';
  • sales-igniter-rentals-bookings-lite/trunk/Includes/Validation.php

    r3728745 r3729359  
    163163     * The customer's own order still waiting for payment: its booking rows are already
    164164     * written, and they must not count against the same customer's retry (review §6,
    165      * and Kevin's answer to §7 Q2: pending orders hold their units indefinitely, so a
     165     * and the owner's answer to §7 Q2: pending orders hold their units indefinitely, so a
    166166     * retried payment days later would otherwise be refused by its own reservation).
    167167     *
  • sales-igniter-rentals-bookings-lite/trunk/Languages/acf-strings.php

    r3728745 r3729359  
    2828__( "Date", 'sales-igniter-rentals-bookings-lite' );
    2929__( "Date and Time", 'sales-igniter-rentals-bookings-lite' );
    30 __( "Stay type", 'sales-igniter-rentals-bookings-lite' );
    31 __( "Nights: check-in and check-out, the check-out day can be booked again the same day, and the length is counted in nights. Days: the item comes back on the last day, which stays booked.", 'sales-igniter-rentals-bookings-lite' );
    32 __( "Use the store default", 'sales-igniter-rentals-bookings-lite' );
    33 __( "Days (the item comes back on the last day)", 'sales-igniter-rentals-bookings-lite' );
    34 __( "Nights (check-in and check-out)", 'sales-igniter-rentals-bookings-lite' );
    35 __( "Check-in from", 'sales-igniter-rentals-bookings-lite' );
    36 __( "Shown to the guest, for example \"3:00 pm\". Empty: the store's. Only for stays of nights.", 'sales-igniter-rentals-bookings-lite' );
    37 __( "Check-out by", 'sales-igniter-rentals-bookings-lite' );
    38 __( "Shown to the guest, for example \"11:00 am\". Empty: the store's.", 'sales-igniter-rentals-bookings-lite' );
    3930__( "Pricing", 'sales-igniter-rentals-bookings-lite' );
    4031__( "Fixed Price", 'sales-igniter-rentals-bookings-lite' );
    4132__( "Hourly Price", 'sales-igniter-rentals-bookings-lite' );
    4233__( "Daily Price", 'sales-igniter-rentals-bookings-lite' );
     34__( "Also charge the return day", 'sales-igniter-rentals-bookings-lite' );
     35__( "For rentals booked by the day. No: the 1st to the 3rd is 2 days (the return day is free). Yes: 3 days. Nights are charged per night.", 'sales-igniter-rentals-bookings-lite' );
     36__( "Store setting", 'sales-igniter-rentals-bookings-lite' );
     37__( "No", 'sales-igniter-rentals-bookings-lite' );
     38__( "Yes", 'sales-igniter-rentals-bookings-lite' );
    4339__( "Headline price", 'sales-igniter-rentals-bookings-lite' );
    4440__( "What the product page and the shop show as this product's price.", 'sales-igniter-rentals-bookings-lite' );
    … …  
    4642__( "Your own text", 'sales-igniter-rentals-bookings-lite' );
    4743__( "Price shown per", 'sales-igniter-rentals-bookings-lite' );
    48 __( "The headline reads \"From \$145 / night\". Week and month show what a whole week or month costs at your rates (\$449 / week). Night and event change only the word: a stay from the 7th to the 10th is 3 nights, charged as 3 days.", 'sales-igniter-rentals-bookings-lite' );
    49 __( "Automatic: the store's day or night (an hourly-only rate shows per hour)", 'sales-igniter-rentals-bookings-lite' );
     44__( "The headline reads \"From \$145 / day\", or \"/ night\" for a product booked by Nights. Week and month show what a whole week or month costs at your rates (\$449 / week). Event changes only the word.", 'sales-igniter-rentals-bookings-lite' );
     45__( "Automatic: follows \"Booked by\" (an hourly-only rate shows per hour)", 'sales-igniter-rentals-bookings-lite' );
    5046__( "Hour", 'sales-igniter-rentals-bookings-lite' );
    51 __( "Day", 'sales-igniter-rentals-bookings-lite' );
    52 __( "Night", 'sales-igniter-rentals-bookings-lite' );
    5347__( "Week", 'sales-igniter-rentals-bookings-lite' );
    5448__( "Month", 'sales-igniter-rentals-bookings-lite' );
    … …  
    7973__( "Per unit booked, or per booking when each guest takes a place. On top of the maximum guests. Empty: no limit. 0: no pets.", 'sales-igniter-rentals-bookings-lite' );
    8074__( "Calendar Rules", 'sales-igniter-rentals-bookings-lite' );
     75__( "Booked by", 'sales-igniter-rentals-bookings-lite' );
     76__( "Which days a rental with dates (no times) holds. Bookings you already have keep their rule.", 'sales-igniter-rentals-bookings-lite' );
     77__( "Days: returned on the last day, entire return day is booked", 'sales-igniter-rentals-bookings-lite' );
     78__( "Days: can go out again on the return day", 'sales-igniter-rentals-bookings-lite' );
     79__( "Nights: same-day check-out and check-in", 'sales-igniter-rentals-bookings-lite' );
     80__( "Check-in from", 'sales-igniter-rentals-bookings-lite' );
     81__( "Shown to the guest, for example \"3:00 pm\". Empty: the store's. Only for products booked by Nights.", 'sales-igniter-rentals-bookings-lite' );
     82__( "Check-out by", 'sales-igniter-rentals-bookings-lite' );
     83__( "Shown to the guest, for example \"11:00 am\". Empty: the store's.", 'sales-igniter-rentals-bookings-lite' );
    8184__( "Use Default Store Closed Days / Holidays", 'sales-igniter-rentals-bookings-lite' );
    8285__( "Store Closed Days / Holidays", 'sales-igniter-rentals-bookings-lite' );
    … …  
    118121__( "Store Open Time", 'sales-igniter-rentals-bookings-lite' );
    119122__( "Store Close Time", 'sales-igniter-rentals-bookings-lite' );
    120 __( "Stay type for date-only products", 'sales-igniter-rentals-bookings-lite' );
    121 __( "Nights: check-in and check-out; the check-out day can be booked by the next guest and lengths are counted in nights. Days: the item comes back on the last day. Each product can choose for itself.", 'sales-igniter-rentals-bookings-lite' );
    122 __( "Shown to guests of stays of nights, for example \"3:00 pm\". Text only: it does not change availability.", 'sales-igniter-rentals-bookings-lite' );
    123 __( "For example \"11:00 am\".", 'sales-igniter-rentals-bookings-lite' );
     123__( "Date-only rentals are booked by", 'sales-igniter-rentals-bookings-lite' );
     124__( "Which days a rental with dates (no times) holds.", 'sales-igniter-rentals-bookings-lite' );
     125__( "For Nights. Shown to guests, for example \"3:00 pm\". Text only: it does not change availability.", 'sales-igniter-rentals-bookings-lite' );
     126__( "For Nights. Shown to guests, for example \"11:00 am\". Text only: it does not change availability.", 'sales-igniter-rentals-bookings-lite' );
    124127__( "Allow rentals to start on the same day as they are returned", 'sales-igniter-rentals-bookings-lite' );
    125128__( "For date-only products rented by the day. On: a unit returned on a day can go out again that day, so another rental may start on the return day. Off: the return day stays with the rental that ends on it. Turnover time still applies, and rentals booked before a change keep the days they were booked with. Products rented by the night always free the check-out day; rentals with a time of day are held to the minute.", 'sales-igniter-rentals-bookings-lite' );
  • sales-igniter-rentals-bookings-lite/trunk/Languages/sales-igniter-rentals-bookings-lite.pot

    r3728745 r3729359  
    33msgid ""
    44msgstr ""
    5 "Project-Id-Version: Sales Igniter Rentals & Bookings Lite 1.0.13\n"
     5"Project-Id-Version: Sales Igniter Rentals & Bookings Lite 1.0.14\n"
    66"Report-Msgid-Bugs-To: https://rentalbookingsoftware.com/support/\n"
    77"Last-Translator: Sales Igniter <support@rentalbookingsoftware.com>\n"
    … …  
    1010"Content-Type: text/plain; charset=UTF-8\n"
    1111"Content-Transfer-Encoding: 8bit\n"
    12 "POT-Creation-Date: 2026-10-05T08:35:41+00:00\n"
     12"POT-Creation-Date: 2026-10-05T21:48:27+08:00\n"
    1313"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
    1414"X-Generator: WP-CLI 2.12.0\n"
    … …  
    4646#: Includes/DatesCart.php:573
    4747#: Includes/DatesCart.php:639
    48 #: Languages/acf-strings.php:92
     48#: Languages/acf-strings.php:95
    4949#: Model/CalendarStyle.php:212
    5050#: Assets/public/js/sibookingCalendarUi.js:99
    … …  
    5757#: Includes/DatesCart.php:578
    5858#: Includes/DatesCart.php:642
    59 #: Languages/acf-strings.php:93
     59#: Languages/acf-strings.php:96
    6060#: Model/CalendarStyle.php:214
    6161#: Assets/public/js/sibookingCalendarUi.js:101
    … …  
    7373#: Admin/InventoryReport/InventoryReport.php:14
    7474#: Admin/InventoryReport/InventoryReport.php:21
    75 #: Languages/acf-strings.php:217
     75#: Languages/acf-strings.php:220
    7676msgid "Inventory Report"
    7777msgstr ""
    … …  
    151151#: Ajax/InventoryReport.php:172
    152152#: Ajax/InventoryReport.php:213
    153 #: Languages/acf-strings.php:210
     153#: Languages/acf-strings.php:213
    154154msgid "Product"
    155155msgstr ""
    … …  
    244244#: Admin/Ui/Format.php:41
    245245#: Admin/Ui/Format.php:45
    246 #: Includes/AdminOrderDates.php:483
     246#: Includes/AdminOrderDates.php:487
    247247#: Includes/DatesCart.php:556
    248 #: Languages/acf-strings.php:58
     248#: Languages/acf-strings.php:52
    249249#: Model/Guests.php:113
    250250msgid "Guests"
    … …  
    374374#: Admin/CalendarReport/CalendarReport.php:80
    375375#: Admin/InventoryReport/InventoryReport.php:34
    376 #: Languages/acf-strings.php:209
     376#: Languages/acf-strings.php:212
    377377msgid "Filters"
    378378msgstr ""
    … …  
    419419
    420420#: Admin/CalendarStyler.php:109
    421 #: Languages/acf-strings.php:117
     421#: Languages/acf-strings.php:120
    422422msgid "Calendar"
    423423msgstr ""
    … …  
    657657#: Admin/DataRemovalSettings.php:87
    658658#: Ajax/InventoryReport.php:232
     659#: Includes/BookedBy.php:200
     660#: Languages/acf-strings.php:38
    659661msgid "Yes"
    660662msgstr ""
    … …  
    662664#: Admin/DataRemovalSettings.php:87
    663665#: Ajax/InventoryReport.php:234
     666#: Includes/BookedBy.php:201
     667#: Languages/acf-strings.php:37
    664668msgid "No"
    665669msgstr ""
    … …  
    835839
    836840#: Admin/Kits/KitsPage.php:202
    837 #: Languages/acf-strings.php:211
     841#: Languages/acf-strings.php:214
    838842msgid "Category"
    839843msgstr ""
    … …  
    11621166
    11631167#: Admin/Kits/KitsPage.php:475
    1164 #: Languages/acf-strings.php:87
     1168#: Languages/acf-strings.php:90
    11651169msgid "None"
    11661170msgstr ""
    … …  
    12541258#: Admin/Ui/ListShell.php:105
    12551259#: Admin/Ui/ListShell.php:175
    1256 #: Languages/acf-strings.php:218
     1260#: Languages/acf-strings.php:221
    12571261msgid "Search"
    12581262msgstr ""
    … …  
    12951299
    12961300#: Admin/Upgrade.php:92
    1297 msgid "Extras for nightly stays: half-day cells for partly booked days, a Stay type bulk action, room-nights in the Usage Report"
     1301msgid "Extras for nightly stays: half-day cells for partly booked days, a \"Booked by\" bulk action, room-nights in the Usage Report"
    12981302msgstr ""
    12991303
    … …  
    13351339
    13361340#: Admin/Upgrade.php:103
    1337 #: Languages/acf-strings.php:39
     1341#: Languages/acf-strings.php:30
    13381342msgid "Pricing"
    13391343msgstr ""
    … …  
    15551559msgstr ""
    15561560
    1557 #: Ajax/Calendar.php:777
     1561#: Ajax/Calendar.php:781
    15581562msgid "Closed for stocktake"
    15591563msgstr ""
    … …  
    17101714#. translators: %d: a number of days
    17111715#: Ajax/InventoryReport.php:257
    1712 #: Includes/Price.php:199
     1716#: Includes/Price.php:204
    17131717#: Model/Reminders/Rule.php:140
    17141718#: Assets/public/js/sibookingCalendarUi.js:1234
    … …  
    17281732
    17291733#. translators: %s: hours
    1730 #: Ajax/Price.php:270
     1734#: Ajax/Price.php:275
    17311735#, php-format
    17321736msgid "%s hour"
    … …  
    17351739msgstr[1] ""
    17361740
    1737 #: Ajax/Price.php:343
     1741#: Ajax/Price.php:348
    17381742#: Model/Pricing/Unified.php:264
    17391743msgid "Rental"
    17401744msgstr ""
    17411745
    1742 #: Ajax/Price.php:345
     1746#: Ajax/Price.php:350
    17431747msgid "Discount"
    17441748msgstr ""
    17451749
    1746 #: Ajax/Price.php:345
     1750#: Ajax/Price.php:350
    17471751msgid "Rate adjustment"
    17481752msgstr ""
    … …  
    18141818
    18151819#: Includes/AdminOrderDates.php:131
    1816 #: Includes/AdminOrderDates.php:368
     1820#: Includes/AdminOrderDates.php:372
    18171821msgid "Set the start and end times"
    18181822msgstr ""
    … …  
    19391943msgstr[1] ""
    19401944
    1941 #: Includes/AdminOrderDates.php:363
     1945#: Includes/AdminOrderDates.php:367
    19421946msgid "Pick a start and an end date"
    19431947msgstr ""
    19441948
    1945 #: Includes/AdminOrderDates.php:377
     1949#: Includes/AdminOrderDates.php:381
    19461950msgid "The end must be after the start"
    19471951msgstr ""
    19481952
    1949 #: Includes/AdminOrderDates.php:380
     1953#: Includes/AdminOrderDates.php:384
    19501954#: Includes/CartAvailability.php:198
    19511955msgid "Those dates could not be read"
    19521956msgstr ""
    19531957
    1954 #: Includes/AdminOrderDates.php:564
    1955 #: Includes/AdminOrderDates.php:1381
     1958#: Includes/AdminOrderDates.php:569
     1959#: Includes/AdminOrderDates.php:1388
    19561960msgid "The start or end date falls on a closed day"
    19571961msgstr ""
    19581962
    19591963#. translators: 1: units available, 2: units needed
    1960 #: Includes/AdminOrderDates.php:584
    1961 #: Includes/AdminOrderDates.php:935
     1964#: Includes/AdminOrderDates.php:590
     1965#: Includes/AdminOrderDates.php:941
    19621966#, php-format
    19631967msgid "Only %1$d of the %2$d units needed is free for these dates"
    … …  
    19661970msgstr[1] ""
    19671971
    1968 #: Includes/AdminOrderDates.php:728
    1969 #: Includes/AdminOrderDates.php:1035
     1972#: Includes/AdminOrderDates.php:734
     1973#: Includes/AdminOrderDates.php:1041
    19701974msgid "the system"
    19711975msgstr ""
    19721976
    19731977#. translators: 1: line item name, 2: old party, 3: new party, 4: user
    1974 #: Includes/AdminOrderDates.php:734
     1978#: Includes/AdminOrderDates.php:740
    19751979#, php-format
    19761980msgid "Guests for %1$s changed from %2$s to %3$s by %4$s."
    19771981msgstr ""
    19781982
    1979 #: Includes/AdminOrderDates.php:736
     1983#: Includes/AdminOrderDates.php:742
    19801984msgid "(none)"
    19811985msgstr ""
    19821986
    19831987#. translators: 1 item name, 2 old dates, 3 new dates, 4 user
    1984 #: Includes/AdminOrderDates.php:744
     1988#: Includes/AdminOrderDates.php:750
    19851989#, php-format
    19861990msgid "Rental dates for %1$s changed from %2$s to %3$s by %4$s."
    19871991msgstr ""
    19881992
    1989 #: Includes/AdminOrderDates.php:746
    1990 #: Includes/AdminOrderDates.php:1047
     1993#: Includes/AdminOrderDates.php:752
     1994#: Includes/AdminOrderDates.php:1053
    19911995msgid "(no dates)"
    19921996msgstr ""
    19931997
    19941998#. translators: 1: line item name, 2: user name
    1995 #: Includes/AdminOrderDates.php:754
    1996 #: Includes/AdminOrderDates.php:1064
     1999#: Includes/AdminOrderDates.php:760
     2000#: Includes/AdminOrderDates.php:1070
    19972001#, php-format
    19982002msgid "Rental line %1$s re-priced by %2$s."
    … …  
    20002004
    20012005#. translators: 1: old total, 2: new total
    2002 #: Includes/AdminOrderDates.php:760
    2003 #: Includes/AdminOrderDates.php:1069
     2006#: Includes/AdminOrderDates.php:766
     2007#: Includes/AdminOrderDates.php:1075
    20042008#, php-format
    20052009msgid "Line total %1$s → %2$s."
    … …  
    20072011
    20082012#. translators: 1: current total, 2: price for the new dates
    2009 #: Includes/AdminOrderDates.php:762
    2010 #: Includes/AdminOrderDates.php:1071
     2013#: Includes/AdminOrderDates.php:768
     2014#: Includes/AdminOrderDates.php:1077
    20112015#, php-format
    20122016msgid "Line total left at %1$s (price for the new dates would be %2$s)."
    … …  
    20162020#. translators: 1: a product name, 2: why it could not be added to the cart
    20172021#. translators: 1: a product name, 2: why it cannot be booked ("Only 2 left on your dates")
    2018 #: Includes/AdminOrderDates.php:906
    2019 #: Includes/AdminOrderDates.php:929
     2022#: Includes/AdminOrderDates.php:912
     2023#: Includes/AdminOrderDates.php:935
    20202024#: Includes/GroupedRentals.php:512
    20212025#: Assets/public/js/sibooking-grouped.js:116
    … …  
    20252029msgstr ""
    20262030
    2027 #: Includes/AdminOrderDates.php:1026
     2031#: Includes/AdminOrderDates.php:1032
    20282032msgid "The new dates could not be saved, so nothing was changed. Try again."
    20292033msgstr ""
    20302034
    20312035#. translators: 1: the composite's or bundle's line name, 2: its parts, e.g. "Kayak × 2, Paddle set × 2", 3: old dates, 4: new dates, 5: user
    2032 #: Includes/AdminOrderDates.php:1044
     2036#: Includes/AdminOrderDates.php:1050
    20332037#, php-format
    20342038msgid "Rental dates for %1$s and its parts (%2$s) changed from %3$s to %4$s by %5$s."
    20352039msgstr ""
    20362040
    2037 #: Includes/AdminOrderDates.php:1052
     2041#: Includes/AdminOrderDates.php:1058
    20382042msgid "Parts that were on other dates now follow it."
    20392043msgstr ""
    20402044
    20412045#. translators: 1: the parts that were on other dates, 2: the composite's or bundle's line name, 3: its dates, 4: user
    2042 #: Includes/AdminOrderDates.php:1057
     2046#: Includes/AdminOrderDates.php:1063
    20432047#, php-format
    20442048msgid "Rental dates for %1$s put back on the dates of %2$s (%3$s) by %4$s."
    … …  
    20462050
    20472051#. translators: %d: number of order lines
    2048 #: Includes/AdminOrderDates.php:1463
     2052#: Includes/AdminOrderDates.php:1470
    20492053#, php-format
    20502054msgid "Rental dates updated on %d line."
    … …  
    20532057msgstr[1] ""
    20542058
    2055 #: Includes/AdminOrderDates.php:1466
     2059#: Includes/AdminOrderDates.php:1473
    20562060msgid "Rental dates not changed —"
    20572061msgstr ""
    … …  
    20902094msgstr ""
    20912095
     2096#: Includes/BookedBy.php:74
     2097#: Languages/acf-strings.php:77
     2098msgid "Days: returned on the last day, entire return day is booked"
     2099msgstr ""
     2100
     2101#: Includes/BookedBy.php:75
     2102#: Languages/acf-strings.php:78
     2103msgid "Days: can go out again on the return day"
     2104msgstr ""
     2105
     2106#: Includes/BookedBy.php:76
     2107#: Languages/acf-strings.php:79
     2108msgid "Nights: same-day check-out and check-in"
     2109msgstr ""
     2110
     2111#: Includes/BookedBy.php:83
     2112msgid "The next rental can start the day after."
     2113msgstr ""
     2114
     2115#: Includes/BookedBy.php:84
     2116msgid "A rental can start on the day the last one comes back."
     2117msgstr ""
     2118
     2119#: Includes/BookedBy.php:85
     2120msgid "Like a hotel: the check-out day can be the next guest's check-in day. Prices and lengths read \"per night\"."
     2121msgstr ""
     2122
     2123#: Includes/BookedBy.php:93
     2124#: Includes/Stays.php:206
     2125#: Includes/Stays.php:245
     2126msgid "Nights"
     2127msgstr ""
     2128
     2129#: Includes/BookedBy.php:95
     2130msgid "Days, out again on the return day"
     2131msgstr ""
     2132
     2133#: Includes/BookedBy.php:97
     2134msgid "Days, return day booked"
     2135msgstr ""
     2136
     2137#. translators: %s: the store's "Booked by" choice, e.g. "Nights"
     2138#. translators: %s: the store's answer, Yes or No
     2139#: Includes/BookedBy.php:104
     2140#: Includes/BookedBy.php:203
     2141#, php-format
     2142msgid "Store setting (%s)"
     2143msgstr ""
     2144
     2145#: Includes/BookedBy.php:109
     2146msgid "Products can choose their own. What the return day costs is set under Pricing › \"Also charge the return day\". Bookings you already have keep their rule."
     2147msgstr ""
     2148
     2149#. translators: an old "Price shown per" choice kept on a product
     2150#: Includes/BookedBy.php:223
     2151msgid "Day (old setting)"
     2152msgstr ""
     2153
     2154#. translators: an old "Price shown per" choice kept on a product
     2155#: Includes/BookedBy.php:225
     2156msgid "Night (old setting)"
     2157msgstr ""
     2158
     2159#: Includes/BookedBy.php:229
     2160msgid "Day and Night are no longer choices: Automatic follows \"Booked by\". This product keeps its old setting until you change it."
     2161msgstr ""
     2162
     2163#: Includes/BookedBy.php:257
     2164msgid "Your prices say \"per night\""
     2165msgstr ""
     2166
     2167#: Includes/BookedBy.php:258
     2168msgid "The setting \"Daily rates are priced per: Night\" is gone: \"night\" or \"day\" now follows \"Booked by\", and your prices keep saying \"per night\" for now. If you rent by the night, set \"Date-only rentals are booked by\" to Nights: the check-out day can then be the next guest's check-in day."
     2169msgstr ""
     2170
     2171#: Includes/BookedBy.php:259
     2172msgid "Open \"Booked by\""
     2173msgstr ""
     2174
     2175#: Includes/BookedBy.php:260
     2176msgid "Say \"per day\" instead"
     2177msgstr ""
     2178
     2179#: Includes/BookedBy.php:261
     2180#: Model/AdminAlerts.php:78
     2181msgid "Dismiss"
     2182msgstr ""
     2183
     2184#: Includes/BookedBy.php:268
     2185#: Model/AdminAlerts.php:85
     2186msgid "Sorry, you are not allowed to do that."
     2187msgstr ""
     2188
    20922189#: Includes/Calendar.php:84
    20932190msgid "First choose product options to load the booking calendar"
    … …  
    22802377
    22812378#: Includes/Diagnostics.php:179
    2282 #: Languages/acf-strings.php:167
     2379#: Languages/acf-strings.php:170
    22832380msgid "Debug logging"
    22842381msgstr ""
    … …  
    23332430
    23342431#: Includes/Diagnostics.php:216
    2335 #: Languages/acf-strings.php:171
     2432#: Languages/acf-strings.php:174
    23362433msgid "Delete rental data on uninstall"
    23372434msgstr ""
    … …  
    24182515
    24192516#: Includes/Emails/Pickup.php:44
    2420 #: Languages/acf-strings.php:175
     2517#: Languages/acf-strings.php:178
    24212518#: Model/Emails/Defaults.php:177
    24222519msgid "Pickup Reminder"
    … …  
    24562553#: Includes/Emails/Pickup.php:150
    24572554#: Includes/Emails/Reminder.php:131
    2458 #: Languages/acf-strings.php:190
     2555#: Languages/acf-strings.php:193
    24592556msgid "Subject"
    24602557msgstr ""
    … …  
    24622559#: Includes/Emails/Pickup.php:158
    24632560#: Includes/Emails/Reminder.php:139
    2464 #: Languages/acf-strings.php:192
     2561#: Languages/acf-strings.php:195
    24652562msgid "Email heading"
    24662563msgstr ""
    … …  
    24682565#: Includes/Emails/Pickup.php:167
    24692566#: Includes/Emails/Reminder.php:148
    2470 #: Languages/acf-strings.php:199
     2567#: Languages/acf-strings.php:202
    24712568msgid "Additional content"
    24722569msgstr ""
    … …  
    24842581#: Includes/Emails/Pickup.php:176
    24852582#: Includes/Emails/Reminder.php:157
    2486 #: Languages/acf-strings.php:203
     2583#: Languages/acf-strings.php:206
    24872584msgid "Email type"
    24882585msgstr ""
    … …  
    24942591
    24952592#: Includes/Emails/Reminder.php:41
    2496 #: Languages/acf-strings.php:184
     2593#: Languages/acf-strings.php:187
    24972594#: Model/Emails/Defaults.php:168
    24982595#: Model/Emails/Defaults.php:179
    … …  
    25052602
    25062603#: Includes/Emails/ReminderBody.php:124
    2507 #: Languages/acf-strings.php:194
     2604#: Languages/acf-strings.php:197
    25082605msgid "Email body"
    25092606msgstr ""
    … …  
    25862683
    25872684#. translators: %s: products and dates
    2588 #: Includes/Order.php:421
     2685#: Includes/Order.php:487
    25892686#, php-format
    25902687msgid "Another order booked the same units at the same moment: %s is now booked beyond its stock. The order will be put on hold when it is paid. Contact the customer to change the dates, or free a unit."
    … …  
    25922689
    25932690#. translators: %d: order number
    2594 #: Includes/Order.php:425
     2691#: Includes/Order.php:491
    25952692#, php-format
    25962693msgid "Order #%d was booked at the same moment as another order and some of its rentals are over the stock. It will be on hold; see the order note."
    … …  
    25982695
    25992696#. translators: %d: order number
    2600 #: Includes/Order.php:431
     2697#: Includes/Order.php:497
    26012698#, php-format
    26022699msgid "Rental double booking on order #%d"
    … …  
    26042701
    26052702#. translators: 1: order number, 2: products and dates, 3: link to the order
    2606 #: Includes/Order.php:433
     2703#: Includes/Order.php:499
    26072704#, php-format
    26082705msgid ""
    … …  
    26152712
    26162713#. translators: 1: product name, 2: the rental dates
    2617 #: Includes/Order.php:635
     2714#: Includes/Order.php:701
    26182715#, php-format
    26192716msgid "The rental booking for %1$s (%2$s) could not be saved, so these dates are not reserved. Add the booking by hand under Rentals > Bookings, or edit the dates on this order to try again."
    … …  
    26212718
    26222719#. translators: %d: order number
    2623 #: Includes/Order.php:639
     2720#: Includes/Order.php:705
    26242721#, php-format
    26252722msgid "A rental booking could not be saved for order #%d, so its dates are not reserved. The order has a note with the details."
    … …  
    26322729msgstr ""
    26332730
    2634 #: Includes/Price.php:170
    2635 #: Includes/Price.php:395
    2636 #: Includes/Price.php:1017
     2731#: Includes/Price.php:175
     2732#: Includes/Price.php:400
     2733#: Includes/Price.php:1022
    26372734msgid "hour"
    26382735msgstr ""
    26392736
    2640 #: Includes/Price.php:171
     2737#: Includes/Price.php:176
    26412738msgid "night"
    26422739msgstr ""
    26432740
    2644 #: Includes/Price.php:172
     2741#: Includes/Price.php:177
    26452742msgid "week"
    26462743msgstr ""
    26472744
    2648 #: Includes/Price.php:173
     2745#: Includes/Price.php:178
    26492746msgid "month"
    26502747msgstr ""
    26512748
    26522749#. translators: the price covers one event (a party, a wedding)
    2653 #: Includes/Price.php:175
     2750#: Includes/Price.php:180
    26542751msgid "event"
    26552752msgstr ""
    26562753
    26572754#. translators: the price is per guest
    2658 #: Includes/Price.php:177
     2755#: Includes/Price.php:182
    26592756msgid "person"
    26602757msgstr ""
    26612758
    2662 #: Includes/Price.php:179
    2663 #: Includes/Price.php:395
    2664 #: Includes/Price.php:1017
     2759#: Includes/Price.php:184
     2760#: Includes/Price.php:400
     2761#: Includes/Price.php:1022
    26652762msgid "day"
    26662763msgstr ""
    26672764
    26682765#. translators: %d: number of nights
    2669 #: Includes/Price.php:197
     2766#: Includes/Price.php:202
    26702767#: Assets/public/js/sibookingCalendarUi.js:1252
    26712768#, php-format,js-format
    … …  
    26762773
    26772774#. translators: 1: price, 2: "day" or "hour"
    2678 #: Includes/Price.php:399
     2775#: Includes/Price.php:404
    26792776#, php-format
    26802777msgctxt "admin product list price"
    … …  
    26832780
    26842781#. translators: 1: price, 2: "day" or "hour"
    2685 #: Includes/Price.php:403
     2782#: Includes/Price.php:408
    26862783#, php-format
    26872784msgctxt "admin product list price"
    … …  
    26902787
    26912788#. translators: %d: number of rental rate tiers
    2692 #: Includes/Price.php:408
     2789#: Includes/Price.php:413
    26932790#, php-format
    26942791msgid "(%d rate)"
    … …  
    26972794msgstr[1] ""
    26982795
    2699 #: Includes/Price.php:471
    2700 #: Languages/acf-strings.php:50
     2796#: Includes/Price.php:476
     2797#: Languages/acf-strings.php:46
    27012798msgid "Hour"
    27022799msgid_plural "Hours"
    … …  
    27042801msgstr[1] ""
    27052802
    2706 #: Includes/Price.php:473
    2707 #: Languages/acf-strings.php:51
     2803#: Includes/Price.php:478
    27082804msgid "Day"
    27092805msgid_plural "Days"
    … …  
    27112807msgstr[1] ""
    27122808
    2713 #: Includes/Price.php:476
    2714 #: Languages/acf-strings.php:52
     2809#: Includes/Price.php:481
    27152810msgid "Night"
    27162811msgid_plural "Nights"
    … …  
    27182813msgstr[1] ""
    27192814
    2720 #: Includes/Price.php:478
    2721 #: Languages/acf-strings.php:53
     2815#: Includes/Price.php:483
     2816#: Languages/acf-strings.php:47
    27222817msgid "Week"
    27232818msgid_plural "Weeks"
    … …  
    27252820msgstr[1] ""
    27262821
    2727 #: Includes/Price.php:480
    2728 #: Languages/acf-strings.php:54
     2822#: Includes/Price.php:485
     2823#: Languages/acf-strings.php:48
    27292824msgid "Month"
    27302825msgid_plural "Months"
    … …  
    27322827msgstr[1] ""
    27332828
    2734 #: Includes/Price.php:482
     2829#: Includes/Price.php:487
    27352830msgid "Year"
    27362831msgid_plural "Years"
    … …  
    27392834
    27402835#. translators: %s: product name
    2741 #: Includes/Price.php:713
     2836#: Includes/Price.php:718
    27422837#, php-format
    27432838msgid "%s was removed from your cart because its price could not be worked out for those dates. Please add it again with other dates, or contact us."
    … …  
    27452840
    27462841#. translators: %s: product name
    2747 #: Includes/Price.php:713
     2842#: Includes/Price.php:718
    27482843msgid "A rental was removed from your cart because its price could not be worked out for those dates. Please add it again with other dates, or contact us."
    27492844msgstr ""
    27502845
    2751 #: Includes/Price.php:867
    2752 #: Includes/Price.php:971
     2846#: Includes/Price.php:872
     2847#: Includes/Price.php:976
    27532848msgid "Per hour"
    27542849msgstr ""
    27552850
    2756 #: Includes/Price.php:870
    2757 #: Includes/Price.php:1053
     2851#: Includes/Price.php:875
     2852#: Includes/Price.php:1058
    27582853msgid "Per night"
    27592854msgstr ""
    27602855
    2761 #: Includes/Price.php:870
    2762 #: Includes/Price.php:972
    2763 #: Includes/Price.php:1055
     2856#: Includes/Price.php:875
     2857#: Includes/Price.php:977
     2858#: Includes/Price.php:1060
    27642859msgid "Per day"
    27652860msgstr ""
    27662861
    27672862#. translators: 1: price per night, 2: period label
    2768 #: Includes/Price.php:905
     2863#: Includes/Price.php:910
    27692864#, php-format
    27702865msgid "As low as %1$s / night on %2$s"
    … …  
    27722867
    27732868#. translators: 1: price per day, 2: period label
    2774 #: Includes/Price.php:906
     2869#: Includes/Price.php:911
    27752870#, php-format
    27762871msgid "As low as %1$s / day on %2$s"
    27772872msgstr ""
    27782873
    2779 #: Includes/Price.php:1021
     2874#: Includes/Price.php:1026
    27802875#: Includes/RentBuy.php:233
    2781 #: Languages/acf-strings.php:84
     2876#: Languages/acf-strings.php:87
    27822877msgid "From"
    27832878msgstr ""
    27842879
    2785 #: Includes/Price.php:1031
     2880#: Includes/Price.php:1036
    27862881msgid "Booked out"
    27872882msgstr ""
    27882883
    27892884#. translators: %d: units available
    2790 #: Includes/Price.php:1031
     2885#: Includes/Price.php:1036
    27912886#, php-format
    27922887msgid "%d unit available"
    … …  
    27952890msgstr[1] ""
    27962891
    2797 #: Includes/Price.php:1044
     2892#: Includes/Price.php:1049
    27982893msgid "Rates"
    27992894msgstr ""
    28002895
    2801 #: Includes/Price.php:1053
     2896#: Includes/Price.php:1058
    28022897msgid "Per night / extra"
    28032898msgstr ""
    28042899
    2805 #: Includes/Price.php:1055
     2900#: Includes/Price.php:1060
    28062901msgid "Per day / extra"
    28072902msgstr ""
    28082903
    28092904#. translators: 1: add-on price, 2: add-on period
    2810 #: Includes/Price.php:1065
     2905#: Includes/Price.php:1070
    28112906#, php-format
    28122907msgid "then %1$s / extra %2$s"
    … …  
    28142909
    28152910#. translators: %s: price
    2816 #: Includes/Price.php:1067
     2911#: Includes/Price.php:1072
    28172912#, php-format
    28182913msgid "%s / hour"
    … …  
    28202915
    28212916#. translators: %s: price
    2822 #: Includes/Price.php:1070
     2917#: Includes/Price.php:1075
    28232918#, php-format
    28242919msgid "%s / night"
    … …  
    28262921
    28272922#. translators: %s: price
    2828 #: Includes/Price.php:1071
     2923#: Includes/Price.php:1076
    28292924#, php-format
    28302925msgid "%s / day"
    28312926msgstr ""
    28322927
    2833 #: Includes/Price.php:1076
     2928#: Includes/Price.php:1081
    28342929msgid "Period"
    28352930msgstr ""
    28362931
    2837 #: Includes/Price.php:1076
     2932#: Includes/Price.php:1081
    28382933msgid "Price"
    28392934msgstr ""
    … …  
    28782973msgstr ""
    28792974
    2880 #: Includes/Stays.php:105
    2881 #: Includes/Stays.php:192
    2882 #: Includes/Stays.php:225
     2975#: Includes/Stays.php:107
     2976#: Includes/Stays.php:203
     2977#: Includes/Stays.php:236
    28832978msgid "Check-in"
    28842979msgstr ""
    28852980
    2886 #: Includes/Stays.php:108
    2887 #: Includes/Stays.php:193
    2888 #: Includes/Stays.php:229
     2981#: Includes/Stays.php:110
     2982#: Includes/Stays.php:204
     2983#: Includes/Stays.php:240
    28892984msgid "Check-out"
    28902985msgstr ""
    28912986
    2892 #: Includes/Stays.php:166
     2987#: Includes/Stays.php:177
    28932988msgid "Subscription rentals always count in days."
    28942989msgstr ""
    28952990
    2896 #: Includes/Stays.php:195
    2897 #: Includes/Stays.php:234
    2898 msgid "Nights"
    2899 msgstr ""
    2900 
    2901 #: Includes/Stays.php:284
     2991#: Includes/Stays.php:295
    29022992msgid "Whether the line is a stay of nights."
    29032993msgstr ""
    29042994
    2905 #: Includes/Stays.php:285
     2995#: Includes/Stays.php:296
    29062996msgid "Check-in date (Y-m-d)."
    29072997msgstr ""
    29082998
    2909 #: Includes/Stays.php:286
     2999#: Includes/Stays.php:297
    29103000msgid "Check-out date (Y-m-d), the morning the stay ends."
    29113001msgstr ""
    29123002
    2913 #: Includes/Stays.php:287
     3003#: Includes/Stays.php:298
    29143004msgid "Number of nights."
    29153005msgstr ""
    29163006
    2917 #: Includes/Stays.php:345
     3007#: Includes/Stays.php:356
    29183008msgid "For a stay of nights: how many nights; empty otherwise"
    29193009msgstr ""
    29203010
    2921 #: Includes/Stays.php:346
     3011#: Includes/Stays.php:357
    29223012msgid "For a stay of nights: the check-in date"
    29233013msgstr ""
    29243014
    2925 #: Includes/Stays.php:347
     3015#: Includes/Stays.php:358
    29263016msgid "For a stay of nights: the check-out date"
    29273017msgstr ""
    29283018
    2929 #: Includes/Stays.php:348
     3019#: Includes/Stays.php:359
    29303020msgid "The check-in time you set (Rentals > Settings), e.g. 3:00 pm"
    29313021msgstr ""
    29323022
    2933 #: Includes/Stays.php:349
     3023#: Includes/Stays.php:360
    29343024msgid "The check-out time you set, e.g. 11:00 am"
    29353025msgstr ""
    29363026
    29373027#. translators: %s: the check-in date
    2938 #: Includes/Stays.php:373
     3028#: Includes/Stays.php:384
    29393029#, php-format
    29403030msgid "Check-in: %s"
    … …  
    29423032
    29433033#. translators: %s: the check-out date
    2944 #: Includes/Stays.php:375
     3034#: Includes/Stays.php:386
    29453035#, php-format
    29463036msgid "Check-out: %s"
    … …  
    29503040#: Includes/StaysSwitch.php:167
    29513041#, php-format
    2952 msgid "%d booking moved to the new stay type."
    2953 msgid_plural "%d bookings moved to the new stay type."
     3042msgid "%d booking now follows the new \"Booked by\"."
     3043msgid_plural "%d bookings now follow the new \"Booked by\"."
    29543044msgstr[0] ""
    29553045msgstr[1] ""
    … …  
    30253115
    30263116#: Includes/Validation.php:103
    3027 #: Model/Availability.php:76
     3117#: Model/Availability.php:78
    30283118msgid "Not enough inventory for the dates selected"
    30293119msgstr ""
    … …  
    30773167msgstr ""
    30783168
    3079 #: Languages/acf-strings.php:30
    3080 msgid "Stay type"
    3081 msgstr ""
    3082 
    30833169#: Languages/acf-strings.php:31
    3084 msgid "Nights: check-in and check-out, the check-out day can be booked again the same day, and the length is counted in nights. Days: the item comes back on the last day, which stays booked."
     3170msgid "Fixed Price"
    30853171msgstr ""
    30863172
    30873173#: Languages/acf-strings.php:32
    3088 msgid "Use the store default"
     3174msgid "Hourly Price"
    30893175msgstr ""
    30903176
    30913177#: Languages/acf-strings.php:33
    3092 msgid "Days (the item comes back on the last day)"
     3178msgid "Daily Price"
    30933179msgstr ""
    30943180
    30953181#: Languages/acf-strings.php:34
    3096 msgid "Nights (check-in and check-out)"
     3182msgid "Also charge the return day"
    30973183msgstr ""
    30983184
    30993185#: Languages/acf-strings.php:35
    3100 msgid "Check-in from"
     3186msgid "For rentals booked by the day. No: the 1st to the 3rd is 2 days (the return day is free). Yes: 3 days. Nights are charged per night."
    31013187msgstr ""
    31023188
    31033189#: Languages/acf-strings.php:36
    3104 msgid "Shown to the guest, for example \"3:00 pm\". Empty: the store's. Only for stays of nights."
    3105 msgstr ""
    3106 
    3107 #: Languages/acf-strings.php:37
    3108 msgid "Check-out by"
    3109 msgstr ""
    3110 
    3111 #: Languages/acf-strings.php:38
    3112 msgid "Shown to the guest, for example \"11:00 am\". Empty: the store's."
     3190msgid "Store setting"
     3191msgstr ""
     3192
     3193#: Languages/acf-strings.php:39
     3194msgid "Headline price"
    31133195msgstr ""
    31143196
    31153197#: Languages/acf-strings.php:40
    3116 msgid "Fixed Price"
     3198msgid "What the product page and the shop show as this product's price."
    31173199msgstr ""
    31183200
    31193201#: Languages/acf-strings.php:41
    3120 msgid "Hourly Price"
     3202msgid "From the rates (From $50 / day)"
    31213203msgstr ""
    31223204
    31233205#: Languages/acf-strings.php:42
    3124 msgid "Daily Price"
     3206msgid "Your own text"
    31253207msgstr ""
    31263208
    31273209#: Languages/acf-strings.php:43
    3128 msgid "Headline price"
     3210msgid "Price shown per"
    31293211msgstr ""
    31303212
    31313213#: Languages/acf-strings.php:44
    3132 msgid "What the product page and the shop show as this product's price."
     3214msgid "The headline reads \"From $145 / day\", or \"/ night\" for a product booked by Nights. Week and month show what a whole week or month costs at your rates ($449 / week). Event changes only the word."
    31333215msgstr ""
    31343216
    31353217#: Languages/acf-strings.php:45
    3136 msgid "From the rates (From $50 / day)"
    3137 msgstr ""
    3138 
    3139 #: Languages/acf-strings.php:46
    3140 msgid "Your own text"
    3141 msgstr ""
    3142 
    3143 #: Languages/acf-strings.php:47
    3144 msgid "Price shown per"
    3145 msgstr ""
    3146 
    3147 #: Languages/acf-strings.php:48
    3148 msgid "The headline reads \"From $145 / night\". Week and month show what a whole week or month costs at your rates ($449 / week). Night and event change only the word: a stay from the 7th to the 10th is 3 nights, charged as 3 days."
     3218msgid "Automatic: follows \"Booked by\" (an hourly-only rate shows per hour)"
    31493219msgstr ""
    31503220
    31513221#: Languages/acf-strings.php:49
    3152 msgid "Automatic: the store's day or night (an hourly-only rate shows per hour)"
     3222msgid "Event"
     3223msgstr ""
     3224
     3225#: Languages/acf-strings.php:50
     3226msgid "Your own price text"
     3227msgstr ""
     3228
     3229#: Languages/acf-strings.php:51
     3230msgid "Replaces the price wherever a customer sees it. HTML is allowed. {price} is the headline amount and {period} its period, for example: <strong>{price}</strong> per {period}, cleaning included."
     3231msgstr ""
     3232
     3233#: Languages/acf-strings.php:53
     3234msgid "Off leaves the product as it is: the quantity is the number of units. \"Each unit holds guests\" checks the party against the maximum per unit: a cottage that sleeps 4, or a room type for 2 with 5 in stock, where 5 guests need 3 rooms. \"Each guest takes one place\" makes the quantity the number of guests: seats on a cruise or a tour."
     3235msgstr ""
     3236
     3237#: Languages/acf-strings.php:54
     3238msgid "Off: the quantity is all that is asked"
    31533239msgstr ""
    31543240
    31553241#: Languages/acf-strings.php:55
    3156 msgid "Event"
     3242msgid "Each unit holds up to a number of guests"
    31573243msgstr ""
    31583244
    31593245#: Languages/acf-strings.php:56
    3160 msgid "Your own price text"
     3246msgid "Each guest takes one place (seats)"
    31613247msgstr ""
    31623248
    31633249#: Languages/acf-strings.php:57
    3164 msgid "Replaces the price wherever a customer sees it. HTML is allowed. {price} is the headline amount and {period} its period, for example: <strong>{price}</strong> per {period}, cleaning included."
     3250msgid "Maximum guests"
     3251msgstr ""
     3252
     3253#: Languages/acf-strings.php:58
     3254msgid "Per unit when each unit holds guests (a cottage that sleeps 4: 4). Per booking when each guest takes a place; leave empty to allow up to the stock."
    31653255msgstr ""
    31663256
    31673257#: Languages/acf-strings.php:59
    3168 msgid "Off leaves the product as it is: the quantity is the number of units. \"Each unit holds guests\" checks the party against the maximum per unit: a cottage that sleeps 4, or a room type for 2 with 5 in stock, where 5 guests need 3 rooms. \"Each guest takes one place\" makes the quantity the number of guests: seats on a cruise or a tour."
     3258msgid "Minimum guests per booking"
    31693259msgstr ""
    31703260
    31713261#: Languages/acf-strings.php:60
    3172 msgid "Off: the quantity is all that is asked"
     3262msgid "The fewest guests one booking may have (a private tour for at least 2)."
    31733263msgstr ""
    31743264
    31753265#: Languages/acf-strings.php:61
    3176 msgid "Each unit holds up to a number of guests"
     3266msgid "What guests are called"
    31773267msgstr ""
    31783268
    31793269#: Languages/acf-strings.php:62
    3180 msgid "Each guest takes one place (seats)"
     3270msgid "Shown beside the guest count on the product page, for example Riders or Passengers. Leave empty for Guests."
    31813271msgstr ""
    31823272
    31833273#: Languages/acf-strings.php:63
    3184 msgid "Maximum guests"
     3274msgid "Use the store's guest types"
    31853275msgstr ""
    31863276
    31873277#: Languages/acf-strings.php:64
    3188 msgid "Per unit when each unit holds guests (a cottage that sleeps 4: 4). Per booking when each guest takes a place; leave empty to allow up to the stock."
     3278msgid "Rentals > Settings > Guests sets which guests are asked for: a single count, or adults with children, infants and pets."
    31893279msgstr ""
    31903280
    31913281#: Languages/acf-strings.php:65
    3192 msgid "Minimum guests per booking"
     3282msgid "Guest types"
    31933283msgstr ""
    31943284
    31953285#: Languages/acf-strings.php:66
    3196 msgid "The fewest guests one booking may have (a private tour for at least 2)."
     3286msgid "Adults are always asked for. Tick none for a single guest count."
    31973287msgstr ""
    31983288
    31993289#: Languages/acf-strings.php:67
    3200 msgid "What guests are called"
    3201 msgstr ""
    3202 
    3203 #: Languages/acf-strings.php:68
    3204 msgid "Shown beside the guest count on the product page, for example Riders or Passengers. Leave empty for Guests."
    3205 msgstr ""
    3206 
    3207 #: Languages/acf-strings.php:69
    3208 msgid "Use the store's guest types"
    3209 msgstr ""
    3210 
    3211 #: Languages/acf-strings.php:70
    3212 msgid "Rentals > Settings > Guests sets which guests are asked for: a single count, or adults with children, infants and pets."
    3213 msgstr ""
    3214 
    3215 #: Languages/acf-strings.php:71
    3216 msgid "Guest types"
    3217 msgstr ""
    3218 
    3219 #: Languages/acf-strings.php:72
    3220 msgid "Adults are always asked for. Tick none for a single guest count."
    3221 msgstr ""
    3222 
    3223 #: Languages/acf-strings.php:73
    32243290#: Model/Guests.php:202
    32253291#: Model/Pricing/Unified.php:343
    … …  
    32273293msgstr ""
    32283294
    3229 #: Languages/acf-strings.php:74
     3295#: Languages/acf-strings.php:68
    32303296#: Model/Guests.php:210
    32313297msgid "Infants"
    32323298msgstr ""
    32333299
    3234 #: Languages/acf-strings.php:75
     3300#: Languages/acf-strings.php:69
    32353301#: Model/Guests.php:210
    32363302msgid "Pets"
    32373303msgstr ""
    32383304
     3305#: Languages/acf-strings.php:70
     3306msgid "Most infants"
     3307msgstr ""
     3308
     3309#: Languages/acf-strings.php:71
     3310msgid "Per unit booked, or per booking when each guest takes a place. On top of the maximum guests. Empty: no limit. 0: this rental does not take infants (steep stairs, a boat)."
     3311msgstr ""
     3312
     3313#: Languages/acf-strings.php:72
     3314msgid "Most pets"
     3315msgstr ""
     3316
     3317#: Languages/acf-strings.php:73
     3318msgid "Per unit booked, or per booking when each guest takes a place. On top of the maximum guests. Empty: no limit. 0: no pets."
     3319msgstr ""
     3320
     3321#: Languages/acf-strings.php:74
     3322msgid "Calendar Rules"
     3323msgstr ""
     3324
     3325#: Languages/acf-strings.php:75
     3326msgid "Booked by"
     3327msgstr ""
     3328
    32393329#: Languages/acf-strings.php:76
    3240 msgid "Most infants"
    3241 msgstr ""
    3242 
    3243 #: Languages/acf-strings.php:77
    3244 msgid "Per unit booked, or per booking when each guest takes a place. On top of the maximum guests. Empty: no limit. 0: this rental does not take infants (steep stairs, a boat)."
    3245 msgstr ""
    3246 
    3247 #: Languages/acf-strings.php:78
    3248 msgid "Most pets"
    3249 msgstr ""
    3250 
    3251 #: Languages/acf-strings.php:79
    3252 msgid "Per unit booked, or per booking when each guest takes a place. On top of the maximum guests. Empty: no limit. 0: no pets."
     3330msgid "Which days a rental with dates (no times) holds. Bookings you already have keep their rule."
    32533331msgstr ""
    32543332
    32553333#: Languages/acf-strings.php:80
    3256 msgid "Calendar Rules"
     3334msgid "Check-in from"
    32573335msgstr ""
    32583336
    32593337#: Languages/acf-strings.php:81
     3338msgid "Shown to the guest, for example \"3:00 pm\". Empty: the store's. Only for products booked by Nights."
     3339msgstr ""
     3340
     3341#: Languages/acf-strings.php:82
     3342msgid "Check-out by"
     3343msgstr ""
     3344
     3345#: Languages/acf-strings.php:83
     3346msgid "Shown to the guest, for example \"11:00 am\". Empty: the store's."
     3347msgstr ""
     3348
     3349#: Languages/acf-strings.php:84
    32603350msgid "Use Default Store Closed Days / Holidays"
    32613351msgstr ""
    32623352
    3263 #: Languages/acf-strings.php:82
     3353#: Languages/acf-strings.php:85
    32643354msgid "Store Closed Days / Holidays"
    32653355msgstr ""
    32663356
    3267 #: Languages/acf-strings.php:83
     3357#: Languages/acf-strings.php:86
    32683358msgid "Add Row"
    32693359msgstr ""
    32703360
    3271 #: Languages/acf-strings.php:85
     3361#: Languages/acf-strings.php:88
    32723362msgid "To"
    32733363msgstr ""
    32743364
    3275 #: Languages/acf-strings.php:86
     3365#: Languages/acf-strings.php:89
    32763366msgid "Repeats"
    32773367msgstr ""
    32783368
    3279 #: Languages/acf-strings.php:88
     3369#: Languages/acf-strings.php:91
    32803370msgid "Weekly"
    32813371msgstr ""
    32823372
    3283 #: Languages/acf-strings.php:89
     3373#: Languages/acf-strings.php:92
    32843374msgid "Monthly"
    32853375msgstr ""
    32863376
    3287 #: Languages/acf-strings.php:90
     3377#: Languages/acf-strings.php:93
    32883378msgid "Yearly"
    32893379msgstr ""
    32903380
    3291 #: Languages/acf-strings.php:91
     3381#: Languages/acf-strings.php:94
    32923382msgid "Applies To"
    32933383msgstr ""
    32943384
    3295 #: Languages/acf-strings.php:94
     3385#: Languages/acf-strings.php:97
    32963386msgid "Both"
    32973387msgstr ""
    32983388
    3299 #: Languages/acf-strings.php:95
     3389#: Languages/acf-strings.php:98
    33003390msgid "Every day in the range: nothing may be rented across it"
    33013391msgstr ""
    33023392
    3303 #: Languages/acf-strings.php:96
     3393#: Languages/acf-strings.php:99
    33043394msgid "Reason"
    33053395msgstr ""
    33063396
    3307 #: Languages/acf-strings.php:97
     3397#: Languages/acf-strings.php:100
    33083398msgid "Block out reason is optional. If you add it, it will show to the customer when they hover over those days that are disabled; otherwise the reason will just be \"Disabled\"."
    33093399msgstr ""
    33103400
    3311 #: Languages/acf-strings.php:98
     3401#: Languages/acf-strings.php:101
    33123402msgid "e.g. Closed for stocktake"
    33133403msgstr ""
    33143404
    3315 #: Languages/acf-strings.php:99
     3405#: Languages/acf-strings.php:102
    33163406msgid "Use Default Start Date Padding"
    33173407msgstr ""
    33183408
    3319 #: Languages/acf-strings.php:100
     3409#: Languages/acf-strings.php:103
    33203410msgid "This is the padding starting from now that you don't want to accept bookings for. For example if you set this to 2 days and today is January 1, the customer would not be able to select a start date until January 3. This is different than buffer before start date below in that it is not actually blocked out before the booked dates for future orders."
    33213411msgstr ""
    33223412
    3323 #: Languages/acf-strings.php:101
     3413#: Languages/acf-strings.php:104
    33243414msgid "Start Date Padding"
    33253415msgstr ""
    33263416
    3327 #: Languages/acf-strings.php:102
     3417#: Languages/acf-strings.php:105
    33283418msgid "Type"
    33293419msgstr ""
    33303420
    3331 #: Languages/acf-strings.php:103
     3421#: Languages/acf-strings.php:106
    33323422msgid "Hours"
    33333423msgstr ""
    33343424
    3335 #: Languages/acf-strings.php:104
     3425#: Languages/acf-strings.php:107
    33363426msgid "Days"
    33373427msgstr ""
    33383428
    3339 #: Languages/acf-strings.php:105
     3429#: Languages/acf-strings.php:108
    33403430msgid "Use Default Buffer Before Booking"
    33413431msgstr ""
    33423432
    3343 #: Languages/acf-strings.php:106
     3433#: Languages/acf-strings.php:109
    33443434msgid "Buffer Before Booking"
    33453435msgstr ""
    33463436
    3347 #: Languages/acf-strings.php:107
     3437#: Languages/acf-strings.php:110
    33483438msgid "Minutes"
    33493439msgstr ""
    33503440
    3351 #: Languages/acf-strings.php:108
     3441#: Languages/acf-strings.php:111
    33523442msgid "Use Default Buffer After Booking"
    33533443msgstr ""
    33543444
    3355 #: Languages/acf-strings.php:109
     3445#: Languages/acf-strings.php:112
    33563446msgid "Buffer After Booking"
    33573447msgstr ""
    33583448
    3359 #: Languages/acf-strings.php:110
     3449#: Languages/acf-strings.php:113
    33603450msgid "Use Default Minimum Booking Length"
    33613451msgstr ""
    33623452
    3363 #: Languages/acf-strings.php:111
     3453#: Languages/acf-strings.php:114
    33643454msgid "Minimum Booking Length"
    33653455msgstr ""
    33663456
    3367 #: Languages/acf-strings.php:112
     3457#: Languages/acf-strings.php:115
    33683458msgid "Use Default Maximum Booking Length"
    33693459msgstr ""
    33703460
    3371 #: Languages/acf-strings.php:113
     3461#: Languages/acf-strings.php:116
    33723462msgid "Maximum Booking Length"
    33733463msgstr ""
    33743464
    3375 #: Languages/acf-strings.php:114
     3465#: Languages/acf-strings.php:117
    33763466msgid "Maximum Days In The Future A Booking Is Allowed"
    33773467msgstr ""
    33783468
    3379 #: Languages/acf-strings.php:115
     3469#: Languages/acf-strings.php:118
    33803470msgid "Use Default Maximum Days In The Future A Booking Is Allowed"
    33813471msgstr ""
    33823472
    3383 #: Languages/acf-strings.php:116
     3473#: Languages/acf-strings.php:119
    33843474msgid "Rentals & Bookings Settings"
    33853475msgstr ""
    33863476
    3387 #: Languages/acf-strings.php:118
     3477#: Languages/acf-strings.php:121
    33883478msgid "Store Open Time"
    33893479msgstr ""
    33903480
    3391 #: Languages/acf-strings.php:119
     3481#: Languages/acf-strings.php:122
    33923482msgid "Store Close Time"
    33933483msgstr ""
    33943484
    3395 #: Languages/acf-strings.php:120
    3396 msgid "Stay type for date-only products"
    3397 msgstr ""
    3398 
    3399 #: Languages/acf-strings.php:121
    3400 msgid "Nights: check-in and check-out; the check-out day can be booked by the next guest and lengths are counted in nights. Days: the item comes back on the last day. Each product can choose for itself."
    3401 msgstr ""
    3402 
    3403 #: Languages/acf-strings.php:122
    3404 msgid "Shown to guests of stays of nights, for example \"3:00 pm\". Text only: it does not change availability."
    3405 msgstr ""
    3406 
    34073485#: Languages/acf-strings.php:123
    3408 msgid "For example \"11:00 am\"."
     3486msgid "Date-only rentals are booked by"
    34093487msgstr ""
    34103488
    34113489#: Languages/acf-strings.php:124
     3490msgid "Which days a rental with dates (no times) holds."
     3491msgstr ""
     3492
     3493#: Languages/acf-strings.php:125
     3494msgid "For Nights. Shown to guests, for example \"3:00 pm\". Text only: it does not change availability."
     3495msgstr ""
     3496
     3497#: Languages/acf-strings.php:126
     3498msgid "For Nights. Shown to guests, for example \"11:00 am\". Text only: it does not change availability."
     3499msgstr ""
     3500
     3501#: Languages/acf-strings.php:127
    34123502msgid "Allow rentals to start on the same day as they are returned"
    34133503msgstr ""
    34143504
    3415 #: Languages/acf-strings.php:125
     3505#: Languages/acf-strings.php:128
    34163506msgid "For date-only products rented by the day. On: a unit returned on a day can go out again that day, so another rental may start on the return day. Off: the return day stays with the rental that ends on it. Turnover time still applies, and rentals booked before a change keep the days they were booked with. Products rented by the night always free the check-out day; rentals with a time of day are held to the minute."
    34173507msgstr ""
    34183508
    3419 #: Languages/acf-strings.php:126
     3509#: Languages/acf-strings.php:129
    34203510msgid "Date Formatting"
    34213511msgstr ""
    34223512
    3423 #: Languages/acf-strings.php:127
     3513#: Languages/acf-strings.php:130
    34243514msgid "Customer Side Date Field Input"
    34253515msgstr ""
    34263516
    3427 #: Languages/acf-strings.php:128
     3517#: Languages/acf-strings.php:131
    34283518msgid "Year-Month-Day (i.e. 2022-01-10)"
    34293519msgstr ""
    34303520
    3431 #: Languages/acf-strings.php:129
     3521#: Languages/acf-strings.php:132
    34323522msgid "Month day, Year (i.e. January 10, 2022)"
    34333523msgstr ""
    34343524
    3435 #: Languages/acf-strings.php:130
     3525#: Languages/acf-strings.php:133
    34363526msgid "month/day/year (i.e. 01/10/2022)"
    34373527msgstr ""
    34383528
    3439 #: Languages/acf-strings.php:131
     3529#: Languages/acf-strings.php:134
    34403530msgid "day/month/year (i.e. 10/01/2022)"
    34413531msgstr ""
    34423532
    3443 #: Languages/acf-strings.php:132
     3533#: Languages/acf-strings.php:135
    34443534msgid "month/day/year, no leading zeros (i.e. 1/10/2022)"
    34453535msgstr ""
    34463536
    3447 #: Languages/acf-strings.php:133
     3537#: Languages/acf-strings.php:136
    34483538msgid "day/month/year, no leading zeros (i.e. 10/1/2022)"
    34493539msgstr ""
    34503540
    3451 #: Languages/acf-strings.php:134
     3541#: Languages/acf-strings.php:137
    34523542msgid "day.month.year (i.e. 10.01.2022)"
    34533543msgstr ""
    34543544
    3455 #: Languages/acf-strings.php:135
     3545#: Languages/acf-strings.php:138
    34563546msgid "day-month-year (i.e. 10-01-2022)"
    34573547msgstr ""
    34583548
    3459 #: Languages/acf-strings.php:136
     3549#: Languages/acf-strings.php:139
    34603550msgid "Year/Month/Day (i.e. 2022/01/10)"
    34613551msgstr ""
    34623552
    3463 #: Languages/acf-strings.php:137
     3553#: Languages/acf-strings.php:140
    34643554msgid "Mon day, Year (i.e. Jan 10, 2022)"
    34653555msgstr ""
    34663556
    3467 #: Languages/acf-strings.php:138
     3557#: Languages/acf-strings.php:141
    34683558msgid "day Mon Year (i.e. 10 Jan 2022)"
    34693559msgstr ""
    34703560
    3471 #: Languages/acf-strings.php:139
     3561#: Languages/acf-strings.php:142
    34723562msgid "day Month Year (i.e. 10 January 2022)"
    34733563msgstr ""
    34743564
    3475 #: Languages/acf-strings.php:140
     3565#: Languages/acf-strings.php:143
    34763566msgid "Weekday, Mon day, Year (i.e. Mon, Jan 10, 2022)"
    34773567msgstr ""
    34783568
    3479 #: Languages/acf-strings.php:141
     3569#: Languages/acf-strings.php:144
    34803570msgid "Weekday day Mon Year (i.e. Mon 10 Jan 2022)"
    34813571msgstr ""
    34823572
    3483 #: Languages/acf-strings.php:142
     3573#: Languages/acf-strings.php:145
    34843574msgid "Weekday, Month day, Year (i.e. Monday, January 10, 2022)"
    34853575msgstr ""
    34863576
    3487 #: Languages/acf-strings.php:143
     3577#: Languages/acf-strings.php:146
    34883578msgid "Customer Side Time Field"
    34893579msgstr ""
    34903580
    3491 #: Languages/acf-strings.php:144
     3581#: Languages/acf-strings.php:147
    34923582msgid "4:33 am (am / pm)"
    34933583msgstr ""
    34943584
    3495 #: Languages/acf-strings.php:145
     3585#: Languages/acf-strings.php:148
    34963586msgid "4:33 AM (AM / PM)"
    34973587msgstr ""
    34983588
    3499 #: Languages/acf-strings.php:146
     3589#: Languages/acf-strings.php:149
    35003590msgid "04:33 (24 hour time format)"
    35013591msgstr ""
    35023592
    3503 #: Languages/acf-strings.php:147
     3593#: Languages/acf-strings.php:150
    35043594msgid "Daily rates are priced per"
    35053595msgstr ""
    35063596
    3507 #: Languages/acf-strings.php:148
     3597#: Languages/acf-strings.php:151
    35083598msgid "How a daily rate is worded on product pages and in the booking bar. A holiday let is priced per night and a kayak per day; the arithmetic is the same (the 7th to the 10th is 3 nights or 3 days). A product can choose its own under Pricing."
    35093599msgstr ""
    35103600
    3511 #: Languages/acf-strings.php:149
     3601#: Languages/acf-strings.php:152
    35123602msgid "Day ($50 / day, 3 days)"
    35133603msgstr ""
    35143604
    3515 #: Languages/acf-strings.php:150
     3605#: Languages/acf-strings.php:153
    35163606msgid "Night ($50 / night, 3 nights)"
    35173607msgstr ""
    35183608
    3519 #: Languages/acf-strings.php:151
     3609#: Languages/acf-strings.php:154
    35203610msgid "Which products ask for guests"
    35213611msgstr ""
    35223612
    3523 #: Languages/acf-strings.php:152
     3613#: Languages/acf-strings.php:155
    35243614msgid "A product asks for guests once its Guests tab is not Off. These are the store's defaults for those products; a product can choose its own guest types."
    35253615msgstr ""
    35263616
    3527 #: Languages/acf-strings.php:153
     3617#: Languages/acf-strings.php:156
    35283618msgid "Age note for adults"
    35293619msgstr ""
    35303620
    3531 #: Languages/acf-strings.php:154
     3621#: Languages/acf-strings.php:157
    35323622#: Model/Guests.php:298
    35333623msgid "Ages 13 or above"
    35343624msgstr ""
    35353625
    3536 #: Languages/acf-strings.php:155
     3626#: Languages/acf-strings.php:158
    35373627msgid "Age note for children"
    35383628msgstr ""
    35393629
    3540 #: Languages/acf-strings.php:156
     3630#: Languages/acf-strings.php:159
    35413631#: Model/Guests.php:300
    35423632msgid "Ages 2–12"
    35433633msgstr ""
    35443634
    3545 #: Languages/acf-strings.php:157
     3635#: Languages/acf-strings.php:160
    35463636msgid "Age note for infants"
    35473637msgstr ""
    35483638
    3549 #: Languages/acf-strings.php:158
     3639#: Languages/acf-strings.php:161
    35503640#: Model/Guests.php:302
    35513641msgid "Under 2"
    35523642msgstr ""
    35533643
    3554 #: Languages/acf-strings.php:159
     3644#: Languages/acf-strings.php:162
    35553645msgid "Note for pets"
    35563646msgstr ""
    35573647
    3558 #: Languages/acf-strings.php:160
     3648#: Languages/acf-strings.php:163
    35593649msgid "Bringing a service animal?"
    35603650msgstr ""
    35613651
    3562 #: Languages/acf-strings.php:161
     3652#: Languages/acf-strings.php:164
    35633653msgid "Infants take a seat"
    35643654msgstr ""
    35653655
    3566 #: Languages/acf-strings.php:162
     3656#: Languages/acf-strings.php:165
    35673657msgid "For products whose guests each take a place (seats on a tour): an infant then takes a seat of their own, free of charge. Off: infants ride on a lap and take no seat."
    35683658msgstr ""
    35693659
    3570 #: Languages/acf-strings.php:163
     3660#: Languages/acf-strings.php:166
    35713661msgid "Advanced"
    35723662msgstr ""
    35733663
    3574 #: Languages/acf-strings.php:164
     3664#: Languages/acf-strings.php:167
    35753665msgid "Mode"
    35763666msgstr ""
    35773667
    3578 #: Languages/acf-strings.php:165
     3668#: Languages/acf-strings.php:168
    35793669msgid "Production"
    35803670msgstr ""
    35813671
    3582 #: Languages/acf-strings.php:166
     3672#: Languages/acf-strings.php:169
    35833673msgid "Developer"
    35843674msgstr ""
    35853675
    3586 #: Languages/acf-strings.php:168
     3676#: Languages/acf-strings.php:171
    35873677msgid "Also write the routine rental events (booking saves, checkout checks, upgrade steps) to WooCommerce > Status > Logs, source \"sibooking\". Errors and warnings are always logged; turn this on while you investigate a problem, then off again."
    35883678msgstr ""
    35893679
    3590 #: Languages/acf-strings.php:169
     3680#: Languages/acf-strings.php:172
    35913681msgid "Use legacy pricing (deprecated)"
    35923682msgstr ""
    35933683
    3594 #: Languages/acf-strings.php:170
     3684#: Languages/acf-strings.php:173
    35953685msgid "Off (recommended): rentals are priced by the unified pricing engine, the same rules as our Magento and Shopify apps. On: the pricing code from before unified pricing prices every rental exactly as it used to. Kept for checking a price a customer asks about. Orders already placed keep their price either way."
    35963686msgstr ""
    35973687
    3598 #: Languages/acf-strings.php:172
     3688#: Languages/acf-strings.php:175
    35993689msgid "ICS Export Security Keys"
    36003690msgstr ""
    36013691
    3602 #: Languages/acf-strings.php:173
     3692#: Languages/acf-strings.php:176
    36033693msgid "Key"
    36043694msgstr ""
    36053695
    3606 #: Languages/acf-strings.php:174
     3696#: Languages/acf-strings.php:177
    36073697msgid "Emails"
    36083698msgstr ""
    36093699
    3610 #: Languages/acf-strings.php:176
     3700#: Languages/acf-strings.php:179
    36113701msgid "Remind the customer before their rental starts. How many reminders, and how far ahead, is set here; what the email says is set further down this section."
    36123702msgstr ""
    36133703
    3614 #: Languages/acf-strings.php:177
     3704#: Languages/acf-strings.php:180
    36153705msgid "Active"
    36163706msgstr ""
    36173707
    3618 #: Languages/acf-strings.php:178
     3708#: Languages/acf-strings.php:181
    36193709msgid "Name"
    36203710msgstr ""
    36213711
    3622 #: Languages/acf-strings.php:179
     3712#: Languages/acf-strings.php:182
    36233713msgid "Optional. A name for this reminder, shown on the settings summary and in the order note."
    36243714msgstr ""
    36253715
    3626 #: Languages/acf-strings.php:180
     3716#: Languages/acf-strings.php:183
    36273717msgid "Time before pickup"
    36283718msgstr ""
    36293719
    3630 #: Languages/acf-strings.php:181
     3720#: Languages/acf-strings.php:184
    36313721msgid "Unit"
    36323722msgstr ""
    36333723
    3634 #: Languages/acf-strings.php:182
     3724#: Languages/acf-strings.php:185
    36353725msgid "Send at (time of day)"
    36363726msgstr ""
    36373727
    3638 #: Languages/acf-strings.php:183
     3728#: Languages/acf-strings.php:186
    36393729msgid "Optional, and only for days: send at this time of day instead of exactly at the offset."
    36403730msgstr ""
    36413731
    3642 #: Languages/acf-strings.php:185
     3732#: Languages/acf-strings.php:188
    36433733msgid "Remind the customer before the rental is due back. How many reminders, and how far ahead, is set here; what the email says is set further down this section."
    36443734msgstr ""
    36453735
    3646 #: Languages/acf-strings.php:186
     3736#: Languages/acf-strings.php:189
    36473737msgid "Time before the rental is due back"
    36483738msgstr ""
    36493739
    3650 #: Languages/acf-strings.php:187
     3740#: Languages/acf-strings.php:190
    36513741msgid "Reminder status"
    36523742msgstr ""
    36533743
    3654 #: Languages/acf-strings.php:188
     3744#: Languages/acf-strings.php:191
    36553745msgid "No rental reminders are configured yet."
    36563746msgstr ""
    36573747
    3658 #: Languages/acf-strings.php:189
     3748#: Languages/acf-strings.php:192
    36593749msgid "Send this email"
    36603750msgstr ""
    36613751
    3662 #: Languages/acf-strings.php:191
     3752#: Languages/acf-strings.php:194
    36633753msgid "{site_title}, {order_date} and {order_number} work here."
    36643754msgstr ""
    36653755
    3666 #: Languages/acf-strings.php:193
     3756#: Languages/acf-strings.php:196
    36673757msgid "The line printed at the top of the email, inside the header."
    36683758msgstr ""
    36693759
    3670 #: Languages/acf-strings.php:195
     3760#: Languages/acf-strings.php:198
    36713761msgid "Other languages"
    36723762msgstr ""
    36733763
    3674 #: Languages/acf-strings.php:196
     3764#: Languages/acf-strings.php:199
    36753765msgid "A customer is emailed in the language they booked in, in the wording we ship for that language — add a row only to change it."
    36763766msgstr ""
    36773767
    3678 #: Languages/acf-strings.php:197
     3768#: Languages/acf-strings.php:200
    36793769msgid "Add a language"
    36803770msgstr ""
    36813771
    3682 #: Languages/acf-strings.php:198
     3772#: Languages/acf-strings.php:201
    36833773msgid "Language"
    36843774msgstr ""
    36853775
    3686 #: Languages/acf-strings.php:200
     3776#: Languages/acf-strings.php:203
    36873777msgid "Printed under the products, above the footer. Merge tags work here too."
    36883778msgstr ""
    36893779
    3690 #: Languages/acf-strings.php:201
     3780#: Languages/acf-strings.php:204
    36913781msgid "BCC admin recipient(s)"
    36923782msgstr ""
    36933783
    3694 #: Languages/acf-strings.php:202
     3784#: Languages/acf-strings.php:205
    36953785msgid "Comma separated. Leave empty to send only to the customer."
    36963786msgstr ""
    36973787
    3698 #: Languages/acf-strings.php:204
     3788#: Languages/acf-strings.php:207
    36993789msgid "Which format to send. Multipart sends both and lets the mail client choose."
    37003790msgstr ""
    37013791
    3702 #: Languages/acf-strings.php:205
     3792#: Languages/acf-strings.php:208
    37033793msgid "Plain text"
    37043794msgstr ""
    37053795
    3706 #: Languages/acf-strings.php:206
     3796#: Languages/acf-strings.php:209
    37073797msgid "HTML"
    37083798msgstr ""
    37093799
    3710 #: Languages/acf-strings.php:207
     3800#: Languages/acf-strings.php:210
    37113801msgid "Multipart"
    37123802msgstr ""
    37133803
    3714 #: Languages/acf-strings.php:208
     3804#: Languages/acf-strings.php:211
    37153805msgid "Calendar Report"
    37163806msgstr ""
    37173807
    3718 #: Languages/acf-strings.php:212
     3808#: Languages/acf-strings.php:215
    37193809msgid "Calendar Format"
    37203810msgstr ""
    37213811
    3722 #: Languages/acf-strings.php:213
     3812#: Languages/acf-strings.php:216
    37233813msgid "Start & End Date"
    37243814msgstr ""
    37253815
    3726 #: Languages/acf-strings.php:214
     3816#: Languages/acf-strings.php:217
    37273817msgid "Start through End Date"
    37283818msgstr ""
    37293819
    3730 #: Languages/acf-strings.php:215
     3820#: Languages/acf-strings.php:218
    37313821msgid "Start & End Date With Buffer"
    37323822msgstr ""
    37333823
    3734 #: Languages/acf-strings.php:216
     3824#: Languages/acf-strings.php:219
    37353825msgid "Start through End Date With Buffer"
    37363826msgstr ""
    37373827
    3738 #: Languages/acf-strings.php:219
     3828#: Languages/acf-strings.php:222
    37393829msgid "Search product name or sku"
    37403830msgstr ""
    … …  
    37563846msgstr ""
    37573847
    3758 #: Model/AdminAlerts.php:78
    3759 msgid "Dismiss"
    3760 msgstr ""
    3761 
    3762 #: Model/AdminAlerts.php:85
    3763 msgid "Sorry, you are not allowed to do that."
    3764 msgstr ""
    3765 
    3766 #: Model/Availability.php:64
     3848#: Model/Availability.php:66
    37673849msgid "The start date is too early"
    37683850msgstr ""
    37693851
    3770 #: Model/Availability.php:67
     3852#: Model/Availability.php:69
    37713853msgid "The start or end date is disabled"
    37723854msgstr ""
    37733855
    3774 #: Model/Availability.php:152
     3856#: Model/Availability.php:157
    37753857msgid "Choose at least one night: the check-out day must be after the check-in day."
    37763858msgstr ""
    37773859
    37783860#. translators: %d: nights
    3779 #: Model/Availability.php:158
     3861#: Model/Availability.php:163
    37803862#, php-format
    37813863msgid "The minimum stay is %d night."
    … …  
    37853867
    37863868#. translators: %d: nights
    3787 #: Model/Availability.php:162
     3869#: Model/Availability.php:167
    37883870#, php-format
    37893871msgid "The maximum stay is %d night."
    … …  
    37933875
    37943876#. translators: 1: number, 2: unit (hours or days)
    3795 #: Model/Availability.php:193
    3796 #: Model/Availability.php:222
     3877#: Model/Availability.php:198
     3878#: Model/Availability.php:227
    37973879#, php-format
    37983880msgid "The minimum rental length is %1$s %2$s"
    … …  
    38003882
    38013883#. translators: 1: number, 2: unit (hours or days)
    3802 #: Model/Availability.php:200
    3803 #: Model/Availability.php:229
     3884#: Model/Availability.php:205
     3885#: Model/Availability.php:234
    38043886#, php-format
    38053887msgid "The maximum rental length is %1$s %2$s"
  • sales-igniter-rentals-bookings-lite/trunk/Model/ACF/group_61d28753aedd5.json

    r3728745 r3729359  
    9494            "ajax": 0,
    9595            "placeholder": ""
    96         },
    97         {
    98             "key": "field_68f6a00000001",
    99             "label": "Stay type",
    100             "name": "sibooking_stay_type",
    101             "aria-label": "",
    102             "type": "select",
    103             "instructions": "Nights: check-in and check-out, the check-out day can be booked again the same day, and the length is counted in nights. Days: the item comes back on the last day, which stays booked.",
    104             "required": 0,
    105             "conditional_logic": [
    106                 [
    107                     {
    108                         "field": "field_61d28a385f491",
    109                         "operator": "==",
    110                         "value": "date"
    111                     }
    112                 ]
    113             ],
    114             "wrapper": {
    115                 "width": "",
    116                 "class": "",
    117                 "id": ""
    118             },
    119             "choices": {
    120                 "default": "Use the store default",
    121                 "days": "Days (the item comes back on the last day)",
    122                 "nights": "Nights (check-in and check-out)"
    123             },
    124             "default_value": "default",
    125             "return_format": "value",
    126             "multiple": 0,
    127             "allow_null": 0,
    128             "ui": 0,
    129             "ajax": 0,
    130             "placeholder": ""
    131         },
    132         {
    133             "key": "field_68f6a00000002",
    134             "label": "Check-in from",
    135             "name": "sibooking_check_in_time",
    136             "aria-label": "",
    137             "type": "text",
    138             "instructions": "Shown to the guest, for example \"3:00 pm\". Empty: the store's. Only for stays of nights.",
    139             "required": 0,
    140             "conditional_logic": [
    141                 [
    142                     {
    143                         "field": "field_68f6a00000001",
    144                         "operator": "!=",
    145                         "value": "days"
    146                     }
    147                 ]
    148             ],
    149             "wrapper": {
    150                 "width": "",
    151                 "class": "",
    152                 "id": ""
    153             },
    154             "default_value": "",
    155             "maxlength": 40,
    156             "placeholder": "",
    157             "prepend": "",
    158             "append": ""
    159         },
    160         {
    161             "key": "field_68f6a00000003",
    162             "label": "Check-out by",
    163             "name": "sibooking_check_out_time",
    164             "aria-label": "",
    165             "type": "text",
    166             "instructions": "Shown to the guest, for example \"11:00 am\". Empty: the store's.",
    167             "required": 0,
    168             "conditional_logic": [
    169                 [
    170                     {
    171                         "field": "field_68f6a00000001",
    172                         "operator": "!=",
    173                         "value": "days"
    174                     }
    175                 ]
    176             ],
    177             "wrapper": {
    178                 "width": "",
    179                 "class": "",
    180                 "id": ""
    181             },
    182             "default_value": "",
    183             "maxlength": 40,
    184             "placeholder": "",
    185             "prepend": "",
    186             "append": ""
    18796        },
    18897        {
    … …  
    271180        },
    272181        {
     182            "key": "field_68f6a00000033",
     183            "label": "Also charge the return day",
     184            "name": "sibooking_charge_return_day",
     185            "aria-label": "",
     186            "type": "select",
     187            "instructions": "For rentals booked by the day. No: the 1st to the 3rd is 2 days (the return day is free). Yes: 3 days. Nights are charged per night.",
     188            "required": 0,
     189            "conditional_logic": [
     190                [
     191                    {
     192                        "field": "field_61d28a385f491",
     193                        "operator": "==",
     194                        "value": "date"
     195                    }
     196                ]
     197            ],
     198            "wrapper": {
     199                "width": "",
     200                "class": "",
     201                "id": ""
     202            },
     203            "choices": {
     204                "default": "Store setting",
     205                "no": "No",
     206                "yes": "Yes"
     207            },
     208            "default_value": "default",
     209            "return_format": "value",
     210            "multiple": 0,
     211            "allow_null": 0,
     212            "ui": 0,
     213            "ajax": 0,
     214            "placeholder": ""
     215        },
     216        {
    273217            "key": "field_68d3c1a0b0010",
    274218            "label": "Headline price",
    … …  
    302246            "aria-label": "",
    303247            "type": "select",
    304             "instructions": "The headline reads \"From $145 / night\". Week and month show what a whole week or month costs at your rates ($449 / week). Night and event change only the word: a stay from the 7th to the 10th is 3 nights, charged as 3 days.",
     248            "instructions": "The headline reads \"From $145 / day\", or \"/ night\" for a product booked by Nights. Week and month show what a whole week or month costs at your rates ($449 / week). Event changes only the word.",
    305249            "required": 0,
    306250            "conditional_logic": [
    … …  
    319263            },
    320264            "choices": {
    321                 "auto": "Automatic: the store's day or night (an hourly-only rate shows per hour)",
     265                "auto": "Automatic: follows \"Booked by\" (an hourly-only rate shows per hour)",
    322266                "hour": "Hour",
    323                 "day": "Day",
    324                 "night": "Night",
    325267                "week": "Week",
    326268                "month": "Month",
    … …  
    641583            "endpoint": 0,
    642584            "selected": 0
     585        },
     586        {
     587            "key": "field_68f6a00000001",
     588            "label": "Booked by",
     589            "name": "sibooking_stay_type",
     590            "aria-label": "",
     591            "type": "select",
     592            "instructions": "Which days a rental with dates (no times) holds. Bookings you already have keep their rule.",
     593            "required": 0,
     594            "conditional_logic": [
     595                [
     596                    {
     597                        "field": "field_61d28a385f491",
     598                        "operator": "==",
     599                        "value": "date"
     600                    }
     601                ]
     602            ],
     603            "wrapper": {
     604                "width": "",
     605                "class": "",
     606                "id": ""
     607            },
     608            "choices": {
     609                "default": "Store setting",
     610                "days": "Days: returned on the last day, entire return day is booked",
     611                "days_free": "Days: can go out again on the return day",
     612                "nights": "Nights: same-day check-out and check-in"
     613            },
     614            "default_value": "default",
     615            "return_format": "value",
     616            "multiple": 0,
     617            "allow_null": 0,
     618            "ui": 0,
     619            "ajax": 0,
     620            "placeholder": ""
     621        },
     622        {
     623            "key": "field_68f6a00000002",
     624            "label": "Check-in from",
     625            "name": "sibooking_check_in_time",
     626            "aria-label": "",
     627            "type": "text",
     628            "instructions": "Shown to the guest, for example \"3:00 pm\". Empty: the store's. Only for products booked by Nights.",
     629            "required": 0,
     630            "conditional_logic": [
     631                [
     632                    {
     633                        "field": "field_68f6a00000001",
     634                        "operator": "==",
     635                        "value": "nights"
     636                    }
     637                ]
     638            ],
     639            "wrapper": {
     640                "width": "",
     641                "class": "",
     642                "id": ""
     643            },
     644            "default_value": "",
     645            "maxlength": 40,
     646            "placeholder": "",
     647            "prepend": "",
     648            "append": ""
     649        },
     650        {
     651            "key": "field_68f6a00000003",
     652            "label": "Check-out by",
     653            "name": "sibooking_check_out_time",
     654            "aria-label": "",
     655            "type": "text",
     656            "instructions": "Shown to the guest, for example \"11:00 am\". Empty: the store's.",
     657            "required": 0,
     658            "conditional_logic": [
     659                [
     660                    {
     661                        "field": "field_68f6a00000001",
     662                        "operator": "==",
     663                        "value": "nights"
     664                    }
     665                ]
     666            ],
     667            "wrapper": {
     668                "width": "",
     669                "class": "",
     670                "id": ""
     671            },
     672            "default_value": "",
     673            "maxlength": 40,
     674            "placeholder": "",
     675            "prepend": "",
     676            "append": ""
    643677        },
    644678        {
    … …  
    13531387    "show_in_rest": 0,
    13541388    "private": true,
    1355     "modified": 1790665195
     1389    "modified": 1791205690
    13561390}
  • sales-igniter-rentals-bookings-lite/trunk/Model/ACF/group_61d2ba5188b94.json

    r3728745 r3729359  
    44    "fields": [
    55        {
    6             "key": "field_61d2bea09d8f6",
    7             "label": "Calendar",
     6            "key": "field_68f6a00000031",
     7            "label": "Pricing",
    88            "name": "",
    99            "aria-label": "",
    … …  
    2222        },
    2323        {
    24             "key": "field_61d2bfd4998a1",
    25             "label": "Store Open Time",
    26             "name": "sibooking_store_open_time",
    27             "aria-label": "",
    28             "type": "time_picker",
    29             "instructions": "",
    30             "required": 0,
    31             "conditional_logic": 0,
    32             "wrapper": {
    33                 "width": "",
    34                 "class": "",
    35                 "id": ""
    36             },
    37             "display_format": "g:i a",
    38             "return_format": "H:i:s"
    39         },
    40         {
    41             "key": "field_61d2bffe998a2",
    42             "label": "Store Close Time",
    43             "name": "sibooking_store_close_time",
    44             "aria-label": "",
    45             "type": "time_picker",
    46             "instructions": "",
    47             "required": 0,
    48             "conditional_logic": 0,
    49             "wrapper": {
    50                 "width": "",
    51                 "class": "",
    52                 "id": ""
    53             },
    54             "display_format": "g:i a",
    55             "return_format": "H:i:s"
    56         },
    57         {
    58             "key": "field_68f6a00000011",
    59             "label": "Stay type for date-only products",
    60             "name": "sibooking_stay_type_default",
    61             "aria-label": "",
    62             "type": "select",
    63             "instructions": "Nights: check-in and check-out; the check-out day can be booked by the next guest and lengths are counted in nights. Days: the item comes back on the last day. Each product can choose for itself.",
    64             "required": 0,
    65             "conditional_logic": 0,
    66             "wrapper": {
    67                 "width": "",
    68                 "class": "",
    69                 "id": ""
    70             },
    71             "choices": {
    72                 "days": "Days (the item comes back on the last day)",
    73                 "nights": "Nights (check-in and check-out)"
    74             },
    75             "default_value": "days",
    76             "return_format": "value",
    77             "multiple": 0,
    78             "allow_null": 0,
    79             "ui": 0,
    80             "ajax": 0,
    81             "placeholder": ""
    82         },
    83         {
    84             "key": "field_68f6a00000012",
    85             "label": "Check-in from",
    86             "name": "sibooking_check_in_time_default",
    87             "aria-label": "",
    88             "type": "text",
    89             "instructions": "Shown to guests of stays of nights, for example \"3:00 pm\". Text only: it does not change availability.",
    90             "required": 0,
    91             "conditional_logic": 0,
    92             "wrapper": {
    93                 "width": "",
    94                 "class": "",
    95                 "id": ""
    96             },
    97             "default_value": "",
    98             "maxlength": 40,
    99             "placeholder": "",
    100             "prepend": "",
    101             "append": ""
    102         },
    103         {
    104             "key": "field_68f6a00000013",
    105             "label": "Check-out by",
    106             "name": "sibooking_check_out_time_default",
    107             "aria-label": "",
    108             "type": "text",
    109             "instructions": "For example \"11:00 am\".",
    110             "required": 0,
    111             "conditional_logic": 0,
    112             "wrapper": {
    113                 "width": "",
    114                 "class": "",
    115                 "id": ""
    116             },
    117             "default_value": "",
    118             "maxlength": 40,
    119             "placeholder": "",
    120             "prepend": "",
    121             "append": ""
    122         },
    123         {
    124             "key": "field_68f6a00000014",
    125             "label": "Allow rentals to start on the same day as they are returned",
    126             "name": "sibooking_start_on_return_day",
     24            "key": "field_68f6a00000032",
     25            "label": "Also charge the return day",
     26            "name": "sibooking_charge_return_day_default",
    12727            "aria-label": "",
    12828            "type": "true_false",
    129             "instructions": "For date-only products rented by the day. On: a unit returned on a day can go out again that day, so another rental may start on the return day. Off: the return day stays with the rental that ends on it. Turnover time still applies, and rentals booked before a change keep the days they were booked with. Products rented by the night always free the check-out day; rentals with a time of day are held to the minute.",
     29            "instructions": "For rentals booked by the day. No: the 1st to the 3rd is 2 days (the return day is free). Yes: 3 days. Nights are charged per night.",
    13030            "required": 0,
    13131            "conditional_logic": 0,
    … …  
    14040            "ui_on_text": "",
    14141            "ui_off_text": ""
     42        },
     43        {
     44            "key": "field_61d2bea09d8f6",
     45            "label": "Calendar",
     46            "name": "",
     47            "aria-label": "",
     48            "type": "accordion",
     49            "instructions": "",
     50            "required": 0,
     51            "conditional_logic": 0,
     52            "wrapper": {
     53                "width": "",
     54                "class": "",
     55                "id": ""
     56            },
     57            "open": 0,
     58            "multi_expand": 1,
     59            "endpoint": 0
     60        },
     61        {
     62            "key": "field_61d2bfd4998a1",
     63            "label": "Store Open Time",
     64            "name": "sibooking_store_open_time",
     65            "aria-label": "",
     66            "type": "time_picker",
     67            "instructions": "",
     68            "required": 0,
     69            "conditional_logic": 0,
     70            "wrapper": {
     71                "width": "",
     72                "class": "",
     73                "id": ""
     74            },
     75            "display_format": "g:i a",
     76            "return_format": "H:i:s"
     77        },
     78        {
     79            "key": "field_61d2bffe998a2",
     80            "label": "Store Close Time",
     81            "name": "sibooking_store_close_time",
     82            "aria-label": "",
     83            "type": "time_picker",
     84            "instructions": "",
     85            "required": 0,
     86            "conditional_logic": 0,
     87            "wrapper": {
     88                "width": "",
     89                "class": "",
     90                "id": ""
     91            },
     92            "display_format": "g:i a",
     93            "return_format": "H:i:s"
    14294        },
    14395        {
    … …  
    347299                }
    348300            ]
     301        },
     302        {
     303            "key": "field_68f6a00000011",
     304            "label": "Date-only rentals are booked by",
     305            "name": "sibooking_stay_type_default",
     306            "aria-label": "",
     307            "type": "select",
     308            "instructions": "Which days a rental with dates (no times) holds.",
     309            "required": 0,
     310            "conditional_logic": 0,
     311            "wrapper": {
     312                "width": "",
     313                "class": "",
     314                "id": ""
     315            },
     316            "choices": {
     317                "days": "Days: returned on the last day, entire return day is booked",
     318                "days_free": "Days: can go out again on the return day",
     319                "nights": "Nights: same-day check-out and check-in"
     320            },
     321            "default_value": "days",
     322            "return_format": "value",
     323            "multiple": 0,
     324            "allow_null": 0,
     325            "ui": 0,
     326            "ajax": 0,
     327            "placeholder": ""
     328        },
     329        {
     330            "key": "field_68f6a00000012",
     331            "label": "Check-in from",
     332            "name": "sibooking_check_in_time_default",
     333            "aria-label": "",
     334            "type": "text",
     335            "instructions": "For Nights. Shown to guests, for example \"3:00 pm\". Text only: it does not change availability.",
     336            "required": 0,
     337            "conditional_logic": [
     338                [
     339                    {
     340                        "field": "field_68f6a00000011",
     341                        "operator": "==",
     342                        "value": "nights"
     343                    }
     344                ]
     345            ],
     346            "wrapper": {
     347                "width": "",
     348                "class": "",
     349                "id": ""
     350            },
     351            "default_value": "",
     352            "maxlength": 40,
     353            "placeholder": "",
     354            "prepend": "",
     355            "append": ""
     356        },
     357        {
     358            "key": "field_68f6a00000013",
     359            "label": "Check-out by",
     360            "name": "sibooking_check_out_time_default",
     361            "aria-label": "",
     362            "type": "text",
     363            "instructions": "For Nights. Shown to guests, for example \"11:00 am\". Text only: it does not change availability.",
     364            "required": 0,
     365            "conditional_logic": [
     366                [
     367                    {
     368                        "field": "field_68f6a00000011",
     369                        "operator": "==",
     370                        "value": "nights"
     371                    }
     372                ]
     373            ],
     374            "wrapper": {
     375                "width": "",
     376                "class": "",
     377                "id": ""
     378            },
     379            "default_value": "",
     380            "maxlength": 40,
     381            "placeholder": "",
     382            "prepend": "",
     383            "append": ""
     384        },
     385        {
     386            "key": "field_68f6a00000014",
     387            "label": "Allow rentals to start on the same day as they are returned",
     388            "name": "sibooking_start_on_return_day",
     389            "aria-label": "",
     390            "type": "true_false",
     391            "instructions": "For date-only products rented by the day. On: a unit returned on a day can go out again that day, so another rental may start on the return day. Off: the return day stays with the rental that ends on it. Turnover time still applies, and rentals booked before a change keep the days they were booked with. Products rented by the night always free the check-out day; rentals with a time of day are held to the minute.",
     392            "required": 0,
     393            "conditional_logic": 0,
     394            "wrapper": {
     395                "width": "",
     396                "class": "",
     397                "id": ""
     398            },
     399            "message": "",
     400            "default_value": 0,
     401            "ui": 1,
     402            "ui_on_text": "",
     403            "ui_off_text": ""
    349404        },
    350405        {
    … …  
    19492004    "show_in_rest": 0,
    19502005    "private": true,
    1951     "modified": 1790867000
     2006    "modified": 1791205690
    19522007}
  • sales-igniter-rentals-bookings-lite/trunk/Model/Availability.php

    r3728745 r3729359  
    4343         *                                      the whole-cart check counts stock across lines itself)
    4444         *     @type bool|null  $nightly        the rule the line runs under (Model\Stay); default the product's
     45         *     @type bool|null  $returnDayFree  whether the line runs under "Days: can go out again on the return
     46         *                                      day" (Includes\Order::lineReturnDayFree()); default the product's
    4547         *     @type int        $stock_id       the pool: a variation with its own stock (default the product)
    4648         *     @type int[]      $exclude_orders orders whose rows do not count (the order being edited...)
    … …  
    9799        public static function available( $productId, $start, $end, array $context = [] ) {
    98100                $stay = array_key_exists( 'nightly', $context ) && null !== $context['nightly'] ? [ 'nightly' => (bool) $context['nightly'] ] : [];
     101                if ( array_key_exists( 'returnDayFree', $context ) && null !== $context['returnDayFree'] ) {
     102                        $stay['returnDayFree'] = (bool) $context['returnDayFree'];
     103                }
    99104                [ $from, $to ] = Validation::bufferedWindow( $productId, $start, $end, $context['line'] ?? null, $stay );
    100105                $stockId = (int) ( $context['stock_id'] ?? 0 ) > 0 ? (int) $context['stock_id'] : (int) $productId;
  • sales-igniter-rentals-bookings-lite/trunk/Model/Buffer.php

    r3728745 r3729359  
    99
    1010    /**
    11      * The store setting "Allow rentals to start on the same day as they are returned"
    12      * (true/false, default off; K5): a date-only rental sold by the day then holds its
    13      * days up to its return day's 00:00, so another rental may start on the return day.
     11     * The old store yes/no "Allow rentals to start on the same day as they are returned"
     12     * (K5, 1.0.13 to 1.0.14). Since 1.0.15 it is the middle "Booked by" choice, "Days: can go
     13     * out again on the return day" (Model\Stay::DAYS_FREE): a store that had it on was switched
     14     * over once (Model\Stay::switchOver()); the option is kept and no longer read. Also the
     15     * name of the filter startOnReturnDay() runs.
    1416     */
    1517    const START_ON_RETURN_DAY = 'sibooking_start_on_return_day';
    … …  
    5456     *
    5557     * The return day of a date-only rental is held by that rental (it runs to 23:59 of
    56      * its last day) unless the store turns this on. A stay of nights frees the check-out
    57      * day anyway (Model\Stay), and a rental with a time of day is held to the minute, so
    58      * the answer only changes date-only rentals sold by the day.
     58     * its last day) unless the product is "Booked by" "Days: can go out again on the return
     59     * day" (Model\Stay::DAYS_FREE, its own choice or the store's). A stay of nights frees the
     60     * check-out day anyway (Model\Stay), and a rental with a time of day is held to the
     61     * minute, so the answer only changes date-only rentals sold by the day.
    5962     *
    60      * @param int|null $productId the product whose rental is asked about (a variation's parent)
     63     * @param int|null $productId the product whose rental is asked about (a variation's parent);
     64     *                            null: the store's "Date-only rentals are booked by"
    6165     * @return bool
    6266     */
    6367    public static function startOnReturnDay( $productId = null ) {
     68        $rule = $productId ? Stay::rule( (int) $productId ) : Stay::storeRule();
    6469        /**
    65          * Whether a date-only rental's return day is free for another rental to start on.
    66          * The value passed in is the store setting "Allow rentals to start on the same day
    67          * as they are returned" (default off). Stays of nights and rentals with a time of
    68          * day do not ask.
     70         * Whether a date-only rental's return day is free for another rental to start on: the
     71         * "Booked by" choice "Days: can go out again on the return day". The value passed in
     72         * is true when the product's "Booked by" (its own, else the store's "Date-only rentals
     73         * are booked by") is that choice. Return true to give a product that rule, false to
     74         * hold its return day. Until 1.0.15 the value was the store yes/no "Allow rentals to
     75         * start on the same day as they are returned", which is now that choice. Stays of nights
     76         * and rentals with a time of day do not ask.
    6977         *
    7078         * @since 1.0.13
    … …  
    7381         * @param int|null $productId
    7482         */
    75         return (bool) apply_filters( 'sibooking_start_on_return_day', (bool) Config::option( self::START_ON_RETURN_DAY ), $productId ? (int) $productId : null );
     83        return (bool) apply_filters( 'sibooking_start_on_return_day', Stay::DAYS_FREE === $rule, $productId ? (int) $productId : null );
    7684    }
    7785
    … …  
    155163     *                       on a later day than it starts is held to its end day's 00:00.
    156164     *                       Without 'start' the end day is held (the safe answer).
     165     *                       'returnDayFree' (the rule an order line was sold under,
     166     *                       Includes\Order::lineReturnDayFree()) wins over the product's
     167     *                       current "Booked by"; 'parent' (a kit's parent) rules its children.
    157168     */
    158169    public function calculateBufferAfterEndDate($productid,$endDateTime, array $context = []) {
    … …  
    198209
    199210    /**
    200      * Is a date-only rental's return day left free for the next rental? Only with the
    201      * store setting on (startOnReturnDay()) and the start known and on an earlier day.
     211     * Is a date-only rental's return day left free for the next rental? Only when it is
     212     * "Booked by" "Days: can go out again on the return day" (startOnReturnDay(), or the rule
     213     * the line was sold under) and the start is known and on an earlier day.
    202214     */
    203215    private static function returnDayIsFree($productid, Carbon $end, array $context) {
    … …  
    213225            return false;
    214226        }
    215         $settingsId = $productid ? PricingSubject::of((int) $productid) : null;
     227        if (isset($context['returnDayFree']) && null !== $context['returnDayFree']) {
     228            // the rule an order line was sold under (Includes\Order::lineReturnDayFree()) wins
     229            return (bool) $context['returnDayFree'];
     230        }
     231        // a kit's children follow the parent's rule, as for a stay of nights
     232        $ruleId = !empty($context['parent']) ? (int) $context['parent'] : (int) $productid;
     233        $settingsId = $ruleId ? PricingSubject::of($ruleId) : null;
    216234        return self::startOnReturnDay($settingsId);
    217235    }
  • sales-igniter-rentals-bookings-lite/trunk/Model/DataRemoval.php

    r3722835 r3729359  
    55/**
    66 * "Delete all rental data when the plugin is deleted" (review finding BOOT-05, and
    7  * Kevin's answer to §7 Q8: keep everything by default; deleting is a DOUBLE opt-in that
     7 * The owner's answer to §7 Q8: keep everything by default; deleting is a DOUBLE opt-in that
    88 * needs the confirmation typed).
    99 *
  • sales-igniter-rentals-bookings-lite/trunk/Model/DisabledDates.php

    r3728745 r3729359  
    130130    /**
    131131     * A monthly rule: its days of the month, every month. "The 25th to the 3rd" (the end's day
    132      * comes before the start's) runs from its start day into the next month (Kevin's decision,
     132     * comes before the start's) runs from its start day into the next month (the owner's decision,
    133133     * PRC-02 / the unified spec's R13); each occurrence used to end on the 3rd of its OWN month,
    134134     * before it began. A start on a day the month lacks (the 31st) overflows into the next
  • sales-igniter-rentals-bookings-lite/trunk/Model/FieldSlots.php

    r3722835 r3729359  
    4040        // Rentals > Settings, Calendar section: after the store's opening hours
    4141        'settings.store_hours'    => 'field_61d2bffe998a2',
     42        // Rentals > Settings, Pricing section (the first): after its last field, "Also charge
     43        // the return day" (1.0.15; an add-on's store pricing settings follow it)
     44        'settings.pricing'        => 'field_68f6a00000032',
    4245    ];
    4346
  • sales-igniter-rentals-bookings-lite/trunk/Model/Pricing/ConfigFactory.php

    r3728745 r3729359  
    1717 *    price) -> points with add-ons, priced by cheapest cover even when the add-on that registers
    1818 *    the type is off (PRC-15: the classic fallback derived a per-day rate from the shortest point);
    19  *  - guests: a per-person product books seats; whether infants take a seat (store setting).
     19 *  - guests: a per-person product books seats; whether infants take a seat (store setting);
     20 *  - "Also charge the return day" (the product's Pricing tab: store setting | no | yes, else the
     21 *    store's Settings > Pricing yes/no, default no) -> chargeReturnDay: a day window from the 1st
     22 *    to the 3rd is then 3 days instead of 2. Only day windows read it: a stay of nights is priced
     23 *    per night (the engine never charges the check-out day) and a timed rental per minute.
    2024 *
    2125 * Add-ons add their own inputs on `sibooking_pricing_config` (the Regular add-on: date rules as
    … …  
    3337        'sibooking_advanced_price',
    3438        'sibooking_guests_mode',
     39        self::CHARGE_RETURN_DAY,
    3540    ];
     41
     42    /** The product's "Also charge the return day": default (the store's) | no | yes. */
     43    const CHARGE_RETURN_DAY = 'sibooking_charge_return_day';
     44
     45    /** The store's "Also charge the return day" (Settings > Pricing, yes/no, default no). */
     46    const CHARGE_RETURN_DAY_DEFAULT = 'sibooking_charge_return_day_default';
     47
     48    /** @return bool the store's "Also charge the return day" */
     49    public static function storeChargesReturnDay() {
     50        return self::flag( Config::option( self::CHARGE_RETURN_DAY_DEFAULT ) );
     51    }
     52
     53    /**
     54     * Does a rental of this product booked by the day pay for its return day too? The product's
     55     * own Yes / No, else the store's. Stays of nights are priced per night whatever this says.
     56     *
     57     * @param int|string $productId a product or variation (the pricing subject answers)
     58     * @return bool
     59     */
     60    public static function chargesReturnDay( $productId ) {
     61        $id = PricingSubject::of( $productId );
     62        return self::returnDayFlag( $id ? Config::field( self::CHARGE_RETURN_DAY, $id ) : null, self::storeChargesReturnDay() );
     63    }
     64
     65    /**
     66     * Does the length shown to the customer ("3 days") count the return day of a date-only
     67     * rental of this product? Only where the engine charges it: unified pricing on (the
     68     * deprecated legacy pricing never charges the return day), not a stay of nights.
     69     *
     70     * @param int|string $productId
     71     * @return bool
     72     */
     73    public static function countsReturnDay( $productId ) {
     74        return Unified::enabled( $productId ) && ! \Sibooking\Model\Stay::nightly( $productId ) && self::chargesReturnDay( $productId );
     75    }
     76
     77    /**
     78     * @param mixed $own   the product's choice: 'yes' | 'no' | 'default' / empty (the store's)
     79     * @param bool  $store the store's answer
     80     */
     81    public static function returnDayFlag( $own, $store ) {
     82        if ( 'yes' === $own ) {
     83            return true;
     84        }
     85        if ( 'no' === $own ) {
     86            return false;
     87        }
     88        return (bool) $store;
     89    }
    3690
    3791    /**
    … …  
    53107            $fields['sibooking_advanced_price'] = PriceTypes::storedPoints( $id );
    54108        }
    55         $config = self::fromFields( $fields, [ 'sibooking_infants_take_seat' => Config::option( 'sibooking_infants_take_seat' ) ] );
     109        $config = self::fromFields( $fields, [
     110            'sibooking_infants_take_seat'        => Config::option( 'sibooking_infants_take_seat' ),
     111            self::CHARGE_RETURN_DAY_DEFAULT      => Config::option( self::CHARGE_RETURN_DAY_DEFAULT ),
     112        ] );
    56113
    57114        /**
    … …  
    71128    /**
    72129     * @param array $p     the product's fields (ACF names)
    73      * @param array $store store options: sibooking_infants_take_seat
     130     * @param array $store store options: sibooking_infants_take_seat, sibooking_charge_return_day_default
    74131     * @return array<string,mixed>
    75132     */
    76133    public static function fromFields( array $p, array $store = [] ) {
    77134        $c = [ 'rates' => [] ];
     135        if ( self::returnDayFlag( $p[ self::CHARGE_RETURN_DAY ] ?? null, self::flag( $store[ self::CHARGE_RETURN_DAY_DEFAULT ] ?? false ) ) ) {
     136            // "Also charge the return day": a day window 1st -> 3rd is 3 days (nights ignore it)
     137            $c['chargeReturnDay'] = true;
     138        }
    78139        if ( 'advanced' === ( $p['sibooking_price_type'] ?? 'simple' ) ) {
    79140            foreach ( self::rows( $p['sibooking_advanced_price'] ?? [] ) as $r ) {
  • sales-igniter-rentals-bookings-lite/trunk/Model/RateLimit.php

    r3728745 r3729359  
    44
    55/**
    6  * One rate limiter for the public shopper calls (review finding SEC-05, Kevin's answer
     6 * One rate limiter for the public shopper calls (review finding SEC-05, the owner's answer
    77 * to §7 Q10): the calendar data, the price quote, the global widget, the map data and
    88 * the "Where" search. They must stay open to visitors -- a shopper sees availability and
  • sales-igniter-rentals-bookings-lite/trunk/Model/ResponseCache.php

    r3728745 r3729359  
    44
    55/**
    6  * A short-lived cache for the public shopper calls (review finding SEC-05, Kevin's
     6 * A short-lived cache for the public shopper calls (review finding SEC-05, the owner's
    77 * answer to §7 Q10): the same question -- a product's availability, the price of a
    88 * rental, the map for an area -- answered from storage while nothing it depends on has
  • sales-igniter-rentals-bookings-lite/trunk/Model/Services.php

    r3728745 r3729359  
    4040            'Sibooking\\Ajax\\InventoryReport'           => self::ADMIN,
    4141            'Sibooking\\Ajax\\OrderDates'                => self::ADMIN,
     42            'Sibooking\\Includes\\BookedBy'              => self::ADMIN, // "Booked by" / "Also charge the return day" on the screens, the night-wording notice
    4243            'Sibooking\\Includes\\BookingEditAssets'     => self::ADMIN,
    4344            'Sibooking\\Includes\\Diagnostics'           => self::ADMIN,
  • sales-igniter-rentals-bookings-lite/trunk/Model/Stay.php

    r3728745 r3729359  
    1212 * nights = check-out - check-in, at least 1. No clock times anywhere.
    1313 *
    14  * Which products are sold by the night is this plugin's own setting: the product's "Stay
    15  * type" (store default | days | nights) and the store's "Stay type for date-only products"
    16  * (Includes\Stays registers both; days unless a shop picks nights). `sibooking_nightly` then
    17  * lets another plugin adjust the answer. The answer is recorded on each booking row
    18  * (`nightly`) when the order is written, so a product switched between the two rules never
    19  * changes what was already sold.
     14 * Which products are sold by the night is this plugin's own setting, "Booked by" (the owner,
     15 * 2026-10-05): the product's own (Calendar Rules tab: store setting | days | days_free |
     16 * nights) and the store's "Date-only rentals are booked by" (Settings > Calendar), stored in
     17 * the names the older "Stay type" fields used:
     18 *
     19 *  - `days`      Days: returned on the last day, entire return day is booked;
     20 *  - `days_free` Days: can go out again on the return day (Model\Buffer::startOnReturnDay(),
     21 *                what the store yes/no "Allow rentals to start on the same day as they are
     22 *                returned" did until 1.0.15; a store that had it on is switched over once,
     23 *                switchOver());
     24 *  - `nights`    Nights: same-day check-out and check-in (a stay, this class).
     25 *
     26 * Includes\Stays registers the fields. `sibooking_nightly` lets another plugin adjust the
     27 * Nights answer. The answer is recorded on each booking row (`nightly`) and order line when the
     28 * order is written, so a product switched between the rules never changes what was already sold.
    2029 *
    2130 * No init(): InitLoader skips it.
    … …  
    2332final class Stay {
    2433
    25     /** The product's Stay type: default (the store's) | days | nights. */
     34    /** The product's "Booked by": default (the store's) | days | days_free | nights. */
    2635    const TYPE = 'sibooking_stay_type';
    2736
    28     /** The store's Stay type for date-only products: days | nights. */
     37    /** The store's "Date-only rentals are booked by": days | days_free | nights. */
    2938    const TYPE_DEFAULT = 'sibooking_stay_type_default';
     39
     40    /** Days: returned on the last day, entire return day is booked. */
     41    const DAYS = 'days';
     42
     43    /** Days: can go out again on the return day. */
     44    const DAYS_FREE = 'days_free';
     45
     46    /** Nights: same-day check-out and check-in. */
     47    const NIGHTS = 'nights';
     48
     49    /** The three "Booked by" rules, in the order the settings offer them. */
     50    const RULES = [ self::DAYS, self::DAYS_FREE, self::NIGHTS ];
     51
     52    /**
     53     * Set once the store's old yes/no "Allow rentals to start on the same day as they are
     54     * returned" has been carried over into "Booked by" (switchOver()).
     55     */
     56    const SWITCHED_OPTION = 'sibooking_booked_by_switched';
     57
     58    /** The old store yes/no (kept, no longer read; Model\Buffer::START_ON_RETURN_DAY). */
     59    const OLD_RETURN_DAY_OPTION = 'sibooking_start_on_return_day';
    3060
    3161    /**
    … …  
    73103
    74104    /**
    75      * The shop's setting for a product: its own Stay type, else the store's. A product
     105     * The shop's setting for a product: booked by Nights, its own "Booked by" else the store's. A product
    76106     * another plugin rules out (`sibooking_product_allows_stay`) is never a stay.
    77107     *
    … …  
    81111    public static function configured( $productId ) {
    82112        $productId = (int) $productId;
    83         if ( ! $productId || ! self::allowed( $productId ) ) {
    84             return false;
    85         }
    86         $type = (string) Config::field( self::TYPE, $productId );
    87         if ( 'nights' === $type ) {
    88             return true;
    89         }
    90         if ( 'days' === $type ) {
    91             return false;
    92         }
    93         return 'nights' === (string) Config::option( self::TYPE_DEFAULT );
     113        if ( ! $productId ) {
     114            return false;
     115        }
     116        return self::NIGHTS === self::rule( $productId );
     117    }
     118
     119    /**
     120     * The store's "Date-only rentals are booked by".
     121     *
     122     * @return string days | days_free | nights (days when nothing valid is stored)
     123     */
     124    public static function storeRule() {
     125        $rule = (string) Config::option( self::TYPE_DEFAULT );
     126        return in_array( $rule, self::RULES, true ) ? $rule : self::DAYS;
     127    }
     128
     129    /**
     130     * A product's "Booked by" as the shop set it: its own choice, else the store's. A product
     131     * that may not be a stay (allowed()) is never Nights: it is held by the day. Not filtered:
     132     * Model\Stay::nightly() and Model\Buffer::startOnReturnDay() put their filters on top.
     133     *
     134     * @param int $productId a product or variation (a variation answers for its parent)
     135     * @return string days | days_free | nights
     136     */
     137    public static function rule( $productId ) {
     138        $id   = (int) $productId > 0 ? (int) Guests::fieldsId( (int) $productId ) : 0;
     139        $own  = $id ? (string) Config::field( self::TYPE, $id ) : '';
     140        $rule = in_array( $own, self::RULES, true ) ? $own : self::storeRule();
     141        if ( self::NIGHTS === $rule && $id && ! self::allowed( $id ) ) {
     142            return self::DAYS;
     143        }
     144        return $rule;
     145    }
     146
     147    /**
     148     * Once per site: a store that had the old yes/no "Allow rentals to start on the same day
     149     * as they are returned" ON while its date-only rentals were booked by the day now has
     150     * "Booked by" set to the middle choice, "Days: can go out again on the return day" -- the
     151     * same behaviour, now one of the three choices. A store booked by the night keeps Nights
     152     * (it already frees the check-out day). The old option is left as it is and no longer read.
     153     *
     154     * Runs at plugins_loaded (bootstrap.php) and costs one autoloaded option read once done.
     155     *
     156     * @return bool|null true when the store was switched, false when there was nothing to switch,
     157     *                   null when it had already run
     158     */
     159    public static function switchOver() {
     160        if ( get_option( self::SWITCHED_OPTION ) ) {
     161            return null;
     162        }
     163        $old     = get_option( 'options_' . self::OLD_RETURN_DAY_OPTION, null );
     164        $on      = in_array( $old, [ 1, '1', true, 'yes' ], true );
     165        $current = get_option( 'options_' . self::TYPE_DEFAULT, null );
     166        $days    = null === $current || '' === $current || false === $current || self::DAYS === $current;
     167        $switch  = $on && $days;
     168        if ( $switch ) {
     169            update_option( 'options_' . self::TYPE_DEFAULT, self::DAYS_FREE );
     170            update_option( '_options_' . self::TYPE_DEFAULT, \Sibooking\Includes\Stays::KEY_TYPE_DEFAULT );
     171        }
     172        update_option( self::SWITCHED_OPTION, [
     173            'time'     => time(),
     174            'version'  => defined( 'SIBOOKING_VERSION' ) ? SIBOOKING_VERSION : '',
     175            'switched' => $switch,
     176        ], true );
     177        return $switch;
    94178    }
    95179
    … …  
    119203     */
    120204    public static function storeUsesNights() {
    121         if ( 'nights' === (string) Config::option( self::TYPE_DEFAULT ) ) {
     205        if ( self::NIGHTS === self::storeRule() ) {
    122206            return true;
    123207        }
  • sales-igniter-rentals-bookings-lite/trunk/bootstrap.php

    r3728890 r3729359  
    2323 */
    2424define( 'SIBOOKING_URL', plugin_dir_url( __FILE__ ) );
    25 define( 'SIBOOKING_VERSION', '1.0.14' );
     25define( 'SIBOOKING_VERSION', '1.0.15' );
    2626/**
    2727 * The add-on contract's version (Sibooking\Api, review ARCH-01). Raised only when a method
    … …  
    145145        }
    146146        new \Sibooking\Model\InitLoader( $sibookinglite_libraries );
     147        // once per site (1.0.15): the old yes/no "Allow rentals to start on the same day as they are
     148        // returned" becomes the "Booked by" choice "Days: can go out again on the return day"
     149        \Sibooking\Model\Stay::switchOver();
    147150        // table upgrades: here on an admin page / WP-Cron / WP-CLI, queued to the background
    148151        // run otherwise (Model\Schema, review BOOT-03), which does them before its steps
  • sales-igniter-rentals-bookings-lite/trunk/composer.json

    r3728890 r3729359  
    99    "spatie/icalendar-generator": "^2.5"
    1010  },
    11   "version": "1.0.14",
     11  "version": "1.0.15",
    1212  "autoload": {
    1313    "psr-4": {
  • sales-igniter-rentals-bookings-lite/trunk/readme.txt

    r3728890 r3729359  
    55Tested up to: 7.1
    66Requires PHP: 8.1
    7 Stable tag: 1.0.14
     7Stable tag: 1.0.15
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    179179== Changelog ==
    180180
    181 = 1.0.14 =
    182 * Fixed: variable, composite and bundle rentals: the booking calendar loads again on sites with script debugging off (1.0.13). [Full changelog](https://rentalbookingsoftware.com/docs/lite-changelog/)
     181= 1.0.15 =
     182* "Booked by" (days with the return day booked, days that can go out again on the return day, or nights; for the store and per product) and "Also charge the return day"; the unified pricing engine (some rental prices change; Settings > Advanced has a legacy pricing switch), one date picker for grouped rentals, Shop Managers can run rentals, faster product pages, and calendar, cart and order-editor fixes. [Full changelog](https://rentalbookingsoftware.com/docs/lite-changelog/)
    183183
    184184== Upgrade Notice ==
  • sales-igniter-rentals-bookings-lite/trunk/sales-igniter-rentals-bookings-lite.php

    r3728890 r3729359  
    1010 * Plugin URI:        https://rentalbookingsoftware.com/free-woocommerce-rental-booking-plugin/
    1111 * Description:       Rent out products by date or by date and time from your WooCommerce store: availability calendar, quantity-based inventory, daily and hourly pricing, store rules and a bookings admin.
    12  * Version:           1.0.14
     12 * Version:           1.0.15
    1313 * Author:            Sales Igniter
    1414 * Author URI:        https://rentalbookingsoftware.com
  • sales-igniter-rentals-bookings-lite/trunk/vendor/composer/autoload_classmap.php

    r3728745 r3729359  
    88return array(
    99    'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
     10    'Sibooking\\ActivateDeactivate' => $baseDir . '/ActivateDeactivate.php',
    1011    'Sibooking\\Admin\\AddOrderColumns' => $baseDir . '/Admin/AddOrderColumns.php',
    1112    'Sibooking\\Admin\\AdminMenu' => $baseDir . '/Admin/AdminMenu.php',
    … …  
    2627    'Sibooking\\Ajax\\OrderDates' => $baseDir . '/Ajax/OrderDates.php',
    2728    'Sibooking\\Ajax\\Price' => $baseDir . '/Ajax/Price.php',
     29    'Sibooking\\Api' => $baseDir . '/Api.php',
    2830    'Sibooking\\Includes\\AcfTooltips' => $baseDir . '/Includes/AcfTooltips.php',
    2931    'Sibooking\\Includes\\AcfTranslations' => $baseDir . '/Includes/AcfTranslations.php',
    … …  
    3234    'Sibooking\\Includes\\AdminUi' => $baseDir . '/Includes/AdminUi.php',
    3335    'Sibooking\\Includes\\BidiText' => $baseDir . '/Includes/BidiText.php',
     36    'Sibooking\\Includes\\BookedBy' => $baseDir . '/Includes/BookedBy.php',
    3437    'Sibooking\\Includes\\BookingEditAssets' => $baseDir . '/Includes/BookingEditAssets.php',
    3538    'Sibooking\\Includes\\Calendar' => $baseDir . '/Includes/Calendar.php',
  • sales-igniter-rentals-bookings-lite/trunk/vendor/composer/autoload_static.php

    r3728745 r3729359  
    2323    public static $classMap = array (
    2424        'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
     25        'Sibooking\\ActivateDeactivate' => __DIR__ . '/../..' . '/ActivateDeactivate.php',
    2526        'Sibooking\\Admin\\AddOrderColumns' => __DIR__ . '/../..' . '/Admin/AddOrderColumns.php',
    2627        'Sibooking\\Admin\\AdminMenu' => __DIR__ . '/../..' . '/Admin/AdminMenu.php',
    … …  
    4142        'Sibooking\\Ajax\\OrderDates' => __DIR__ . '/../..' . '/Ajax/OrderDates.php',
    4243        'Sibooking\\Ajax\\Price' => __DIR__ . '/../..' . '/Ajax/Price.php',
     44        'Sibooking\\Api' => __DIR__ . '/../..' . '/Api.php',
    4345        'Sibooking\\Includes\\AcfTooltips' => __DIR__ . '/../..' . '/Includes/AcfTooltips.php',
    4446        'Sibooking\\Includes\\AcfTranslations' => __DIR__ . '/../..' . '/Includes/AcfTranslations.php',
    … …  
    4749        'Sibooking\\Includes\\AdminUi' => __DIR__ . '/../..' . '/Includes/AdminUi.php',
    4850        'Sibooking\\Includes\\BidiText' => __DIR__ . '/../..' . '/Includes/BidiText.php',
     51        'Sibooking\\Includes\\BookedBy' => __DIR__ . '/../..' . '/Includes/BookedBy.php',
    4952        'Sibooking\\Includes\\BookingEditAssets' => __DIR__ . '/../..' . '/Includes/BookingEditAssets.php',
    5053        'Sibooking\\Includes\\Calendar' => __DIR__ . '/../..' . '/Includes/Calendar.php',
Note: See TracChangeset for help on using the changeset viewer.