feat(app): ask 'how did it do?' when a harvest is noted (schema v13)

The note growers most wish they had two seasons later — did it do well,
was it worth keeping — never had a home. New append-only GardenOutcomes
table (lotId, season year, three-face rating, free note); the question
appears ONCE, inline, right after recording a harvest in the batch story,
and is skippable without a trace. The answer shows as one more story
line. Rides backups/sync like every mutable table (JSON codec, LWW
import, HLC clock absorb). Migration v12→v13 guarded + verified from
every historical version.
This commit is contained in:
vjrj 2026-07-18 12:18:25 +02:00
parent 92fd84590b
commit bb5b3d4a43
20 changed files with 6030 additions and 15 deletions

View file

@ -18,6 +18,7 @@ part 'database.g.dart';
Lots,
GerminationTests,
ConditionChecks,
GardenOutcomes,
Movements,
Parties,
Attachments,
@ -31,7 +32,7 @@ class AppDatabase extends _$AppDatabase {
/// Current schema version; also stamped into interchange exports so an
/// importer knows which app generation wrote the file (data-model §7).
static const int currentSchemaVersion = 12;
static const int currentSchemaVersion = 13;
@override
int get schemaVersion => currentSchemaVersion;
@ -187,6 +188,14 @@ class AppDatabase extends _$AppDatabase {
await addIfMissing('return_kind', plantares.returnKind);
await addIfMissing('work_hours', plantares.workHours);
}
// v13: GardenOutcomes the per-season "how did it do in my garden"
// answer (rating + note) captured when a harvest is recorded. Guarded
// (see the v7 note above).
if (from < 13) {
if (!await _hasTable('garden_outcomes')) {
await m.createTable(gardenOutcomes);
}
}
},
);

View file

@ -5765,6 +5765,624 @@ class ConditionChecksCompanion extends UpdateCompanion<ConditionCheck> {
}
}
class $GardenOutcomesTable extends GardenOutcomes
with TableInfo<$GardenOutcomesTable, GardenOutcome> {
@override
final GeneratedDatabase attachedDatabase;
final String? _alias;
$GardenOutcomesTable(this.attachedDatabase, [this._alias]);
static const VerificationMeta _idMeta = const VerificationMeta('id');
@override
late final GeneratedColumn<String> id = GeneratedColumn<String>(
'id',
aliasedName,
false,
type: DriftSqlType.string,
requiredDuringInsert: true,
);
static const VerificationMeta _createdAtMeta = const VerificationMeta(
'createdAt',
);
@override
late final GeneratedColumn<int> createdAt = GeneratedColumn<int>(
'created_at',
aliasedName,
false,
type: DriftSqlType.int,
requiredDuringInsert: true,
);
static const VerificationMeta _updatedAtMeta = const VerificationMeta(
'updatedAt',
);
@override
late final GeneratedColumn<String> updatedAt = GeneratedColumn<String>(
'updated_at',
aliasedName,
false,
type: DriftSqlType.string,
requiredDuringInsert: true,
);
static const VerificationMeta _lastAuthorMeta = const VerificationMeta(
'lastAuthor',
);
@override
late final GeneratedColumn<String> lastAuthor = GeneratedColumn<String>(
'last_author',
aliasedName,
false,
type: DriftSqlType.string,
requiredDuringInsert: true,
);
static const VerificationMeta _isDeletedMeta = const VerificationMeta(
'isDeleted',
);
@override
late final GeneratedColumn<bool> isDeleted = GeneratedColumn<bool>(
'is_deleted',
aliasedName,
false,
type: DriftSqlType.bool,
requiredDuringInsert: false,
defaultConstraints: GeneratedColumn.constraintIsAlways(
'CHECK ("is_deleted" IN (0, 1))',
),
defaultValue: const Constant(false),
);
static const VerificationMeta _schemaRowVersionMeta = const VerificationMeta(
'schemaRowVersion',
);
@override
late final GeneratedColumn<int> schemaRowVersion = GeneratedColumn<int>(
'schema_row_version',
aliasedName,
false,
type: DriftSqlType.int,
requiredDuringInsert: false,
defaultValue: const Constant(1),
);
static const VerificationMeta _lotIdMeta = const VerificationMeta('lotId');
@override
late final GeneratedColumn<String> lotId = GeneratedColumn<String>(
'lot_id',
aliasedName,
false,
type: DriftSqlType.string,
requiredDuringInsert: true,
);
static const VerificationMeta _yearMeta = const VerificationMeta('year');
@override
late final GeneratedColumn<int> year = GeneratedColumn<int>(
'year',
aliasedName,
true,
type: DriftSqlType.int,
requiredDuringInsert: false,
);
@override
late final GeneratedColumnWithTypeConverter<GardenOutcomeRating?, String>
rating = GeneratedColumn<String>(
'rating',
aliasedName,
true,
type: DriftSqlType.string,
requiredDuringInsert: false,
).withConverter<GardenOutcomeRating?>($GardenOutcomesTable.$converterratingn);
static const VerificationMeta _notesMeta = const VerificationMeta('notes');
@override
late final GeneratedColumn<String> notes = GeneratedColumn<String>(
'notes',
aliasedName,
true,
type: DriftSqlType.string,
requiredDuringInsert: false,
);
@override
List<GeneratedColumn> get $columns => [
id,
createdAt,
updatedAt,
lastAuthor,
isDeleted,
schemaRowVersion,
lotId,
year,
rating,
notes,
];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'garden_outcomes';
@override
VerificationContext validateIntegrity(
Insertable<GardenOutcome> instance, {
bool isInserting = false,
}) {
final context = VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('id')) {
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
} else if (isInserting) {
context.missing(_idMeta);
}
if (data.containsKey('created_at')) {
context.handle(
_createdAtMeta,
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta),
);
} else if (isInserting) {
context.missing(_createdAtMeta);
}
if (data.containsKey('updated_at')) {
context.handle(
_updatedAtMeta,
updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta),
);
} else if (isInserting) {
context.missing(_updatedAtMeta);
}
if (data.containsKey('last_author')) {
context.handle(
_lastAuthorMeta,
lastAuthor.isAcceptableOrUnknown(data['last_author']!, _lastAuthorMeta),
);
} else if (isInserting) {
context.missing(_lastAuthorMeta);
}
if (data.containsKey('is_deleted')) {
context.handle(
_isDeletedMeta,
isDeleted.isAcceptableOrUnknown(data['is_deleted']!, _isDeletedMeta),
);
}
if (data.containsKey('schema_row_version')) {
context.handle(
_schemaRowVersionMeta,
schemaRowVersion.isAcceptableOrUnknown(
data['schema_row_version']!,
_schemaRowVersionMeta,
),
);
}
if (data.containsKey('lot_id')) {
context.handle(
_lotIdMeta,
lotId.isAcceptableOrUnknown(data['lot_id']!, _lotIdMeta),
);
} else if (isInserting) {
context.missing(_lotIdMeta);
}
if (data.containsKey('year')) {
context.handle(
_yearMeta,
year.isAcceptableOrUnknown(data['year']!, _yearMeta),
);
}
if (data.containsKey('notes')) {
context.handle(
_notesMeta,
notes.isAcceptableOrUnknown(data['notes']!, _notesMeta),
);
}
return context;
}
@override
Set<GeneratedColumn> get $primaryKey => {id};
@override
GardenOutcome map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return GardenOutcome(
id: attachedDatabase.typeMapping.read(
DriftSqlType.string,
data['${effectivePrefix}id'],
)!,
createdAt: attachedDatabase.typeMapping.read(
DriftSqlType.int,
data['${effectivePrefix}created_at'],
)!,
updatedAt: attachedDatabase.typeMapping.read(
DriftSqlType.string,
data['${effectivePrefix}updated_at'],
)!,
lastAuthor: attachedDatabase.typeMapping.read(
DriftSqlType.string,
data['${effectivePrefix}last_author'],
)!,
isDeleted: attachedDatabase.typeMapping.read(
DriftSqlType.bool,
data['${effectivePrefix}is_deleted'],
)!,
schemaRowVersion: attachedDatabase.typeMapping.read(
DriftSqlType.int,
data['${effectivePrefix}schema_row_version'],
)!,
lotId: attachedDatabase.typeMapping.read(
DriftSqlType.string,
data['${effectivePrefix}lot_id'],
)!,
year: attachedDatabase.typeMapping.read(
DriftSqlType.int,
data['${effectivePrefix}year'],
),
rating: $GardenOutcomesTable.$converterratingn.fromSql(
attachedDatabase.typeMapping.read(
DriftSqlType.string,
data['${effectivePrefix}rating'],
),
),
notes: attachedDatabase.typeMapping.read(
DriftSqlType.string,
data['${effectivePrefix}notes'],
),
);
}
@override
$GardenOutcomesTable createAlias(String alias) {
return $GardenOutcomesTable(attachedDatabase, alias);
}
static JsonTypeConverter2<GardenOutcomeRating, String, String>
$converterrating = const EnumNameConverter<GardenOutcomeRating>(
GardenOutcomeRating.values,
);
static JsonTypeConverter2<GardenOutcomeRating?, String?, String?>
$converterratingn = JsonTypeConverter2.asNullable($converterrating);
}
class GardenOutcome extends DataClass implements Insertable<GardenOutcome> {
final String id;
final int createdAt;
final String updatedAt;
final String lastAuthor;
final bool isDeleted;
final int schemaRowVersion;
final String lotId;
final int? year;
final GardenOutcomeRating? rating;
final String? notes;
const GardenOutcome({
required this.id,
required this.createdAt,
required this.updatedAt,
required this.lastAuthor,
required this.isDeleted,
required this.schemaRowVersion,
required this.lotId,
this.year,
this.rating,
this.notes,
});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
map['id'] = Variable<String>(id);
map['created_at'] = Variable<int>(createdAt);
map['updated_at'] = Variable<String>(updatedAt);
map['last_author'] = Variable<String>(lastAuthor);
map['is_deleted'] = Variable<bool>(isDeleted);
map['schema_row_version'] = Variable<int>(schemaRowVersion);
map['lot_id'] = Variable<String>(lotId);
if (!nullToAbsent || year != null) {
map['year'] = Variable<int>(year);
}
if (!nullToAbsent || rating != null) {
map['rating'] = Variable<String>(
$GardenOutcomesTable.$converterratingn.toSql(rating),
);
}
if (!nullToAbsent || notes != null) {
map['notes'] = Variable<String>(notes);
}
return map;
}
GardenOutcomesCompanion toCompanion(bool nullToAbsent) {
return GardenOutcomesCompanion(
id: Value(id),
createdAt: Value(createdAt),
updatedAt: Value(updatedAt),
lastAuthor: Value(lastAuthor),
isDeleted: Value(isDeleted),
schemaRowVersion: Value(schemaRowVersion),
lotId: Value(lotId),
year: year == null && nullToAbsent ? const Value.absent() : Value(year),
rating: rating == null && nullToAbsent
? const Value.absent()
: Value(rating),
notes: notes == null && nullToAbsent
? const Value.absent()
: Value(notes),
);
}
factory GardenOutcome.fromJson(
Map<String, dynamic> json, {
ValueSerializer? serializer,
}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return GardenOutcome(
id: serializer.fromJson<String>(json['id']),
createdAt: serializer.fromJson<int>(json['createdAt']),
updatedAt: serializer.fromJson<String>(json['updatedAt']),
lastAuthor: serializer.fromJson<String>(json['lastAuthor']),
isDeleted: serializer.fromJson<bool>(json['isDeleted']),
schemaRowVersion: serializer.fromJson<int>(json['schemaRowVersion']),
lotId: serializer.fromJson<String>(json['lotId']),
year: serializer.fromJson<int?>(json['year']),
rating: $GardenOutcomesTable.$converterratingn.fromJson(
serializer.fromJson<String?>(json['rating']),
),
notes: serializer.fromJson<String?>(json['notes']),
);
}
@override
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<String>(id),
'createdAt': serializer.toJson<int>(createdAt),
'updatedAt': serializer.toJson<String>(updatedAt),
'lastAuthor': serializer.toJson<String>(lastAuthor),
'isDeleted': serializer.toJson<bool>(isDeleted),
'schemaRowVersion': serializer.toJson<int>(schemaRowVersion),
'lotId': serializer.toJson<String>(lotId),
'year': serializer.toJson<int?>(year),
'rating': serializer.toJson<String?>(
$GardenOutcomesTable.$converterratingn.toJson(rating),
),
'notes': serializer.toJson<String?>(notes),
};
}
GardenOutcome copyWith({
String? id,
int? createdAt,
String? updatedAt,
String? lastAuthor,
bool? isDeleted,
int? schemaRowVersion,
String? lotId,
Value<int?> year = const Value.absent(),
Value<GardenOutcomeRating?> rating = const Value.absent(),
Value<String?> notes = const Value.absent(),
}) => GardenOutcome(
id: id ?? this.id,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
lastAuthor: lastAuthor ?? this.lastAuthor,
isDeleted: isDeleted ?? this.isDeleted,
schemaRowVersion: schemaRowVersion ?? this.schemaRowVersion,
lotId: lotId ?? this.lotId,
year: year.present ? year.value : this.year,
rating: rating.present ? rating.value : this.rating,
notes: notes.present ? notes.value : this.notes,
);
GardenOutcome copyWithCompanion(GardenOutcomesCompanion data) {
return GardenOutcome(
id: data.id.present ? data.id.value : this.id,
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt,
lastAuthor: data.lastAuthor.present
? data.lastAuthor.value
: this.lastAuthor,
isDeleted: data.isDeleted.present ? data.isDeleted.value : this.isDeleted,
schemaRowVersion: data.schemaRowVersion.present
? data.schemaRowVersion.value
: this.schemaRowVersion,
lotId: data.lotId.present ? data.lotId.value : this.lotId,
year: data.year.present ? data.year.value : this.year,
rating: data.rating.present ? data.rating.value : this.rating,
notes: data.notes.present ? data.notes.value : this.notes,
);
}
@override
String toString() {
return (StringBuffer('GardenOutcome(')
..write('id: $id, ')
..write('createdAt: $createdAt, ')
..write('updatedAt: $updatedAt, ')
..write('lastAuthor: $lastAuthor, ')
..write('isDeleted: $isDeleted, ')
..write('schemaRowVersion: $schemaRowVersion, ')
..write('lotId: $lotId, ')
..write('year: $year, ')
..write('rating: $rating, ')
..write('notes: $notes')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(
id,
createdAt,
updatedAt,
lastAuthor,
isDeleted,
schemaRowVersion,
lotId,
year,
rating,
notes,
);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is GardenOutcome &&
other.id == this.id &&
other.createdAt == this.createdAt &&
other.updatedAt == this.updatedAt &&
other.lastAuthor == this.lastAuthor &&
other.isDeleted == this.isDeleted &&
other.schemaRowVersion == this.schemaRowVersion &&
other.lotId == this.lotId &&
other.year == this.year &&
other.rating == this.rating &&
other.notes == this.notes);
}
class GardenOutcomesCompanion extends UpdateCompanion<GardenOutcome> {
final Value<String> id;
final Value<int> createdAt;
final Value<String> updatedAt;
final Value<String> lastAuthor;
final Value<bool> isDeleted;
final Value<int> schemaRowVersion;
final Value<String> lotId;
final Value<int?> year;
final Value<GardenOutcomeRating?> rating;
final Value<String?> notes;
final Value<int> rowid;
const GardenOutcomesCompanion({
this.id = const Value.absent(),
this.createdAt = const Value.absent(),
this.updatedAt = const Value.absent(),
this.lastAuthor = const Value.absent(),
this.isDeleted = const Value.absent(),
this.schemaRowVersion = const Value.absent(),
this.lotId = const Value.absent(),
this.year = const Value.absent(),
this.rating = const Value.absent(),
this.notes = const Value.absent(),
this.rowid = const Value.absent(),
});
GardenOutcomesCompanion.insert({
required String id,
required int createdAt,
required String updatedAt,
required String lastAuthor,
this.isDeleted = const Value.absent(),
this.schemaRowVersion = const Value.absent(),
required String lotId,
this.year = const Value.absent(),
this.rating = const Value.absent(),
this.notes = const Value.absent(),
this.rowid = const Value.absent(),
}) : id = Value(id),
createdAt = Value(createdAt),
updatedAt = Value(updatedAt),
lastAuthor = Value(lastAuthor),
lotId = Value(lotId);
static Insertable<GardenOutcome> custom({
Expression<String>? id,
Expression<int>? createdAt,
Expression<String>? updatedAt,
Expression<String>? lastAuthor,
Expression<bool>? isDeleted,
Expression<int>? schemaRowVersion,
Expression<String>? lotId,
Expression<int>? year,
Expression<String>? rating,
Expression<String>? notes,
Expression<int>? rowid,
}) {
return RawValuesInsertable({
if (id != null) 'id': id,
if (createdAt != null) 'created_at': createdAt,
if (updatedAt != null) 'updated_at': updatedAt,
if (lastAuthor != null) 'last_author': lastAuthor,
if (isDeleted != null) 'is_deleted': isDeleted,
if (schemaRowVersion != null) 'schema_row_version': schemaRowVersion,
if (lotId != null) 'lot_id': lotId,
if (year != null) 'year': year,
if (rating != null) 'rating': rating,
if (notes != null) 'notes': notes,
if (rowid != null) 'rowid': rowid,
});
}
GardenOutcomesCompanion copyWith({
Value<String>? id,
Value<int>? createdAt,
Value<String>? updatedAt,
Value<String>? lastAuthor,
Value<bool>? isDeleted,
Value<int>? schemaRowVersion,
Value<String>? lotId,
Value<int?>? year,
Value<GardenOutcomeRating?>? rating,
Value<String?>? notes,
Value<int>? rowid,
}) {
return GardenOutcomesCompanion(
id: id ?? this.id,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
lastAuthor: lastAuthor ?? this.lastAuthor,
isDeleted: isDeleted ?? this.isDeleted,
schemaRowVersion: schemaRowVersion ?? this.schemaRowVersion,
lotId: lotId ?? this.lotId,
year: year ?? this.year,
rating: rating ?? this.rating,
notes: notes ?? this.notes,
rowid: rowid ?? this.rowid,
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (id.present) {
map['id'] = Variable<String>(id.value);
}
if (createdAt.present) {
map['created_at'] = Variable<int>(createdAt.value);
}
if (updatedAt.present) {
map['updated_at'] = Variable<String>(updatedAt.value);
}
if (lastAuthor.present) {
map['last_author'] = Variable<String>(lastAuthor.value);
}
if (isDeleted.present) {
map['is_deleted'] = Variable<bool>(isDeleted.value);
}
if (schemaRowVersion.present) {
map['schema_row_version'] = Variable<int>(schemaRowVersion.value);
}
if (lotId.present) {
map['lot_id'] = Variable<String>(lotId.value);
}
if (year.present) {
map['year'] = Variable<int>(year.value);
}
if (rating.present) {
map['rating'] = Variable<String>(
$GardenOutcomesTable.$converterratingn.toSql(rating.value),
);
}
if (notes.present) {
map['notes'] = Variable<String>(notes.value);
}
if (rowid.present) {
map['rowid'] = Variable<int>(rowid.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('GardenOutcomesCompanion(')
..write('id: $id, ')
..write('createdAt: $createdAt, ')
..write('updatedAt: $updatedAt, ')
..write('lastAuthor: $lastAuthor, ')
..write('isDeleted: $isDeleted, ')
..write('schemaRowVersion: $schemaRowVersion, ')
..write('lotId: $lotId, ')
..write('year: $year, ')
..write('rating: $rating, ')
..write('notes: $notes, ')
..write('rowid: $rowid')
..write(')'))
.toString();
}
}
class $MovementsTable extends Movements
with TableInfo<$MovementsTable, Movement> {
@override
@ -10810,6 +11428,7 @@ abstract class _$AppDatabase extends GeneratedDatabase {
late final $ConditionChecksTable conditionChecks = $ConditionChecksTable(
this,
);
late final $GardenOutcomesTable gardenOutcomes = $GardenOutcomesTable(this);
late final $MovementsTable movements = $MovementsTable(this);
late final $PartiesTable parties = $PartiesTable(this);
late final $AttachmentsTable attachments = $AttachmentsTable(this);
@ -10828,6 +11447,7 @@ abstract class _$AppDatabase extends GeneratedDatabase {
lots,
germinationTests,
conditionChecks,
gardenOutcomes,
movements,
parties,
attachments,
@ -13499,6 +14119,312 @@ typedef $$ConditionChecksTableProcessedTableManager =
ConditionCheck,
PrefetchHooks Function()
>;
typedef $$GardenOutcomesTableCreateCompanionBuilder =
GardenOutcomesCompanion Function({
required String id,
required int createdAt,
required String updatedAt,
required String lastAuthor,
Value<bool> isDeleted,
Value<int> schemaRowVersion,
required String lotId,
Value<int?> year,
Value<GardenOutcomeRating?> rating,
Value<String?> notes,
Value<int> rowid,
});
typedef $$GardenOutcomesTableUpdateCompanionBuilder =
GardenOutcomesCompanion Function({
Value<String> id,
Value<int> createdAt,
Value<String> updatedAt,
Value<String> lastAuthor,
Value<bool> isDeleted,
Value<int> schemaRowVersion,
Value<String> lotId,
Value<int?> year,
Value<GardenOutcomeRating?> rating,
Value<String?> notes,
Value<int> rowid,
});
class $$GardenOutcomesTableFilterComposer
extends Composer<_$AppDatabase, $GardenOutcomesTable> {
$$GardenOutcomesTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
ColumnFilters<String> get id => $composableBuilder(
column: $table.id,
builder: (column) => ColumnFilters(column),
);
ColumnFilters<int> get createdAt => $composableBuilder(
column: $table.createdAt,
builder: (column) => ColumnFilters(column),
);
ColumnFilters<String> get updatedAt => $composableBuilder(
column: $table.updatedAt,
builder: (column) => ColumnFilters(column),
);
ColumnFilters<String> get lastAuthor => $composableBuilder(
column: $table.lastAuthor,
builder: (column) => ColumnFilters(column),
);
ColumnFilters<bool> get isDeleted => $composableBuilder(
column: $table.isDeleted,
builder: (column) => ColumnFilters(column),
);
ColumnFilters<int> get schemaRowVersion => $composableBuilder(
column: $table.schemaRowVersion,
builder: (column) => ColumnFilters(column),
);
ColumnFilters<String> get lotId => $composableBuilder(
column: $table.lotId,
builder: (column) => ColumnFilters(column),
);
ColumnFilters<int> get year => $composableBuilder(
column: $table.year,
builder: (column) => ColumnFilters(column),
);
ColumnWithTypeConverterFilters<
GardenOutcomeRating?,
GardenOutcomeRating,
String
>
get rating => $composableBuilder(
column: $table.rating,
builder: (column) => ColumnWithTypeConverterFilters(column),
);
ColumnFilters<String> get notes => $composableBuilder(
column: $table.notes,
builder: (column) => ColumnFilters(column),
);
}
class $$GardenOutcomesTableOrderingComposer
extends Composer<_$AppDatabase, $GardenOutcomesTable> {
$$GardenOutcomesTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
ColumnOrderings<String> get id => $composableBuilder(
column: $table.id,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<int> get createdAt => $composableBuilder(
column: $table.createdAt,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<String> get updatedAt => $composableBuilder(
column: $table.updatedAt,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<String> get lastAuthor => $composableBuilder(
column: $table.lastAuthor,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<bool> get isDeleted => $composableBuilder(
column: $table.isDeleted,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<int> get schemaRowVersion => $composableBuilder(
column: $table.schemaRowVersion,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<String> get lotId => $composableBuilder(
column: $table.lotId,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<int> get year => $composableBuilder(
column: $table.year,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<String> get rating => $composableBuilder(
column: $table.rating,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<String> get notes => $composableBuilder(
column: $table.notes,
builder: (column) => ColumnOrderings(column),
);
}
class $$GardenOutcomesTableAnnotationComposer
extends Composer<_$AppDatabase, $GardenOutcomesTable> {
$$GardenOutcomesTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
GeneratedColumn<String> get id =>
$composableBuilder(column: $table.id, builder: (column) => column);
GeneratedColumn<int> get createdAt =>
$composableBuilder(column: $table.createdAt, builder: (column) => column);
GeneratedColumn<String> get updatedAt =>
$composableBuilder(column: $table.updatedAt, builder: (column) => column);
GeneratedColumn<String> get lastAuthor => $composableBuilder(
column: $table.lastAuthor,
builder: (column) => column,
);
GeneratedColumn<bool> get isDeleted =>
$composableBuilder(column: $table.isDeleted, builder: (column) => column);
GeneratedColumn<int> get schemaRowVersion => $composableBuilder(
column: $table.schemaRowVersion,
builder: (column) => column,
);
GeneratedColumn<String> get lotId =>
$composableBuilder(column: $table.lotId, builder: (column) => column);
GeneratedColumn<int> get year =>
$composableBuilder(column: $table.year, builder: (column) => column);
GeneratedColumnWithTypeConverter<GardenOutcomeRating?, String> get rating =>
$composableBuilder(column: $table.rating, builder: (column) => column);
GeneratedColumn<String> get notes =>
$composableBuilder(column: $table.notes, builder: (column) => column);
}
class $$GardenOutcomesTableTableManager
extends
RootTableManager<
_$AppDatabase,
$GardenOutcomesTable,
GardenOutcome,
$$GardenOutcomesTableFilterComposer,
$$GardenOutcomesTableOrderingComposer,
$$GardenOutcomesTableAnnotationComposer,
$$GardenOutcomesTableCreateCompanionBuilder,
$$GardenOutcomesTableUpdateCompanionBuilder,
(
GardenOutcome,
BaseReferences<_$AppDatabase, $GardenOutcomesTable, GardenOutcome>,
),
GardenOutcome,
PrefetchHooks Function()
> {
$$GardenOutcomesTableTableManager(
_$AppDatabase db,
$GardenOutcomesTable table,
) : super(
TableManagerState(
db: db,
table: table,
createFilteringComposer: () =>
$$GardenOutcomesTableFilterComposer($db: db, $table: table),
createOrderingComposer: () =>
$$GardenOutcomesTableOrderingComposer($db: db, $table: table),
createComputedFieldComposer: () =>
$$GardenOutcomesTableAnnotationComposer($db: db, $table: table),
updateCompanionCallback:
({
Value<String> id = const Value.absent(),
Value<int> createdAt = const Value.absent(),
Value<String> updatedAt = const Value.absent(),
Value<String> lastAuthor = const Value.absent(),
Value<bool> isDeleted = const Value.absent(),
Value<int> schemaRowVersion = const Value.absent(),
Value<String> lotId = const Value.absent(),
Value<int?> year = const Value.absent(),
Value<GardenOutcomeRating?> rating = const Value.absent(),
Value<String?> notes = const Value.absent(),
Value<int> rowid = const Value.absent(),
}) => GardenOutcomesCompanion(
id: id,
createdAt: createdAt,
updatedAt: updatedAt,
lastAuthor: lastAuthor,
isDeleted: isDeleted,
schemaRowVersion: schemaRowVersion,
lotId: lotId,
year: year,
rating: rating,
notes: notes,
rowid: rowid,
),
createCompanionCallback:
({
required String id,
required int createdAt,
required String updatedAt,
required String lastAuthor,
Value<bool> isDeleted = const Value.absent(),
Value<int> schemaRowVersion = const Value.absent(),
required String lotId,
Value<int?> year = const Value.absent(),
Value<GardenOutcomeRating?> rating = const Value.absent(),
Value<String?> notes = const Value.absent(),
Value<int> rowid = const Value.absent(),
}) => GardenOutcomesCompanion.insert(
id: id,
createdAt: createdAt,
updatedAt: updatedAt,
lastAuthor: lastAuthor,
isDeleted: isDeleted,
schemaRowVersion: schemaRowVersion,
lotId: lotId,
year: year,
rating: rating,
notes: notes,
rowid: rowid,
),
withReferenceMapper: (p0) => p0
.map((e) => (e.readTable(table), BaseReferences(db, table, e)))
.toList(),
prefetchHooksCallback: null,
),
);
}
typedef $$GardenOutcomesTableProcessedTableManager =
ProcessedTableManager<
_$AppDatabase,
$GardenOutcomesTable,
GardenOutcome,
$$GardenOutcomesTableFilterComposer,
$$GardenOutcomesTableOrderingComposer,
$$GardenOutcomesTableAnnotationComposer,
$$GardenOutcomesTableCreateCompanionBuilder,
$$GardenOutcomesTableUpdateCompanionBuilder,
(
GardenOutcome,
BaseReferences<_$AppDatabase, $GardenOutcomesTable, GardenOutcome>,
),
GardenOutcome,
PrefetchHooks Function()
>;
typedef $$MovementsTableCreateCompanionBuilder =
MovementsCompanion Function({
required String id,
@ -15800,6 +16726,8 @@ class $AppDatabaseManager {
$$GerminationTestsTableTableManager(_db, _db.germinationTests);
$$ConditionChecksTableTableManager get conditionChecks =>
$$ConditionChecksTableTableManager(_db, _db.conditionChecks);
$$GardenOutcomesTableTableManager get gardenOutcomes =>
$$GardenOutcomesTableTableManager(_db, _db.gardenOutcomes);
$$MovementsTableTableManager get movements =>
$$MovementsTableTableManager(_db, _db.movements);
$$PartiesTableTableManager get parties =>

View file

@ -67,6 +67,12 @@ enum PreservationFormat {
/// - `fresh` just renewed (lila/violet).
enum DesiccantState { none, add, replace, dry, fresh }
/// How a sowing from this batch turned out in the grower's own garden — the
/// note people most wish they had two seasons later. Deliberately coarse
/// (three faces, not a breeder's scorecard): the free-text note carries any
/// nuance.
enum GardenOutcomeRating { good, mixed, poor }
/// Append-only event kinds on a Lot (data-model §2.4).
enum MovementType {
received,

View file

@ -142,6 +142,18 @@ class ConditionChecks extends Table with SyncColumns {
TextColumn get notes => text().nullable()();
}
/// "How did it do in MY garden" one optional, skippable answer per season
/// (v13). Captured at the natural moment (recording a harvest), shown as one
/// more line of the lot's story. Deliberately minimal — a coarse rating plus a
/// free note; the note carries any nuance. Mirrors [GerminationTests]: a dated
/// log under a Lot.
class GardenOutcomes extends Table with SyncColumns {
TextColumn get lotId => text()();
IntColumn get year => integer().nullable()(); // season, e.g. 2026
TextColumn get rating => textEnum<GardenOutcomeRating>().nullable()();
TextColumn get notes => text().nullable()();
}
/// The append-only event log on a Lot history + provenance DAG.
class Movements extends Table with AppendOnlyColumns {
TextColumn get lotId => text()();