feat(db): lot presentation + attachment sort order (schema v4→v5)
Expand LotType from {seed, plant} to six append-only forms (seed,
seedling, plant, tree, bulb, cutting) and add an optional Presentation
attribute (pot/tray/plug/bareRoot/rootBall) kept separate from quantity
so 'how many' and 'in what packaging' never conflate.
Add attachments.sortOrder so the cover photo is the lowest-ordered one,
enabling reordering from the full-screen viewer. Expand QuantityKind
with the new plant-aware forms.
Migrations are additive (v1→v5 all covered by migration_test); enum
values are stored by name and appended only, keeping CRDT sync safe.
This commit is contained in:
parent
06aed2a586
commit
3954c62aa6
16 changed files with 6174 additions and 32 deletions
1495
apps/app_seeds/drift_schemas/drift_schema_v4.json
Normal file
1495
apps/app_seeds/drift_schemas/drift_schema_v4.json
Normal file
File diff suppressed because it is too large
Load diff
1505
apps/app_seeds/drift_schemas/drift_schema_v5.json
Normal file
1505
apps/app_seeds/drift_schemas/drift_schema_v5.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -67,6 +67,7 @@ class VarietyLot extends Equatable {
|
||||||
this.harvestYear,
|
this.harvestYear,
|
||||||
this.harvestMonth,
|
this.harvestMonth,
|
||||||
this.quantity,
|
this.quantity,
|
||||||
|
this.presentation,
|
||||||
this.storageLocation,
|
this.storageLocation,
|
||||||
this.germinationTests = const [],
|
this.germinationTests = const [],
|
||||||
});
|
});
|
||||||
|
|
@ -78,6 +79,9 @@ class VarietyLot extends Equatable {
|
||||||
/// Optional harvest month (1..12). Only meaningful when [harvestYear] is set.
|
/// Optional harvest month (1..12). Only meaningful when [harvestYear] is set.
|
||||||
final int? harvestMonth;
|
final int? harvestMonth;
|
||||||
final Quantity? quantity;
|
final Quantity? quantity;
|
||||||
|
|
||||||
|
/// How living material is packaged (pot, tray, bare-root…); null for seeds.
|
||||||
|
final Presentation? presentation;
|
||||||
final String? storageLocation;
|
final String? storageLocation;
|
||||||
final List<GerminationEntry> germinationTests;
|
final List<GerminationEntry> germinationTests;
|
||||||
|
|
||||||
|
|
@ -92,6 +96,7 @@ class VarietyLot extends Equatable {
|
||||||
harvestYear,
|
harvestYear,
|
||||||
harvestMonth,
|
harvestMonth,
|
||||||
quantity,
|
quantity,
|
||||||
|
presentation,
|
||||||
storageLocation,
|
storageLocation,
|
||||||
germinationTests,
|
germinationTests,
|
||||||
];
|
];
|
||||||
|
|
@ -252,13 +257,18 @@ class VarietyRepository {
|
||||||
) async {
|
) async {
|
||||||
if (varietyIds.isEmpty) return const {};
|
if (varietyIds.isEmpty) return const {};
|
||||||
final rows =
|
final rows =
|
||||||
await (_db.select(_db.attachments)..where(
|
await (_db.select(_db.attachments)
|
||||||
(a) =>
|
..where(
|
||||||
a.parentId.isIn(varietyIds) &
|
(a) =>
|
||||||
a.parentType.equalsValue(ParentType.variety) &
|
a.parentId.isIn(varietyIds) &
|
||||||
a.kind.equalsValue(AttachmentKind.photo) &
|
a.parentType.equalsValue(ParentType.variety) &
|
||||||
a.isDeleted.equals(false),
|
a.kind.equalsValue(AttachmentKind.photo) &
|
||||||
))
|
a.isDeleted.equals(false),
|
||||||
|
)
|
||||||
|
..orderBy([
|
||||||
|
(a) => OrderingTerm(expression: a.sortOrder),
|
||||||
|
(a) => OrderingTerm(expression: a.createdAt),
|
||||||
|
]))
|
||||||
.get();
|
.get();
|
||||||
final byVariety = <String, Uint8List>{};
|
final byVariety = <String, Uint8List>{};
|
||||||
for (final row in rows) {
|
for (final row in rows) {
|
||||||
|
|
@ -431,7 +441,10 @@ class VarietyRepository {
|
||||||
a.kind.equalsValue(AttachmentKind.photo) &
|
a.kind.equalsValue(AttachmentKind.photo) &
|
||||||
a.isDeleted.equals(false),
|
a.isDeleted.equals(false),
|
||||||
)
|
)
|
||||||
..orderBy([(a) => OrderingTerm(expression: a.createdAt)]))
|
..orderBy([
|
||||||
|
(a) => OrderingTerm(expression: a.sortOrder),
|
||||||
|
(a) => OrderingTerm(expression: a.createdAt),
|
||||||
|
]))
|
||||||
.get();
|
.get();
|
||||||
|
|
||||||
final links =
|
final links =
|
||||||
|
|
@ -527,6 +540,7 @@ class VarietyRepository {
|
||||||
int? harvestYear,
|
int? harvestYear,
|
||||||
int? harvestMonth,
|
int? harvestMonth,
|
||||||
Quantity? quantity,
|
Quantity? quantity,
|
||||||
|
Presentation? presentation,
|
||||||
String? storageLocation,
|
String? storageLocation,
|
||||||
}) async {
|
}) async {
|
||||||
final (created, updated) = _stamp();
|
final (created, updated) = _stamp();
|
||||||
|
|
@ -546,6 +560,7 @@ class VarietyRepository {
|
||||||
quantityKind: Value(quantity?.kind.name),
|
quantityKind: Value(quantity?.kind.name),
|
||||||
quantityPrecise: Value(quantity?.count?.toDouble()),
|
quantityPrecise: Value(quantity?.count?.toDouble()),
|
||||||
quantityLabel: Value(quantity?.label),
|
quantityLabel: Value(quantity?.label),
|
||||||
|
presentation: Value(presentation),
|
||||||
storageLocation: Value(storageLocation),
|
storageLocation: Value(storageLocation),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -560,6 +575,7 @@ class VarietyRepository {
|
||||||
int? harvestYear,
|
int? harvestYear,
|
||||||
int? harvestMonth,
|
int? harvestMonth,
|
||||||
Quantity? quantity,
|
Quantity? quantity,
|
||||||
|
Presentation? presentation,
|
||||||
String? storageLocation,
|
String? storageLocation,
|
||||||
}) async {
|
}) async {
|
||||||
final (_, updated) = _stamp();
|
final (_, updated) = _stamp();
|
||||||
|
|
@ -571,6 +587,7 @@ class VarietyRepository {
|
||||||
quantityKind: Value(quantity?.kind.name),
|
quantityKind: Value(quantity?.kind.name),
|
||||||
quantityPrecise: Value(quantity?.count?.toDouble()),
|
quantityPrecise: Value(quantity?.count?.toDouble()),
|
||||||
quantityLabel: Value(quantity?.label),
|
quantityLabel: Value(quantity?.label),
|
||||||
|
presentation: Value(presentation),
|
||||||
storageLocation: Value(storageLocation),
|
storageLocation: Value(storageLocation),
|
||||||
updatedAt: Value(updated),
|
updatedAt: Value(updated),
|
||||||
lastAuthor: Value(nodeId),
|
lastAuthor: Value(nodeId),
|
||||||
|
|
@ -666,6 +683,34 @@ class VarietyRepository {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Makes [attachmentId] the cover (preferred) attachment of [varietyId] by
|
||||||
|
/// giving it a [sortOrder] below every current sibling. The cover is what the
|
||||||
|
/// inventory list shows as the avatar and the detail gallery shows first.
|
||||||
|
Future<void> setPreferredPhoto(String varietyId, String attachmentId) async {
|
||||||
|
final siblings =
|
||||||
|
await (_db.select(_db.attachments)..where(
|
||||||
|
(a) =>
|
||||||
|
a.parentId.equals(varietyId) &
|
||||||
|
a.parentType.equalsValue(ParentType.variety) &
|
||||||
|
a.isDeleted.equals(false),
|
||||||
|
))
|
||||||
|
.get();
|
||||||
|
var minOrder = 0;
|
||||||
|
for (final a in siblings) {
|
||||||
|
if (a.sortOrder < minOrder) minOrder = a.sortOrder;
|
||||||
|
}
|
||||||
|
final (_, updated) = _stamp();
|
||||||
|
await (_db.update(
|
||||||
|
_db.attachments,
|
||||||
|
)..where((a) => a.id.equals(attachmentId))).write(
|
||||||
|
AttachmentsCompanion(
|
||||||
|
sortOrder: Value(minOrder - 1),
|
||||||
|
updatedAt: Value(updated),
|
||||||
|
lastAuthor: Value(nodeId),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Adds an external link (URL, optional title) to a variety. Returns its id.
|
/// Adds an external link (URL, optional title) to a variety. Returns its id.
|
||||||
Future<String> addExternalLink(
|
Future<String> addExternalLink(
|
||||||
String varietyId,
|
String varietyId,
|
||||||
|
|
@ -756,6 +801,7 @@ class VarietyRepository {
|
||||||
type: l.type,
|
type: l.type,
|
||||||
harvestYear: l.harvestYear,
|
harvestYear: l.harvestYear,
|
||||||
harvestMonth: l.harvestMonth,
|
harvestMonth: l.harvestMonth,
|
||||||
|
presentation: l.presentation,
|
||||||
storageLocation: l.storageLocation,
|
storageLocation: l.storageLocation,
|
||||||
germinationTests: germinationTests,
|
germinationTests: germinationTests,
|
||||||
quantity: hasQuantity
|
quantity: hasQuantity
|
||||||
|
|
|
||||||
|
|
@ -27,13 +27,13 @@ class AppDatabase extends _$AppDatabase {
|
||||||
AppDatabase(super.e);
|
AppDatabase(super.e);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get schemaVersion => 3;
|
int get schemaVersion => 5;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
MigrationStrategy get migration => MigrationStrategy(
|
MigrationStrategy get migration => MigrationStrategy(
|
||||||
onCreate: (m) async => m.createAll(),
|
onCreate: (m) async => m.createAll(),
|
||||||
onUpgrade: (m, from, to) async {
|
onUpgrade: (m, from, to) async {
|
||||||
// v2: lots can hold seeds or plants/seedlings (plantel).
|
// v2: lots can hold seeds or living plant material.
|
||||||
if (from < 2) {
|
if (from < 2) {
|
||||||
await m.addColumn(lots, lots.type);
|
await m.addColumn(lots, lots.type);
|
||||||
}
|
}
|
||||||
|
|
@ -41,6 +41,14 @@ class AppDatabase extends _$AppDatabase {
|
||||||
if (from < 3) {
|
if (from < 3) {
|
||||||
await m.addColumn(lots, lots.harvestMonth);
|
await m.addColumn(lots, lots.harvestMonth);
|
||||||
}
|
}
|
||||||
|
// v4: optional packaging/supply form for living lots.
|
||||||
|
if (from < 4) {
|
||||||
|
await m.addColumn(lots, lots.presentation);
|
||||||
|
}
|
||||||
|
// v5: display order for attachments (cover photo = lowest sortOrder).
|
||||||
|
if (from < 5) {
|
||||||
|
await m.addColumn(attachments, attachments.sortOrder);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2675,6 +2675,15 @@ class $LotsTable extends Lots with TableInfo<$LotsTable, Lot> {
|
||||||
type: DriftSqlType.string,
|
type: DriftSqlType.string,
|
||||||
requiredDuringInsert: false,
|
requiredDuringInsert: false,
|
||||||
);
|
);
|
||||||
|
@override
|
||||||
|
late final GeneratedColumnWithTypeConverter<Presentation?, String>
|
||||||
|
presentation = GeneratedColumn<String>(
|
||||||
|
'presentation',
|
||||||
|
aliasedName,
|
||||||
|
true,
|
||||||
|
type: DriftSqlType.string,
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
).withConverter<Presentation?>($LotsTable.$converterpresentationn);
|
||||||
static const VerificationMeta _storageLocationMeta = const VerificationMeta(
|
static const VerificationMeta _storageLocationMeta = const VerificationMeta(
|
||||||
'storageLocation',
|
'storageLocation',
|
||||||
);
|
);
|
||||||
|
|
@ -2722,6 +2731,7 @@ class $LotsTable extends Lots with TableInfo<$LotsTable, Lot> {
|
||||||
quantityKind,
|
quantityKind,
|
||||||
quantityPrecise,
|
quantityPrecise,
|
||||||
quantityLabel,
|
quantityLabel,
|
||||||
|
presentation,
|
||||||
storageLocation,
|
storageLocation,
|
||||||
offerStatus,
|
offerStatus,
|
||||||
seedbankId,
|
seedbankId,
|
||||||
|
|
@ -2913,6 +2923,12 @@ class $LotsTable extends Lots with TableInfo<$LotsTable, Lot> {
|
||||||
DriftSqlType.string,
|
DriftSqlType.string,
|
||||||
data['${effectivePrefix}quantity_label'],
|
data['${effectivePrefix}quantity_label'],
|
||||||
),
|
),
|
||||||
|
presentation: $LotsTable.$converterpresentationn.fromSql(
|
||||||
|
attachedDatabase.typeMapping.read(
|
||||||
|
DriftSqlType.string,
|
||||||
|
data['${effectivePrefix}presentation'],
|
||||||
|
),
|
||||||
|
),
|
||||||
storageLocation: attachedDatabase.typeMapping.read(
|
storageLocation: attachedDatabase.typeMapping.read(
|
||||||
DriftSqlType.string,
|
DriftSqlType.string,
|
||||||
data['${effectivePrefix}storage_location'],
|
data['${effectivePrefix}storage_location'],
|
||||||
|
|
@ -2937,6 +2953,14 @@ class $LotsTable extends Lots with TableInfo<$LotsTable, Lot> {
|
||||||
|
|
||||||
static JsonTypeConverter2<LotType, String, String> $convertertype =
|
static JsonTypeConverter2<LotType, String, String> $convertertype =
|
||||||
const EnumNameConverter<LotType>(LotType.values);
|
const EnumNameConverter<LotType>(LotType.values);
|
||||||
|
static JsonTypeConverter2<Presentation, String, String>
|
||||||
|
$converterpresentation = const EnumNameConverter<Presentation>(
|
||||||
|
Presentation.values,
|
||||||
|
);
|
||||||
|
static JsonTypeConverter2<Presentation?, String?, String?>
|
||||||
|
$converterpresentationn = JsonTypeConverter2.asNullable(
|
||||||
|
$converterpresentation,
|
||||||
|
);
|
||||||
static JsonTypeConverter2<OfferStatus, String, String> $converterofferStatus =
|
static JsonTypeConverter2<OfferStatus, String, String> $converterofferStatus =
|
||||||
const EnumNameConverter<OfferStatus>(OfferStatus.values);
|
const EnumNameConverter<OfferStatus>(OfferStatus.values);
|
||||||
}
|
}
|
||||||
|
|
@ -2955,6 +2979,7 @@ class Lot extends DataClass implements Insertable<Lot> {
|
||||||
final String? quantityKind;
|
final String? quantityKind;
|
||||||
final double? quantityPrecise;
|
final double? quantityPrecise;
|
||||||
final String? quantityLabel;
|
final String? quantityLabel;
|
||||||
|
final Presentation? presentation;
|
||||||
final String? storageLocation;
|
final String? storageLocation;
|
||||||
final OfferStatus offerStatus;
|
final OfferStatus offerStatus;
|
||||||
final String? seedbankId;
|
final String? seedbankId;
|
||||||
|
|
@ -2972,6 +2997,7 @@ class Lot extends DataClass implements Insertable<Lot> {
|
||||||
this.quantityKind,
|
this.quantityKind,
|
||||||
this.quantityPrecise,
|
this.quantityPrecise,
|
||||||
this.quantityLabel,
|
this.quantityLabel,
|
||||||
|
this.presentation,
|
||||||
this.storageLocation,
|
this.storageLocation,
|
||||||
required this.offerStatus,
|
required this.offerStatus,
|
||||||
this.seedbankId,
|
this.seedbankId,
|
||||||
|
|
@ -3004,6 +3030,11 @@ class Lot extends DataClass implements Insertable<Lot> {
|
||||||
if (!nullToAbsent || quantityLabel != null) {
|
if (!nullToAbsent || quantityLabel != null) {
|
||||||
map['quantity_label'] = Variable<String>(quantityLabel);
|
map['quantity_label'] = Variable<String>(quantityLabel);
|
||||||
}
|
}
|
||||||
|
if (!nullToAbsent || presentation != null) {
|
||||||
|
map['presentation'] = Variable<String>(
|
||||||
|
$LotsTable.$converterpresentationn.toSql(presentation),
|
||||||
|
);
|
||||||
|
}
|
||||||
if (!nullToAbsent || storageLocation != null) {
|
if (!nullToAbsent || storageLocation != null) {
|
||||||
map['storage_location'] = Variable<String>(storageLocation);
|
map['storage_location'] = Variable<String>(storageLocation);
|
||||||
}
|
}
|
||||||
|
|
@ -3043,6 +3074,9 @@ class Lot extends DataClass implements Insertable<Lot> {
|
||||||
quantityLabel: quantityLabel == null && nullToAbsent
|
quantityLabel: quantityLabel == null && nullToAbsent
|
||||||
? const Value.absent()
|
? const Value.absent()
|
||||||
: Value(quantityLabel),
|
: Value(quantityLabel),
|
||||||
|
presentation: presentation == null && nullToAbsent
|
||||||
|
? const Value.absent()
|
||||||
|
: Value(presentation),
|
||||||
storageLocation: storageLocation == null && nullToAbsent
|
storageLocation: storageLocation == null && nullToAbsent
|
||||||
? const Value.absent()
|
? const Value.absent()
|
||||||
: Value(storageLocation),
|
: Value(storageLocation),
|
||||||
|
|
@ -3074,6 +3108,9 @@ class Lot extends DataClass implements Insertable<Lot> {
|
||||||
quantityKind: serializer.fromJson<String?>(json['quantityKind']),
|
quantityKind: serializer.fromJson<String?>(json['quantityKind']),
|
||||||
quantityPrecise: serializer.fromJson<double?>(json['quantityPrecise']),
|
quantityPrecise: serializer.fromJson<double?>(json['quantityPrecise']),
|
||||||
quantityLabel: serializer.fromJson<String?>(json['quantityLabel']),
|
quantityLabel: serializer.fromJson<String?>(json['quantityLabel']),
|
||||||
|
presentation: $LotsTable.$converterpresentationn.fromJson(
|
||||||
|
serializer.fromJson<String?>(json['presentation']),
|
||||||
|
),
|
||||||
storageLocation: serializer.fromJson<String?>(json['storageLocation']),
|
storageLocation: serializer.fromJson<String?>(json['storageLocation']),
|
||||||
offerStatus: $LotsTable.$converterofferStatus.fromJson(
|
offerStatus: $LotsTable.$converterofferStatus.fromJson(
|
||||||
serializer.fromJson<String>(json['offerStatus']),
|
serializer.fromJson<String>(json['offerStatus']),
|
||||||
|
|
@ -3098,6 +3135,9 @@ class Lot extends DataClass implements Insertable<Lot> {
|
||||||
'quantityKind': serializer.toJson<String?>(quantityKind),
|
'quantityKind': serializer.toJson<String?>(quantityKind),
|
||||||
'quantityPrecise': serializer.toJson<double?>(quantityPrecise),
|
'quantityPrecise': serializer.toJson<double?>(quantityPrecise),
|
||||||
'quantityLabel': serializer.toJson<String?>(quantityLabel),
|
'quantityLabel': serializer.toJson<String?>(quantityLabel),
|
||||||
|
'presentation': serializer.toJson<String?>(
|
||||||
|
$LotsTable.$converterpresentationn.toJson(presentation),
|
||||||
|
),
|
||||||
'storageLocation': serializer.toJson<String?>(storageLocation),
|
'storageLocation': serializer.toJson<String?>(storageLocation),
|
||||||
'offerStatus': serializer.toJson<String>(
|
'offerStatus': serializer.toJson<String>(
|
||||||
$LotsTable.$converterofferStatus.toJson(offerStatus),
|
$LotsTable.$converterofferStatus.toJson(offerStatus),
|
||||||
|
|
@ -3120,6 +3160,7 @@ class Lot extends DataClass implements Insertable<Lot> {
|
||||||
Value<String?> quantityKind = const Value.absent(),
|
Value<String?> quantityKind = const Value.absent(),
|
||||||
Value<double?> quantityPrecise = const Value.absent(),
|
Value<double?> quantityPrecise = const Value.absent(),
|
||||||
Value<String?> quantityLabel = const Value.absent(),
|
Value<String?> quantityLabel = const Value.absent(),
|
||||||
|
Value<Presentation?> presentation = const Value.absent(),
|
||||||
Value<String?> storageLocation = const Value.absent(),
|
Value<String?> storageLocation = const Value.absent(),
|
||||||
OfferStatus? offerStatus,
|
OfferStatus? offerStatus,
|
||||||
Value<String?> seedbankId = const Value.absent(),
|
Value<String?> seedbankId = const Value.absent(),
|
||||||
|
|
@ -3141,6 +3182,7 @@ class Lot extends DataClass implements Insertable<Lot> {
|
||||||
quantityLabel: quantityLabel.present
|
quantityLabel: quantityLabel.present
|
||||||
? quantityLabel.value
|
? quantityLabel.value
|
||||||
: this.quantityLabel,
|
: this.quantityLabel,
|
||||||
|
presentation: presentation.present ? presentation.value : this.presentation,
|
||||||
storageLocation: storageLocation.present
|
storageLocation: storageLocation.present
|
||||||
? storageLocation.value
|
? storageLocation.value
|
||||||
: this.storageLocation,
|
: this.storageLocation,
|
||||||
|
|
@ -3176,6 +3218,9 @@ class Lot extends DataClass implements Insertable<Lot> {
|
||||||
quantityLabel: data.quantityLabel.present
|
quantityLabel: data.quantityLabel.present
|
||||||
? data.quantityLabel.value
|
? data.quantityLabel.value
|
||||||
: this.quantityLabel,
|
: this.quantityLabel,
|
||||||
|
presentation: data.presentation.present
|
||||||
|
? data.presentation.value
|
||||||
|
: this.presentation,
|
||||||
storageLocation: data.storageLocation.present
|
storageLocation: data.storageLocation.present
|
||||||
? data.storageLocation.value
|
? data.storageLocation.value
|
||||||
: this.storageLocation,
|
: this.storageLocation,
|
||||||
|
|
@ -3204,6 +3249,7 @@ class Lot extends DataClass implements Insertable<Lot> {
|
||||||
..write('quantityKind: $quantityKind, ')
|
..write('quantityKind: $quantityKind, ')
|
||||||
..write('quantityPrecise: $quantityPrecise, ')
|
..write('quantityPrecise: $quantityPrecise, ')
|
||||||
..write('quantityLabel: $quantityLabel, ')
|
..write('quantityLabel: $quantityLabel, ')
|
||||||
|
..write('presentation: $presentation, ')
|
||||||
..write('storageLocation: $storageLocation, ')
|
..write('storageLocation: $storageLocation, ')
|
||||||
..write('offerStatus: $offerStatus, ')
|
..write('offerStatus: $offerStatus, ')
|
||||||
..write('seedbankId: $seedbankId')
|
..write('seedbankId: $seedbankId')
|
||||||
|
|
@ -3226,6 +3272,7 @@ class Lot extends DataClass implements Insertable<Lot> {
|
||||||
quantityKind,
|
quantityKind,
|
||||||
quantityPrecise,
|
quantityPrecise,
|
||||||
quantityLabel,
|
quantityLabel,
|
||||||
|
presentation,
|
||||||
storageLocation,
|
storageLocation,
|
||||||
offerStatus,
|
offerStatus,
|
||||||
seedbankId,
|
seedbankId,
|
||||||
|
|
@ -3247,6 +3294,7 @@ class Lot extends DataClass implements Insertable<Lot> {
|
||||||
other.quantityKind == this.quantityKind &&
|
other.quantityKind == this.quantityKind &&
|
||||||
other.quantityPrecise == this.quantityPrecise &&
|
other.quantityPrecise == this.quantityPrecise &&
|
||||||
other.quantityLabel == this.quantityLabel &&
|
other.quantityLabel == this.quantityLabel &&
|
||||||
|
other.presentation == this.presentation &&
|
||||||
other.storageLocation == this.storageLocation &&
|
other.storageLocation == this.storageLocation &&
|
||||||
other.offerStatus == this.offerStatus &&
|
other.offerStatus == this.offerStatus &&
|
||||||
other.seedbankId == this.seedbankId);
|
other.seedbankId == this.seedbankId);
|
||||||
|
|
@ -3266,6 +3314,7 @@ class LotsCompanion extends UpdateCompanion<Lot> {
|
||||||
final Value<String?> quantityKind;
|
final Value<String?> quantityKind;
|
||||||
final Value<double?> quantityPrecise;
|
final Value<double?> quantityPrecise;
|
||||||
final Value<String?> quantityLabel;
|
final Value<String?> quantityLabel;
|
||||||
|
final Value<Presentation?> presentation;
|
||||||
final Value<String?> storageLocation;
|
final Value<String?> storageLocation;
|
||||||
final Value<OfferStatus> offerStatus;
|
final Value<OfferStatus> offerStatus;
|
||||||
final Value<String?> seedbankId;
|
final Value<String?> seedbankId;
|
||||||
|
|
@ -3284,6 +3333,7 @@ class LotsCompanion extends UpdateCompanion<Lot> {
|
||||||
this.quantityKind = const Value.absent(),
|
this.quantityKind = const Value.absent(),
|
||||||
this.quantityPrecise = const Value.absent(),
|
this.quantityPrecise = const Value.absent(),
|
||||||
this.quantityLabel = const Value.absent(),
|
this.quantityLabel = const Value.absent(),
|
||||||
|
this.presentation = const Value.absent(),
|
||||||
this.storageLocation = const Value.absent(),
|
this.storageLocation = const Value.absent(),
|
||||||
this.offerStatus = const Value.absent(),
|
this.offerStatus = const Value.absent(),
|
||||||
this.seedbankId = const Value.absent(),
|
this.seedbankId = const Value.absent(),
|
||||||
|
|
@ -3303,6 +3353,7 @@ class LotsCompanion extends UpdateCompanion<Lot> {
|
||||||
this.quantityKind = const Value.absent(),
|
this.quantityKind = const Value.absent(),
|
||||||
this.quantityPrecise = const Value.absent(),
|
this.quantityPrecise = const Value.absent(),
|
||||||
this.quantityLabel = const Value.absent(),
|
this.quantityLabel = const Value.absent(),
|
||||||
|
this.presentation = const Value.absent(),
|
||||||
this.storageLocation = const Value.absent(),
|
this.storageLocation = const Value.absent(),
|
||||||
this.offerStatus = const Value.absent(),
|
this.offerStatus = const Value.absent(),
|
||||||
this.seedbankId = const Value.absent(),
|
this.seedbankId = const Value.absent(),
|
||||||
|
|
@ -3326,6 +3377,7 @@ class LotsCompanion extends UpdateCompanion<Lot> {
|
||||||
Expression<String>? quantityKind,
|
Expression<String>? quantityKind,
|
||||||
Expression<double>? quantityPrecise,
|
Expression<double>? quantityPrecise,
|
||||||
Expression<String>? quantityLabel,
|
Expression<String>? quantityLabel,
|
||||||
|
Expression<String>? presentation,
|
||||||
Expression<String>? storageLocation,
|
Expression<String>? storageLocation,
|
||||||
Expression<String>? offerStatus,
|
Expression<String>? offerStatus,
|
||||||
Expression<String>? seedbankId,
|
Expression<String>? seedbankId,
|
||||||
|
|
@ -3345,6 +3397,7 @@ class LotsCompanion extends UpdateCompanion<Lot> {
|
||||||
if (quantityKind != null) 'quantity_kind': quantityKind,
|
if (quantityKind != null) 'quantity_kind': quantityKind,
|
||||||
if (quantityPrecise != null) 'quantity_precise': quantityPrecise,
|
if (quantityPrecise != null) 'quantity_precise': quantityPrecise,
|
||||||
if (quantityLabel != null) 'quantity_label': quantityLabel,
|
if (quantityLabel != null) 'quantity_label': quantityLabel,
|
||||||
|
if (presentation != null) 'presentation': presentation,
|
||||||
if (storageLocation != null) 'storage_location': storageLocation,
|
if (storageLocation != null) 'storage_location': storageLocation,
|
||||||
if (offerStatus != null) 'offer_status': offerStatus,
|
if (offerStatus != null) 'offer_status': offerStatus,
|
||||||
if (seedbankId != null) 'seedbank_id': seedbankId,
|
if (seedbankId != null) 'seedbank_id': seedbankId,
|
||||||
|
|
@ -3366,6 +3419,7 @@ class LotsCompanion extends UpdateCompanion<Lot> {
|
||||||
Value<String?>? quantityKind,
|
Value<String?>? quantityKind,
|
||||||
Value<double?>? quantityPrecise,
|
Value<double?>? quantityPrecise,
|
||||||
Value<String?>? quantityLabel,
|
Value<String?>? quantityLabel,
|
||||||
|
Value<Presentation?>? presentation,
|
||||||
Value<String?>? storageLocation,
|
Value<String?>? storageLocation,
|
||||||
Value<OfferStatus>? offerStatus,
|
Value<OfferStatus>? offerStatus,
|
||||||
Value<String?>? seedbankId,
|
Value<String?>? seedbankId,
|
||||||
|
|
@ -3385,6 +3439,7 @@ class LotsCompanion extends UpdateCompanion<Lot> {
|
||||||
quantityKind: quantityKind ?? this.quantityKind,
|
quantityKind: quantityKind ?? this.quantityKind,
|
||||||
quantityPrecise: quantityPrecise ?? this.quantityPrecise,
|
quantityPrecise: quantityPrecise ?? this.quantityPrecise,
|
||||||
quantityLabel: quantityLabel ?? this.quantityLabel,
|
quantityLabel: quantityLabel ?? this.quantityLabel,
|
||||||
|
presentation: presentation ?? this.presentation,
|
||||||
storageLocation: storageLocation ?? this.storageLocation,
|
storageLocation: storageLocation ?? this.storageLocation,
|
||||||
offerStatus: offerStatus ?? this.offerStatus,
|
offerStatus: offerStatus ?? this.offerStatus,
|
||||||
seedbankId: seedbankId ?? this.seedbankId,
|
seedbankId: seedbankId ?? this.seedbankId,
|
||||||
|
|
@ -3436,6 +3491,11 @@ class LotsCompanion extends UpdateCompanion<Lot> {
|
||||||
if (quantityLabel.present) {
|
if (quantityLabel.present) {
|
||||||
map['quantity_label'] = Variable<String>(quantityLabel.value);
|
map['quantity_label'] = Variable<String>(quantityLabel.value);
|
||||||
}
|
}
|
||||||
|
if (presentation.present) {
|
||||||
|
map['presentation'] = Variable<String>(
|
||||||
|
$LotsTable.$converterpresentationn.toSql(presentation.value),
|
||||||
|
);
|
||||||
|
}
|
||||||
if (storageLocation.present) {
|
if (storageLocation.present) {
|
||||||
map['storage_location'] = Variable<String>(storageLocation.value);
|
map['storage_location'] = Variable<String>(storageLocation.value);
|
||||||
}
|
}
|
||||||
|
|
@ -3469,6 +3529,7 @@ class LotsCompanion extends UpdateCompanion<Lot> {
|
||||||
..write('quantityKind: $quantityKind, ')
|
..write('quantityKind: $quantityKind, ')
|
||||||
..write('quantityPrecise: $quantityPrecise, ')
|
..write('quantityPrecise: $quantityPrecise, ')
|
||||||
..write('quantityLabel: $quantityLabel, ')
|
..write('quantityLabel: $quantityLabel, ')
|
||||||
|
..write('presentation: $presentation, ')
|
||||||
..write('storageLocation: $storageLocation, ')
|
..write('storageLocation: $storageLocation, ')
|
||||||
..write('offerStatus: $offerStatus, ')
|
..write('offerStatus: $offerStatus, ')
|
||||||
..write('seedbankId: $seedbankId, ')
|
..write('seedbankId: $seedbankId, ')
|
||||||
|
|
@ -5752,6 +5813,18 @@ class $AttachmentsTable extends Attachments
|
||||||
type: DriftSqlType.string,
|
type: DriftSqlType.string,
|
||||||
requiredDuringInsert: false,
|
requiredDuringInsert: false,
|
||||||
);
|
);
|
||||||
|
static const VerificationMeta _sortOrderMeta = const VerificationMeta(
|
||||||
|
'sortOrder',
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
late final GeneratedColumn<int> sortOrder = GeneratedColumn<int>(
|
||||||
|
'sort_order',
|
||||||
|
aliasedName,
|
||||||
|
false,
|
||||||
|
type: DriftSqlType.int,
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
defaultValue: const Constant(0),
|
||||||
|
);
|
||||||
@override
|
@override
|
||||||
List<GeneratedColumn> get $columns => [
|
List<GeneratedColumn> get $columns => [
|
||||||
id,
|
id,
|
||||||
|
|
@ -5766,6 +5839,7 @@ class $AttachmentsTable extends Attachments
|
||||||
uri,
|
uri,
|
||||||
bytes,
|
bytes,
|
||||||
mimeType,
|
mimeType,
|
||||||
|
sortOrder,
|
||||||
];
|
];
|
||||||
@override
|
@override
|
||||||
String get aliasedName => _alias ?? actualTableName;
|
String get aliasedName => _alias ?? actualTableName;
|
||||||
|
|
@ -5849,6 +5923,12 @@ class $AttachmentsTable extends Attachments
|
||||||
mimeType.isAcceptableOrUnknown(data['mime_type']!, _mimeTypeMeta),
|
mimeType.isAcceptableOrUnknown(data['mime_type']!, _mimeTypeMeta),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (data.containsKey('sort_order')) {
|
||||||
|
context.handle(
|
||||||
|
_sortOrderMeta,
|
||||||
|
sortOrder.isAcceptableOrUnknown(data['sort_order']!, _sortOrderMeta),
|
||||||
|
);
|
||||||
|
}
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5910,6 +5990,10 @@ class $AttachmentsTable extends Attachments
|
||||||
DriftSqlType.string,
|
DriftSqlType.string,
|
||||||
data['${effectivePrefix}mime_type'],
|
data['${effectivePrefix}mime_type'],
|
||||||
),
|
),
|
||||||
|
sortOrder: attachedDatabase.typeMapping.read(
|
||||||
|
DriftSqlType.int,
|
||||||
|
data['${effectivePrefix}sort_order'],
|
||||||
|
)!,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5937,6 +6021,12 @@ class Attachment extends DataClass implements Insertable<Attachment> {
|
||||||
final String? uri;
|
final String? uri;
|
||||||
final Uint8List? bytes;
|
final Uint8List? bytes;
|
||||||
final String? mimeType;
|
final String? mimeType;
|
||||||
|
|
||||||
|
/// Display order among sibling attachments (lower first). The lowest-ordered
|
||||||
|
/// photo is the "preferred"/cover — used as the variety avatar and shown
|
||||||
|
/// first. New photos append at the end; "set as cover" moves one to the
|
||||||
|
/// front. A plain int (not a bool flag) so it generalizes to full reordering.
|
||||||
|
final int sortOrder;
|
||||||
const Attachment({
|
const Attachment({
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.createdAt,
|
required this.createdAt,
|
||||||
|
|
@ -5950,6 +6040,7 @@ class Attachment extends DataClass implements Insertable<Attachment> {
|
||||||
this.uri,
|
this.uri,
|
||||||
this.bytes,
|
this.bytes,
|
||||||
this.mimeType,
|
this.mimeType,
|
||||||
|
required this.sortOrder,
|
||||||
});
|
});
|
||||||
@override
|
@override
|
||||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||||
|
|
@ -5980,6 +6071,7 @@ class Attachment extends DataClass implements Insertable<Attachment> {
|
||||||
if (!nullToAbsent || mimeType != null) {
|
if (!nullToAbsent || mimeType != null) {
|
||||||
map['mime_type'] = Variable<String>(mimeType);
|
map['mime_type'] = Variable<String>(mimeType);
|
||||||
}
|
}
|
||||||
|
map['sort_order'] = Variable<int>(sortOrder);
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -6001,6 +6093,7 @@ class Attachment extends DataClass implements Insertable<Attachment> {
|
||||||
mimeType: mimeType == null && nullToAbsent
|
mimeType: mimeType == null && nullToAbsent
|
||||||
? const Value.absent()
|
? const Value.absent()
|
||||||
: Value(mimeType),
|
: Value(mimeType),
|
||||||
|
sortOrder: Value(sortOrder),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -6026,6 +6119,7 @@ class Attachment extends DataClass implements Insertable<Attachment> {
|
||||||
uri: serializer.fromJson<String?>(json['uri']),
|
uri: serializer.fromJson<String?>(json['uri']),
|
||||||
bytes: serializer.fromJson<Uint8List?>(json['bytes']),
|
bytes: serializer.fromJson<Uint8List?>(json['bytes']),
|
||||||
mimeType: serializer.fromJson<String?>(json['mimeType']),
|
mimeType: serializer.fromJson<String?>(json['mimeType']),
|
||||||
|
sortOrder: serializer.fromJson<int>(json['sortOrder']),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@override
|
@override
|
||||||
|
|
@ -6048,6 +6142,7 @@ class Attachment extends DataClass implements Insertable<Attachment> {
|
||||||
'uri': serializer.toJson<String?>(uri),
|
'uri': serializer.toJson<String?>(uri),
|
||||||
'bytes': serializer.toJson<Uint8List?>(bytes),
|
'bytes': serializer.toJson<Uint8List?>(bytes),
|
||||||
'mimeType': serializer.toJson<String?>(mimeType),
|
'mimeType': serializer.toJson<String?>(mimeType),
|
||||||
|
'sortOrder': serializer.toJson<int>(sortOrder),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -6064,6 +6159,7 @@ class Attachment extends DataClass implements Insertable<Attachment> {
|
||||||
Value<String?> uri = const Value.absent(),
|
Value<String?> uri = const Value.absent(),
|
||||||
Value<Uint8List?> bytes = const Value.absent(),
|
Value<Uint8List?> bytes = const Value.absent(),
|
||||||
Value<String?> mimeType = const Value.absent(),
|
Value<String?> mimeType = const Value.absent(),
|
||||||
|
int? sortOrder,
|
||||||
}) => Attachment(
|
}) => Attachment(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
createdAt: createdAt ?? this.createdAt,
|
createdAt: createdAt ?? this.createdAt,
|
||||||
|
|
@ -6077,6 +6173,7 @@ class Attachment extends DataClass implements Insertable<Attachment> {
|
||||||
uri: uri.present ? uri.value : this.uri,
|
uri: uri.present ? uri.value : this.uri,
|
||||||
bytes: bytes.present ? bytes.value : this.bytes,
|
bytes: bytes.present ? bytes.value : this.bytes,
|
||||||
mimeType: mimeType.present ? mimeType.value : this.mimeType,
|
mimeType: mimeType.present ? mimeType.value : this.mimeType,
|
||||||
|
sortOrder: sortOrder ?? this.sortOrder,
|
||||||
);
|
);
|
||||||
Attachment copyWithCompanion(AttachmentsCompanion data) {
|
Attachment copyWithCompanion(AttachmentsCompanion data) {
|
||||||
return Attachment(
|
return Attachment(
|
||||||
|
|
@ -6098,6 +6195,7 @@ class Attachment extends DataClass implements Insertable<Attachment> {
|
||||||
uri: data.uri.present ? data.uri.value : this.uri,
|
uri: data.uri.present ? data.uri.value : this.uri,
|
||||||
bytes: data.bytes.present ? data.bytes.value : this.bytes,
|
bytes: data.bytes.present ? data.bytes.value : this.bytes,
|
||||||
mimeType: data.mimeType.present ? data.mimeType.value : this.mimeType,
|
mimeType: data.mimeType.present ? data.mimeType.value : this.mimeType,
|
||||||
|
sortOrder: data.sortOrder.present ? data.sortOrder.value : this.sortOrder,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -6115,7 +6213,8 @@ class Attachment extends DataClass implements Insertable<Attachment> {
|
||||||
..write('kind: $kind, ')
|
..write('kind: $kind, ')
|
||||||
..write('uri: $uri, ')
|
..write('uri: $uri, ')
|
||||||
..write('bytes: $bytes, ')
|
..write('bytes: $bytes, ')
|
||||||
..write('mimeType: $mimeType')
|
..write('mimeType: $mimeType, ')
|
||||||
|
..write('sortOrder: $sortOrder')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
@ -6134,6 +6233,7 @@ class Attachment extends DataClass implements Insertable<Attachment> {
|
||||||
uri,
|
uri,
|
||||||
$driftBlobEquality.hash(bytes),
|
$driftBlobEquality.hash(bytes),
|
||||||
mimeType,
|
mimeType,
|
||||||
|
sortOrder,
|
||||||
);
|
);
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
|
|
@ -6150,7 +6250,8 @@ class Attachment extends DataClass implements Insertable<Attachment> {
|
||||||
other.kind == this.kind &&
|
other.kind == this.kind &&
|
||||||
other.uri == this.uri &&
|
other.uri == this.uri &&
|
||||||
$driftBlobEquality.equals(other.bytes, this.bytes) &&
|
$driftBlobEquality.equals(other.bytes, this.bytes) &&
|
||||||
other.mimeType == this.mimeType);
|
other.mimeType == this.mimeType &&
|
||||||
|
other.sortOrder == this.sortOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
class AttachmentsCompanion extends UpdateCompanion<Attachment> {
|
class AttachmentsCompanion extends UpdateCompanion<Attachment> {
|
||||||
|
|
@ -6166,6 +6267,7 @@ class AttachmentsCompanion extends UpdateCompanion<Attachment> {
|
||||||
final Value<String?> uri;
|
final Value<String?> uri;
|
||||||
final Value<Uint8List?> bytes;
|
final Value<Uint8List?> bytes;
|
||||||
final Value<String?> mimeType;
|
final Value<String?> mimeType;
|
||||||
|
final Value<int> sortOrder;
|
||||||
final Value<int> rowid;
|
final Value<int> rowid;
|
||||||
const AttachmentsCompanion({
|
const AttachmentsCompanion({
|
||||||
this.id = const Value.absent(),
|
this.id = const Value.absent(),
|
||||||
|
|
@ -6180,6 +6282,7 @@ class AttachmentsCompanion extends UpdateCompanion<Attachment> {
|
||||||
this.uri = const Value.absent(),
|
this.uri = const Value.absent(),
|
||||||
this.bytes = const Value.absent(),
|
this.bytes = const Value.absent(),
|
||||||
this.mimeType = const Value.absent(),
|
this.mimeType = const Value.absent(),
|
||||||
|
this.sortOrder = const Value.absent(),
|
||||||
this.rowid = const Value.absent(),
|
this.rowid = const Value.absent(),
|
||||||
});
|
});
|
||||||
AttachmentsCompanion.insert({
|
AttachmentsCompanion.insert({
|
||||||
|
|
@ -6195,6 +6298,7 @@ class AttachmentsCompanion extends UpdateCompanion<Attachment> {
|
||||||
this.uri = const Value.absent(),
|
this.uri = const Value.absent(),
|
||||||
this.bytes = const Value.absent(),
|
this.bytes = const Value.absent(),
|
||||||
this.mimeType = const Value.absent(),
|
this.mimeType = const Value.absent(),
|
||||||
|
this.sortOrder = const Value.absent(),
|
||||||
this.rowid = const Value.absent(),
|
this.rowid = const Value.absent(),
|
||||||
}) : id = Value(id),
|
}) : id = Value(id),
|
||||||
createdAt = Value(createdAt),
|
createdAt = Value(createdAt),
|
||||||
|
|
@ -6216,6 +6320,7 @@ class AttachmentsCompanion extends UpdateCompanion<Attachment> {
|
||||||
Expression<String>? uri,
|
Expression<String>? uri,
|
||||||
Expression<Uint8List>? bytes,
|
Expression<Uint8List>? bytes,
|
||||||
Expression<String>? mimeType,
|
Expression<String>? mimeType,
|
||||||
|
Expression<int>? sortOrder,
|
||||||
Expression<int>? rowid,
|
Expression<int>? rowid,
|
||||||
}) {
|
}) {
|
||||||
return RawValuesInsertable({
|
return RawValuesInsertable({
|
||||||
|
|
@ -6231,6 +6336,7 @@ class AttachmentsCompanion extends UpdateCompanion<Attachment> {
|
||||||
if (uri != null) 'uri': uri,
|
if (uri != null) 'uri': uri,
|
||||||
if (bytes != null) 'bytes': bytes,
|
if (bytes != null) 'bytes': bytes,
|
||||||
if (mimeType != null) 'mime_type': mimeType,
|
if (mimeType != null) 'mime_type': mimeType,
|
||||||
|
if (sortOrder != null) 'sort_order': sortOrder,
|
||||||
if (rowid != null) 'rowid': rowid,
|
if (rowid != null) 'rowid': rowid,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -6248,6 +6354,7 @@ class AttachmentsCompanion extends UpdateCompanion<Attachment> {
|
||||||
Value<String?>? uri,
|
Value<String?>? uri,
|
||||||
Value<Uint8List?>? bytes,
|
Value<Uint8List?>? bytes,
|
||||||
Value<String?>? mimeType,
|
Value<String?>? mimeType,
|
||||||
|
Value<int>? sortOrder,
|
||||||
Value<int>? rowid,
|
Value<int>? rowid,
|
||||||
}) {
|
}) {
|
||||||
return AttachmentsCompanion(
|
return AttachmentsCompanion(
|
||||||
|
|
@ -6263,6 +6370,7 @@ class AttachmentsCompanion extends UpdateCompanion<Attachment> {
|
||||||
uri: uri ?? this.uri,
|
uri: uri ?? this.uri,
|
||||||
bytes: bytes ?? this.bytes,
|
bytes: bytes ?? this.bytes,
|
||||||
mimeType: mimeType ?? this.mimeType,
|
mimeType: mimeType ?? this.mimeType,
|
||||||
|
sortOrder: sortOrder ?? this.sortOrder,
|
||||||
rowid: rowid ?? this.rowid,
|
rowid: rowid ?? this.rowid,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -6310,6 +6418,9 @@ class AttachmentsCompanion extends UpdateCompanion<Attachment> {
|
||||||
if (mimeType.present) {
|
if (mimeType.present) {
|
||||||
map['mime_type'] = Variable<String>(mimeType.value);
|
map['mime_type'] = Variable<String>(mimeType.value);
|
||||||
}
|
}
|
||||||
|
if (sortOrder.present) {
|
||||||
|
map['sort_order'] = Variable<int>(sortOrder.value);
|
||||||
|
}
|
||||||
if (rowid.present) {
|
if (rowid.present) {
|
||||||
map['rowid'] = Variable<int>(rowid.value);
|
map['rowid'] = Variable<int>(rowid.value);
|
||||||
}
|
}
|
||||||
|
|
@ -6331,6 +6442,7 @@ class AttachmentsCompanion extends UpdateCompanion<Attachment> {
|
||||||
..write('uri: $uri, ')
|
..write('uri: $uri, ')
|
||||||
..write('bytes: $bytes, ')
|
..write('bytes: $bytes, ')
|
||||||
..write('mimeType: $mimeType, ')
|
..write('mimeType: $mimeType, ')
|
||||||
|
..write('sortOrder: $sortOrder, ')
|
||||||
..write('rowid: $rowid')
|
..write('rowid: $rowid')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
.toString();
|
.toString();
|
||||||
|
|
@ -8246,6 +8358,7 @@ typedef $$LotsTableCreateCompanionBuilder =
|
||||||
Value<String?> quantityKind,
|
Value<String?> quantityKind,
|
||||||
Value<double?> quantityPrecise,
|
Value<double?> quantityPrecise,
|
||||||
Value<String?> quantityLabel,
|
Value<String?> quantityLabel,
|
||||||
|
Value<Presentation?> presentation,
|
||||||
Value<String?> storageLocation,
|
Value<String?> storageLocation,
|
||||||
Value<OfferStatus> offerStatus,
|
Value<OfferStatus> offerStatus,
|
||||||
Value<String?> seedbankId,
|
Value<String?> seedbankId,
|
||||||
|
|
@ -8266,6 +8379,7 @@ typedef $$LotsTableUpdateCompanionBuilder =
|
||||||
Value<String?> quantityKind,
|
Value<String?> quantityKind,
|
||||||
Value<double?> quantityPrecise,
|
Value<double?> quantityPrecise,
|
||||||
Value<String?> quantityLabel,
|
Value<String?> quantityLabel,
|
||||||
|
Value<Presentation?> presentation,
|
||||||
Value<String?> storageLocation,
|
Value<String?> storageLocation,
|
||||||
Value<OfferStatus> offerStatus,
|
Value<OfferStatus> offerStatus,
|
||||||
Value<String?> seedbankId,
|
Value<String?> seedbankId,
|
||||||
|
|
@ -8346,6 +8460,12 @@ class $$LotsTableFilterComposer extends Composer<_$AppDatabase, $LotsTable> {
|
||||||
builder: (column) => ColumnFilters(column),
|
builder: (column) => ColumnFilters(column),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ColumnWithTypeConverterFilters<Presentation?, Presentation, String>
|
||||||
|
get presentation => $composableBuilder(
|
||||||
|
column: $table.presentation,
|
||||||
|
builder: (column) => ColumnWithTypeConverterFilters(column),
|
||||||
|
);
|
||||||
|
|
||||||
ColumnFilters<String> get storageLocation => $composableBuilder(
|
ColumnFilters<String> get storageLocation => $composableBuilder(
|
||||||
column: $table.storageLocation,
|
column: $table.storageLocation,
|
||||||
builder: (column) => ColumnFilters(column),
|
builder: (column) => ColumnFilters(column),
|
||||||
|
|
@ -8436,6 +8556,11 @@ class $$LotsTableOrderingComposer extends Composer<_$AppDatabase, $LotsTable> {
|
||||||
builder: (column) => ColumnOrderings(column),
|
builder: (column) => ColumnOrderings(column),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ColumnOrderings<String> get presentation => $composableBuilder(
|
||||||
|
column: $table.presentation,
|
||||||
|
builder: (column) => ColumnOrderings(column),
|
||||||
|
);
|
||||||
|
|
||||||
ColumnOrderings<String> get storageLocation => $composableBuilder(
|
ColumnOrderings<String> get storageLocation => $composableBuilder(
|
||||||
column: $table.storageLocation,
|
column: $table.storageLocation,
|
||||||
builder: (column) => ColumnOrderings(column),
|
builder: (column) => ColumnOrderings(column),
|
||||||
|
|
@ -8514,6 +8639,12 @@ class $$LotsTableAnnotationComposer
|
||||||
builder: (column) => column,
|
builder: (column) => column,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
GeneratedColumnWithTypeConverter<Presentation?, String> get presentation =>
|
||||||
|
$composableBuilder(
|
||||||
|
column: $table.presentation,
|
||||||
|
builder: (column) => column,
|
||||||
|
);
|
||||||
|
|
||||||
GeneratedColumn<String> get storageLocation => $composableBuilder(
|
GeneratedColumn<String> get storageLocation => $composableBuilder(
|
||||||
column: $table.storageLocation,
|
column: $table.storageLocation,
|
||||||
builder: (column) => column,
|
builder: (column) => column,
|
||||||
|
|
@ -8572,6 +8703,7 @@ class $$LotsTableTableManager
|
||||||
Value<String?> quantityKind = const Value.absent(),
|
Value<String?> quantityKind = const Value.absent(),
|
||||||
Value<double?> quantityPrecise = const Value.absent(),
|
Value<double?> quantityPrecise = const Value.absent(),
|
||||||
Value<String?> quantityLabel = const Value.absent(),
|
Value<String?> quantityLabel = const Value.absent(),
|
||||||
|
Value<Presentation?> presentation = const Value.absent(),
|
||||||
Value<String?> storageLocation = const Value.absent(),
|
Value<String?> storageLocation = const Value.absent(),
|
||||||
Value<OfferStatus> offerStatus = const Value.absent(),
|
Value<OfferStatus> offerStatus = const Value.absent(),
|
||||||
Value<String?> seedbankId = const Value.absent(),
|
Value<String?> seedbankId = const Value.absent(),
|
||||||
|
|
@ -8590,6 +8722,7 @@ class $$LotsTableTableManager
|
||||||
quantityKind: quantityKind,
|
quantityKind: quantityKind,
|
||||||
quantityPrecise: quantityPrecise,
|
quantityPrecise: quantityPrecise,
|
||||||
quantityLabel: quantityLabel,
|
quantityLabel: quantityLabel,
|
||||||
|
presentation: presentation,
|
||||||
storageLocation: storageLocation,
|
storageLocation: storageLocation,
|
||||||
offerStatus: offerStatus,
|
offerStatus: offerStatus,
|
||||||
seedbankId: seedbankId,
|
seedbankId: seedbankId,
|
||||||
|
|
@ -8610,6 +8743,7 @@ class $$LotsTableTableManager
|
||||||
Value<String?> quantityKind = const Value.absent(),
|
Value<String?> quantityKind = const Value.absent(),
|
||||||
Value<double?> quantityPrecise = const Value.absent(),
|
Value<double?> quantityPrecise = const Value.absent(),
|
||||||
Value<String?> quantityLabel = const Value.absent(),
|
Value<String?> quantityLabel = const Value.absent(),
|
||||||
|
Value<Presentation?> presentation = const Value.absent(),
|
||||||
Value<String?> storageLocation = const Value.absent(),
|
Value<String?> storageLocation = const Value.absent(),
|
||||||
Value<OfferStatus> offerStatus = const Value.absent(),
|
Value<OfferStatus> offerStatus = const Value.absent(),
|
||||||
Value<String?> seedbankId = const Value.absent(),
|
Value<String?> seedbankId = const Value.absent(),
|
||||||
|
|
@ -8628,6 +8762,7 @@ class $$LotsTableTableManager
|
||||||
quantityKind: quantityKind,
|
quantityKind: quantityKind,
|
||||||
quantityPrecise: quantityPrecise,
|
quantityPrecise: quantityPrecise,
|
||||||
quantityLabel: quantityLabel,
|
quantityLabel: quantityLabel,
|
||||||
|
presentation: presentation,
|
||||||
storageLocation: storageLocation,
|
storageLocation: storageLocation,
|
||||||
offerStatus: offerStatus,
|
offerStatus: offerStatus,
|
||||||
seedbankId: seedbankId,
|
seedbankId: seedbankId,
|
||||||
|
|
@ -9677,6 +9812,7 @@ typedef $$AttachmentsTableCreateCompanionBuilder =
|
||||||
Value<String?> uri,
|
Value<String?> uri,
|
||||||
Value<Uint8List?> bytes,
|
Value<Uint8List?> bytes,
|
||||||
Value<String?> mimeType,
|
Value<String?> mimeType,
|
||||||
|
Value<int> sortOrder,
|
||||||
Value<int> rowid,
|
Value<int> rowid,
|
||||||
});
|
});
|
||||||
typedef $$AttachmentsTableUpdateCompanionBuilder =
|
typedef $$AttachmentsTableUpdateCompanionBuilder =
|
||||||
|
|
@ -9693,6 +9829,7 @@ typedef $$AttachmentsTableUpdateCompanionBuilder =
|
||||||
Value<String?> uri,
|
Value<String?> uri,
|
||||||
Value<Uint8List?> bytes,
|
Value<Uint8List?> bytes,
|
||||||
Value<String?> mimeType,
|
Value<String?> mimeType,
|
||||||
|
Value<int> sortOrder,
|
||||||
Value<int> rowid,
|
Value<int> rowid,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -9766,6 +9903,11 @@ class $$AttachmentsTableFilterComposer
|
||||||
column: $table.mimeType,
|
column: $table.mimeType,
|
||||||
builder: (column) => ColumnFilters(column),
|
builder: (column) => ColumnFilters(column),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ColumnFilters<int> get sortOrder => $composableBuilder(
|
||||||
|
column: $table.sortOrder,
|
||||||
|
builder: (column) => ColumnFilters(column),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class $$AttachmentsTableOrderingComposer
|
class $$AttachmentsTableOrderingComposer
|
||||||
|
|
@ -9836,6 +9978,11 @@ class $$AttachmentsTableOrderingComposer
|
||||||
column: $table.mimeType,
|
column: $table.mimeType,
|
||||||
builder: (column) => ColumnOrderings(column),
|
builder: (column) => ColumnOrderings(column),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ColumnOrderings<int> get sortOrder => $composableBuilder(
|
||||||
|
column: $table.sortOrder,
|
||||||
|
builder: (column) => ColumnOrderings(column),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class $$AttachmentsTableAnnotationComposer
|
class $$AttachmentsTableAnnotationComposer
|
||||||
|
|
@ -9889,6 +10036,9 @@ class $$AttachmentsTableAnnotationComposer
|
||||||
|
|
||||||
GeneratedColumn<String> get mimeType =>
|
GeneratedColumn<String> get mimeType =>
|
||||||
$composableBuilder(column: $table.mimeType, builder: (column) => column);
|
$composableBuilder(column: $table.mimeType, builder: (column) => column);
|
||||||
|
|
||||||
|
GeneratedColumn<int> get sortOrder =>
|
||||||
|
$composableBuilder(column: $table.sortOrder, builder: (column) => column);
|
||||||
}
|
}
|
||||||
|
|
||||||
class $$AttachmentsTableTableManager
|
class $$AttachmentsTableTableManager
|
||||||
|
|
@ -9934,6 +10084,7 @@ class $$AttachmentsTableTableManager
|
||||||
Value<String?> uri = const Value.absent(),
|
Value<String?> uri = const Value.absent(),
|
||||||
Value<Uint8List?> bytes = const Value.absent(),
|
Value<Uint8List?> bytes = const Value.absent(),
|
||||||
Value<String?> mimeType = const Value.absent(),
|
Value<String?> mimeType = const Value.absent(),
|
||||||
|
Value<int> sortOrder = const Value.absent(),
|
||||||
Value<int> rowid = const Value.absent(),
|
Value<int> rowid = const Value.absent(),
|
||||||
}) => AttachmentsCompanion(
|
}) => AttachmentsCompanion(
|
||||||
id: id,
|
id: id,
|
||||||
|
|
@ -9948,6 +10099,7 @@ class $$AttachmentsTableTableManager
|
||||||
uri: uri,
|
uri: uri,
|
||||||
bytes: bytes,
|
bytes: bytes,
|
||||||
mimeType: mimeType,
|
mimeType: mimeType,
|
||||||
|
sortOrder: sortOrder,
|
||||||
rowid: rowid,
|
rowid: rowid,
|
||||||
),
|
),
|
||||||
createCompanionCallback:
|
createCompanionCallback:
|
||||||
|
|
@ -9964,6 +10116,7 @@ class $$AttachmentsTableTableManager
|
||||||
Value<String?> uri = const Value.absent(),
|
Value<String?> uri = const Value.absent(),
|
||||||
Value<Uint8List?> bytes = const Value.absent(),
|
Value<Uint8List?> bytes = const Value.absent(),
|
||||||
Value<String?> mimeType = const Value.absent(),
|
Value<String?> mimeType = const Value.absent(),
|
||||||
|
Value<int> sortOrder = const Value.absent(),
|
||||||
Value<int> rowid = const Value.absent(),
|
Value<int> rowid = const Value.absent(),
|
||||||
}) => AttachmentsCompanion.insert(
|
}) => AttachmentsCompanion.insert(
|
||||||
id: id,
|
id: id,
|
||||||
|
|
@ -9978,6 +10131,7 @@ class $$AttachmentsTableTableManager
|
||||||
uri: uri,
|
uri: uri,
|
||||||
bytes: bytes,
|
bytes: bytes,
|
||||||
mimeType: mimeType,
|
mimeType: mimeType,
|
||||||
|
sortOrder: sortOrder,
|
||||||
rowid: rowid,
|
rowid: rowid,
|
||||||
),
|
),
|
||||||
withReferenceMapper: (p0) => p0
|
withReferenceMapper: (p0) => p0
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,26 @@
|
||||||
// may only be appended, never renumbered or reused; when sync arrives, readers
|
// may only be appended, never renumbered or reused; when sync arrives, readers
|
||||||
// must tolerate unknown values (map to a safe default). No sync yet in Block 1.
|
// must tolerate unknown values (map to a safe default). No sync yet in Block 1.
|
||||||
|
|
||||||
/// Whether a lot holds seeds or living plants/seedlings (plantel). Germination
|
/// The form in which a lot is held. `seed` is dry seed (the only form
|
||||||
/// tests apply only to seed lots.
|
/// germination tests apply to); the rest are living/vegetative material. Values
|
||||||
enum LotType { seed, plant }
|
/// are stored by name and append-only (data-model §5): never renamed/reordered.
|
||||||
|
///
|
||||||
|
/// - `seedling` — a young plant grown from seed, to transplant (plantón).
|
||||||
|
/// - `plant` — an established plant (potted or bare-root).
|
||||||
|
/// - `tree` — a woody plant: tree or shrub.
|
||||||
|
/// - `bulb` — a storage organ: bulb, corm, tuber or rhizome.
|
||||||
|
/// - `cutting` — vegetative propagation material (cutting, scion).
|
||||||
|
enum LotType { seed, plant, seedling, tree, bulb, cutting }
|
||||||
|
|
||||||
|
/// How living plant material is packaged/supplied — an optional attribute of a
|
||||||
|
/// non-seed [Lot], kept separate from the amount. Append-only by name.
|
||||||
|
///
|
||||||
|
/// - `pot` — in a pot/container (maceta/contenedor).
|
||||||
|
/// - `tray` — in a plug/cell tray (bandeja de alvéolos).
|
||||||
|
/// - `plug` — a single plug/cell.
|
||||||
|
/// - `bareRoot` — dormant, roots exposed, no soil (raíz desnuda).
|
||||||
|
/// - `rootBall` — dug with an intact soil ball (cepellón).
|
||||||
|
enum Presentation { pot, tray, plug, bareRoot, rootBall }
|
||||||
|
|
||||||
/// Per-lot visibility (data-model §2.3). Used by the future sharing layer.
|
/// Per-lot visibility (data-model §2.3). Used by the future sharing layer.
|
||||||
enum OfferStatus { private, shared, exchange, sell }
|
enum OfferStatus { private, shared, exchange, sell }
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,8 @@ class Lots extends Table with SyncColumns {
|
||||||
TextColumn get quantityKind => text().nullable()(); // QuantityKind.name
|
TextColumn get quantityKind => text().nullable()(); // QuantityKind.name
|
||||||
RealColumn get quantityPrecise => real().nullable()();
|
RealColumn get quantityPrecise => real().nullable()();
|
||||||
TextColumn get quantityLabel => text().nullable()();
|
TextColumn get quantityLabel => text().nullable()();
|
||||||
|
// How living (non-seed) material is packaged; null for seed lots or unset.
|
||||||
|
TextColumn get presentation => textEnum<Presentation>().nullable()();
|
||||||
TextColumn get storageLocation => text().nullable()();
|
TextColumn get storageLocation => text().nullable()();
|
||||||
TextColumn get offerStatus =>
|
TextColumn get offerStatus =>
|
||||||
textEnum<OfferStatus>().withDefault(const Constant('private'))();
|
textEnum<OfferStatus>().withDefault(const Constant('private'))();
|
||||||
|
|
@ -99,6 +101,12 @@ class Attachments extends Table with SyncColumns {
|
||||||
TextColumn get uri => text().nullable()();
|
TextColumn get uri => text().nullable()();
|
||||||
BlobColumn get bytes => blob().nullable()();
|
BlobColumn get bytes => blob().nullable()();
|
||||||
TextColumn get mimeType => text().nullable()();
|
TextColumn get mimeType => text().nullable()();
|
||||||
|
|
||||||
|
/// Display order among sibling attachments (lower first). The lowest-ordered
|
||||||
|
/// photo is the "preferred"/cover — used as the variety avatar and shown
|
||||||
|
/// first. New photos append at the end; "set as cover" moves one to the
|
||||||
|
/// front. A plain int (not a bool flag) so it generalizes to full reordering.
|
||||||
|
IntColumn get sortOrder => integer().withDefault(const Constant(0))();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Any pasted URL (Wikipedia, forum…). Polymorphic parent.
|
/// Any pasted URL (Wikipedia, forum…). Polymorphic parent.
|
||||||
|
|
|
||||||
|
|
@ -57,12 +57,14 @@ class VarietyDetailCubit extends Cubit<VarietyDetailState> {
|
||||||
int? year,
|
int? year,
|
||||||
int? month,
|
int? month,
|
||||||
Quantity? quantity,
|
Quantity? quantity,
|
||||||
|
Presentation? presentation,
|
||||||
}) => _repo.addLot(
|
}) => _repo.addLot(
|
||||||
varietyId: varietyId,
|
varietyId: varietyId,
|
||||||
type: type,
|
type: type,
|
||||||
harvestYear: year,
|
harvestYear: year,
|
||||||
harvestMonth: month,
|
harvestMonth: month,
|
||||||
quantity: quantity,
|
quantity: quantity,
|
||||||
|
presentation: presentation,
|
||||||
);
|
);
|
||||||
|
|
||||||
Future<void> updateLot({
|
Future<void> updateLot({
|
||||||
|
|
@ -71,12 +73,14 @@ class VarietyDetailCubit extends Cubit<VarietyDetailState> {
|
||||||
int? year,
|
int? year,
|
||||||
int? month,
|
int? month,
|
||||||
Quantity? quantity,
|
Quantity? quantity,
|
||||||
|
Presentation? presentation,
|
||||||
}) => _repo.updateLot(
|
}) => _repo.updateLot(
|
||||||
lotId: lotId,
|
lotId: lotId,
|
||||||
type: type,
|
type: type,
|
||||||
harvestYear: year,
|
harvestYear: year,
|
||||||
harvestMonth: month,
|
harvestMonth: month,
|
||||||
quantity: quantity,
|
quantity: quantity,
|
||||||
|
presentation: presentation,
|
||||||
);
|
);
|
||||||
|
|
||||||
Future<void> deleteLot(String lotId) => _repo.softDeleteLot(lotId);
|
Future<void> deleteLot(String lotId) => _repo.softDeleteLot(lotId);
|
||||||
|
|
@ -92,6 +96,9 @@ class VarietyDetailCubit extends Cubit<VarietyDetailState> {
|
||||||
Future<void> removePhoto(String attachmentId) =>
|
Future<void> removePhoto(String attachmentId) =>
|
||||||
_repo.removePhoto(attachmentId);
|
_repo.removePhoto(attachmentId);
|
||||||
|
|
||||||
|
Future<void> setPreferredPhoto(String attachmentId) =>
|
||||||
|
_repo.setPreferredPhoto(varietyId, attachmentId);
|
||||||
|
|
||||||
Future<void> addExternalLink(String url, {String? title}) =>
|
Future<void> addExternalLink(String url, {String? title}) =>
|
||||||
_repo.addExternalLink(varietyId, url, title: title);
|
_repo.addExternalLink(varietyId, url, title: title);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -201,6 +201,43 @@ void main() {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('setPreferredPhoto moves a photo to the front (cover)', () async {
|
||||||
|
final id = await repo.addQuickVariety(label: 'Maize');
|
||||||
|
await repo.addPhoto(id, Uint8List.fromList([1]));
|
||||||
|
await repo.addPhoto(id, Uint8List.fromList([2]));
|
||||||
|
final p3 = await repo.addPhoto(id, Uint8List.fromList([3]));
|
||||||
|
|
||||||
|
// Insertion order until a cover is chosen.
|
||||||
|
expect(
|
||||||
|
(await repo.watchVariety(id).first)!.photos.map((p) => p.bytes.toList()),
|
||||||
|
[
|
||||||
|
[1],
|
||||||
|
[2],
|
||||||
|
[3],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
await repo.setPreferredPhoto(id, p3);
|
||||||
|
|
||||||
|
final photos = (await repo.watchVariety(id).first)!.photos;
|
||||||
|
expect(photos.map((p) => p.bytes.toList()), [
|
||||||
|
[3],
|
||||||
|
[1],
|
||||||
|
[2],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('setPreferredPhoto changes the inventory avatar to the cover', () async {
|
||||||
|
final id = await repo.addQuickVariety(label: 'Maize');
|
||||||
|
await repo.addPhoto(id, Uint8List.fromList([1]));
|
||||||
|
final p2 = await repo.addPhoto(id, Uint8List.fromList([2]));
|
||||||
|
|
||||||
|
expect((await repo.watchInventory().first).single.photo!.toList(), [1]);
|
||||||
|
|
||||||
|
await repo.setPreferredPhoto(id, p2);
|
||||||
|
expect((await repo.watchInventory().first).single.photo!.toList(), [2]);
|
||||||
|
});
|
||||||
|
|
||||||
test('watchInventory avatar refreshes when a photo is added later', () async {
|
test('watchInventory avatar refreshes when a photo is added later', () async {
|
||||||
final id = await repo.addQuickVariety(label: 'Maize');
|
final id = await repo.addQuickVariety(label: 'Maize');
|
||||||
final queue = StreamQueue(repo.watchInventory());
|
final queue = StreamQueue(repo.watchInventory());
|
||||||
|
|
|
||||||
|
|
@ -11,28 +11,40 @@ void main() {
|
||||||
verifier = SchemaVerifier(GeneratedHelper());
|
verifier = SchemaVerifier(GeneratedHelper());
|
||||||
});
|
});
|
||||||
|
|
||||||
test('freshly created database matches the exported schema v3', () async {
|
test('freshly created database matches the exported schema v5', () async {
|
||||||
final schema = await verifier.schemaAt(3);
|
final schema = await verifier.schemaAt(5);
|
||||||
final db = AppDatabase(schema.newConnection());
|
final db = AppDatabase(schema.newConnection());
|
||||||
await verifier.migrateAndValidate(db, 3);
|
await verifier.migrateAndValidate(db, 5);
|
||||||
await db.close();
|
await db.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('upgrades v1 → v3 (adds Lot.type, then harvestMonth) and matches the '
|
test('upgrades v1 → v5 (Lot.type, harvestMonth, presentation, '
|
||||||
'fresh schema', () async {
|
'Attachment.sortOrder) and matches the fresh schema', () async {
|
||||||
final connection = await verifier.startAt(1);
|
final connection = await verifier.startAt(1);
|
||||||
final db = AppDatabase(connection);
|
final db = AppDatabase(connection);
|
||||||
await verifier.migrateAndValidate(db, 3);
|
await verifier.migrateAndValidate(db, 5);
|
||||||
await db.close();
|
await db.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
test(
|
test('upgrades v2 → v5 and matches the fresh schema', () async {
|
||||||
'upgrades v2 → v3 (adds Lot.harvestMonth) and matches the fresh schema',
|
final connection = await verifier.startAt(2);
|
||||||
() async {
|
final db = AppDatabase(connection);
|
||||||
final connection = await verifier.startAt(2);
|
await verifier.migrateAndValidate(db, 5);
|
||||||
final db = AppDatabase(connection);
|
await db.close();
|
||||||
await verifier.migrateAndValidate(db, 3);
|
});
|
||||||
await db.close();
|
|
||||||
},
|
test('upgrades v3 → v5 and matches the fresh schema', () async {
|
||||||
);
|
final connection = await verifier.startAt(3);
|
||||||
|
final db = AppDatabase(connection);
|
||||||
|
await verifier.migrateAndValidate(db, 5);
|
||||||
|
await db.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('upgrades v4 → v5 (adds Attachment.sortOrder) and matches the fresh '
|
||||||
|
'schema', () async {
|
||||||
|
final connection = await verifier.startAt(4);
|
||||||
|
final db = AppDatabase(connection);
|
||||||
|
await verifier.migrateAndValidate(db, 5);
|
||||||
|
await db.close();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ import 'package:drift/internal/migrations.dart';
|
||||||
import 'schema_v1.dart' as v1;
|
import 'schema_v1.dart' as v1;
|
||||||
import 'schema_v2.dart' as v2;
|
import 'schema_v2.dart' as v2;
|
||||||
import 'schema_v3.dart' as v3;
|
import 'schema_v3.dart' as v3;
|
||||||
|
import 'schema_v4.dart' as v4;
|
||||||
|
import 'schema_v5.dart' as v5;
|
||||||
|
|
||||||
class GeneratedHelper implements SchemaInstantiationHelper {
|
class GeneratedHelper implements SchemaInstantiationHelper {
|
||||||
@override
|
@override
|
||||||
|
|
@ -18,10 +20,14 @@ class GeneratedHelper implements SchemaInstantiationHelper {
|
||||||
return v2.DatabaseAtV2(db);
|
return v2.DatabaseAtV2(db);
|
||||||
case 3:
|
case 3:
|
||||||
return v3.DatabaseAtV3(db);
|
return v3.DatabaseAtV3(db);
|
||||||
|
case 4:
|
||||||
|
return v4.DatabaseAtV4(db);
|
||||||
|
case 5:
|
||||||
|
return v5.DatabaseAtV5(db);
|
||||||
default:
|
default:
|
||||||
throw MissingSchemaException(version, versions);
|
throw MissingSchemaException(version, versions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const versions = const [1, 2, 3];
|
static const versions = const [1, 2, 3, 4, 5];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1408
apps/app_seeds/test/db/schema/schema_v4.dart
Normal file
1408
apps/app_seeds/test/db/schema/schema_v4.dart
Normal file
File diff suppressed because it is too large
Load diff
1418
apps/app_seeds/test/db/schema/schema_v5.dart
Normal file
1418
apps/app_seeds/test/db/schema/schema_v5.dart
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -71,8 +71,10 @@ Zero or more per `Variety`, each with its **own year and its own unit** — and
|
||||||
| `id` | UUID | |
|
| `id` | UUID | |
|
||||||
| `variety_id` | UUID → `Variety` | |
|
| `variety_id` | UUID → `Variety` | |
|
||||||
| `harvest_year` | INTEGER | Nullable. **This is where "year" lives** (germination decays with lot age). |
|
| `harvest_year` | INTEGER | Nullable. **This is where "year" lives** (germination decays with lot age). |
|
||||||
|
| `type` | enum `LotType` | The **form** held — see §2.3.2. |
|
||||||
| `quantity_label` | TEXT + `quantity_kind` | Qualitative by default. **Not only kitchen units** — see §2.3.1. |
|
| `quantity_label` | TEXT + `quantity_kind` | Qualitative by default. **Not only kitchen units** — see §2.3.1. |
|
||||||
| `quantity_precise` | REAL + `quantity_unit` | Optional precise amount (grams / seed count). |
|
| `quantity_precise` | REAL + `quantity_unit` | Optional precise amount (grams / seed count). |
|
||||||
|
| `presentation` | enum `Presentation` | Nullable. How living material is packaged — see §2.3.2. |
|
||||||
| `storage_location` | TEXT | Nullable. Free text with suggestions ("fridge", "village bank"). |
|
| `storage_location` | TEXT | Nullable. Free text with suggestions ("fridge", "village bank"). |
|
||||||
| `offer_status` | enum | `private` / `shared` / `exchange` / `sell` — the visibility from PLAN §3 Layer 2. Per lot. |
|
| `offer_status` | enum | `private` / `shared` / `exchange` / `sell` — the visibility from PLAN §3 Layer 2. Per lot. |
|
||||||
| `seedbank_id` | UUID → `SeedBank` | Nullable. If this lot belongs to a collective bank. |
|
| `seedbank_id` | UUID → `SeedBank` | Nullable. If this lot belongs to a collective bank. |
|
||||||
|
|
@ -90,6 +92,19 @@ Quantity is deliberately *rough and human*, not precise. But the informal vocabu
|
||||||
|
|
||||||
Modelling: store `quantity_kind` (a stable enum key like `pod`, `cob`, `head`, `packet`, `handful`, `grams`, `count`) + an optional numeric `quantity_precise` (and a free-text `quantity_label` for a rough amount when no key fits). The **display label is localized** from the key, so "pod"→"vaina"/"beina"… Units are *labels for a rough amount*, generally **not convertible** between each other or to grams — and that's fine. Suggestion logic (e.g. Poaceae → ear/cob, Fabaceae → pod, Helianthus → flower head) lives in the catalog mapping, not hardcoded, so it stays extensible and translatable. **To think through further:** the full starter list per family, and whether users can add their own unit keys (probably yes, as free `quantity_label` text when no key fits).
|
Modelling: store `quantity_kind` (a stable enum key like `pod`, `cob`, `head`, `packet`, `handful`, `grams`, `count`) + an optional numeric `quantity_precise` (and a free-text `quantity_label` for a rough amount when no key fits). The **display label is localized** from the key, so "pod"→"vaina"/"beina"… Units are *labels for a rough amount*, generally **not convertible** between each other or to grams — and that's fine. Suggestion logic (e.g. Poaceae → ear/cob, Fabaceae → pod, Helianthus → flower head) lives in the catalog mapping, not hardcoded, so it stays extensible and translatable. **To think through further:** the full starter list per family, and whether users can add their own unit keys (probably yes, as free `quantity_label` text when no key fits).
|
||||||
|
|
||||||
|
#### 2.3.2 Lot form (`LotType`) and packaging (`Presentation`) — design note
|
||||||
|
|
||||||
|
A lot isn't always dry seed. Reviewing the vocabulary of real seed shops and nurseries (viveros) — where "plantel" is only the *seedbed/batch*, not a unit — the **form** a lot is held in is a small, append-only enum `LotType` (stored by name, data-model §5):
|
||||||
|
|
||||||
|
- `seed` — dry seed. **The only form germination tests apply to.**
|
||||||
|
- `seedling` — a young plant grown from seed, to transplant (*plantón* / plug).
|
||||||
|
- `plant` — an established plant (potted or bare-root).
|
||||||
|
- `tree` — a woody plant: tree or shrub.
|
||||||
|
- `bulb` — a storage organ: bulb, corm, tuber or rhizome.
|
||||||
|
- `cutting` — vegetative propagation material (cutting, scion).
|
||||||
|
|
||||||
|
**Form drives the offered units**, and only `seed` gets the rich plant-form scale of §2.3.1; living forms are counted as **whole individuals** (`seedling`, `plant`, `tree`, `cutting`; `bulb` also allows grams). The *packaging* is deliberately a **separate optional attribute** `Presentation` (`pot`, `tray`, `plug`, `bareRoot`, `rootBall`) so "how many" is never conflated with "in what format" — that conflation was the original flaw (the old `pot`/`tray` "amounts", now retained only for old rows). Presentation is offered only for `seedling`/`plant`/`tree`. **Left open:** grafts/divisions/forestry seedlings (nicho) fold into `cutting`/`plant` for now, or a free note; revisit if real exchanges demand their own key.
|
||||||
|
|
||||||
### 2.4 `Movement` — the append-only event log
|
### 2.4 `Movement` — the append-only event log
|
||||||
Immutable events on a `Lot`. Entries/exits are just types. This *is* the history tab and the provenance chain.
|
Immutable events on a `Lot`. Entries/exits are just types. This *is* the history tab and the provenance chain.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ Estas fijan `schemaVersion = 1` y el arranque técnico:
|
||||||
- **Cifrado en reposo (el "cómo" técnico):** SQLCipher + llave aleatoria en el almacén del sistema + QR de recuperación. Conceptualmente decidido; falta bajar el mecanismo. → [security-privacy.md](security-privacy.md)
|
- **Cifrado en reposo (el "cómo" técnico):** SQLCipher + llave aleatoria en el almacén del sistema + QR de recuperación. Conceptualmente decidido; falta bajar el mecanismo. → [security-privacy.md](security-privacy.md)
|
||||||
- **Identidad → ✅ una sola clave** raíz (Duniter/Ğ1), de la que se deriva la secp256k1 para Nostr. La clave simétrica que cifra la BD en reposo es un **detalle interno aparte** (no es una identidad), así que no contradice el "una sola identidad". → [g1-integration.md](g1-integration.md), [security-privacy.md](security-privacy.md)
|
- **Identidad → ✅ una sola clave** raíz (Duniter/Ğ1), de la que se deriva la secp256k1 para Nostr. La clave simétrica que cifra la BD en reposo es un **detalle interno aparte** (no es una identidad), así que no contradice el "una sola identidad". → [g1-integration.md](g1-integration.md), [security-privacy.md](security-privacy.md)
|
||||||
- **Detalles de modelo que fijan el esquema → ✅** `Quantity` como **tipo compartido** (Lot y Movement); `offer_status` **solo en Lot**; `category` **texto libre** prerrellenado desde `Species.family`. → [data-model.md](data-model.md) §6
|
- **Detalles de modelo que fijan el esquema → ✅** `Quantity` como **tipo compartido** (Lot y Movement); `offer_status` **solo en Lot**; `category` **texto libre** prerrellenado desde `Species.family`. → [data-model.md](data-model.md) §6
|
||||||
|
- **Forma del lote (`LotType`) y envase (`Presentation`) → ✅** (schema v4). `LotType` pasa de `{seed, plant}` a **6 formas** append-only: `seed, seedling(plantón), plant, tree, bulb, cutting` — tras repasar el vocabulario de viveros/tiendas ("plantel" era mal nombre: es el semillero, no una unidad). El **envase** (`pot/tray/plug/bareRoot/rootBall`) es un **atributo opcional aparte**, no una cantidad, para no mezclar "cuántas" con "en qué formato". Germinación sigue siendo solo-`seed`. → [data-model.md](data-model.md) §2.3.2
|
||||||
- **Licencia** del código → ✅ **AGPL-3.0** (GPLv3 + cláusula de red; compatible con GPLv3 y con Duniter/Ğ1nkgo). Falta añadir el fichero `LICENSE` al abrir el repo.
|
- **Licencia** del código → ✅ **AGPL-3.0** (GPLv3 + cláusula de red; compatible con GPLv3 y con Duniter/Ğ1nkgo). Falta añadir el fichero `LICENSE` al abrir el repo.
|
||||||
- **Varilla v1 → ✅ viable** con datos abiertos: nombres/identificación (Wikidata+GBIF, fácil), **conservación desde Kew SID** (dominio público, ~52k taxones), cuidados enlazados de Permapeople. Falta: confirmar acceso/descarga de Kew SID y el subconjunto curado a empaquetar. → [data-notes.md](data-notes.md) §3-ter
|
- **Varilla v1 → ✅ viable** con datos abiertos: nombres/identificación (Wikidata+GBIF, fácil), **conservación desde Kew SID** (dominio público, ~52k taxones), cuidados enlazados de Permapeople. Falta: confirmar acceso/descarga de Kew SID y el subconjunto curado a empaquetar. → [data-notes.md](data-notes.md) §3-ter
|
||||||
- **Estructura del workspace** (`commons_core` + `app_seeds`, pub workspaces) — decidido; falta materializarlo.
|
- **Estructura del workspace** (`commons_core` + `app_seeds`, pub workspaces) — decidido; falta materializarlo.
|
||||||
|
|
|
||||||
|
|
@ -33,10 +33,15 @@ enum QuantityKind {
|
||||||
tuber(QuantityGroup.seedForm, countable: true),
|
tuber(QuantityGroup.seedForm, countable: true),
|
||||||
seedHead(QuantityGroup.seedForm, countable: true),
|
seedHead(QuantityGroup.seedForm, countable: true),
|
||||||
bunch(QuantityGroup.seedForm, countable: true),
|
bunch(QuantityGroup.seedForm, countable: true),
|
||||||
// Seedlings / plants (for a plant lot).
|
// Living material counted as whole individuals (seedling / plant / tree /
|
||||||
|
// bulb / cutting lots). [pot] and [tray] are retained for old rows but are no
|
||||||
|
// longer offered as amounts — packaging now lives in [Presentation] instead.
|
||||||
plant(QuantityGroup.plant, countable: true),
|
plant(QuantityGroup.plant, countable: true),
|
||||||
pot(QuantityGroup.plant, countable: true),
|
pot(QuantityGroup.plant, countable: true),
|
||||||
tray(QuantityGroup.plant, countable: true),
|
tray(QuantityGroup.plant, countable: true),
|
||||||
|
seedling(QuantityGroup.plant, countable: true),
|
||||||
|
tree(QuantityGroup.plant, countable: true),
|
||||||
|
cutting(QuantityGroup.plant, countable: true),
|
||||||
// Precise (optional).
|
// Precise (optional).
|
||||||
grams(QuantityGroup.precise, countable: true),
|
grams(QuantityGroup.precise, countable: true),
|
||||||
count(QuantityGroup.precise, countable: true);
|
count(QuantityGroup.precise, countable: true);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue