diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..62dca38 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +scouts_finances_flutter/ios/Podfile.lock +scouts_finances_flutter/ios/Runner.xcodeproj/project.pbxproj diff --git a/scouts_finances_client/lib/src/protocol/client.dart b/scouts_finances_client/lib/src/protocol/client.dart index 8663f33..4c00fef 100644 --- a/scouts_finances_client/lib/src/protocol/client.dart +++ b/scouts_finances_client/lib/src/protocol/client.dart @@ -34,6 +34,12 @@ class EndpointAdmin extends _i1.EndpointRef { 'resetDb', {}, ); + + _i2.Future dummyData() => caller.callServerEndpoint( + 'admin', + 'dummyData', + {}, + ); } /// {@category Endpoint} diff --git a/scouts_finances_flutter/lib/events/home.dart b/scouts_finances_flutter/lib/events/home.dart index a6a2924..2ce9811 100644 --- a/scouts_finances_flutter/lib/events/home.dart +++ b/scouts_finances_flutter/lib/events/home.dart @@ -19,7 +19,8 @@ class EventHome extends StatefulWidget { } class _EventHomeState extends State { - late List events; + late List futureEvents; + late List pastEvents; late EventPaidCounts paidCounts; // eventId -> (paidCount, totalCount) String? errorMessage; int loading = 2; @@ -37,7 +38,10 @@ class _EventHomeState extends State { try { final result = await client.event.getEvents(); setState(() { - events = result; + futureEvents = + result.where((e) => e.date.isAfter(DateTime.now())).toList(); + pastEvents = + result.where((e) => e.date.isBefore(DateTime.now())).toList(); }); } catch (e) { setState(() { @@ -105,75 +109,27 @@ class _EventHomeState extends State { } // Filter events based on the search query - List filteredEvents = events.where((event) { - final scoutGroupName = event.scoutGroup?.name ?? ''; - return event.name.toLowerCase().contains(query.toLowerCase()) || - scoutGroupName.toLowerCase().contains(query.toLowerCase()); - }).toList(); + List filteredFutureEvents = + futureEvents.where((e) => _eventQueryFilter(e)).toList(); + List filteredPastEvents = + pastEvents.where((e) => _eventQueryFilter(e)).toList(); - filteredEvents.sort((a, b) { - switch (sortIndex) { - case 0: // Upcoming First - return a.date.compareTo(b.date); - case 1: // Upcoming Last - return b.date.compareTo(a.date); - case 2: // Most Paid - final (paidA, totalA) = paidCounts[a.id!] ?? (0, 0); - final (paidB, totalB) = paidCounts[b.id!] ?? (0, 0); - return paidB.compareTo(paidA); // Sort by most paid - case 3: // Least Paid - final (paidA, totalA) = paidCounts[a.id!] ?? (0, 0); - final (paidB, totalB) = paidCounts[b.id!] ?? (0, 0); - return paidA.compareTo(paidB); // Sort by least paid - default: - return 0; // No sorting - } - }); + filteredFutureEvents.sort((a, b) => _sortEvents(a, b)); + filteredPastEvents.sort((a, b) => _sortEvents(a, b)); - Widget eventExpansionTile = + Widget futureEventsExpansionTile = Consumer2( - builder: (context, scoutGroupService, accountTypeService, child) { - final accountType = accountTypeService.accountType; - - final relevantEvents = filteredEvents - .where((e) => - (e.scoutGroupId == scoutGroupService.currentScoutGroup.id) || - accountType == AccountType.treasurer) - .toList(); + builder: (context, scoutGroupService, accountTypeService, child) => + _eventsBuilder(context, scoutGroupService, accountTypeService, + child, filteredFutureEvents, + title: 'Upcoming Events')); - List eventCards = relevantEvents.map((event) { - final (paid, total) = paidCounts[event.id!] ?? (0, 0); - String trailing = accountType == AccountType.treasurer - ? '- ${event.scoutGroup!.name}' - : ''; - - return Card( - child: ListTile( - title: Text('${event.name} $trailing'), - subtitle: Row( - children: [ - Text('$paid/$total Paid'), - const Spacer(), - Icon(Icons.calendar_today, size: 14), - const SizedBox(width: 4.0), - Text(event.date.toLocal().toString().split(' ')[0]), - ], - ), - onTap: () { - Navigator.of(context).push(MaterialPageRoute( - builder: (context) => SingleEvent(eventId: event.id!))); - }, - trailing: const Icon(Icons.arrow_forward), - ), - ); - }).toList(); - - return ExpansionTile( - title: const Text('Future Events'), - initiallyExpanded: true, - controlAffinity: ListTileControlAffinity.leading, - children: eventCards); - }); + Widget pastEventsExpansionTile = + Consumer2( + builder: (ctx, scoutGroupService, accountTypeService, child) => + _eventsBuilder(context, scoutGroupService, accountTypeService, + child, filteredPastEvents, + title: 'Past Events')); SearchBar searchBar = SearchBar( hintText: 'Search by name...', @@ -215,31 +171,8 @@ class _EventHomeState extends State { ListView body = ListView(children: [ searchBar, sortSelection, - eventExpansionTile, - // child: ListView(children: [ - // ...eventCards, - ExpansionTile( - title: const Text('Past Events'), - controlAffinity: ListTileControlAffinity.leading, - initiallyExpanded: false, - children: [ - Card( - child: ListTile( - title: const Text('Winter Camp'), - subtitle: Row( - children: [ - const Text('15/20 paid'), - const Spacer(), - const Text('01/01/2025'), - ], - ), - onTap: () { - // Navigate to event details - }, - trailing: const Icon(Icons.arrow_forward), - ), - ), - ]), + futureEventsExpansionTile, + pastEventsExpansionTile, const SizedBox(height: 128.0), ]); @@ -279,4 +212,78 @@ class _EventHomeState extends State { floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, ); } + + bool _eventQueryFilter(Event event) { + final scoutGroupName = event.scoutGroup?.name ?? ''; + return (event.name.toLowerCase().contains(query.toLowerCase()) || + scoutGroupName.toLowerCase().contains(query.toLowerCase())); + } + + int _sortEvents(Event a, Event b) { + switch (sortIndex) { + case 0: // Upcoming First + return a.date.compareTo(b.date); + case 1: // Upcoming Last + return b.date.compareTo(a.date); + case 2: // Most Paid + final (paidA, totalA) = paidCounts[a.id!] ?? (0, 0); + final (paidB, totalB) = paidCounts[b.id!] ?? (0, 0); + return paidB.compareTo(paidA); // Sort by most paid + case 3: // Least Paid + final (paidA, totalA) = paidCounts[a.id!] ?? (0, 0); + final (paidB, totalB) = paidCounts[b.id!] ?? (0, 0); + return paidA.compareTo(paidB); // Sort by least paid + default: + return 0; // No sorting + } + } + + Widget _eventsBuilder( + BuildContext context, + ScoutGroupsService scoutGroupService, + AccountTypeService accountTypeService, + Widget? child, + List filteredPastEvents, + {required String title}) { + final accountType = accountTypeService.accountType; + final relevantEvents = filteredPastEvents + .where((e) => + (scoutGroupService.scoutGroups.isNotEmpty && + e.scoutGroupId == scoutGroupService.currentScoutGroup.id) || + accountType == AccountType.treasurer) + .toList(); + + List eventCards = relevantEvents.map((event) { + final (paid, total) = paidCounts[event.id!] ?? (0, 0); + String trailing = accountType == AccountType.treasurer + ? '- ${event.scoutGroup?.name ?? 'Unknown Group'}' + : ''; + + return Card( + child: ListTile( + title: Text('${event.name} $trailing'), + subtitle: Row( + children: [ + Text('$paid/$total Paid'), + const Spacer(), + Icon(Icons.calendar_today, size: 14), + const SizedBox(width: 4.0), + Text(event.date.toLocal().toString().split(' ')[0]), + ], + ), + onTap: () { + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => SingleEvent(eventId: event.id!))); + }, + trailing: const Icon(Icons.arrow_forward), + ), + ); + }).toList(); + + return ExpansionTile( + title: Text(title), + initiallyExpanded: true, + controlAffinity: ListTileControlAffinity.leading, + children: eventCards); + } } diff --git a/scouts_finances_flutter/lib/settings/home.dart b/scouts_finances_flutter/lib/settings/home.dart index eebb0b8..0d2b478 100644 --- a/scouts_finances_flutter/lib/settings/home.dart +++ b/scouts_finances_flutter/lib/settings/home.dart @@ -273,8 +273,8 @@ class _SettingsHomeState extends State { children: [ ListTile( title: const Text('Reset Database'), - subtitle: - const Text('Reseed the database with default data'), + subtitle: const Text( + 'Reseed the database with dynamically generated data'), trailing: const Icon(Icons.refresh), onTap: () async { await client.admin.resetDb(); @@ -287,6 +287,25 @@ class _SettingsHomeState extends State { ], ), ), + Card( + child: Column( + children: [ + ListTile( + title: const Text('Dummy Data'), + subtitle: const Text( + 'Replace the database with static dummy data for demo purposes'), + trailing: const Icon(Icons.refresh), + onTap: () async { + await client.admin.dummyData(); + if (!context.mounted) return; + await Provider.of(context, + listen: false) + .refreshScoutGroups(); + }, + ), + ], + ), + ), const SizedBox(height: 32), // Footer Center( diff --git a/scouts_finances_server/lib/src/admin.dart b/scouts_finances_server/lib/src/admin.dart index 2964e9f..68e50cb 100644 --- a/scouts_finances_server/lib/src/admin.dart +++ b/scouts_finances_server/lib/src/admin.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:io'; import 'dart:math'; import 'package:http/http.dart' as http; @@ -6,6 +7,7 @@ import 'package:scouts_finances_server/src/events.dart'; import 'package:scouts_finances_server/src/generated/protocol.dart'; import 'package:scouts_finances_server/src/payments.dart'; import 'package:serverpod/serverpod.dart'; +import 'package:yaml/yaml.dart'; class UserInfo { final String firstName; @@ -194,8 +196,10 @@ class AdminEndpoint extends Endpoint { final cost = 500 + Random().nextInt(2000); // Cost between 5 and 25 pounds // Some time in the next year - final date = - DateTime.now().add(Duration(days: Random().nextInt(365))); + // Randomly pick a date within +/- 180 days from now (past or future) + final date = DateTime.now().add( + Duration(days: Random().nextInt(361) - 180), + ); events.add(Event( name: eventName, cost: cost, @@ -340,4 +344,165 @@ class AdminEndpoint extends Endpoint { } }); } + + Future dummyData(Session session) async { + try { + // --- 1. DROP ALL EXISTING TABLES (in reverse order of dependency) --- + print('--- Dropping all tables... ---'); + await Payment.db.deleteWhere(session, where: (t) => Constant.bool(true)); + await EventRegistration.db + .deleteWhere(session, where: (t) => Constant.bool(true)); + await Event.db.deleteWhere(session, where: (t) => Constant.bool(true)); + await Child.db.deleteWhere(session, where: (t) => Constant.bool(true)); + await BankAccount.db + .deleteWhere(session, where: (t) => Constant.bool(true)); + await Parent.db.deleteWhere(session, where: (t) => Constant.bool(true)); + await ScoutGroup.db + .deleteWhere(session, where: (t) => Constant.bool(true)); + print('--- All tables dropped successfully. ---'); + + // --- 2. RECREATE TABLES FROM SCHEMA --- + // Skipped: Serverpod does not support recreating tables at runtime. + // Tables are managed via migrations and CLI tools, not programmatically. + print('--- Skipping table recreation (not supported at runtime). ---'); + + // --- 3. READ AND PARSE YAML DATA --- + print('--- Reading and parsing YAML file... ---'); + final file = File('lib/src/data/demo.yaml'); + if (!await file.exists()) { + final errorMessage = 'Error: demo.yaml not found at ${file.path}'; + print(errorMessage); + return errorMessage; + } + final content = await file.readAsString(); + final yaml = loadYaml(content); + print('--- YAML file parsed successfully. ---'); + + // Maps to store the mapping from old YAML IDs to new database IDs + final parentIdMap = {}; + final bankAccountIdMap = {}; + final childIdMap = {}; + final eventIdMap = {}; + final scoutGroupIdMap = + {}; // --- 4. IMPORT DATA IN ORDER OF DEPENDENCY --- + + // Import Scout Groups (no dependencies) - using consistent groups like resetDB + print('Importing scout groups...'); + final scoutGroups = [ + ('Beavers', GroupColour.lightblue), + ('Cubs', GroupColour.green), + ('Scouts', GroupColour.teal) + ]; + + final groups = scoutGroups + .map((group) => ScoutGroup(name: group.$1, colour: group.$2)) + .toList(); + final insertedGroups = await ScoutGroup.db.insert(session, groups); + + // Map the inserted groups to maintain YAML ID compatibility + for (int i = 0; i < insertedGroups.length; i++) { + // Assuming YAML scout groups have IDs 1, 2, 3 for consistency + scoutGroupIdMap[i + 1] = insertedGroups[i].id!; + } + + // Import Parents (no dependencies) + print('Importing parents...'); + for (var item in (yaml['parent'] as YamlList)) { + final oldId = item['id'] as int; + final parent = Parent( + firstName: item['firstName'], + lastName: item['lastName'], + email: item['email'], + phone: item['phone'], + balance: item['balance'], + ); + final newParent = await Parent.db.insertRow(session, parent); + parentIdMap[oldId] = newParent.id!; + } + + // Import Bank Accounts (depends on Parent) + print('Importing bank accounts...'); + for (var item in (yaml['bank_account'] as YamlList)) { + final oldId = item['id'] as int; + final account = BankAccount( + accountNumber: item['accountNumber'], + sortCode: item['sortCode'], + name: item['name'], + parentId: parentIdMap[item['parentId']]!, + ); + final newAccount = await BankAccount.db.insertRow(session, account); + bankAccountIdMap[oldId] = newAccount.id!; + } + + // Import Children (depends on Parent and ScoutGroup) + print('Importing children...'); + for (var item in (yaml['child'] as YamlList)) { + final oldId = item['id'] as int; + final child = Child( + firstName: item['firstName'], + lastName: item['lastName'], + parentId: parentIdMap[item['parentId']]!, + scoutGroupId: scoutGroupIdMap[item['scoutGroupId']]!, + ); + final newChild = await Child.db.insertRow(session, child); + childIdMap[oldId] = newChild.id!; + } + + // Import Events (depends on ScoutGroup) + print('Importing events...'); + for (var item in (yaml['event'] as YamlList)) { + final oldId = item['id'] as int; + final event = Event( + name: item['name'], + date: DateTime.parse(item['date']), + cost: item['cost'], + // Handle optional scout group for "All groups" events + scoutGroupId: item['scoutGroupId'] != null + ? scoutGroupIdMap[item['scoutGroupId']]! + : 0, + ); + final newEvent = await Event.db.insertRow(session, event); + eventIdMap[oldId] = newEvent.id!; + } + + // Import Event Registrations (depends on Event and Child) + print('Importing event registrations...'); + for (var item in (yaml['event_registration'] as YamlList)) { + final registration = EventRegistration( + eventId: eventIdMap[item['eventId']]!, + childId: childIdMap[item['childId']]!, + paidDate: item['paidDate'] != null + ? DateTime.parse(item['paidDate']) + : null, + ); + await EventRegistration.db.insertRow(session, registration); + } + + // Import Payments (depends on BankAccount and Parent) + print('Importing payments...'); + for (var item in (yaml['payment'] as YamlList)) { + final payment = Payment( + amount: item['amount'], + date: DateTime.parse(item['date']), + reference: item['reference'], + method: PaymentMethod.values.byName(item['method']), + payee: item['payee'], + bankAccountId: item['bankAccountId'] != null + ? bankAccountIdMap[item['bankAccountId']] + : null, + parentId: + item['parentId'] != null ? parentIdMap[item['parentId']] : null, + ); + await Payment.db.insertRow(session, payment); + } + + print('--- Data import complete! ---'); + return 'Successfully dropped all tables and populated with dummy data.'; + } catch (e, stackTrace) { + final errorMessage = 'An error occurred during data import: $e'; + print(errorMessage); + print(stackTrace); + return errorMessage; + } + } } diff --git a/scouts_finances_server/lib/src/data/demo.yaml b/scouts_finances_server/lib/src/data/demo.yaml new file mode 100644 index 0000000..2f8dada --- /dev/null +++ b/scouts_finances_server/lib/src/data/demo.yaml @@ -0,0 +1,1071 @@ +# Note: This data is fictional and for demonstration purposes only. +# All monetary values are in pence (GBP). +# Current Date Assumed: 2025-06-18 + +# --- Scout Groups --- +scout_group: + - id: 1 + name: "Beavers" + description: "Beaver colony for ages 6-8. We meet on Tuesdays." + colour: lightblue + - id: 2 + name: "Cubs" + description: "Cub pack for ages 8-10.5. We meet on Wednesdays." + colour: green + - id: 3 + name: "Scouts" + description: "Scout troop for ages 10.5-14. Focused on outdoor adventure." + colour: teal + +# --- Parents --- +parent: + - id: 1 + firstName: "Sarah" + lastName: "Jones" + email: "sarah.jones@example.com" + phone: "07700900123" + balance: -2500 # Owes for Mia's Go-Karting trip + - id: 2 + firstName: "David" + lastName: "Smith" + email: "d.smith@example.com" + phone: "07700900456" + balance: 500 # In credit + - id: 3 + firstName: "Maria" + lastName: "Garcia" + email: "m.garcia@example.com" + phone: "07700900789" + balance: -10000 # Owes balance for Summer Camp + - id: 4 + firstName: "Tom" + lastName: "Brown" + email: "tom.brown@example.com" + phone: "07700900111" + balance: 0 # Fully paid up + - id: 5 + firstName: "Emily" + lastName: "White" + email: "emily.white@example.com" + phone: "07700900222" + balance: -2500 # Owes for Isla's Go-Karting + - id: 6 + firstName: "Peter" + lastName: "Williams" + email: "p.williams@example.com" + phone: "07700900333" + balance: 0 + - id: 7 + firstName: "Jessica" + lastName: "Taylor" + email: "jess.taylor@example.com" + phone: "07700900444" + balance: 0 + - id: 8 + firstName: "Michael" + lastName: "Davies" + email: "m.davies@example.com" + phone: "07700900555" + balance: 0 + - id: 9 + firstName: "Laura" + lastName: "Evans" + email: "laura.evans@example.com" + phone: "07700900666" + balance: 0 + - id: 10 + firstName: "Daniel" + lastName: "Roberts" + email: "dan.roberts@example.com" + phone: "07700900777" + balance: 0 + - id: 11 + firstName: "Sophie" + lastName: "Johnson" + email: "s.johnson@example.com" + phone: "07700900888" + balance: 0 + - id: 12 + firstName: "James" + lastName: "Wilson" + email: "james.wilson@example.com" + phone: "07700900999" + balance: 0 + - id: 13 + firstName: "Charlotte" + lastName: "Green" + email: "charlotte.g@example.com" + phone: "07700900101" + balance: 0 + - id: 14 + firstName: "Matthew" + lastName: "Hall" + email: "m.hall@example.com" + phone: "07700900121" + balance: 0 + - id: 15 + firstName: "Hannah" + lastName: "Walker" + email: "h.walker@example.com" + phone: "07700900131" + balance: 0 + - id: 16 + firstName: "Chris" + lastName: "Harris" + email: "c.harris@example.com" + phone: "07700900141" + balance: 0 + +# --- Children --- +child: + - id: 1 + firstName: "Leo" + lastName: "Jones" + parentId: 1 + scoutGroupId: 1 # Beavers + - id: 2 + firstName: "Mia" + lastName: "Jones" + parentId: 1 + scoutGroupId: 2 # Cubs + - id: 3 + firstName: "Noah" + lastName: "Smith" + parentId: 2 + scoutGroupId: 2 # Cubs + - id: 4 + firstName: "Olivia" + lastName: "Garcia" + parentId: 3 + scoutGroupId: 3 # Scouts + - id: 5 + firstName: "Charlie" + lastName: "Brown" + parentId: 4 + scoutGroupId: 1 # Beavers + - id: 6 + firstName: "Isla" + lastName: "White" + parentId: 5 + scoutGroupId: 2 # Cubs + - id: 7 + firstName: "Jack" + lastName: "White" + parentId: 5 + scoutGroupId: 3 # Scouts + - id: 8 + firstName: "Harry" + lastName: "Williams" + parentId: 6 + scoutGroupId: 1 + - id: 9 + firstName: "Amelia" + lastName: "Williams" + parentId: 6 + scoutGroupId: 1 + - id: 10 + firstName: "George" + lastName: "Taylor" + parentId: 7 + scoutGroupId: 1 + - id: 11 + firstName: "Ava" + lastName: "Davies" + parentId: 8 + scoutGroupId: 1 + - id: 12 + firstName: "Oliver" + lastName: "Evans" + parentId: 9 + scoutGroupId: 1 + - id: 13 + firstName: "Sophia" + lastName: "Roberts" + parentId: 10 + scoutGroupId: 1 + - id: 14 + firstName: "Freddie" + lastName: "Johnson" + parentId: 11 + scoutGroupId: 1 + - id: 15 + firstName: "Grace" + lastName: "Wilson" + parentId: 12 + scoutGroupId: 1 + - id: 16 + firstName: "Arthur" + lastName: "Green" + parentId: 13 + scoutGroupId: 1 + - id: 17 + firstName: "Ruby" + lastName: "Hall" + parentId: 14 + scoutGroupId: 1 + - id: 18 + firstName: "Henry" + lastName: "Walker" + parentId: 15 + scoutGroupId: 1 + - id: 19 + firstName: "Poppy" + lastName: "Harris" + parentId: 16 + scoutGroupId: 1 + - id: 20 + firstName: "Max" + lastName: "Harris" + parentId: 16 + scoutGroupId: 1 + - id: 21 + firstName: "Jacob" + lastName: "Williams" + parentId: 6 + scoutGroupId: 2 + - id: 22 + firstName: "Isabella" + lastName: "Taylor" + parentId: 7 + scoutGroupId: 2 + - id: 23 + firstName: "Thomas" + lastName: "Davies" + parentId: 8 + scoutGroupId: 2 + - id: 24 + firstName: "Rosie" + lastName: "Evans" + parentId: 9 + scoutGroupId: 2 + - id: 25 + firstName: "Oscar" + lastName: "Roberts" + parentId: 10 + scoutGroupId: 2 + - id: 26 + firstName: "Lily" + lastName: "Johnson" + parentId: 11 + scoutGroupId: 2 + - id: 27 + firstName: "William" + lastName: "Wilson" + parentId: 12 + scoutGroupId: 2 + - id: 28 + firstName: "Evie" + lastName: "Green" + parentId: 13 + scoutGroupId: 2 + - id: 29 + firstName: "Alfie" + lastName: "Hall" + parentId: 14 + scoutGroupId: 2 + - id: 30 + firstName: "Florence" + lastName: "Walker" + parentId: 15 + scoutGroupId: 2 + - id: 31 + firstName: "Theo" + lastName: "Harris" + parentId: 16 + scoutGroupId: 2 + - id: 32 + firstName: "Mohammed" + lastName: "Roberts" + parentId: 10 + scoutGroupId: 2 + - id: 33 + firstName: "Jessica" + lastName: "Green" + parentId: 13 + scoutGroupId: 2 + - id: 34 + firstName: "Joshua" + lastName: "Williams" + parentId: 6 + scoutGroupId: 3 + - id: 35 + firstName: "Emily" + lastName: "Taylor" + parentId: 7 + scoutGroupId: 3 + - id: 36 + firstName: "James" + lastName: "Davies" + parentId: 8 + scoutGroupId: 3 + - id: 37 + firstName: "Megan" + lastName: "Evans" + parentId: 9 + scoutGroupId: 3 + - id: 38 + firstName: "Daniel" + lastName: "Roberts" + parentId: 10 + scoutGroupId: 3 + - id: 39 + firstName: "Chloe" + lastName: "Johnson" + parentId: 11 + scoutGroupId: 3 + - id: 40 + firstName: "Samuel" + lastName: "Wilson" + parentId: 12 + scoutGroupId: 3 + - id: 41 + firstName: "Phoebe" + lastName: "Green" + parentId: 13 + scoutGroupId: 3 + - id: 42 + firstName: "Benjamin" + lastName: "Hall" + parentId: 14 + scoutGroupId: 3 + - id: 43 + firstName: "Alice" + lastName: "Walker" + parentId: 15 + scoutGroupId: 3 + - id: 44 + firstName: "Joseph" + lastName: "Harris" + parentId: 16 + scoutGroupId: 3 + - id: 45 + firstName: "Ethan" + lastName: "Evans" + parentId: 9 + scoutGroupId: 3 + - id: 46 + firstName: "Logan" + lastName: "Hall" + parentId: 14 + scoutGroupId: 3 + +# --- Bank Accounts (Linked to Parents) --- +bank_account: + - id: 1 + accountNumber: "12345678" + sortCode: "10-20-30" + name: "Mrs S Jones" + parentId: 1 + - id: 2 + accountNumber: "87654321" + sortCode: "20-30-40" + name: "Mr David Smith" + parentId: 2 + - id: 3 + accountNumber: "11223344" + sortCode: "30-40-50" + name: "M Garcia" + parentId: 3 + - id: 4 + accountNumber: "55667788" + sortCode: "40-50-60" + name: "T Brown" + parentId: 4 + - id: 5 + accountNumber: "99887766" + sortCode: "50-60-70" + name: "E White" + parentId: 5 + - id: 6 + accountNumber: "23456789" + sortCode: "60-70-80" + name: "P Williams" + parentId: 6 + - id: 7 + accountNumber: "34567890" + sortCode: "70-80-90" + name: "J Taylor" + parentId: 7 + - id: 8 + accountNumber: "45678901" + sortCode: "80-90-00" + name: "M Davies" + parentId: 8 + - id: 9 + accountNumber: "56789012" + sortCode: "90-00-10" + name: "L Evans" + parentId: 9 + - id: 10 + accountNumber: "67890123" + sortCode: "00-10-20" + name: "D Roberts" + parentId: 10 + - id: 11 + accountNumber: "78901234" + sortCode: "11-22-33" + name: "S Johnson" + parentId: 11 + - id: 12 + accountNumber: "89012345" + sortCode: "22-33-44" + name: "J Wilson" + parentId: 12 + - id: 13 + accountNumber: "90123456" + sortCode: "33-44-55" + name: "C Green" + parentId: 13 + - id: 14 + accountNumber: "01234567" + sortCode: "44-55-66" + name: "M Hall" + parentId: 14 + - id: 15 + accountNumber: "12345098" + sortCode: "55-66-77" + name: "H Walker" + parentId: 15 + - id: 16 + accountNumber: "23456109" + sortCode: "66-77-88" + name: "C Harris" + parentId: 16 + + +# --- Events (Past and Future) --- +event: + - id: 1 + name: "Spring Sleepover" + date: "2025-04-26T18:00:00Z" + cost: 2000 # £20.00 + scoutGroupId: 2 # Cubs + - id: 2 + name: "District Archery Competition" + date: "2025-05-18T10:00:00Z" + cost: 500 # £5.00 + scoutGroupId: 3 # Scouts + - id: 3 + name: "Summer Camp 2025" + date: "2025-08-05T09:00:00Z" + cost: 15000 # £150.00 + scoutGroupId: 3 # Scouts + - id: 4 + name: "Fun Day" + date: "2025-07-12T11:00:00Z" + cost: 750 # £7.50 + scoutGroupId: 1 # Beavers + - id: 5 + name: "Go-Karting Trip" + date: "2025-09-20T09:30:00Z" + cost: 2500 # £25.00 + scoutGroupId: 2 # Cubs + - id: 6 + name: "Climbing Session" + date: "2025-05-11T09:00:00Z" + cost: 1000 # £10.00 + scoutGroupId: 3 # Scouts + - id: 7 + name: "Teddy Bear Picnic" + date: "2025-06-14T12:00:00Z" + cost: 300 # £3.00 + scoutGroupId: 1 # Beavers + - id: 8 + name: "Christmas Panto Trip" + date: "2025-12-06T14:00:00Z" + cost: 1850 # £18.50 + scoutGroupId: null # All groups + +# --- Event Registrations --- +event_registration: + - id: 1 + eventId: 1 + childId: 2 # Mia Jones, Sleepover, Paid + paidDate: "2025-04-15T11:23:00Z" + - id: 2 + eventId: 1 + childId: 3 # Noah Smith, Sleepover, Paid + paidDate: "2025-04-12T09:01:00Z" + - id: 3 + eventId: 2 + childId: 4 # Olivia Garcia, Archery, Paid + paidDate: "2025-05-10T14:00:00Z" + - id: 4 + eventId: 3 + childId: 4 # Olivia Garcia, Camp, Unpaid + paidDate: null + - id: 5 + eventId: 4 + childId: 1 # Leo Jones, Fun Day, Paid + paidDate: "2025-06-10T18:45:00Z" + - id: 6 + eventId: 5 + childId: 2 # Mia Jones, Go-Karting, Unpaid + paidDate: null + - id: 7 + eventId: 6 + childId: 7 # Jack White, Climbing, Paid + paidDate: "2025-05-01T10:00:00Z" + - id: 8 + eventId: 7 + childId: 5 # Charlie Brown, Picnic, Paid + paidDate: "2025-06-10T19:00:00Z" + - id: 9 + eventId: 4 + childId: 5 # Charlie Brown, Fun Day, Paid + paidDate: "2025-06-10T19:00:00Z" + - id: 10 + eventId: 3 + childId: 7 # Jack White, Camp, Unpaid (deposit paid) + paidDate: null + - id: 11 + eventId: 5 + childId: 6 # Isla White, Go-Karting, Unpaid + paidDate: null + - id: 12 + eventId: 8 + childId: 1 # Leo Jones, Panto, Unpaid + paidDate: null + - id: 13 + eventId: 8 + childId: 6 # Isla White, Panto, Unpaid + paidDate: null + # --- NEW REGISTRATIONS START HERE --- + - id: 14 + eventId: 1 # Spring Sleepover (Cubs) + childId: 21 + paidDate: null + - id: 15 + eventId: 1 + childId: 22 + paidDate: null + - id: 16 + eventId: 1 + childId: 23 + paidDate: null + - id: 17 + eventId: 1 + childId: 24 + paidDate: null + - id: 18 + eventId: 1 + childId: 25 + paidDate: null + - id: 19 + eventId: 1 + childId: 26 + paidDate: null + - id: 20 + eventId: 1 + childId: 27 + paidDate: null + - id: 21 + eventId: 1 + childId: 28 + paidDate: null + - id: 22 + eventId: 1 + childId: 29 + paidDate: null + - id: 23 + eventId: 1 + childId: 30 + paidDate: null + - id: 24 + eventId: 1 + childId: 31 + paidDate: null + - id: 25 + eventId: 1 + childId: 32 + paidDate: null + - id: 26 + eventId: 1 + childId: 33 + paidDate: null + - id: 27 + eventId: 2 # Archery (Scouts) + childId: 7 + paidDate: null + - id: 28 + eventId: 2 + childId: 34 + paidDate: null + - id: 29 + eventId: 2 + childId: 35 + paidDate: null + - id: 30 + eventId: 2 + childId: 36 + paidDate: null + - id: 31 + eventId: 2 + childId: 37 + paidDate: null + - id: 32 + eventId: 2 + childId: 38 + paidDate: null + - id: 33 + eventId: 2 + childId: 39 + paidDate: null + - id: 34 + eventId: 2 + childId: 40 + paidDate: null + - id: 35 + eventId: 2 + childId: 41 + paidDate: null + - id: 36 + eventId: 2 + childId: 42 + paidDate: null + - id: 37 + eventId: 2 + childId: 43 + paidDate: null + - id: 38 + eventId: 2 + childId: 44 + paidDate: null + - id: 39 + eventId: 2 + childId: 45 + paidDate: null + - id: 40 + eventId: 2 + childId: 46 + paidDate: null + - id: 41 + eventId: 3 # Summer Camp (Scouts) + childId: 34 + paidDate: null + - id: 42 + eventId: 3 + childId: 35 + paidDate: null + - id: 43 + eventId: 3 + childId: 36 + paidDate: null + - id: 44 + eventId: 3 + childId: 37 + paidDate: null + - id: 45 + eventId: 3 + childId: 38 + paidDate: null + - id: 46 + eventId: 3 + childId: 39 + paidDate: null + - id: 47 + eventId: 3 + childId: 40 + paidDate: null + - id: 48 + eventId: 3 + childId: 41 + paidDate: null + - id: 49 + eventId: 3 + childId: 42 + paidDate: null + - id: 50 + eventId: 3 + childId: 43 + paidDate: null + - id: 51 + eventId: 3 + childId: 44 + paidDate: null + - id: 52 + eventId: 3 + childId: 45 + paidDate: null + - id: 53 + eventId: 3 + childId: 46 + paidDate: null + - id: 54 + eventId: 4 # Fun Day (Beavers) + childId: 8 + paidDate: null + - id: 55 + eventId: 4 + childId: 9 + paidDate: null + - id: 56 + eventId: 4 + childId: 10 + paidDate: null + - id: 57 + eventId: 4 + childId: 11 + paidDate: null + - id: 58 + eventId: 4 + childId: 12 + paidDate: null + - id: 59 + eventId: 4 + childId: 13 + paidDate: null + - id: 60 + eventId: 4 + childId: 14 + paidDate: null + - id: 61 + eventId: 4 + childId: 15 + paidDate: null + - id: 62 + eventId: 4 + childId: 16 + paidDate: null + - id: 63 + eventId: 4 + childId: 17 + paidDate: null + - id: 64 + eventId: 4 + childId: 18 + paidDate: null + - id: 65 + eventId: 4 + childId: 19 + paidDate: null + - id: 66 + eventId: 4 + childId: 20 + paidDate: null + - id: 67 + eventId: 5 # Go-Karting (Cubs) + childId: 21 + paidDate: null + - id: 68 + eventId: 5 + childId: 22 + paidDate: null + - id: 69 + eventId: 5 + childId: 23 + paidDate: null + - id: 70 + eventId: 5 + childId: 24 + paidDate: null + - id: 71 + eventId: 5 + childId: 25 + paidDate: null + - id: 72 + eventId: 5 + childId: 26 + paidDate: null + - id: 73 + eventId: 5 + childId: 27 + paidDate: null + - id: 74 + eventId: 5 + childId: 28 + paidDate: null + - id: 75 + eventId: 5 + childId: 29 + paidDate: null + - id: 76 + eventId: 5 + childId: 30 + paidDate: null + - id: 77 + eventId: 5 + childId: 31 + paidDate: null + - id: 78 + eventId: 5 + childId: 32 + paidDate: null + - id: 79 + eventId: 5 + childId: 33 + paidDate: null + - id: 80 + eventId: 6 # Climbing (Scouts) + childId: 4 + paidDate: null + - id: 81 + eventId: 6 + childId: 34 + paidDate: null + - id: 82 + eventId: 6 + childId: 35 + paidDate: null + - id: 83 + eventId: 6 + childId: 36 + paidDate: null + - id: 84 + eventId: 6 + childId: 37 + paidDate: null + - id: 85 + eventId: 6 + childId: 38 + paidDate: null + - id: 86 + eventId: 6 + childId: 39 + paidDate: null + - id: 87 + eventId: 6 + childId: 40 + paidDate: null + - id: 88 + eventId: 6 + childId: 41 + paidDate: null + - id: 89 + eventId: 6 + childId: 42 + paidDate: null + - id: 90 + eventId: 6 + childId: 43 + paidDate: null + - id: 91 + eventId: 6 + childId: 44 + paidDate: null + - id: 92 + eventId: 6 + childId: 45 + paidDate: null + - id: 93 + eventId: 6 + childId: 46 + paidDate: null + - id: 94 + eventId: 7 # Teddy Bear Picnic (Beavers) + childId: 1 + paidDate: null + - id: 95 + eventId: 7 + childId: 8 + paidDate: null + - id: 96 + eventId: 7 + childId: 9 + paidDate: null + - id: 97 + eventId: 7 + childId: 10 + paidDate: null + - id: 98 + eventId: 7 + childId: 11 + paidDate: null + - id: 99 + eventId: 7 + childId: 12 + paidDate: null + - id: 100 + eventId: 7 + childId: 13 + paidDate: null + - id: 101 + eventId: 7 + childId: 14 + paidDate: null + - id: 102 + eventId: 7 + childId: 15 + paidDate: null + - id: 103 + eventId: 7 + childId: 16 + paidDate: null + - id: 104 + eventId: 7 + childId: 17 + paidDate: null + - id: 105 + eventId: 7 + childId: 18 + paidDate: null + - id: 106 + eventId: 7 + childId: 19 + paidDate: null + - id: 107 + eventId: 7 + childId: 20 + paidDate: null + - id: 108 + eventId: 8 # Panto (All) + childId: 3 # Noah + paidDate: null + - id: 109 + eventId: 8 + childId: 4 # Olivia + paidDate: null + - id: 110 + eventId: 8 + childId: 7 # Jack + paidDate: null + - id: 111 + eventId: 8 + childId: 8 # Harry + paidDate: null + - id: 112 + eventId: 8 + childId: 10 # George + paidDate: null + - id: 113 + eventId: 8 + childId: 14 # Freddie + paidDate: null + - id: 114 + eventId: 8 + childId: 22 # Isabella + paidDate: null + - id: 115 + eventId: 8 + childId: 25 # Oscar + paidDate: null + - id: 116 + eventId: 8 + childId: 28 # Evie + paidDate: null + - id: 117 + eventId: 8 + childId: 30 # Florence + paidDate: null + - id: 118 + eventId: 8 + childId: 35 # Emily + paidDate: null + - id: 119 + eventId: 8 + childId: 38 # Daniel + paidDate: null + - id: 120 + eventId: 8 + childId: 41 # Phoebe + paidDate: null + + +# --- Payments --- +payment: + - id: 1 + amount: 2000 + date: "2025-04-12T09:00:00Z" + reference: "Noah S Sleepover" + method: bank_transfer + payee: "Cubs" + bankAccountId: 2 + parentId: 2 + - id: 2 + amount: 500 + date: "2025-05-10T14:00:00Z" + reference: "Archery" + method: cash + payee: "Scouts" + bankAccountId: null + parentId: 3 + - id: 3 + amount: 2000 + date: "2025-04-15T11:22:00Z" + reference: "M Jones" + method: bank_transfer + payee: "Cubs" + bankAccountId: 1 + parentId: 1 + - id: 4 + amount: 750 + date: "2025-06-10T18:45:00Z" + reference: "LEO JONES FUN" + method: bank_transfer + payee: "Beavers" + bankAccountId: 1 + parentId: 1 + - id: 5 + amount: 5000 + date: "2025-06-01T20:15:00Z" + reference: "OG Camp Deposit" + method: bank_transfer + payee: "Scouts" + bankAccountId: 3 + parentId: 3 + - id: 6 + amount: 500 + date: "2025-06-15T12:00:00Z" + reference: "Subs" + method: bank_transfer + payee: "Cubs" + bankAccountId: 2 + parentId: 2 + - id: 7 + amount: 1050 # Fun Day (750) + Picnic (300) + date: "2025-06-10T19:00:00Z" + reference: "Charlie B trips" + method: bank_transfer + payee: "Beavers" + bankAccountId: 4 + parentId: 4 + - id: 8 + amount: 1000 # Climbing + date: "2025-05-01T10:00:00Z" + reference: "J White Climb" + method: bank_transfer + payee: "Scouts" + bankAccountId: 5 + parentId: 5 + - id: 9 + amount: 5000 # Camp Deposit + date: "2025-06-05T08:30:00Z" + reference: "Jack White Camp" + method: bank_transfer + payee: "Scouts" + bankAccountId: 5 + parentId: 5 + + # --- UNCLASSIFIED PAYMENTS --- + - id: 10 + amount: 10000 + date: "2025-06-02T09:10:11Z" + reference: "CAMP PAYMENT" + method: bank_transfer + payee: "Scouts" + bankAccountId: null + parentId: null + - id: 11 + amount: 2500 + date: "2025-06-17T15:30:00Z" + reference: "J. DOE GO KART" + method: bank_transfer + payee: "Cubs" + bankAccountId: null + parentId: null + - id: 12 + amount: 1850 + date: "2025-06-18T09:05:00Z" + reference: "PANTO" + method: bank_transfer + payee: "Beavers" + bankAccountId: null + parentId: null + - id: 13 + amount: 500 + date: "2025-06-16T11:00:00Z" + reference: "Payment" + method: cash + payee: "Cubs" + bankAccountId: null + parentId: null \ No newline at end of file diff --git a/scouts_finances_server/lib/src/generated/endpoints.dart b/scouts_finances_server/lib/src/generated/endpoints.dart index 9429c0b..5d0bb20 100644 --- a/scouts_finances_server/lib/src/generated/endpoints.dart +++ b/scouts_finances_server/lib/src/generated/endpoints.dart @@ -78,7 +78,16 @@ class Endpoints extends _i1.EndpointDispatch { Map params, ) async => (endpoints['admin'] as _i2.AdminEndpoint).resetDb(session), - ) + ), + 'dummyData': _i1.MethodConnector( + name: 'dummyData', + params: {}, + call: ( + _i1.Session session, + Map params, + ) async => + (endpoints['admin'] as _i2.AdminEndpoint).dummyData(session), + ), }, ); connectors['event'] = _i1.EndpointConnector( diff --git a/scouts_finances_server/lib/src/generated/protocol.yaml b/scouts_finances_server/lib/src/generated/protocol.yaml index bdab1f3..4dbb3cb 100644 --- a/scouts_finances_server/lib/src/generated/protocol.yaml +++ b/scouts_finances_server/lib/src/generated/protocol.yaml @@ -1,5 +1,6 @@ admin: - resetDb: + - dummyData: event: - getEvents: - getPaidCounts: diff --git a/scouts_finances_server/pubspec.yaml b/scouts_finances_server/pubspec.yaml index ba996f9..a7603d5 100644 --- a/scouts_finances_server/pubspec.yaml +++ b/scouts_finances_server/pubspec.yaml @@ -10,6 +10,7 @@ dependencies: serverpod: 2.7.0 dotenv: ^4.2.0 http: ^1.4.0 + yaml: ^3.1.2 dev_dependencies: lints: '>=3.0.0 <6.0.0' diff --git a/scouts_finances_server/test/integration/test_tools/serverpod_test_tools.dart b/scouts_finances_server/test/integration/test_tools/serverpod_test_tools.dart index cc2157b..cd908a8 100644 --- a/scouts_finances_server/test/integration/test_tools/serverpod_test_tools.dart +++ b/scouts_finances_server/test/integration/test_tools/serverpod_test_tools.dart @@ -190,6 +190,32 @@ class _AdminEndpoint { } }); } + + _i3.Future dummyData(_i1.TestSessionBuilder sessionBuilder) async { + return _i1.callAwaitableFunctionAndHandleExceptions(() async { + var _localUniqueSession = + (sessionBuilder as _i1.InternalTestSessionBuilder).internalBuild( + endpoint: 'admin', + method: 'dummyData', + ); + try { + var _localCallContext = await _endpointDispatch.getMethodCallContext( + createSessionCallback: (_) => _localUniqueSession, + endpointPath: 'admin', + methodName: 'dummyData', + parameters: _i1.testObjectToJson({}), + serializationManager: _serializationManager, + ); + var _localReturnValue = await (_localCallContext.method.call( + _localUniqueSession, + _localCallContext.arguments, + ) as _i3.Future); + return _localReturnValue; + } finally { + await _localUniqueSession.close(); + } + }); + } } class _EventEndpoint {