Files
Scalar360_Attendence/lib/screens/payslips_screen.dart
athul rdandClaude Opus 5.5 2e0d825c24 Payslips: format amounts in the org currency
/api/v1/me/payslips items and /api/v1/me/profile now carry the org base
currency. Payslip and Profile read it; the payslips screen formats with
the payslip currency, else the last profile's, else INR. INR output is
unchanged (rupee sign, lakh grouping, no decimals); other currencies show
the ISO code with their own minor units (AED 1,234.50, BHD 1,234.500).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 22:34:56 +05:30

104 lines
3.9 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../api_service.dart';
import '../money.dart';
import '../theme.dart';
class PayslipsScreen extends StatefulWidget {
final ApiService api;
final VoidCallback onAuthError;
const PayslipsScreen({super.key, required this.api, required this.onAuthError});
@override
State<PayslipsScreen> createState() => _PayslipsScreenState();
}
class _PayslipsScreenState extends State<PayslipsScreen> {
List<Payslip> _rows = [];
bool _loading = true;
@override
void initState() {
super.initState();
_load();
}
Future<void> _load() async {
try {
final r = await widget.api.payslips();
if (mounted) setState(() { _rows = r; _loading = false; });
} on ApiException catch (e) {
if (e.status == 401) return widget.onAuthError();
if (mounted) setState(() => _loading = false);
}
}
String _month(String m) {
try {
return DateFormat('MMMM yyyy').format(DateTime.parse('$m-01'));
} catch (_) {
return m;
}
}
/// Payslip currency, else the profile's, else INR (older servers).
String _money(Payslip p, num v) => formatMoney(v, p.currency ?? widget.api.currency);
@override
Widget build(BuildContext context) {
if (_loading) return const Center(child: CircularProgressIndicator(color: AppColors.emerald));
if (_rows.isEmpty) {
return const Center(child: Text('No payslips yet.', style: TextStyle(color: AppColors.muted)));
}
return RefreshIndicator(
onRefresh: _load,
color: AppColors.emerald,
child: ListView.separated(
padding: const EdgeInsets.all(16),
itemCount: _rows.length,
separatorBuilder: (_, __) => const SizedBox(height: 8),
itemBuilder: (_, i) {
final p = _rows[i];
return Card(
child: ListTile(
title: Text(_month(p.periodMonth), style: const TextStyle(fontWeight: FontWeight.w600)),
subtitle: const Text('Net pay'),
trailing: Column(mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.end, children: [
Text(_money(p, p.netPay), style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: AppColors.emerald)),
Text(p.status, style: const TextStyle(fontSize: 11, color: AppColors.muted)),
]),
onTap: () => showModalBottomSheet(
context: context,
builder: (_) => Padding(
padding: const EdgeInsets.all(20),
child: Column(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [
Text(_month(p.periodMonth), style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: AppColors.navy)),
const SizedBox(height: 16),
_row('Gross earnings', _money(p, p.gross)),
_row('Total deductions', '− ${_money(p, p.totalDeductions)}'),
const Divider(height: 24),
_row('Net pay', _money(p, p.netPay), bold: true),
const SizedBox(height: 16),
const Text('Full PDF payslip is available on the Scalar360 web portal.',
style: TextStyle(fontSize: 12, color: AppColors.muted)),
]),
),
),
),
);
},
),
);
}
Widget _row(String l, String v, {bool bold = false}) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Text(l, style: TextStyle(color: bold ? AppColors.navy : AppColors.muted, fontWeight: bold ? FontWeight.bold : FontWeight.normal)),
Text(v, style: TextStyle(fontWeight: FontWeight.w600, color: bold ? AppColors.emerald : AppColors.navy, fontSize: bold ? 18 : 14)),
]),
);
}
}