parser logic added
This commit is contained in:
@@ -3,8 +3,8 @@ import 'package:receipity/services/backup_format.dart';
|
||||
|
||||
void main() {
|
||||
test('round-trips a backup document', () {
|
||||
final original = const BackupDocument(
|
||||
version: 1,
|
||||
const original = BackupDocument(
|
||||
version: 2,
|
||||
exportedAt: '2026-08-29T12:00:00.000',
|
||||
darkMode: true,
|
||||
supermarkets: [
|
||||
@@ -46,6 +46,24 @@ void main() {
|
||||
'unit_price': 1.5,
|
||||
},
|
||||
],
|
||||
shoppingLists: [
|
||||
{
|
||||
'id': 1,
|
||||
'name': 'Weekly',
|
||||
'created_at': '2026-08-29T12:00:00.000',
|
||||
'updated_at': '2026-08-29T12:00:00.000',
|
||||
},
|
||||
],
|
||||
shoppingListItems: [
|
||||
{
|
||||
'id': 1,
|
||||
'list_id': 1,
|
||||
'product_id': 1,
|
||||
'name': 'Milk',
|
||||
'checked': 0,
|
||||
'sort_order': 0,
|
||||
},
|
||||
],
|
||||
images: {'products/product_1.jpg': 'abc123'},
|
||||
);
|
||||
|
||||
@@ -54,9 +72,31 @@ void main() {
|
||||
expect(parsed.products.single['name'], 'Milk');
|
||||
expect(parsed.shops.single['id'], 8);
|
||||
expect(parsed.shopItems.single['product_id'], 1);
|
||||
expect(parsed.shoppingLists.single['name'], 'Weekly');
|
||||
expect(parsed.shoppingListItems.single['name'], 'Milk');
|
||||
expect(parsed.images['products/product_1.jpg'], 'abc123');
|
||||
});
|
||||
|
||||
test('accepts a version 1 backup without shopping lists', () {
|
||||
const json = '''
|
||||
{
|
||||
"format": "receipity-backup",
|
||||
"version": 1,
|
||||
"exportedAt": "2026-08-29T12:00:00.000",
|
||||
"darkMode": false,
|
||||
"supermarkets": [],
|
||||
"products": [],
|
||||
"shops": [],
|
||||
"shopItems": [],
|
||||
"images": {}
|
||||
}
|
||||
''';
|
||||
final parsed = BackupDocument.parse(json);
|
||||
expect(parsed.version, 1);
|
||||
expect(parsed.shoppingLists, isEmpty);
|
||||
expect(parsed.shoppingListItems, isEmpty);
|
||||
});
|
||||
|
||||
test('rejects a file that is not a Receipity backup', () {
|
||||
expect(
|
||||
() => BackupDocument.parse('{"hello":"world"}'),
|
||||
|
||||
@@ -44,17 +44,59 @@ BROODSTICKS OLIJF
|
||||
expect(items.single.quantity, 4);
|
||||
});
|
||||
|
||||
test('drops discount rows and Dutch totals', () {
|
||||
final items = ReceiptParser.parse('''
|
||||
test('folds a discount into the previous item', () {
|
||||
final result = ReceiptParser.parseReceipt('''
|
||||
AARDBEIEN 7,98
|
||||
ACTIE AARDBEIEN -2,00
|
||||
Totaal 82,99
|
||||
VISA 82,99
|
||||
Totaal korting: -2,21
|
||||
Totaal 5,98
|
||||
VISA 5,98
|
||||
Totaal korting: -2,00
|
||||
''');
|
||||
|
||||
expect(items.map((item) => item.name), ['AARDBEIEN']);
|
||||
expect(items.single.price, 7.98);
|
||||
expect(result.items.map((item) => item.name), ['AARDBEIEN']);
|
||||
expect(result.items.single.price, 5.98);
|
||||
expect(result.items.single.discount, -2.00);
|
||||
expect(result.validationPassed, isTrue);
|
||||
});
|
||||
|
||||
test('parses Kruidvat-style inline quantity', () {
|
||||
final items = ReceiptParser.parse('''
|
||||
ZEEPTABLET NEUTRAL 2 X 3,99 PER STUK 7,98
|
||||
KORTING NEUTRAL ZE.HP -1,00
|
||||
Totaal 6,98
|
||||
''');
|
||||
|
||||
expect(items, hasLength(1));
|
||||
expect(items.single.name, 'ZEEPTABLET NEUTRAL');
|
||||
expect(items.single.quantity, 2);
|
||||
expect(items.single.unitPrice, 3.99);
|
||||
expect(items.single.price, 6.98);
|
||||
});
|
||||
|
||||
test('parses Lidl-style price-reduction lines', () {
|
||||
final items = ReceiptParser.parse('''
|
||||
MELK 1,09 B
|
||||
In prijs verlaagd -0,50
|
||||
Totaal 0,59
|
||||
''');
|
||||
|
||||
expect(items, hasLength(1));
|
||||
expect(items.single.name, 'MELK');
|
||||
expect(items.single.price, 0.59);
|
||||
});
|
||||
|
||||
test('ignores BTW breakdown rows', () {
|
||||
final items = ReceiptParser.parse('''
|
||||
MELK 2,09
|
||||
Totaal 2,09
|
||||
BTW
|
||||
B 9% 0,17
|
||||
C 21% 0,00
|
||||
Bedr.Excl 1,92
|
||||
Bedr.Incl 2,09
|
||||
''');
|
||||
|
||||
expect(items.map((item) => item.name), ['MELK']);
|
||||
});
|
||||
|
||||
test('pairs a name column with a price column from OCR', () {
|
||||
@@ -66,10 +108,16 @@ Totaal korting: -2,21
|
||||
expect(items.first.name, 'JUMBO BIO VOLLE MELK');
|
||||
expect(items.first.price, 2.09);
|
||||
|
||||
final eggs = items.firstWhere((item) => item.name.startsWith('BIOLOGISCH EI'));
|
||||
final eggs = items.firstWhere(
|
||||
(item) => item.name.startsWith('BIOLOGISCH EI'),
|
||||
);
|
||||
expect(eggs.quantity, 2);
|
||||
expect(eggs.price, 5.58);
|
||||
|
||||
final berries = items.firstWhere((item) => item.name == 'AARDBEIEN');
|
||||
expect(berries.quantity, 2);
|
||||
expect(berries.price, 5.98);
|
||||
|
||||
expect(items.last.name, 'JUMBO PREM LUIER M4');
|
||||
expect(items.last.price, 6.20);
|
||||
expect(items, hasLength(26));
|
||||
|
||||
+19
-2
@@ -14,16 +14,33 @@ void main() {
|
||||
expect(find.text('Receipity'), findsOneWidget);
|
||||
expect(find.text('Take photo'), findsOneWidget);
|
||||
expect(find.text('Pick from gallery'), findsOneWidget);
|
||||
expect(find.text('Enter manually'), findsOneWidget);
|
||||
expect(find.text('Use sample receipt'), findsOneWidget);
|
||||
expect(
|
||||
find.textContaining('Take a photo of a shopping receipt'),
|
||||
find.textContaining('Scan a receipt or enter a trip by hand'),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(find.text('Products'), findsOneWidget);
|
||||
expect(find.text('Scan'), findsOneWidget);
|
||||
expect(find.text('Lists'), findsOneWidget);
|
||||
expect(find.text('Add'), findsOneWidget);
|
||||
expect(find.text('Shop log'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('enter manually opens a blank trip', (tester) async {
|
||||
await tester.pumpWidget(ReceipityApp());
|
||||
await tester.tap(find.text('Enter manually'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('New trip'), findsOneWidget);
|
||||
expect(find.text('Add item'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('lists tab shows shopping lists', (tester) async {
|
||||
await tester.pumpWidget(ReceipityApp());
|
||||
await tester.tap(find.text('Lists'));
|
||||
await tester.pump();
|
||||
expect(find.text('Shopping lists'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('review screen lists items and can delete one', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
|
||||
Reference in New Issue
Block a user