added categories, import export, settings

This commit is contained in:
davmar
2026-08-29 16:45:27 +02:00
parent 2a5a922aab
commit 732a0dc14a
30 changed files with 3700 additions and 167 deletions
+48 -1
View File
@@ -8,6 +8,11 @@ class Product {
required this.lastPrice,
required this.timesSeen,
required this.updatedAt,
this.favourite = false,
this.imagePath,
this.supermarket,
this.notes,
this.category,
});
final int id;
@@ -15,14 +20,56 @@ class Product {
final double lastPrice;
final int timesSeen;
final DateTime updatedAt;
final bool favourite;
final String? imagePath;
final String? supermarket;
final String? notes;
final String? category;
factory Product.fromMap(Map<String, Object?> map) {
return Product(
id: map['id']! as int,
name: map['name']! as String,
lastPrice: (map['last_price']! as num).toDouble(),
lastPrice: (map['display_price'] as num? ?? map['last_price']! as num)
.toDouble(),
timesSeen: map['times_seen']! as int,
updatedAt: DateTime.parse(map['updated_at']! as String),
favourite: (map['favourite'] as int? ?? 0) == 1,
imagePath: map['image_path'] as String?,
supermarket: map['supermarket'] as String?,
notes: map['notes'] as String?,
category: map['category'] as String?,
);
}
Product copyWith({
String? name,
double? lastPrice,
int? timesSeen,
DateTime? updatedAt,
bool? favourite,
String? imagePath,
String? supermarket,
String? notes,
String? category,
bool clearImage = false,
bool clearSupermarket = false,
bool clearNotes = false,
bool clearCategory = false,
}) {
return Product(
id: id,
name: name ?? this.name,
lastPrice: lastPrice ?? this.lastPrice,
timesSeen: timesSeen ?? this.timesSeen,
updatedAt: updatedAt ?? this.updatedAt,
favourite: favourite ?? this.favourite,
imagePath: clearImage ? null : (imagePath ?? this.imagePath),
supermarket: clearSupermarket ? null : (supermarket ?? this.supermarket),
notes: clearNotes ? null : (notes ?? this.notes),
category: clearCategory ? null : (category ?? this.category),
);
}
}
enum ProductSort { name, preferred }
+36
View File
@@ -0,0 +1,36 @@
/// One time a product was bought, used for price and purchase history.
library;
class Purchase {
const Purchase({
required this.shopId,
required this.boughtAt,
required this.supermarket,
required this.price,
this.quantity,
this.unitPrice,
this.receiptImagePath,
});
final int shopId;
final DateTime boughtAt;
final String supermarket;
final double price;
final int? quantity;
final double? unitPrice;
final String? receiptImagePath;
double get unitOrLinePrice => unitPrice ?? price;
factory Purchase.fromMap(Map<String, Object?> map) {
return Purchase(
shopId: map['shop_id']! as int,
boughtAt: DateTime.parse(map['shopped_at']! as String),
supermarket: map['supermarket']! as String,
price: (map['price']! as num).toDouble(),
quantity: map['quantity'] as int?,
unitPrice: (map['unit_price'] as num?)?.toDouble(),
receiptImagePath: map['receipt_image_path'] as String?,
);
}
}
+63
View File
@@ -0,0 +1,63 @@
/// A confirmed shopping trip saved from a receipt.
library;
class Shop {
const Shop({
required this.id,
required this.supermarket,
required this.shoppedAt,
required this.total,
required this.itemCount,
this.receiptImagePath,
});
final int id;
final String supermarket;
final DateTime shoppedAt;
final double total;
final int itemCount;
final String? receiptImagePath;
factory Shop.fromMap(Map<String, Object?> map) {
return Shop(
id: map['id']! as int,
supermarket: map['supermarket']! as String,
shoppedAt: DateTime.parse(map['shopped_at']! as String),
total: (map['total']! as num).toDouble(),
itemCount: (map['item_count'] as num?)?.toInt() ?? 0,
receiptImagePath: map['receipt_image_path'] as String?,
);
}
}
class ShopItem {
const ShopItem({
required this.id,
required this.shopId,
required this.productId,
required this.name,
required this.price,
this.quantity,
this.unitPrice,
});
final int id;
final int shopId;
final int productId;
final String name;
final double price;
final int? quantity;
final double? unitPrice;
factory ShopItem.fromMap(Map<String, Object?> map) {
return ShopItem(
id: map['id']! as int,
shopId: map['shop_id']! as int,
productId: map['product_id']! as int,
name: map['name']! as String,
price: (map['price']! as num).toDouble(),
quantity: map['quantity'] as int?,
unitPrice: (map['unit_price'] as num?)?.toDouble(),
);
}
}
+59
View File
@@ -0,0 +1,59 @@
/// A user-defined supermarket with a display color.
library;
import 'package:flutter/material.dart';
class Supermarket {
const Supermarket({
this.id,
required this.name,
required this.colorValue,
this.sortOrder = 0,
});
final int? id;
final String name;
final int colorValue;
final int sortOrder;
Color get color => Color(colorValue);
factory Supermarket.fromMap(Map<String, Object?> map) {
return Supermarket(
id: map['id'] as int?,
name: map['name']! as String,
colorValue: map['color']! as int,
sortOrder: map['sort_order'] as int? ?? 0,
);
}
Supermarket copyWith({
int? id,
String? name,
int? colorValue,
int? sortOrder,
}) {
return Supermarket(
id: id ?? this.id,
name: name ?? this.name,
colorValue: colorValue ?? this.colorValue,
sortOrder: sortOrder ?? this.sortOrder,
);
}
}
/// Palette offered when the user picks a store color.
const List<int> kStoreColorPalette = [
0xFFEEC21B, // Jumbo yellow
0xFF0050AA, // Lidl blue
0xFF00A0E2, // AH blue
0xFF00205B, // Aldi navy
0xFF6EC31E, // Plus green
0xFFE30613, // Dirk / Coop red
0xFF009640, // SPAR green
0xFFE87722, // orange
0xFFE31C5F, // Picnic pink
0xFF7B1FA2, // purple
0xFF00897B, // teal
0xFF78909C, // grey
];