Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,10 @@ public ResponseEntity<List<SeriesView>> list() {
if (materialized.contains(entry.getKey() + "|" + value.getKey())) {
continue;
}
views.add(new SeriesView(entry.getKey(), value.getKey(), value.getValue(), base.prefix(), base.size(), 0, 1,
DocumentNumberService.render(base.prefix(), base.size(), 1), true, true));
// A virtual row previews exactly what its first allocation will materialize: the
// base row's shape AND counter (the base is the tenant's seedable template).
views.add(new SeriesView(entry.getKey(), value.getKey(), value.getValue(), base.prefix(), base.size(), base.counter(),
base.counter() + 1, DocumentNumberService.render(base.prefix(), base.size(), base.counter() + 1), true, true));
}
}
return ResponseEntity.ok(views);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,12 @@
* <li>the management surface writes {@code PREFIX} / {@code SIZE} / the counter reset;</li>
* <li>allocation writes only {@code COUNTER}, via {@code COUNTER = COUNTER + 1} - with one
* deliberate exception: the first allocation for a NEW PARTITION of a declared series materializes
* that partition's row, copying the shape from the series' base ({@code ""}-partition) row.
* Partition values are data (company ids), so no artefact can pre-provision them; the base row is
* the tenant's configured default they inherit. The copy happens once, at row birth - it never
* overwrites anything.</li>
* that partition's row, copying the shape AND the counter from the series' base
* ({@code ""}-partition) row. Partition values are data (company ids), so no artefact can
* pre-provision them; the base row is the tenant's configured template they inherit - including the
* starting point, so seeding the base row's next value BEFORE the first document works (a
* zero-started partition silently ignored the seed, observed live). The copy happens once, at row
* birth - it never overwrites anything.</li>
* </ul>
*
* <p>
Expand Down Expand Up @@ -215,14 +217,18 @@ void provision(String series, String partition, String prefix, int size) throws
if (exists(connection, series, partition)) {
return;
}
insertRow(connection, series, partition, prefix, size);
insertRow(connection, series, partition, prefix, size, 0L);
}
}

/**
* Materializes a NEW partition row of a declared series, inheriting the shape of the series' base
* ({@code ""}-partition) row - the tenant's configured default. Partition values are data, so this
* is the only place a partition row can be born.
* Materializes a NEW partition row of a declared series, inheriting the shape AND the counter of
* the series' base ({@code ""}-partition) row - the tenant's configured template. The counter is
* inherited because the base row is the only thing an operator CAN seed before a partition's first
* allocation (on a fresh tenant nothing marks the series partitioned yet, so the settings page
* offers the base row's next value): starting the partition at zero instead silently discarded the
* seed and the first document rendered ...0001. Partition values are data, so this is the only
* place a partition row can be born.
*
* @param connection the connection (autocommit)
* @param series the series identity
Expand All @@ -236,11 +242,12 @@ private void materializePartition(Connection connection, String series, String p
+ "] is not declared for this tenant - declare it in a .numbers artefact");
}
Allocation base = read(connection, series, "");
insertRow(connection, series, partition, base.prefix(), base.size());
insertRow(connection, series, partition, base.prefix(), base.size(), base.value());
}

/** Inserts one series row with a zero counter, tolerating a concurrent identical insert. */
private void insertRow(Connection connection, String series, String partition, String prefix, int size) throws SQLException {
/** Inserts one series row with the given counter, tolerating a concurrent identical insert. */
private void insertRow(Connection connection, String series, String partition, String prefix, int size, long counter)
throws SQLException {
String sql = SqlFactory.getNative(connection)
.insert()
.into(TABLE_NAME)
Expand All @@ -253,7 +260,7 @@ private void insertRow(Connection connection, String series, String partition, S
try (PreparedStatement statement = connection.prepareStatement(sql)) {
statement.setString(1, series);
statement.setString(2, partition);
statement.setLong(3, 0L);
statement.setLong(3, counter);
statement.setString(4, prefix == null ? "" : prefix);
statement.setInt(5, size);
statement.executeUpdate();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@
"numberingNext": "Следващ",
"numberingExample": "Следващ номер:",
"numberingSaved": "Записано",
"numberingShapeTemplate": "Броячите живеят в редовете за отделните партиции по-долу; нова партиция се появява при първата си употреба, наследявайки тази форма.",
"numberingShapeTemplate": "Броячите живеят в редовете за отделните партиции по-долу; нова партиция се появява при първата си употреба, наследявайки тази форма и началния ѝ брояч.",
"tenantConfigLabels": {
"name": "Име",
"subtitle": "Подзаглавие",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@
"numberingNext": "Next",
"numberingExample": "Next number:",
"numberingSaved": "Saved",
"numberingShapeTemplate": "Counters live on the per-partition rows below; a new partition appears on its first use, inheriting this shape.",
"numberingShapeTemplate": "Counters live on the per-partition rows below; a new partition appears on its first use, inheriting this shape and its starting counter.",
"tenantConfigLabels": {
"name": "Name",
"subtitle": "Subtitle",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@
</template>
<template x-if="numberingIsShapeTemplate(row)">
<span x-h-text.muted class="text-sm"
x-text="T('application-core:shell.settings.numberingShapeTemplate', 'Counters live on the per-partition rows below; a new partition appears on its first use, inheriting this shape.')"></span>
x-text="T('application-core:shell.settings.numberingShapeTemplate', 'Counters live on the per-partition rows below; a new partition appears on its first use, inheriting this shape and its starting counter.')"></span>
</template>
</div>
</template>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,27 @@ void aDeclaredPartitionSourceLabelsRowsAndSeedsCountersBeforeFirstUse() throws E
}, ASSERTION_TIMEOUT_SECONDS);
}

@Test
void aFreshPartitionInheritsTheBaseRowsSeededCounter() {
publishDeclarationAndController();

restAssuredExecutor.execute(() -> {
// On a fresh tenant nothing marks the series partitioned yet, so the base row is the only
// thing an operator CAN seed before the first document. Seed it...
given().contentType(ContentType.JSON)
.body("{\"series\": \"" + SERIES + "\", \"partition\": \"\", \"next\": 300}")
.when()
.put("/services/core/numbering")
.then()
.statusCode(204);
// ...and the FIRST allocation of a brand-new partition must render exactly the seed - the
// partition materializes inheriting the base row's shape AND counter (a zero-started
// partition silently discarded the seed: the first document rendered ...0001).
assertEquals("T-0300", allocate("/next/FRESHSEED"), "a fresh partition's first number is the base row's seeded next value");
assertEquals("T-0301", allocate("/next/FRESHSEED"), "and it continues from there");
}, ASSERTION_TIMEOUT_SECONDS);
}

@Test
void aNewTenantGetsTheDeclaredSeriesWithItsOwnSequence() throws Exception {
publishDeclarationAndController();
Expand Down
Loading