Added report functionality and Dropbox connection
This commit is contained in:
parent
483fbeafb6
commit
c7494eda92
8 changed files with 614 additions and 2 deletions
184
test/fuel_report_test.dart
Normal file
184
test/fuel_report_test.dart
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:fuel_tax_tracker/models/fuel_entry.dart';
|
||||
import 'package:fuel_tax_tracker/models/vehicle.dart';
|
||||
import 'package:fuel_tax_tracker/services/fuel_report.dart';
|
||||
|
||||
void main() {
|
||||
final truck = Vehicle(
|
||||
id: 'v1',
|
||||
vin: '1FMPU18L1TLB51349',
|
||||
nickname: 'Truck',
|
||||
updatedAt: DateTime.utc(2026, 1, 1),
|
||||
);
|
||||
final car = Vehicle(
|
||||
id: 'v2',
|
||||
vin: '1GNWK5EG4DR302326',
|
||||
updatedAt: DateTime.utc(2026, 1, 1),
|
||||
);
|
||||
|
||||
FuelEntry entry({
|
||||
required String id,
|
||||
required String vehicleId,
|
||||
required DateTime date,
|
||||
double gallons = 10,
|
||||
double totalCost = 35,
|
||||
String? receiptImagePath,
|
||||
String? receiptDriveFileId,
|
||||
}) =>
|
||||
FuelEntry(
|
||||
id: id,
|
||||
vehicleId: vehicleId,
|
||||
date: date,
|
||||
gallons: gallons,
|
||||
pricePerGallon: totalCost / gallons,
|
||||
totalCost: totalCost,
|
||||
updatedAt: date,
|
||||
receiptImagePath: receiptImagePath,
|
||||
receiptDriveFileId: receiptDriveFileId,
|
||||
);
|
||||
|
||||
test('sums gallons and cost per vehicle, only for entries with a receipt', () {
|
||||
final report = buildFuelReport(
|
||||
vehicles: [truck, car],
|
||||
fuelEntries: [
|
||||
entry(
|
||||
id: 'e1',
|
||||
vehicleId: 'v1',
|
||||
date: DateTime(2026, 3, 5),
|
||||
gallons: 10,
|
||||
totalCost: 35,
|
||||
receiptImagePath: '/local/a.jpg'),
|
||||
entry(
|
||||
id: 'e2',
|
||||
vehicleId: 'v1',
|
||||
date: DateTime(2026, 3, 20),
|
||||
gallons: 12,
|
||||
totalCost: 42,
|
||||
receiptDriveFileId: 'remote-1'),
|
||||
// No receipt at all — should be excluded even though it's in range.
|
||||
entry(id: 'e3', vehicleId: 'v1', date: DateTime(2026, 3, 10), gallons: 99, totalCost: 999),
|
||||
],
|
||||
startDate: DateTime(2026, 3, 1),
|
||||
endDate: DateTime(2026, 3, 31),
|
||||
);
|
||||
|
||||
expect(report.rows, hasLength(1));
|
||||
expect(report.rows.single.vehicle.id, 'v1');
|
||||
expect(report.rows.single.entryCount, 2);
|
||||
expect(report.rows.single.totalGallons, 22);
|
||||
expect(report.rows.single.totalCost, 77);
|
||||
});
|
||||
|
||||
test('date range is inclusive of both endpoints', () {
|
||||
final report = buildFuelReport(
|
||||
vehicles: [truck],
|
||||
fuelEntries: [
|
||||
entry(
|
||||
id: 'first-day',
|
||||
vehicleId: 'v1',
|
||||
date: DateTime(2026, 3, 1),
|
||||
receiptImagePath: '/a.jpg'),
|
||||
entry(
|
||||
id: 'last-day',
|
||||
vehicleId: 'v1',
|
||||
date: DateTime(2026, 3, 31),
|
||||
receiptImagePath: '/b.jpg'),
|
||||
entry(
|
||||
id: 'day-after',
|
||||
vehicleId: 'v1',
|
||||
date: DateTime(2026, 4, 1),
|
||||
receiptImagePath: '/c.jpg'),
|
||||
],
|
||||
startDate: DateTime(2026, 3, 1),
|
||||
endDate: DateTime(2026, 3, 31),
|
||||
);
|
||||
|
||||
expect(report.rows.single.entryCount, 2);
|
||||
});
|
||||
|
||||
test('a receipt-photo time-of-day does not push it out of range', () {
|
||||
final report = buildFuelReport(
|
||||
vehicles: [truck],
|
||||
fuelEntries: [
|
||||
entry(
|
||||
id: 'late-in-day',
|
||||
vehicleId: 'v1',
|
||||
date: DateTime(2026, 3, 31, 23, 59),
|
||||
receiptImagePath: '/a.jpg',
|
||||
),
|
||||
],
|
||||
startDate: DateTime(2026, 3, 1),
|
||||
endDate: DateTime(2026, 3, 31),
|
||||
);
|
||||
|
||||
expect(report.rows.single.entryCount, 1);
|
||||
});
|
||||
|
||||
test('omits vehicles with no qualifying entries rather than a zero row', () {
|
||||
final report = buildFuelReport(
|
||||
vehicles: [truck, car],
|
||||
fuelEntries: [
|
||||
entry(id: 'e1', vehicleId: 'v1', date: DateTime(2026, 3, 5), receiptImagePath: '/a.jpg'),
|
||||
],
|
||||
startDate: DateTime(2026, 3, 1),
|
||||
endDate: DateTime(2026, 3, 31),
|
||||
);
|
||||
|
||||
expect(report.rows, hasLength(1));
|
||||
expect(report.rows.single.vehicle.id, 'v1');
|
||||
});
|
||||
|
||||
test('rows are sorted by vehicle display label', () {
|
||||
final zVehicle = Vehicle(
|
||||
id: 'v3',
|
||||
vin: 'ZZZ00000000000000',
|
||||
nickname: 'Zeta',
|
||||
updatedAt: DateTime.utc(2026, 1, 1),
|
||||
);
|
||||
final aVehicle = Vehicle(
|
||||
id: 'v4',
|
||||
vin: 'AAA00000000000000',
|
||||
nickname: 'Alpha',
|
||||
updatedAt: DateTime.utc(2026, 1, 1),
|
||||
);
|
||||
|
||||
final report = buildFuelReport(
|
||||
vehicles: [zVehicle, aVehicle],
|
||||
fuelEntries: [
|
||||
entry(id: 'e1', vehicleId: 'v3', date: DateTime(2026, 3, 5), receiptImagePath: '/a.jpg'),
|
||||
entry(id: 'e2', vehicleId: 'v4', date: DateTime(2026, 3, 5), receiptImagePath: '/b.jpg'),
|
||||
],
|
||||
startDate: DateTime(2026, 3, 1),
|
||||
endDate: DateTime(2026, 3, 31),
|
||||
);
|
||||
|
||||
expect(report.rows.map((r) => r.vehicle.id), ['v4', 'v3']);
|
||||
});
|
||||
|
||||
test('report totals sum across all vehicle rows', () {
|
||||
final report = buildFuelReport(
|
||||
vehicles: [truck, car],
|
||||
fuelEntries: [
|
||||
entry(
|
||||
id: 'e1',
|
||||
vehicleId: 'v1',
|
||||
date: DateTime(2026, 3, 5),
|
||||
gallons: 10,
|
||||
totalCost: 35,
|
||||
receiptImagePath: '/a.jpg'),
|
||||
entry(
|
||||
id: 'e2',
|
||||
vehicleId: 'v2',
|
||||
date: DateTime(2026, 3, 6),
|
||||
gallons: 8,
|
||||
totalCost: 28,
|
||||
receiptImagePath: '/b.jpg'),
|
||||
],
|
||||
startDate: DateTime(2026, 3, 1),
|
||||
endDate: DateTime(2026, 3, 31),
|
||||
);
|
||||
|
||||
expect(report.totalGallons, 18);
|
||||
expect(report.totalCost, 63);
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue