The data model always meant sale offers to carry an informational price (data-model §2.8, sharing-model §4.4); with offer state collapsed onto the Lot, the price lives there. Null amount on a sell lot = 'to be agreed' — the NIP-99 event simply omits the price tag. - schema v11: Lots.price_amount/price_currency, guarded migration, schema dump + generated schema_v11 for the round-trip test - ShareableLot/VarietyLot carry the price (sell-only in shareableLots); addLot/updateLot persist it and clear it when the lot leaves sale - OffersCubit.publishLots forwards it (outbox flush picks it up free) - lot sheet: price + currency inputs revealed only on 'For sale', with the €/Ğ1/hours quick-picks extracted to a shared CurrencyQuickPicks widget (reused by the sale sheet) - backup JSON round-trips the new columns - i18n share.price/priceHint in en/es/pt/ast
110 lines
3.5 KiB
Dart
110 lines
3.5 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:tane/db/database.dart';
|
|
import 'package:tane/db/enums.dart';
|
|
|
|
import '../support/test_support.dart';
|
|
|
|
void main() {
|
|
late AppDatabase db;
|
|
setUp(() => db = newTestDatabase());
|
|
tearDown(() => db.close());
|
|
|
|
test('shareableLots returns non-private lots with their id and label',
|
|
() async {
|
|
final repo = newTestRepository(db);
|
|
final varietyId = await repo.addQuickVariety(
|
|
label: 'Tomate rosa',
|
|
category: 'Solanaceae',
|
|
);
|
|
final sharedId = await repo.addLot(
|
|
varietyId: varietyId,
|
|
offerStatus: OfferStatus.exchange,
|
|
);
|
|
|
|
final lots = await repo.shareableLots();
|
|
final shared = lots.where((l) => l.lotId == sharedId).toList();
|
|
expect(shared, hasLength(1));
|
|
expect(shared.single.summary, 'Tomate rosa');
|
|
expect(shared.single.offerStatus, OfferStatus.exchange);
|
|
expect(shared.single.category, 'Solanaceae');
|
|
});
|
|
|
|
test('private lots are never shareable', () async {
|
|
final repo = newTestRepository(db);
|
|
final varietyId = await repo.addQuickVariety(label: 'Pimiento');
|
|
final privateId = await repo.addLot(
|
|
varietyId: varietyId,
|
|
offerStatus: OfferStatus.private,
|
|
);
|
|
|
|
final lots = await repo.shareableLots();
|
|
expect(lots.where((l) => l.lotId == privateId), isEmpty);
|
|
});
|
|
|
|
test('sell lots expose their asking price; other statuses never do',
|
|
() async {
|
|
final repo = newTestRepository(db);
|
|
final varietyId = await repo.addQuickVariety(label: 'Calabaza');
|
|
final sellId = await repo.addLot(
|
|
varietyId: varietyId,
|
|
offerStatus: OfferStatus.sell,
|
|
priceAmount: 3.5,
|
|
priceCurrency: 'Ğ1',
|
|
);
|
|
// Price passed on a gift lot is dropped, not stored.
|
|
final giftId = await repo.addLot(
|
|
varietyId: varietyId,
|
|
offerStatus: OfferStatus.shared,
|
|
priceAmount: 99,
|
|
priceCurrency: '€',
|
|
);
|
|
|
|
final lots = await repo.shareableLots();
|
|
final sell = lots.singleWhere((l) => l.lotId == sellId);
|
|
expect(sell.priceAmount, 3.5);
|
|
expect(sell.priceCurrency, 'Ğ1');
|
|
final gift = lots.singleWhere((l) => l.lotId == giftId);
|
|
expect(gift.priceAmount, isNull);
|
|
expect(gift.priceCurrency, isNull);
|
|
});
|
|
|
|
test('a sell lot without amount is "to be agreed" — shareable, no price',
|
|
() async {
|
|
final repo = newTestRepository(db);
|
|
final varietyId = await repo.addQuickVariety(label: 'Lechuga');
|
|
final sellId = await repo.addLot(
|
|
varietyId: varietyId,
|
|
offerStatus: OfferStatus.sell,
|
|
);
|
|
|
|
final lots = await repo.shareableLots();
|
|
final sell = lots.singleWhere((l) => l.lotId == sellId);
|
|
expect(sell.priceAmount, isNull);
|
|
});
|
|
|
|
test('moving a lot out of sale clears its stored price', () async {
|
|
final repo = newTestRepository(db);
|
|
final varietyId = await repo.addQuickVariety(label: 'Haba');
|
|
final lotId = await repo.addLot(
|
|
varietyId: varietyId,
|
|
offerStatus: OfferStatus.sell,
|
|
priceAmount: 2,
|
|
priceCurrency: '€',
|
|
);
|
|
|
|
await repo.updateLot(
|
|
lotId: lotId,
|
|
type: LotType.seed,
|
|
offerStatus: OfferStatus.shared,
|
|
// Price args still sent by the form; must be ignored off-sale.
|
|
priceAmount: 2,
|
|
priceCurrency: '€',
|
|
);
|
|
|
|
final detail = await repo.watchVariety(varietyId).first;
|
|
final lot = detail!.lots.singleWhere((l) => l.id == lotId);
|
|
expect(lot.priceAmount, isNull);
|
|
expect(lot.priceCurrency, isNull);
|
|
expect(lot.offerStatus, OfferStatus.shared);
|
|
});
|
|
}
|