138 lines
4 KiB
Dart
138 lines
4 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:intl/intl.dart';
|
|
import '../services/invoice_repository.dart';
|
|
|
|
class SalesReportScreen extends StatefulWidget {
|
|
const SalesReportScreen({Key? key}) : super(key: key);
|
|
|
|
@override
|
|
State<SalesReportScreen> createState() => _SalesReportScreenState();
|
|
}
|
|
|
|
class _SalesReportScreenState extends State<SalesReportScreen> {
|
|
final _invoiceRepo = InvoiceRepository();
|
|
int _targetYear = DateTime.now().year;
|
|
Map<String, int> _monthlySales = {};
|
|
int _yearlyTotal = 0;
|
|
bool _isLoading = true;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_loadData();
|
|
}
|
|
|
|
Future<void> _loadData() async {
|
|
setState(() => _isLoading = true);
|
|
final monthly = await _invoiceRepo.getMonthlySales(_targetYear);
|
|
final yearly = await _invoiceRepo.getYearlyTotal(_targetYear);
|
|
setState(() {
|
|
_monthlySales = monthly;
|
|
_yearlyTotal = yearly;
|
|
_isLoading = false;
|
|
});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final fmt = NumberFormat("#,###");
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: const Text("売上・資金管理レポート"),
|
|
backgroundColor: Colors.indigo,
|
|
foregroundColor: Colors.white,
|
|
),
|
|
body: _isLoading
|
|
? const Center(child: CircularProgressIndicator())
|
|
: Column(
|
|
children: [
|
|
_buildYearSelector(),
|
|
_buildYearlySummary(fmt),
|
|
const Divider(height: 1),
|
|
Expanded(child: _buildMonthlyList(fmt)),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildYearSelector() {
|
|
return Container(
|
|
color: Colors.indigo.shade50,
|
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
IconButton(
|
|
icon: const Icon(Icons.chevron_left),
|
|
onPressed: () {
|
|
setState(() => _targetYear--);
|
|
_loadData();
|
|
},
|
|
),
|
|
Text(
|
|
"$_targetYear年度",
|
|
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
|
),
|
|
IconButton(
|
|
icon: const Icon(Icons.chevron_right),
|
|
onPressed: () {
|
|
setState(() => _targetYear++);
|
|
_loadData();
|
|
},
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildYearlySummary(NumberFormat fmt) {
|
|
return Container(
|
|
width: double.infinity,
|
|
padding: const EdgeInsets.all(24),
|
|
decoration: BoxDecoration(
|
|
color: Colors.indigo.shade900,
|
|
),
|
|
child: Column(
|
|
children: [
|
|
const Text("年間売上合計 (請求確定分)", style: TextStyle(color: Colors.white70)),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
"¥${fmt.format(_yearlyTotal)}",
|
|
style: const TextStyle(color: Colors.white, fontSize: 32, fontWeight: FontWeight.bold),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildMonthlyList(NumberFormat fmt) {
|
|
return ListView.builder(
|
|
itemCount: 12,
|
|
itemBuilder: (context, index) {
|
|
final month = (index + 1).toString().padLeft(2, '0');
|
|
final amount = _monthlySales[month] ?? 0;
|
|
final percentage = _yearlyTotal > 0 ? (amount / _yearlyTotal * 100).toStringAsFixed(1) : "0.0";
|
|
|
|
return ListTile(
|
|
leading: CircleAvatar(
|
|
backgroundColor: Colors.blueGrey.shade100,
|
|
child: Text("${index + 1}", style: const TextStyle(color: Colors.indigo)),
|
|
),
|
|
title: Text("${index + 1}月の売上"),
|
|
subtitle: amount > 0 ? Text("シェア: $percentage%") : null,
|
|
trailing: Text(
|
|
"¥${fmt.format(amount)}",
|
|
style: TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 16,
|
|
color: amount > 0 ? Colors.black87 : Colors.grey,
|
|
),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
// FontWeight.bold in Text widget is TextStyle.fontWeight not pw.FontWeight
|
|
// Corrected to FontWeight.bold below in replace or write.
|