Catering, and a pure Java/Spring site: Thymeleaf front to back #10

Merged
austin merged 22 commits from feature/catering-tables into main 2026-07-26 21:26:57 -05:00
2 changed files with 113 additions and 0 deletions
Showing only changes of commit 04cd1d5bea - Show all commits
@@ -0,0 +1,95 @@
-- The wedding table's lines, read the way the spreadsheet meant them.
--
-- V4 carried that table over literally, because the source is offset: the prices sit on the "cupcakes"
-- line, and "4 dz cc or sc" sits on the "12x17 bars" line. Taken at face value it produced five lines,
-- two of which ("Cupcakes", "Sugar cookies") had no quantity in any column and rendered as a row of
-- dashes on the page.
--
-- Reading it as a baker would: a 12x17 pan IS the sheet pan, so "Sheet cakes" and "12x17 bars" are one
-- offering, and "cc or sc" is cupcakes-or-sugar-cookies, which is what the 4/5/6 dozen counts belong to.
-- That gives three lines that each say something:
--
-- Bride & groom cake (8 in) B&G cake B&G cake B&G cake
-- Sheet cakes or 12x17 bars 2 pans 3 pans 4 pans
-- Cupcakes or sugar cookies 4 dz 5 dz 6 dz
--
-- This is an interpretation of somebody else's shorthand, so every statement below is guarded on the
-- text V4 wrote. If the bakery has already edited these lines in the admin, this migration finds nothing
-- to change and leaves their wording alone — which matters, because by the time this runs the tables are
-- live and theirs to edit.
--
-- V4 is deliberately NOT edited: it has already been applied, and Flyway validates checksums.
-- "Sheet cakes" absorbs the pan size, and its cells drop the "or a sheet cake" the label now carries.
update catering_row r
set label = 'Sheet cakes or 12x17 bars'
from catering_package p
where p.id = r.package_id
and p.name = 'Weddings'
and r.label = 'Sheet cakes';
update catering_row_value v
set value = '2 pans'
from catering_row r join catering_package p on p.id = r.package_id
where r.id = v.row_id and p.name = 'Weddings'
and r.label = 'Sheet cakes or 12x17 bars'
and v.position = 0 and v.value = '2 pans or a sheet cake';
update catering_row_value v
set value = '3 pans'
from catering_row r join catering_package p on p.id = r.package_id
where r.id = v.row_id and p.name = 'Weddings'
and r.label = 'Sheet cakes or 12x17 bars'
and v.position = 1 and v.value = '3 pans or a sheet cake';
update catering_row_value v
set value = '4 pans'
from catering_row r join catering_package p on p.id = r.package_id
where r.id = v.row_id and p.name = 'Weddings'
and r.label = 'Sheet cakes or 12x17 bars'
and v.position = 2 and v.value = '4 pans or a sheet cake';
-- The dozens were never about the bars; they are the cupcakes-or-sugar-cookies count. The line takes
-- that name, and the two empty lines below it are what it replaces.
update catering_row r
set label = 'Cupcakes or sugar cookies'
from catering_package p
where p.id = r.package_id
and p.name = 'Weddings'
and r.label = '12x17 bars';
update catering_row_value v
set value = '4 dz'
from catering_row r join catering_package p on p.id = r.package_id
where r.id = v.row_id and p.name = 'Weddings'
and r.label = 'Cupcakes or sugar cookies'
and v.position = 0 and v.value = '4 dz cupcakes or sugar cookies';
update catering_row_value v
set value = '5 dz'
from catering_row r join catering_package p on p.id = r.package_id
where r.id = v.row_id and p.name = 'Weddings'
and r.label = 'Cupcakes or sugar cookies'
and v.position = 1 and v.value = '5 dz cupcakes or sugar cookies';
update catering_row_value v
set value = '6 dz'
from catering_row r join catering_package p on p.id = r.package_id
where r.id = v.row_id and p.name = 'Weddings'
and r.label = 'Cupcakes or sugar cookies'
and v.position = 2 and v.value = '6 dz cupcakes or sugar cookies';
-- Only the wedding table's copies: "Cupcakes" is a real, quantified line in the parties table, which the
-- join on the package name protects. Their cells go with them (catering_row_value cascades).
delete from catering_row r
using catering_package p
where p.id = r.package_id
and p.name = 'Weddings'
and r.label in ('Cupcakes', 'Sugar cookies')
and not exists (
select 1 from catering_row_value v
where v.row_id = r.id and v.value <> ''
);
-- Positions 1, 2, 3 survive the deletion untouched (the removed lines were 4 and 5), so there is nothing
-- to renumber. Stated rather than assumed: the page reads the order from these.
@@ -81,6 +81,24 @@ class CateringMenuTest {
assertThat(tables.get(2).notes()).anySatisfy(note -> assertThat(note).contains("delivery fee")); assertThat(tables.get(2).notes()).anySatisfy(note -> assertThat(note).contains("delivery fee"));
} }
@Test
void theWeddingLinesEachSaySomething() {
// The source spreadsheet is offset — the prices sit on its "cupcakes" line — and V4 carried that
// over literally, which left two lines with no quantity in any column: a row of dashes on the
// page. V5 reads it as a baker would (a 12x17 pan is the sheet pan; "cc or sc" is what the dozens
// count) and the guards in it mean an edit made in the admin since would have survived instead.
CateringMenu.PackageView weddings = catering.menu().packages().get(2);
assertThat(weddings.rows()).extracting(CateringMenu.RowView::label)
.containsExactly("Bride & groom cake (8 in)", "Sheet cakes or 12x17 bars",
"Cupcakes or sugar cookies");
assertThat(weddings.rows().get(1).values()).containsExactly("2 pans", "3 pans", "4 pans");
assertThat(weddings.rows().get(2).values()).containsExactly("4 dz", "5 dz", "6 dz");
// Nothing left that is blank the whole way across.
assertThat(weddings.rows())
.noneMatch(row -> row.values().stream().allMatch(String::isEmpty));
}
@Test @Test
void everyLineCarriesOneEntryPerColumn() { void everyLineCarriesOneEntryPerColumn() {
// The invariant the whole aggregate exists to hold: if these ever fall out of step, a box is // The invariant the whole aggregate exists to hold: if these ever fall out of step, a box is