feat(inventory): local sharing — per-lot share terms, filter and printable catalog
The local half of the original Fase 2 (no network, no Offer entity yet): - Lot sheet gains a 'do you share it?' choice (gift/swap first, sale last, never the default); declaring plenty of abundance reveals it with a nudge. - Lot lines and list tiles badge shared batches; an 'I share' filter and a printable PDF catalog (paper bridge for fairs) round out the view. - Catalog PDF embeds DejaVu (Latin-ext/Cyrillic/Greek); fonts are a per-locale resource like the OCR packs.
This commit is contained in:
parent
e3ec855630
commit
ba87bf2719
20 changed files with 982 additions and 36 deletions
104
apps/app_seeds/test/data/shared_catalog_test.dart
Normal file
104
apps/app_seeds/test/data/shared_catalog_test.dart
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
import 'package:commons_core/commons_core.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:tane/data/species_repository.dart';
|
||||
import 'package:tane/data/variety_repository.dart';
|
||||
import 'package:tane/db/database.dart';
|
||||
import 'package:tane/db/enums.dart';
|
||||
|
||||
import '../support/test_support.dart';
|
||||
|
||||
void main() {
|
||||
late AppDatabase db;
|
||||
late VarietyRepository repo;
|
||||
|
||||
setUp(() {
|
||||
db = newTestDatabase();
|
||||
repo = newTestRepository(db);
|
||||
});
|
||||
tearDown(() => db.close());
|
||||
|
||||
test('addLot and updateLot persist the share terms', () async {
|
||||
final id = await repo.addQuickVariety(label: 'Maize');
|
||||
final lotId = await repo.addLot(
|
||||
varietyId: id,
|
||||
offerStatus: OfferStatus.shared,
|
||||
);
|
||||
|
||||
var detail = await repo.watchVariety(id).first;
|
||||
expect(detail!.lots.single.offerStatus, OfferStatus.shared);
|
||||
|
||||
await repo.updateLot(
|
||||
lotId: lotId,
|
||||
type: LotType.seed,
|
||||
offerStatus: OfferStatus.exchange,
|
||||
);
|
||||
detail = await repo.watchVariety(id).first;
|
||||
expect(detail!.lots.single.offerStatus, OfferStatus.exchange);
|
||||
});
|
||||
|
||||
test('list items flag varieties with some lot offered', () async {
|
||||
final sharedId = await repo.addQuickVariety(label: 'Shared bean');
|
||||
await repo.addLot(varietyId: sharedId, offerStatus: OfferStatus.sell);
|
||||
final privateId = await repo.addQuickVariety(label: 'Private bean');
|
||||
await repo.addLot(varietyId: privateId);
|
||||
|
||||
final items = await repo.watchInventoryView().first;
|
||||
final byLabel = {for (final i in items.items) i.label: i};
|
||||
expect(byLabel['Shared bean']!.isShared, isTrue);
|
||||
expect(byLabel['Private bean']!.isShared, isFalse);
|
||||
});
|
||||
|
||||
test('sharedCatalog lists only offered lots, sorted and joined', () async {
|
||||
final species = newTestSpeciesRepository(db);
|
||||
await species.seedBundled(const [
|
||||
SpeciesSeed(
|
||||
scientificName: 'Zea mays',
|
||||
family: 'Poaceae',
|
||||
commonNames: {
|
||||
'en': ['Maize'],
|
||||
},
|
||||
),
|
||||
]);
|
||||
|
||||
final maizeId = await repo.addQuickVariety(label: 'Rebordanes maize');
|
||||
final matches = await species.search('Zea', languageCode: 'en');
|
||||
await repo.linkSpecies(maizeId, matches.single.id);
|
||||
await repo.addLot(
|
||||
varietyId: maizeId,
|
||||
harvestYear: 2024,
|
||||
quantity: const Quantity(kind: QuantityKind.cob, count: 2),
|
||||
offerStatus: OfferStatus.shared,
|
||||
);
|
||||
// A private lot of the same variety stays out of the catalog.
|
||||
await repo.addLot(varietyId: maizeId, harvestYear: 2023);
|
||||
|
||||
final beanId = await repo.addQuickVariety(label: 'Alubia de Tolosa');
|
||||
await repo.addLot(
|
||||
varietyId: beanId,
|
||||
abundance: Abundance.plentyToShare,
|
||||
offerStatus: OfferStatus.exchange,
|
||||
);
|
||||
|
||||
// A deleted shared lot disappears with its tombstone.
|
||||
final goneId = await repo.addQuickVariety(label: 'Gone');
|
||||
final goneLot = await repo.addLot(
|
||||
varietyId: goneId,
|
||||
offerStatus: OfferStatus.shared,
|
||||
);
|
||||
await repo.softDeleteLot(goneLot);
|
||||
|
||||
final catalog = await repo.sharedCatalog();
|
||||
expect(catalog, hasLength(2));
|
||||
// Sorted by label, case-insensitively.
|
||||
expect(catalog.first.varietyLabel, 'Alubia de Tolosa');
|
||||
expect(catalog.first.offerStatus, OfferStatus.exchange);
|
||||
expect(catalog.first.abundance, Abundance.plentyToShare);
|
||||
|
||||
final maize = catalog.last;
|
||||
expect(maize.varietyLabel, 'Rebordanes maize');
|
||||
expect(maize.scientificName, 'Zea mays');
|
||||
expect(maize.category, 'Poaceae');
|
||||
expect(maize.harvestYear, 2024);
|
||||
expect(maize.quantity!.count, 2);
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue