feat: accommodation booking type and status as dedicated properties
This commit is contained in:
1 parent
2cfa87f44b
commit
aa8fa5c1b2
22 files changed
+218
-151
No files matched your search
@@ -1,33 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 700 490" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g>
|
||||
<g>
|
||||
<defs>
|
||||
<mask id="Mask">
|
||||
|
||||
<g transform="matrix(0.00288044,8.84736e-05,8.84736e-05,-0.00288044,-0.5862,0.835135)"><image id="_Image2" width="700px" height="490px" xlink:href="data:image/png;base64,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"/>
|
||||
</g>
|
||||
</mask>
|
||||
</defs>
|
||||
<g mask="url(#Mask)">
|
||||
<path d="M194.119,305.842C276.606,300.464 338.37,307.962 339.484,308.146C443.176,325.422 503.557,327.571 540.702,324.728L540.867,319.638C514.426,320.951 478.688,316.871 434.918,311.559C400.804,307.419 306.697,277.408 195.03,276.09L194.119,305.842Z" style="fill:url(#_Linear1);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<path d="M197.896,191.813L263.503,143.048L280.105,147.93L308,116.748L343.653,164.795L368.768,174.295L433.766,244.544L364.027,189.537L348.682,194.693L311.065,147L294.029,164.035L268.834,159.229L238.564,191.454L239.319,171.647L197.896,191.813Z" style="fill:rgb(25,64,80);fill-rule:nonzero;"/>
|
||||
<path d="M38.408,306.474C32.18,326.891 27.643,336.159 27.643,336.159C142.218,243.379 280.978,259.659 329.465,268.035C309.049,261.755 276.356,253.993 236.312,252.714C233.474,252.619 230.617,252.577 227.737,252.577C164.07,252.577 90.101,274.674 38.408,306.474Z" style="fill:url(#_Linear3);fill-rule:nonzero;"/>
|
||||
<path d="M602.405,251.387C598.632,265.755 587.357,277.441 568.9,286.133C545.646,297.083 503.477,293.225 446.803,275.075C503.429,297.055 546.732,303.825 570.113,294.462C588.056,287.276 599.307,277.049 603.538,264.06C606.541,254.838 605.79,245.126 603.793,236.74C604.062,241.537 603.698,246.481 602.405,251.387Z" style="fill:url(#_Linear4);fill-rule:nonzero;"/>
|
||||
<path d="M605.397,211.094L598.994,215.409C599.174,215.674 617.073,242.823 609.679,268.117C605.51,282.372 593.852,293.765 575.037,301.985C550.593,312.652 506.299,307.123 446.929,285.984C401.072,269.647 362.435,249.627 362.114,249.457C360.612,248.668 215.374,173.608 69.773,226.274C77.162,248.414 43.719,292.66 43.719,292.66C200.142,196.079 356.618,273.877 358.332,274.656C497.597,337.936 553.312,328.53 581.212,316.131C655.404,283.16 606.303,212.439 605.397,211.094" style="fill:rgb(17,142,206);fill-rule:nonzero;"/>
|
||||
<path d="M600.278,217.512L606.147,212.228L600.581,202.639L578.087,165.139L562.014,162.674L523.519,106.019L514.326,111.057L492.375,98.148L455.456,140.096L390.467,39.17C390.467,39.17 377.747,37.716 367.194,35.266C356.622,32.81 330.597,14.462 330.597,14.462L227.896,119.816L206.63,102.138L79.887,212.658L205.945,124.457L222.915,143.594L339.412,39.199L361.863,104.782L369.352,79.049L419.68,129.184L437.972,178.766L453.331,174.016L458.28,217.927L509.42,132.182L532.48,143.533L563.393,178.941L577.921,181.66L595.665,210.07L600.278,217.512Z" style="fill:rgb(25,64,80);fill-rule:nonzero;"/>
|
||||
<path d="M598.991,215.408C599.175,215.672 617.07,242.822 609.681,268.116C605.511,282.37 593.854,293.764 575.038,301.984C551.165,312.4 508.335,307.334 451.085,287.418L450.377,310.289C524.455,332.854 560.505,325.328 581.209,316.13C655.4,283.159 606.305,212.438 605.398,211.092L598.991,215.408Z" style="fill:url(#_Linear5);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(141.308,0,0,141.308,702.346,466.324)">
|
||||
</g>
|
||||
<text x="-0.827px" y="466.324px" style="font-family:'BebasNeue-Regular', 'Bebas Neue', sans-serif;font-size:141.308px;fill:rgb(30,28,26);">A<tspan x="55.719px 104.212px 158.639px 185.652px 245.873px 297.051px 346.956px 403.22px 457.224px 507.27px 558.589px 585.602px 642.008px " y="466.324px 466.324px 466.324px 466.324px 466.324px 466.324px 466.324px 466.324px 466.324px 466.324px 466.324px 466.324px 466.324px ">LPINEVACATION</tspan></text>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(346.842,10.6534,-10.6534,346.842,194.415,296.129)"><stop offset="0" style="stop-color:rgb(188,187,186);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(86,85,83);stop-opacity:1"/></linearGradient>
|
||||
<linearGradient id="_Linear3" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(301.821,0,0,301.821,27.6449,294.368)"><stop offset="0" style="stop-color:rgb(56,165,72);stop-opacity:1"/><stop offset="0.04" style="stop-color:rgb(56,165,72);stop-opacity:1"/><stop offset="0.91" style="stop-color:rgb(163,198,57);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(163,198,57);stop-opacity:1"/></linearGradient>
|
||||
<linearGradient id="_Linear4" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(158.742,0,0,158.742,446.802,267.649)"><stop offset="0" style="stop-color:rgb(56,165,72);stop-opacity:1"/><stop offset="0.04" style="stop-color:rgb(56,165,72);stop-opacity:1"/><stop offset="0.91" style="stop-color:rgb(163,198,57);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(163,198,57);stop-opacity:1"/></linearGradient>
|
||||
<linearGradient id="_Linear5" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(170.152,5.22629,-5.22629,170.152,451.782,264.535)"><stop offset="0" style="stop-color:rgb(17,142,206);stop-opacity:1"/><stop offset="0.04" style="stop-color:rgb(17,142,206);stop-opacity:1"/><stop offset="0.91" style="stop-color:rgb(24,64,80);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(24,64,80);stop-opacity:1"/></linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 700 490" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><mask id="a"><image height="490" transform="matrix(.00288044 .00008847 .00008847 -.00288044 -.5862 .835135)" width="700" xlink:href="data:image/png;base64,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"/></mask><linearGradient id="b" gradientTransform="matrix(346.842 10.6534 -10.6534 346.842 194.415 296.129)" gradientUnits="userSpaceOnUse" x1="0" x2="1" y1="0" y2="0"><stop offset="0" stop-color="#bcbbba"/><stop offset="1" stop-color="#565553"/></linearGradient><linearGradient id="c"><stop offset="0" stop-color="#38a548"/><stop offset=".04" stop-color="#38a548"/><stop offset=".91" stop-color="#a3c639"/><stop offset="1" stop-color="#a3c639"/></linearGradient><linearGradient id="d" gradientUnits="userSpaceOnUse" x1="27.6449" x2="329.4659" xlink:href="#c" y1="294.368" y2="294.368"/><linearGradient id="e" gradientUnits="userSpaceOnUse" x1="446.802" x2="605.544" xlink:href="#c" y1="267.649" y2="267.649"/><linearGradient id="f" gradientTransform="matrix(170.152 5.22629 -5.22629 170.152 451.782 264.535)" gradientUnits="userSpaceOnUse" x1="0" x2="1" y1="0" y2="0"><stop offset="0" stop-color="#118ece"/><stop offset=".04" stop-color="#118ece"/><stop offset=".91" stop-color="#184050"/><stop offset="1" stop-color="#184050"/></linearGradient><g mask="url(#a)"><path d="m194.119 305.842c82.487-5.378 144.251 2.12 145.365 2.304 103.692 17.276 164.073 19.425 201.218 16.582l.165-5.09c-26.441 1.313-62.179-2.767-105.949-8.079-34.114-4.14-128.221-34.151-239.888-35.469z" fill="url(#b)" fill-rule="nonzero"/></g><g fill-rule="nonzero"><path d="m197.896 191.813 65.607-48.765 16.602 4.882 27.895-31.182 35.653 48.047 25.115 9.5 64.998 70.249-69.739-55.007-15.345 5.156-37.617-47.693-17.036 17.035-25.195-4.806-30.27 32.225.755-19.807z" fill="#194050"/><path d="m38.408 306.474c-6.228 20.417-10.765 29.685-10.765 29.685 114.575-92.78 253.335-76.5 301.822-68.124-20.416-6.28-53.109-14.042-93.153-15.321-2.838-.095-5.695-.137-8.575-.137-63.667 0-137.636 22.097-189.329 53.897z" fill="url(#d)"/><path d="m602.405 251.387c-3.773 14.368-15.048 26.054-33.505 34.746-23.254 10.95-65.423 7.092-122.097-11.058 56.626 21.98 99.929 28.75 123.31 19.387 17.943-7.186 29.194-17.413 33.425-30.402 3.003-9.222 2.252-18.934.255-27.32.269 4.797-.095 9.741-1.388 14.647z" fill="url(#e)"/><path d="m605.397 211.094-6.403 4.315c.18.265 18.079 27.414 10.685 52.708-4.169 14.255-15.827 25.648-34.642 33.868-24.444 10.667-68.738 5.138-128.108-16.001-45.857-16.337-84.494-36.357-84.815-36.527-Line truncated
|
||||
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 8.4 KiB |
@@ -1,33 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 700 490" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g>
|
||||
<g>
|
||||
<defs>
|
||||
<mask id="Mask">
|
||||
|
||||
<g transform="matrix(0.00288044,8.84736e-05,8.84736e-05,-0.00288044,-0.5862,0.835135)"><image id="_Image2" width="700px" height="490px" xlink:href="data:image/png;base64,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"/>
|
||||
</g>
|
||||
</mask>
|
||||
</defs>
|
||||
<g mask="url(#Mask)">
|
||||
<path d="M194.119,305.842C276.606,300.464 338.37,307.962 339.484,308.146C443.176,325.422 503.557,327.571 540.702,324.728L540.867,319.638C514.426,320.951 478.688,316.871 434.918,311.559C400.804,307.419 306.697,277.408 195.03,276.09L194.119,305.842Z" style="fill:url(#_Linear1);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<path d="M197.896,191.813L263.503,143.048L280.105,147.93L308,116.748L343.653,164.795L368.768,174.295L433.766,244.544L364.027,189.537L348.682,194.693L311.065,147L294.029,164.035L268.834,159.229L238.564,191.454L239.319,171.647L197.896,191.813Z" style="fill:rgb(25,64,80);fill-rule:nonzero;"/>
|
||||
<path d="M38.408,306.474C32.18,326.891 27.643,336.159 27.643,336.159C142.218,243.379 280.978,259.659 329.465,268.035C309.049,261.755 276.356,253.993 236.312,252.714C233.474,252.619 230.617,252.577 227.737,252.577C164.07,252.577 90.101,274.674 38.408,306.474Z" style="fill:url(#_Linear3);fill-rule:nonzero;"/>
|
||||
<path d="M602.405,251.387C598.632,265.755 587.357,277.441 568.9,286.133C545.646,297.083 503.477,293.225 446.803,275.075C503.429,297.055 546.732,303.825 570.113,294.462C588.056,287.276 599.307,277.049 603.538,264.06C606.541,254.838 605.79,245.126 603.793,236.74C604.062,241.537 603.698,246.481 602.405,251.387Z" style="fill:url(#_Linear4);fill-rule:nonzero;"/>
|
||||
<path d="M605.397,211.094L598.994,215.409C599.174,215.674 617.073,242.823 609.679,268.117C605.51,282.372 593.852,293.765 575.037,301.985C550.593,312.652 506.299,307.123 446.929,285.984C401.072,269.647 362.435,249.627 362.114,249.457C360.612,248.668 215.374,173.608 69.773,226.274C77.162,248.414 43.719,292.66 43.719,292.66C200.142,196.079 356.618,273.877 358.332,274.656C497.597,337.936 553.312,328.53 581.212,316.131C655.404,283.16 606.303,212.439 605.397,211.094" style="fill:rgb(17,142,206);fill-rule:nonzero;"/>
|
||||
<path d="M600.278,217.512L606.147,212.228L600.581,202.639L578.087,165.139L562.014,162.674L523.519,106.019L514.326,111.057L492.375,98.148L455.456,140.096L390.467,39.17C390.467,39.17 377.747,37.716 367.194,35.266C356.622,32.81 330.597,14.462 330.597,14.462L227.896,119.816L206.63,102.138L79.887,212.658L205.945,124.457L222.915,143.594L339.412,39.199L361.863,104.782L369.352,79.049L419.68,129.184L437.972,178.766L453.331,174.016L458.28,217.927L509.42,132.182L532.48,143.533L563.393,178.941L577.921,181.66L595.665,210.07L600.278,217.512Z" style="fill:rgb(25,64,80);fill-rule:nonzero;"/>
|
||||
<path d="M598.991,215.408C599.175,215.672 617.07,242.822 609.681,268.116C605.511,282.37 593.854,293.764 575.038,301.984C551.165,312.4 508.335,307.334 451.085,287.418L450.377,310.289C524.455,332.854 560.505,325.328 581.209,316.13C655.4,283.159 606.305,212.438 605.398,211.092L598.991,215.408Z" style="fill:url(#_Linear5);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(141.308,0,0,141.308,674.94,466.324)">
|
||||
</g>
|
||||
<text x="24.191px" y="466.324px" style="font-family:'BebasNeue-Regular', 'Bebas Neue', sans-serif;font-size:141.308px;fill:rgb(30,28,26);">C<tspan x="78.195px 126.687px 164.157px 215.476px 237.967px 288.721px 345.126px 401.814px 458.644px 485.657px 538.106px 589.425px 616.438px " y="466.324px 466.324px 466.324px 466.324px 466.324px 466.324px 466.324px 466.324px 466.324px 466.324px 466.324px 466.324px 466.324px ">LLT Touristik</tspan></text>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(346.842,10.6534,-10.6534,346.842,194.415,296.129)"><stop offset="0" style="stop-color:rgb(188,187,186);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(86,85,83);stop-opacity:1"/></linearGradient>
|
||||
<linearGradient id="_Linear3" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(301.821,0,0,301.821,27.6449,294.368)"><stop offset="0" style="stop-color:rgb(56,165,72);stop-opacity:1"/><stop offset="0.04" style="stop-color:rgb(56,165,72);stop-opacity:1"/><stop offset="0.91" style="stop-color:rgb(163,198,57);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(163,198,57);stop-opacity:1"/></linearGradient>
|
||||
<linearGradient id="_Linear4" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(158.742,0,0,158.742,446.802,267.649)"><stop offset="0" style="stop-color:rgb(56,165,72);stop-opacity:1"/><stop offset="0.04" style="stop-color:rgb(56,165,72);stop-opacity:1"/><stop offset="0.91" style="stop-color:rgb(163,198,57);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(163,198,57);stop-opacity:1"/></linearGradient>
|
||||
<linearGradient id="_Linear5" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(170.152,5.22629,-5.22629,170.152,451.782,264.535)"><stop offset="0" style="stop-color:rgb(17,142,206);stop-opacity:1"/><stop offset="0.04" style="stop-color:rgb(17,142,206);stop-opacity:1"/><stop offset="0.91" style="stop-color:rgb(24,64,80);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(24,64,80);stop-opacity:1"/></linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 700 490" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><mask id="a"><image height="490" transform="matrix(.00288044 .00008847 .00008847 -.00288044 -.5862 .835135)" width="700" xlink:href="data:image/png;base64,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"/></mask><linearGradient id="b" gradientTransform="matrix(346.842 10.6534 -10.6534 346.842 194.415 296.129)" gradientUnits="userSpaceOnUse" x1="0" x2="1" y1="0" y2="0"><stop offset="0" stop-color="#bcbbba"/><stop offset="1" stop-color="#565553"/></linearGradient><linearGradient id="c"><stop offset="0" stop-color="#38a548"/><stop offset=".04" stop-color="#38a548"/><stop offset=".91" stop-color="#a3c639"/><stop offset="1" stop-color="#a3c639"/></linearGradient><linearGradient id="d" gradientUnits="userSpaceOnUse" x1="27.6449" x2="329.4659" xlink:href="#c" y1="294.368" y2="294.368"/><linearGradient id="e" gradientUnits="userSpaceOnUse" x1="446.802" x2="605.544" xlink:href="#c" y1="267.649" y2="267.649"/><linearGradient id="f" gradientTransform="matrix(170.152 5.22629 -5.22629 170.152 451.782 264.535)" gradientUnits="userSpaceOnUse" x1="0" x2="1" y1="0" y2="0"><stop offset="0" stop-color="#118ece"/><stop offset=".04" stop-color="#118ece"/><stop offset=".91" stop-color="#184050"/><stop offset="1" stop-color="#184050"/></linearGradient><g mask="url(#a)"><path d="m194.119 305.842c82.487-5.378 144.251 2.12 145.365 2.304 103.692 17.276 164.073 19.425 201.218 16.582l.165-5.09c-26.441 1.313-62.179-2.767-105.949-8.079-34.114-4.14-128.221-34.151-239.888-35.469z" fill="url(#b)" fill-rule="nonzero"/></g><g fill-rule="nonzero"><path d="m197.896 191.813 65.607-48.765 16.602 4.882 27.895-31.182 35.653 48.047 25.115 9.5 64.998 70.249-69.739-55.007-15.345 5.156-37.617-47.693-17.036 17.035-25.195-4.806-30.27 32.225.755-19.807z" fill="#194050"/><path d="m38.408 306.474c-6.228 20.417-10.765 29.685-10.765 29.685 114.575-92.78 253.335-76.5 301.822-68.124-20.416-6.28-53.109-14.042-93.153-15.321-2.838-.095-5.695-.137-8.575-.137-63.667 0-137.636 22.097-189.329 53.897z" fill="url(#d)"/><path d="m602.405 251.387c-3.773 14.368-15.048 26.054-33.505 34.746-23.254 10.95-65.423 7.092-122.097-11.058 56.626 21.98 99.929 28.75 123.31 19.387 17.943-7.186 29.194-17.413 33.425-30.402 3.003-9.222 2.252-18.934.255-27.32.269 4.797-.095 9.741-1.388 14.647z" fill="url(#e)"/><path d="m605.397 211.094-6.403 4.315c.18.265 18.079 27.414 10.685 52.708-4.169 14.255-15.827 25.648-34.642 33.868-24.444 10.667-68.738 5.138-128.108-16.001-45.857-16.337-84.494-36.357-84.815-36.527-Line truncated
|
||||
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 9.2 KiB |
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20260806120000 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Split the accommodation booking status into a status and a type';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE accommodation_booking ADD type VARCHAR(20) DEFAULT NULL');
|
||||
|
||||
// An old 'booking' row with an accepted_at came from an accepted offer, so it is an
|
||||
// Anfrage; without one it was placed directly. Rows in 'draft'/'discarded' carry no
|
||||
// trace of their kind any more — they are mapped to 'inquiry', which is where the
|
||||
// office path produces them.
|
||||
$this->addSql("UPDATE accommodation_booking SET type = CASE
|
||||
WHEN status = 'booking' AND accepted_at IS NULL THEN 'booking'
|
||||
ELSE 'inquiry' END");
|
||||
|
||||
$this->addSql("UPDATE accommodation_booking SET status = CASE
|
||||
WHEN status = 'inquiry' THEN 'open'
|
||||
WHEN status = 'booking' THEN 'accepted'
|
||||
ELSE status END");
|
||||
|
||||
$this->addSql('ALTER TABLE accommodation_booking CHANGE type type VARCHAR(20) NOT NULL');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql("UPDATE accommodation_booking SET status = CASE
|
||||
WHEN status IN ('draft', 'discarded') THEN status
|
||||
WHEN type = 'inquiry' AND status = 'open' THEN 'inquiry'
|
||||
ELSE 'booking' END");
|
||||
|
||||
$this->addSql('ALTER TABLE accommodation_booking DROP type');
|
||||
}
|
||||
}
|
||||
@@ -127,9 +127,9 @@ class EditController extends AbstractController
|
||||
$this->entityManager->persist($booking);
|
||||
$this->entityManager->flush();
|
||||
|
||||
// Entering Anfrage or Buchung is what the customer needs to hear about: the offer
|
||||
// Entering Offen or Bestätigt is what the customer needs to hear about: the offer
|
||||
// becomes viewable, or the booking is confirmed. Entwurf and Absage stay silent.
|
||||
$notifiableStatuses = [AccommodationBookingStatus::Inquiry, AccommodationBookingStatus::Booking];
|
||||
$notifiableStatuses = [AccommodationBookingStatus::Open, AccommodationBookingStatus::Accepted];
|
||||
if ($previousStatus !== $booking->getStatus() && in_array($booking->getStatus(), $notifiableStatuses, true)) {
|
||||
$this->bookingService->issueAccessLink($booking);
|
||||
$this->bookingService->sendCustomerConfirmationEmail($booking);
|
||||
|
||||
@@ -93,7 +93,7 @@ class OfferController extends AbstractController
|
||||
return $this->redirectToOfferPage($request, $uuid);
|
||||
}
|
||||
|
||||
if (!$booking->isInquiry()) {
|
||||
if (!$booking->isInquiry() || !$booking->isOpen()) {
|
||||
return $this->redirectToOfferPage($request, $uuid);
|
||||
}
|
||||
|
||||
|
||||
@@ -153,7 +153,7 @@ class Step4Controller extends AbstractAccommodationController
|
||||
{
|
||||
$prices = $this->bookingService->loadPrices($dto, $accommodation);
|
||||
$booking = $this->bookingService->finalizeBooking($dto, $accommodation, $prices, $services);
|
||||
$this->addFlash('groups_booking_result', $booking->getStatus()->value);
|
||||
$this->addFlash('groups_booking_result', $booking->getType()->value);
|
||||
$this->sessionManager->clear($request);
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ use App\Entity\TimestampableEntity;
|
||||
use App\Entity\TimestampableEntityInterface;
|
||||
use App\Entity\User;
|
||||
use App\Enum\Groups\AccommodationBookingStatus;
|
||||
use App\Enum\Groups\AccommodationBookingType;
|
||||
use App\Enum\Groups\AdditionalServiceType;
|
||||
use App\Repository\Groups\AccommodationBookingRepository;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
@@ -79,6 +80,14 @@ class AccommodationBooking implements BlameableEntityInterface, TimestampableEnt
|
||||
#[ORM\Column(length: 20, enumType: AccommodationBookingStatus::class)]
|
||||
private AccommodationBookingStatus $status = AccommodationBookingStatus::Draft;
|
||||
|
||||
/**
|
||||
* What kind of record this is — an offer the customer still has to accept, or a
|
||||
* directly placed, binding booking. Independent of the status: an accepted offer
|
||||
* keeps its Inquiry type, which is how offer-originated bookings stay recognisable.
|
||||
*/
|
||||
#[ORM\Column(length: 20, enumType: AccommodationBookingType::class)]
|
||||
private AccommodationBookingType $type = AccommodationBookingType::Inquiry;
|
||||
|
||||
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
|
||||
private ?\DateTimeImmutable $accessLinkIssuedAt = null;
|
||||
|
||||
@@ -353,14 +362,14 @@ class AccommodationBooking implements BlameableEntityInterface, TimestampableEnt
|
||||
return AccommodationBookingStatus::Draft === $this->status;
|
||||
}
|
||||
|
||||
public function isInquiry(): bool
|
||||
public function isOpen(): bool
|
||||
{
|
||||
return AccommodationBookingStatus::Inquiry === $this->status;
|
||||
return AccommodationBookingStatus::Open === $this->status;
|
||||
}
|
||||
|
||||
public function isBooking(): bool
|
||||
public function isAccepted(): bool
|
||||
{
|
||||
return AccommodationBookingStatus::Booking === $this->status;
|
||||
return AccommodationBookingStatus::Accepted === $this->status;
|
||||
}
|
||||
|
||||
public function isDiscarded(): bool
|
||||
@@ -368,6 +377,28 @@ class AccommodationBooking implements BlameableEntityInterface, TimestampableEnt
|
||||
return AccommodationBookingStatus::Discarded === $this->status;
|
||||
}
|
||||
|
||||
public function getType(): AccommodationBookingType
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function setType(AccommodationBookingType $type): self
|
||||
{
|
||||
$this->type = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isInquiry(): bool
|
||||
{
|
||||
return AccommodationBookingType::Inquiry === $this->type;
|
||||
}
|
||||
|
||||
public function isBooking(): bool
|
||||
{
|
||||
return AccommodationBookingType::Booking === $this->type;
|
||||
}
|
||||
|
||||
public function getAccessLinkIssuedAt(): ?\DateTimeImmutable
|
||||
{
|
||||
return $this->accessLinkIssuedAt;
|
||||
|
||||
@@ -5,16 +5,16 @@ namespace App\Enum\Groups;
|
||||
enum AccommodationBookingStatus: string
|
||||
{
|
||||
case Draft = 'draft';
|
||||
case Inquiry = 'inquiry';
|
||||
case Booking = 'booking';
|
||||
case Open = 'open';
|
||||
case Accepted = 'accepted';
|
||||
case Discarded = 'discarded';
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Draft => 'Entwurf',
|
||||
self::Inquiry => 'Anfrage',
|
||||
self::Booking => 'Buchung',
|
||||
self::Open => 'Offen',
|
||||
self::Accepted => 'Bestätigt',
|
||||
self::Discarded => 'Absage',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enum\Groups;
|
||||
|
||||
enum AccommodationBookingType: string
|
||||
{
|
||||
case Inquiry = 'inquiry';
|
||||
case Booking = 'booking';
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Inquiry => 'Anfrage',
|
||||
self::Booking => 'Buchung',
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -53,8 +53,8 @@ class AccommodationBookingCreateType extends AbstractType
|
||||
'class' => AccommodationBookingStatus::class,
|
||||
'choices' => [
|
||||
AccommodationBookingStatus::Draft,
|
||||
AccommodationBookingStatus::Inquiry,
|
||||
AccommodationBookingStatus::Booking,
|
||||
AccommodationBookingStatus::Open,
|
||||
AccommodationBookingStatus::Accepted,
|
||||
],
|
||||
'choice_label' => fn (AccommodationBookingStatus $status) => $status->label(),
|
||||
])
|
||||
|
||||
@@ -17,6 +17,9 @@ final readonly class AccommodationBookingApiResponse
|
||||
#[Groups(['api:single'])]
|
||||
public string $status;
|
||||
|
||||
#[Groups(['api:single'])]
|
||||
public string $type;
|
||||
|
||||
#[Groups(['api:single'])]
|
||||
#[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])]
|
||||
public ?\DateTimeImmutable $dateFrom;
|
||||
@@ -89,6 +92,7 @@ final readonly class AccommodationBookingApiResponse
|
||||
{
|
||||
$this->uuid = $booking->getUuid();
|
||||
$this->status = $booking->getStatus()->value;
|
||||
$this->type = $booking->getType()->value;
|
||||
$this->dateFrom = $booking->getDateFrom();
|
||||
$this->dateTo = $booking->getDateTo();
|
||||
$this->nights = $booking->getNights();
|
||||
|
||||
@@ -11,6 +11,7 @@ use App\Entity\Groups\AccommodationPrice;
|
||||
use App\Entity\Groups\AdditionalService;
|
||||
use App\Entity\Groups\BoardService;
|
||||
use App\Enum\Groups\AccommodationBookingStatus;
|
||||
use App\Enum\Groups\AccommodationBookingType;
|
||||
use App\Form\Model\AccommodationBookingDto;
|
||||
use App\Model\AccommodationBookingQueryParams;
|
||||
use App\Model\CmsHotelData;
|
||||
@@ -265,11 +266,11 @@ class AccommodationBookingService
|
||||
$booking->setPaxCount($dto->paxCount);
|
||||
$booking->setMinorsCount($dto->minorsCount);
|
||||
$booking->setChildrenCount($dto->childrenCount);
|
||||
$booking->setStatus(
|
||||
$dto->forceInquiry || $this->computeInquiryStatus($dto, $prices)->isInquiry
|
||||
? AccommodationBookingStatus::Inquiry
|
||||
: AccommodationBookingStatus::Booking
|
||||
);
|
||||
// An inquiry arrives live — the office has nothing to prepare first, so it goes
|
||||
// straight to Offen. A direct booking is binding on arrival, hence Bestätigt.
|
||||
$isInquiry = $dto->forceInquiry || $this->computeInquiryStatus($dto, $prices)->isInquiry;
|
||||
$booking->setType($isInquiry ? AccommodationBookingType::Inquiry : AccommodationBookingType::Booking);
|
||||
$booking->setStatus($isInquiry ? AccommodationBookingStatus::Open : AccommodationBookingStatus::Accepted);
|
||||
|
||||
// Freeze board service as scalar fields (no FK)
|
||||
if (null !== $dto->selectedBoardServiceId) {
|
||||
@@ -432,9 +433,9 @@ class AccommodationBookingService
|
||||
}
|
||||
|
||||
/**
|
||||
* Issues the access link only for a confirmed booking. Inquiries from the public flow
|
||||
* Issues the access link only for a direct booking. Inquiries from the public flow
|
||||
* deliberately get no link — the office prepares the offer first and issues the link
|
||||
* when it switches the booking to Anfrage.
|
||||
* when it publishes the offer.
|
||||
*/
|
||||
public function issueAccessLinkForDirectBooking(AccommodationBooking $booking): void
|
||||
{
|
||||
@@ -452,7 +453,7 @@ class AccommodationBookingService
|
||||
*/
|
||||
public function sendCustomerConfirmationEmail(AccommodationBooking $booking): void
|
||||
{
|
||||
if (!$booking->isInquiry()) {
|
||||
if ($booking->isAccepted()) {
|
||||
$subject = 'Deine Buchung ist bestätigt';
|
||||
} elseif (null !== $booking->getAccessLinkIssuedAt()) {
|
||||
$subject = 'Dein Angebot ist bereit';
|
||||
@@ -481,16 +482,18 @@ class AccommodationBookingService
|
||||
}
|
||||
|
||||
/**
|
||||
* Transitions a booking from inquiry to booking and notifies office and customer.
|
||||
* Idempotent — a no-op (including no emails) if the booking is already accepted.
|
||||
* Accepts an open offer and notifies office and customer. The type stays Anfrage —
|
||||
* only the status moves to Bestätigt, so an offer-originated booking remains
|
||||
* distinguishable from a direct one.
|
||||
* Idempotent — a no-op (including no emails) if the offer is not open any more.
|
||||
*/
|
||||
public function acceptBooking(AccommodationBooking $booking): void
|
||||
{
|
||||
if (!$booking->isInquiry()) {
|
||||
if (!$booking->isInquiry() || !$booking->isOpen()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$booking->setStatus(AccommodationBookingStatus::Booking);
|
||||
$booking->setStatus(AccommodationBookingStatus::Accepted);
|
||||
$booking->setAcceptedAt(new \DateTimeImmutable());
|
||||
$this->entityManager->flush();
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ booking.status.label }}
|
||||
{{ booking.type.label }} · {{ booking.status.label }}
|
||||
</td>
|
||||
<td>
|
||||
{% set discounts = [] %}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
{% block content %}
|
||||
<twig:page:heading>
|
||||
{{ booking.status.label }}
|
||||
{{ booking.type.label }} · {{ booking.status.label }}
|
||||
{{ booking.groupName }}
|
||||
</twig:page:heading>
|
||||
|
||||
@@ -53,6 +53,8 @@
|
||||
<dt class="font-medium">davon Kinder (4–{{ booking.accommodation.maxAdolescentAge }} Jahre)</dt>
|
||||
<dd>{{ booking.childrenCount }}</dd>
|
||||
{% endif %}
|
||||
<dt class="font-medium">Art</dt>
|
||||
<dd>{{ booking.type.label }}</dd>
|
||||
<dt class="font-medium">Status</dt>
|
||||
<dd>{{ booking.status.label }}</dd>
|
||||
{% if booking.acceptedAt is not null %}
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
{% block body %}
|
||||
{% if accessLink %}
|
||||
<h1>{{ booking.inquiry ? 'Dein Angebot ist bereit' : 'Deine Buchung ist bestätigt' }}</h1>
|
||||
<h1>{{ booking.accepted ? 'Deine Buchung ist bestätigt' : 'Dein Angebot ist bereit' }}</h1>
|
||||
|
||||
<p>
|
||||
Hallo {{ booking.firstName }},
|
||||
{% if booking.inquiry %}
|
||||
{% if not booking.accepted %}
|
||||
vielen Dank für deine Anfrage! Wir haben sie geprüft — dein Angebot wartet auf dich.
|
||||
{% else %}
|
||||
vielen Dank! Deine Buchung ist bei uns eingegangen und bestätigt.
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
<p>
|
||||
<a href="{{ accessLink }}" class="button">
|
||||
{{ booking.inquiry ? 'Angebot ansehen & bestätigen' : 'Buchung ansehen' }}
|
||||
{{ booking.accepted ? 'Buchung ansehen' : 'Angebot ansehen & bestätigen' }}
|
||||
</a>
|
||||
</p>
|
||||
{% else %}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
{% import _self as macros %}
|
||||
|
||||
{% block title %}{{ booking.inquiry ? 'Dein Angebot' : 'Deine Buchung' }}{% endblock %}
|
||||
{% block title %}{{ booking.accepted ? 'Deine Buchung' : 'Dein Angebot' }}{% endblock %}
|
||||
|
||||
{% block background %}bg-outer bg-outer--summer{% endblock %}
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
|
||||
<hgroup>
|
||||
<h1 class="text-2xl lg:text-4xl font-bold mb-2">
|
||||
{{ booking.inquiry ? 'Dein Angebot' : 'Deine Buchung' }}
|
||||
{{ booking.accepted ? 'Deine Buchung' : 'Dein Angebot' }}
|
||||
</h1>
|
||||
<p class="text-2xl">
|
||||
{{ ctx.accommodation.name }}
|
||||
@@ -63,7 +63,7 @@
|
||||
{% if booking.boardServiceLabel or booking.additionalServices | length > 0 %}
|
||||
<div>
|
||||
<h2 class="text-xl font-bold mb-2">
|
||||
{{ booking.inquiry ? 'Angefragte' : 'Gebuchte' }} Leistungen
|
||||
{{ booking.accepted ? 'Gebuchte' : 'Angefragte' }} Leistungen
|
||||
</h2>
|
||||
<ul class="list-disc pl-4">
|
||||
{% if booking.boardServiceLabel %}
|
||||
@@ -76,7 +76,7 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if booking.inquiry %}
|
||||
{% if booking.inquiry and booking.open %}
|
||||
<div>
|
||||
<button type="button"
|
||||
class="button button--primary w-full"
|
||||
|
||||
@@ -25,32 +25,32 @@ use Symfony\Component\HttpFoundation\Response;
|
||||
*/
|
||||
class EditControllerTest extends TestCase
|
||||
{
|
||||
public function testTransitionToInquiryIssuesAccessLinkAndSendsCustomerEmail(): void
|
||||
public function testTransitionToOpenIssuesAccessLinkAndSendsCustomerEmail(): void
|
||||
{
|
||||
$bookingService = $this->assertNotified(AccommodationBookingStatus::Inquiry);
|
||||
$bookingService = $this->assertNotified(AccommodationBookingStatus::Open);
|
||||
|
||||
$this->submitStatusChange(AccommodationBookingStatus::Draft, AccommodationBookingStatus::Inquiry, $bookingService);
|
||||
$this->submitStatusChange(AccommodationBookingStatus::Draft, AccommodationBookingStatus::Open, $bookingService);
|
||||
}
|
||||
|
||||
public function testTransitionToBookingIssuesAccessLinkAndSendsCustomerEmail(): void
|
||||
public function testTransitionToAcceptedIssuesAccessLinkAndSendsCustomerEmail(): void
|
||||
{
|
||||
$bookingService = $this->assertNotified(AccommodationBookingStatus::Booking);
|
||||
$bookingService = $this->assertNotified(AccommodationBookingStatus::Accepted);
|
||||
|
||||
$this->submitStatusChange(AccommodationBookingStatus::Draft, AccommodationBookingStatus::Booking, $bookingService);
|
||||
$this->submitStatusChange(AccommodationBookingStatus::Draft, AccommodationBookingStatus::Accepted, $bookingService);
|
||||
}
|
||||
|
||||
public function testTransitionToDiscardedSendsNothing(): void
|
||||
{
|
||||
$bookingService = $this->assertNotNotified();
|
||||
|
||||
$this->submitStatusChange(AccommodationBookingStatus::Inquiry, AccommodationBookingStatus::Discarded, $bookingService);
|
||||
$this->submitStatusChange(AccommodationBookingStatus::Open, AccommodationBookingStatus::Discarded, $bookingService);
|
||||
}
|
||||
|
||||
public function testSavingWithoutStatusChangeSendsNothing(): void
|
||||
{
|
||||
$bookingService = $this->assertNotNotified();
|
||||
|
||||
$this->submitStatusChange(AccommodationBookingStatus::Booking, AccommodationBookingStatus::Booking, $bookingService);
|
||||
$this->submitStatusChange(AccommodationBookingStatus::Accepted, AccommodationBookingStatus::Accepted, $bookingService);
|
||||
}
|
||||
|
||||
private function assertNotified(AccommodationBookingStatus $expected): AccommodationBookingService
|
||||
|
||||
@@ -8,6 +8,7 @@ use App\Controller\Api\AccommodationBookingController;
|
||||
use App\Entity\Groups\Accommodation;
|
||||
use App\Entity\Groups\AccommodationBooking;
|
||||
use App\Enum\Groups\AccommodationBookingStatus;
|
||||
use App\Enum\Groups\AccommodationBookingType;
|
||||
use App\Repository\Groups\AccommodationBookingRepository;
|
||||
use App\Service\AccommodationBookingBreakdownCalculator;
|
||||
use App\Service\AccommodationBookingService;
|
||||
@@ -55,7 +56,8 @@ class AccommodationBookingControllerTest extends TestCase
|
||||
$booking->setAccommodationDiscount(10);
|
||||
$booking->setBoardServiceDiscount(20);
|
||||
$booking->setAdditionalServicesDiscount(30);
|
||||
$booking->setStatus(AccommodationBookingStatus::Booking);
|
||||
$booking->setStatus(AccommodationBookingStatus::Accepted);
|
||||
$booking->setType(AccommodationBookingType::Booking);
|
||||
$booking->setAcceptedAt(new \DateTimeImmutable('2026-07-15T10:00:00+00:00'));
|
||||
|
||||
$bookingRepository = $this->createMock(AccommodationBookingRepository::class);
|
||||
@@ -85,7 +87,8 @@ class AccommodationBookingControllerTest extends TestCase
|
||||
self::assertSame(Response::HTTP_OK, $response->getStatusCode());
|
||||
self::assertSame([
|
||||
'uuid' => $booking->getUuid(),
|
||||
'status' => 'booking',
|
||||
'status' => 'accepted',
|
||||
'type' => 'booking',
|
||||
'dateFrom' => '2026-07-20',
|
||||
'dateTo' => '2026-07-25',
|
||||
'nights' => 5,
|
||||
@@ -135,7 +138,7 @@ class AccommodationBookingControllerTest extends TestCase
|
||||
self::assertArrayNotHasKey('currency', $payload['accommodation']);
|
||||
}
|
||||
|
||||
public function testSingleReturnsInquiryStatusWhenBookingIsInquiry(): void
|
||||
public function testSingleReturnsOpenStatusAndInquiryTypeForAnOffer(): void
|
||||
{
|
||||
$booking = new AccommodationBooking();
|
||||
$booking->setDateFrom(new \DateTimeImmutable('2026-08-01'));
|
||||
@@ -144,7 +147,8 @@ class AccommodationBookingControllerTest extends TestCase
|
||||
$booking->setFirstName('Tom');
|
||||
$booking->setLastName('Beispiel');
|
||||
$booking->setEmail('[email protected]');
|
||||
$booking->setStatus(AccommodationBookingStatus::Inquiry);
|
||||
$booking->setStatus(AccommodationBookingStatus::Open);
|
||||
$booking->setType(AccommodationBookingType::Inquiry);
|
||||
|
||||
$bookingRepository = $this->createMock(AccommodationBookingRepository::class);
|
||||
$bookingRepository
|
||||
@@ -160,7 +164,8 @@ class AccommodationBookingControllerTest extends TestCase
|
||||
$response = $controller->single($booking->getUuid());
|
||||
$payload = json_decode((string) $response->getContent(), true, 512, JSON_THROW_ON_ERROR);
|
||||
|
||||
self::assertSame('inquiry', $payload['status']);
|
||||
self::assertSame('open', $payload['status']);
|
||||
self::assertSame('inquiry', $payload['type']);
|
||||
self::assertNull($payload['priceBreakdown']);
|
||||
self::assertNull($payload['acceptedAt']);
|
||||
}
|
||||
@@ -186,7 +191,7 @@ class AccommodationBookingControllerTest extends TestCase
|
||||
self::assertSame(['message' => 'Not found'], $payload);
|
||||
}
|
||||
|
||||
public function testAcceptTransitionsInquiryToBookingAndSetsAcceptedAt(): void
|
||||
public function testAcceptAcceptsOpenOfferAndSetsAcceptedAt(): void
|
||||
{
|
||||
$booking = new AccommodationBooking();
|
||||
$booking->setDateFrom(new \DateTimeImmutable('2026-08-01'));
|
||||
@@ -195,7 +200,7 @@ class AccommodationBookingControllerTest extends TestCase
|
||||
$booking->setFirstName('Tom');
|
||||
$booking->setLastName('Beispiel');
|
||||
$booking->setEmail('[email protected]');
|
||||
$booking->setStatus(AccommodationBookingStatus::Inquiry);
|
||||
$booking->setStatus(AccommodationBookingStatus::Open);
|
||||
|
||||
$bookingRepository = $this->createMock(AccommodationBookingRepository::class);
|
||||
$bookingRepository
|
||||
@@ -213,7 +218,7 @@ class AccommodationBookingControllerTest extends TestCase
|
||||
->method('acceptBooking')
|
||||
->with($booking)
|
||||
->willReturnCallback(static function (AccommodationBooking $b): void {
|
||||
$b->setStatus(AccommodationBookingStatus::Booking);
|
||||
$b->setStatus(AccommodationBookingStatus::Accepted);
|
||||
$b->setAcceptedAt(new \DateTimeImmutable());
|
||||
});
|
||||
|
||||
@@ -223,8 +228,9 @@ class AccommodationBookingControllerTest extends TestCase
|
||||
$payload = json_decode((string) $response->getContent(), true, 512, JSON_THROW_ON_ERROR);
|
||||
|
||||
self::assertSame(Response::HTTP_OK, $response->getStatusCode());
|
||||
self::assertSame('booking', $payload['status']);
|
||||
self::assertSame(AccommodationBookingStatus::Booking, $booking->getStatus());
|
||||
self::assertSame('accepted', $payload['status']);
|
||||
self::assertSame('inquiry', $payload['type']);
|
||||
self::assertSame(AccommodationBookingStatus::Accepted, $booking->getStatus());
|
||||
self::assertNotNull($booking->getAcceptedAt());
|
||||
self::assertSame($booking->getAcceptedAt()->format(\DATE_ATOM), $payload['acceptedAt']);
|
||||
}
|
||||
@@ -240,7 +246,7 @@ class AccommodationBookingControllerTest extends TestCase
|
||||
$booking->setFirstName('Tom');
|
||||
$booking->setLastName('Beispiel');
|
||||
$booking->setEmail('[email protected]');
|
||||
$booking->setStatus(AccommodationBookingStatus::Booking);
|
||||
$booking->setStatus(AccommodationBookingStatus::Accepted);
|
||||
$booking->setAcceptedAt($acceptedAt);
|
||||
|
||||
$bookingRepository = $this->createMock(AccommodationBookingRepository::class);
|
||||
@@ -261,7 +267,7 @@ class AccommodationBookingControllerTest extends TestCase
|
||||
$payload = json_decode((string) $response->getContent(), true, 512, JSON_THROW_ON_ERROR);
|
||||
|
||||
self::assertSame(Response::HTTP_OK, $response->getStatusCode());
|
||||
self::assertSame('booking', $payload['status']);
|
||||
self::assertSame('accepted', $payload['status']);
|
||||
self::assertSame($acceptedAt, $booking->getAcceptedAt());
|
||||
self::assertSame($acceptedAt->format(\DATE_ATOM), $payload['acceptedAt']);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ class OfferControllerTest extends TestCase
|
||||
public function testAccessAuthorizesSessionAndRedirectsToView(): void
|
||||
{
|
||||
$booking = new AccommodationBooking();
|
||||
$booking->setStatus(AccommodationBookingStatus::Inquiry);
|
||||
$booking->setStatus(AccommodationBookingStatus::Open);
|
||||
|
||||
$bookingRepository = $this->createMock(AccommodationBookingRepository::class);
|
||||
$bookingRepository->method('findOneBy')->with(['uuid' => $booking->getUuid()])->willReturn($booking);
|
||||
@@ -151,7 +151,7 @@ class OfferControllerTest extends TestCase
|
||||
public function testViewRendersOfferWhenSessionAuthorized(): void
|
||||
{
|
||||
$booking = new AccommodationBooking();
|
||||
$booking->setStatus(AccommodationBookingStatus::Inquiry);
|
||||
$booking->setStatus(AccommodationBookingStatus::Open);
|
||||
$booking->setAccommodation(new Accommodation());
|
||||
|
||||
$bookingRepository = $this->createMock(AccommodationBookingRepository::class);
|
||||
@@ -207,7 +207,7 @@ class OfferControllerTest extends TestCase
|
||||
public function testConfirmGetRendersModalWithFreshForm(): void
|
||||
{
|
||||
$booking = new AccommodationBooking();
|
||||
$booking->setStatus(AccommodationBookingStatus::Inquiry);
|
||||
$booking->setStatus(AccommodationBookingStatus::Open);
|
||||
|
||||
$bookingRepository = $this->createMock(AccommodationBookingRepository::class);
|
||||
$bookingRepository->method('findOneBy')->with(['uuid' => $booking->getUuid()])->willReturn($booking);
|
||||
@@ -238,7 +238,7 @@ class OfferControllerTest extends TestCase
|
||||
public function testConfirmPostValidAcceptsBookingAndRedirects(): void
|
||||
{
|
||||
$booking = new AccommodationBooking();
|
||||
$booking->setStatus(AccommodationBookingStatus::Inquiry);
|
||||
$booking->setStatus(AccommodationBookingStatus::Open);
|
||||
|
||||
$bookingRepository = $this->createMock(AccommodationBookingRepository::class);
|
||||
$bookingRepository->method('findOneBy')->willReturn($booking);
|
||||
@@ -273,7 +273,7 @@ class OfferControllerTest extends TestCase
|
||||
public function testConfirmPostValidViaHtmxReturnsHxRedirect(): void
|
||||
{
|
||||
$booking = new AccommodationBooking();
|
||||
$booking->setStatus(AccommodationBookingStatus::Inquiry);
|
||||
$booking->setStatus(AccommodationBookingStatus::Open);
|
||||
|
||||
$bookingRepository = $this->createMock(AccommodationBookingRepository::class);
|
||||
$bookingRepository->method('findOneBy')->willReturn($booking);
|
||||
@@ -308,7 +308,7 @@ class OfferControllerTest extends TestCase
|
||||
public function testConfirmPostInvalidReRendersModalWithoutAccepting(): void
|
||||
{
|
||||
$booking = new AccommodationBooking();
|
||||
$booking->setStatus(AccommodationBookingStatus::Inquiry);
|
||||
$booking->setStatus(AccommodationBookingStatus::Open);
|
||||
|
||||
$bookingRepository = $this->createMock(AccommodationBookingRepository::class);
|
||||
$bookingRepository->method('findOneBy')->willReturn($booking);
|
||||
@@ -342,7 +342,7 @@ class OfferControllerTest extends TestCase
|
||||
public function testConfirmRedirectsWhenAlreadyAccepted(): void
|
||||
{
|
||||
$booking = new AccommodationBooking();
|
||||
$booking->setStatus(AccommodationBookingStatus::Booking);
|
||||
$booking->setStatus(AccommodationBookingStatus::Accepted);
|
||||
|
||||
$bookingRepository = $this->createMock(AccommodationBookingRepository::class);
|
||||
$bookingRepository->method('findOneBy')->willReturn($booking);
|
||||
@@ -462,7 +462,7 @@ final class TestableOfferController extends OfferController
|
||||
$this->renderedParameters = $parameters;
|
||||
|
||||
if ('groups/booking/offer.html.twig' === $view) {
|
||||
$content = $parameters['booking']->isInquiry() ? '<html>offer</html>' : '<html>confirmed</html>';
|
||||
$content = $parameters['booking']->isAccepted() ? '<html>confirmed</html>' : '<html>offer</html>';
|
||||
} else {
|
||||
$content = '<html>unavailable</html>';
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace App\Tests\Controller\Groups;
|
||||
use App\Controller\Groups\Step4Controller;
|
||||
use App\Entity\Groups\Accommodation;
|
||||
use App\Entity\Groups\AccommodationBooking;
|
||||
use App\Enum\Groups\AccommodationBookingStatus;
|
||||
use App\Enum\Groups\AccommodationBookingType;
|
||||
use App\Form\Model\AccommodationBookingDto;
|
||||
use App\Service\AccommodationBookingService;
|
||||
use App\Service\AccommodationSessionManager;
|
||||
@@ -102,7 +102,7 @@ class Step4ControllerTest extends TestCase
|
||||
->setCalendarCode('HOTEL')
|
||||
->setMaxAdolescentAge(17);
|
||||
|
||||
$booking = (new AccommodationBooking())->setStatus(AccommodationBookingStatus::Inquiry);
|
||||
$booking = (new AccommodationBooking())->setType(AccommodationBookingType::Inquiry);
|
||||
|
||||
$bookingService = $this->createMock(AccommodationBookingService::class);
|
||||
$bookingService->method('loadAccommodation')->with(1)->willReturn($accommodation);
|
||||
@@ -275,7 +275,7 @@ class Step4ControllerTest extends TestCase
|
||||
->setCalendarCode('HOTEL')
|
||||
->setMaxAdolescentAge(17);
|
||||
|
||||
$booking = (new AccommodationBooking())->setStatus(AccommodationBookingStatus::Booking);
|
||||
$booking = (new AccommodationBooking())->setType(AccommodationBookingType::Booking);
|
||||
|
||||
$bookingService = $this->createMock(AccommodationBookingService::class);
|
||||
$bookingService->method('loadAccommodation')->with(1)->willReturn($accommodation);
|
||||
|
||||
@@ -47,8 +47,8 @@ class AccommodationBookingTypeTest extends TestCase
|
||||
*/
|
||||
public static function customerFacingStatuses(): iterable
|
||||
{
|
||||
yield 'inquiry' => [AccommodationBookingStatus::Inquiry];
|
||||
yield 'booking' => [AccommodationBookingStatus::Booking];
|
||||
yield 'open' => [AccommodationBookingStatus::Open];
|
||||
yield 'accepted' => [AccommodationBookingStatus::Accepted];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,7 +67,7 @@ class AccommodationBookingTypeTest extends TestCase
|
||||
public function testPromotedBookingRequiresContactData(): void
|
||||
{
|
||||
$booking = new AccommodationBooking();
|
||||
$booking->setStatus(AccommodationBookingStatus::Inquiry);
|
||||
$booking->setStatus(AccommodationBookingStatus::Open);
|
||||
|
||||
$violations = $this->validate($booking, ['Default', 'edit']);
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ use App\Entity\Groups\Accommodation;
|
||||
use App\Entity\Groups\AccommodationBooking;
|
||||
use App\Entity\Groups\AccommodationPrice;
|
||||
use App\Enum\Groups\AccommodationBookingStatus;
|
||||
use App\Enum\Groups\AccommodationBookingType;
|
||||
use App\Model\AccommodationBookingQueryParams;
|
||||
use App\Repository\Groups\AccommodationPriceRepository;
|
||||
use App\Repository\Groups\AccommodationRepository;
|
||||
@@ -66,7 +67,7 @@ class AccommodationBookingServiceTest extends TestCase
|
||||
$service = $this->createServiceWithAccommodation(entityManager: $entityManager);
|
||||
|
||||
$booking = new AccommodationBooking();
|
||||
$booking->setStatus(AccommodationBookingStatus::Booking);
|
||||
$booking->setType(AccommodationBookingType::Booking);
|
||||
|
||||
$service->issueAccessLinkForDirectBooking($booking);
|
||||
|
||||
@@ -81,7 +82,7 @@ class AccommodationBookingServiceTest extends TestCase
|
||||
$service = $this->createServiceWithAccommodation(entityManager: $entityManager);
|
||||
|
||||
$booking = new AccommodationBooking();
|
||||
$booking->setStatus(AccommodationBookingStatus::Inquiry);
|
||||
$booking->setType(AccommodationBookingType::Inquiry);
|
||||
|
||||
$service->issueAccessLinkForDirectBooking($booking);
|
||||
|
||||
@@ -96,7 +97,7 @@ class AccommodationBookingServiceTest extends TestCase
|
||||
$service = $this->createServiceWithAccommodation(entityManager: $entityManager);
|
||||
|
||||
$booking = new AccommodationBooking();
|
||||
$booking->setStatus(AccommodationBookingStatus::Booking);
|
||||
$booking->setType(AccommodationBookingType::Booking);
|
||||
$issuedAt = new \DateTimeImmutable('2026-01-01');
|
||||
$booking->setAccessLinkIssuedAt($issuedAt);
|
||||
|
||||
@@ -113,7 +114,7 @@ class AccommodationBookingServiceTest extends TestCase
|
||||
$service = $this->createServiceWithAccommodation(entityManager: $entityManager);
|
||||
|
||||
$booking = new AccommodationBooking();
|
||||
$booking->setStatus(AccommodationBookingStatus::Inquiry);
|
||||
$booking->setStatus(AccommodationBookingStatus::Open);
|
||||
|
||||
$service->issueAccessLink($booking);
|
||||
|
||||
@@ -128,7 +129,7 @@ class AccommodationBookingServiceTest extends TestCase
|
||||
$service = $this->createServiceWithAccommodation(entityManager: $entityManager);
|
||||
|
||||
$booking = new AccommodationBooking();
|
||||
$booking->setStatus(AccommodationBookingStatus::Inquiry);
|
||||
$booking->setStatus(AccommodationBookingStatus::Open);
|
||||
$issuedAt = new \DateTimeImmutable('2026-01-01');
|
||||
$booking->setAccessLinkIssuedAt($issuedAt);
|
||||
|
||||
@@ -141,7 +142,7 @@ class AccommodationBookingServiceTest extends TestCase
|
||||
{
|
||||
$booking = new AccommodationBooking();
|
||||
$booking->setEmail('[email protected]');
|
||||
$booking->setStatus(AccommodationBookingStatus::Inquiry);
|
||||
$booking->setStatus(AccommodationBookingStatus::Open);
|
||||
$booking->setAccessLinkIssuedAt(new \DateTimeImmutable());
|
||||
|
||||
$linkSigner = $this->createMock(AccommodationBookingLinkSigner::class);
|
||||
@@ -168,7 +169,7 @@ class AccommodationBookingServiceTest extends TestCase
|
||||
{
|
||||
$booking = new AccommodationBooking();
|
||||
$booking->setEmail('[email protected]');
|
||||
$booking->setStatus(AccommodationBookingStatus::Booking);
|
||||
$booking->setStatus(AccommodationBookingStatus::Accepted);
|
||||
$booking->setAccessLinkIssuedAt(new \DateTimeImmutable());
|
||||
|
||||
$linkSigner = $this->createMock(AccommodationBookingLinkSigner::class);
|
||||
@@ -197,7 +198,7 @@ class AccommodationBookingServiceTest extends TestCase
|
||||
{
|
||||
$booking = new AccommodationBooking();
|
||||
$booking->setEmail('[email protected]');
|
||||
$booking->setStatus(AccommodationBookingStatus::Inquiry);
|
||||
$booking->setStatus(AccommodationBookingStatus::Open);
|
||||
|
||||
$breakdownCalculator = $this->createMock(AccommodationBookingBreakdownCalculator::class);
|
||||
$breakdownCalculator->method('compute')->willReturn(null);
|
||||
@@ -220,7 +221,7 @@ class AccommodationBookingServiceTest extends TestCase
|
||||
{
|
||||
$booking = new AccommodationBooking();
|
||||
$booking->setEmail('[email protected]');
|
||||
$booking->setStatus(AccommodationBookingStatus::Inquiry);
|
||||
$booking->setStatus(AccommodationBookingStatus::Open);
|
||||
|
||||
$breakdownCalculator = $this->createMock(AccommodationBookingBreakdownCalculator::class);
|
||||
$breakdownCalculator->method('compute')->willReturn(null);
|
||||
@@ -240,7 +241,7 @@ class AccommodationBookingServiceTest extends TestCase
|
||||
{
|
||||
$booking = new AccommodationBooking();
|
||||
$booking->setEmail('[email protected]');
|
||||
$booking->setStatus(AccommodationBookingStatus::Inquiry);
|
||||
$booking->setStatus(AccommodationBookingStatus::Open);
|
||||
$previousIssuedAt = new \DateTimeImmutable('2026-01-01');
|
||||
$booking->setAccessLinkIssuedAt($previousIssuedAt);
|
||||
|
||||
@@ -264,13 +265,13 @@ class AccommodationBookingServiceTest extends TestCase
|
||||
self::assertNotSame($previousIssuedAt, $booking->getAccessLinkIssuedAt());
|
||||
}
|
||||
|
||||
public function testAcceptBookingTransitionsInquiryToBookingAndSendsNotifications(): void
|
||||
public function testAcceptBookingAcceptsOpenInquiryAndSendsNotifications(): void
|
||||
{
|
||||
$entityManager = $this->createMock(EntityManagerInterface::class);
|
||||
$entityManager->expects(self::once())->method('flush');
|
||||
|
||||
$booking = new AccommodationBooking();
|
||||
$booking->setStatus(AccommodationBookingStatus::Inquiry);
|
||||
$booking->setStatus(AccommodationBookingStatus::Open);
|
||||
$booking->setEmail('[email protected]');
|
||||
|
||||
$mailer = $this->createMock(Mailer::class);
|
||||
@@ -288,7 +289,8 @@ class AccommodationBookingServiceTest extends TestCase
|
||||
|
||||
$service->acceptBooking($booking);
|
||||
|
||||
self::assertSame(AccommodationBookingStatus::Booking, $booking->getStatus());
|
||||
self::assertSame(AccommodationBookingStatus::Accepted, $booking->getStatus());
|
||||
self::assertSame(AccommodationBookingType::Inquiry, $booking->getType(), 'an accepted offer stays an Anfrage');
|
||||
self::assertNotNull($booking->getAcceptedAt());
|
||||
}
|
||||
|
||||
@@ -303,17 +305,37 @@ class AccommodationBookingServiceTest extends TestCase
|
||||
$service = $this->createServiceWithAccommodation(entityManager: $entityManager, mailer: $mailer);
|
||||
|
||||
$booking = new AccommodationBooking();
|
||||
$booking->setStatus(AccommodationBookingStatus::Booking);
|
||||
$booking->setStatus(AccommodationBookingStatus::Accepted);
|
||||
|
||||
$service->acceptBooking($booking);
|
||||
|
||||
self::assertNull($booking->getAcceptedAt());
|
||||
}
|
||||
|
||||
public function testAcceptBookingNoOpsForOfferThatIsNotOpenYet(): void
|
||||
{
|
||||
$entityManager = $this->createMock(EntityManagerInterface::class);
|
||||
$entityManager->expects(self::never())->method('flush');
|
||||
|
||||
$mailer = $this->createMock(Mailer::class);
|
||||
$mailer->expects(self::never())->method('createAndSendEmail');
|
||||
|
||||
$service = $this->createServiceWithAccommodation(entityManager: $entityManager, mailer: $mailer);
|
||||
|
||||
$booking = new AccommodationBooking();
|
||||
$booking->setType(AccommodationBookingType::Inquiry);
|
||||
$booking->setStatus(AccommodationBookingStatus::Draft);
|
||||
|
||||
$service->acceptBooking($booking);
|
||||
|
||||
self::assertSame(AccommodationBookingStatus::Draft, $booking->getStatus());
|
||||
self::assertNull($booking->getAcceptedAt());
|
||||
}
|
||||
|
||||
public function testSendOfferAcceptedNotificationEmailLogsAndSwallowsMailerFailures(): void
|
||||
{
|
||||
$booking = new AccommodationBooking();
|
||||
$booking->setStatus(AccommodationBookingStatus::Booking);
|
||||
$booking->setStatus(AccommodationBookingStatus::Accepted);
|
||||
|
||||
$mailer = $this->createMock(Mailer::class);
|
||||
$mailer->method('createAndSendEmail')->willThrowException(new \RuntimeException('SMTP down'));
|
||||
@@ -330,7 +352,7 @@ class AccommodationBookingServiceTest extends TestCase
|
||||
{
|
||||
$booking = new AccommodationBooking();
|
||||
$booking->setEmail('[email protected]');
|
||||
$booking->setStatus(AccommodationBookingStatus::Booking);
|
||||
$booking->setStatus(AccommodationBookingStatus::Accepted);
|
||||
|
||||
$mailer = $this->createMock(Mailer::class);
|
||||
$mailer->method('createAndSendEmail')->willThrowException(new \RuntimeException('SMTP down'));
|
||||
|
||||
Reference in new issue
Block a user