check
This commit is contained in:
@@ -199,20 +199,49 @@ class _EndShiftScreenState extends ConsumerState<EndShiftScreen> {
|
||||
);
|
||||
},
|
||||
),
|
||||
bottomNavigationBar: _bottomBar(pending, variance),
|
||||
bottomNavigationBar: _bottomBar(pending, expected, variance),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _bottomBar(int pending, double variance) {
|
||||
/// Whether the drawer has been counted and agrees with what was rung.
|
||||
///
|
||||
/// Signing out is gated on this. A shift that ends with the cash unaccounted
|
||||
/// for is a shift nobody can settle afterwards — the person who worked it has
|
||||
/// gone home, and the difference becomes an argument rather than a number. To
|
||||
/// the rupee, because that is the smallest note anyone hands over.
|
||||
bool _balances(double expected) => (_countedTotal - expected).abs() < 0.5;
|
||||
|
||||
Widget _bottomBar(int pending, double expected, double variance) {
|
||||
final counted = _noteCount > 0;
|
||||
final short = variance < -0.5;
|
||||
final over = variance > 0.5;
|
||||
final balanced = _balances(expected);
|
||||
|
||||
// The gate. A shift closes on a drawer that reconciles and on nothing
|
||||
// else — an uncounted or mismatched till is settled at the counter, with
|
||||
// the person who worked it still there, not discovered by the back office
|
||||
// the next morning.
|
||||
final balanced = counted && !short && !over;
|
||||
final (icon, tone, message) = switch (0) {
|
||||
_ when !counted => (
|
||||
Icons.info_outline_rounded,
|
||||
AppColors.textTertiary,
|
||||
'Count the drawer to finish. Signing out needs the count to match '
|
||||
'${Formatters.money(expected)}.',
|
||||
),
|
||||
_ when short => (
|
||||
Icons.error_outline_rounded,
|
||||
AppColors.danger,
|
||||
'The drawer is ${Formatters.money(variance.abs())} short. Recount, '
|
||||
'or find the difference before signing out.',
|
||||
),
|
||||
_ when over => (
|
||||
Icons.error_outline_rounded,
|
||||
AppColors.warning,
|
||||
'The drawer is ${Formatters.money(variance)} over. Recount, or find '
|
||||
'the difference before signing out.',
|
||||
),
|
||||
_ => (
|
||||
Icons.check_circle_outline_rounded,
|
||||
AppColors.success,
|
||||
'The drawer matches what was rung. You can sign out.',
|
||||
),
|
||||
};
|
||||
|
||||
return SafeArea(
|
||||
child: Container(
|
||||
@@ -231,40 +260,15 @@ class _EndShiftScreenState extends ConsumerState<EndShiftScreen> {
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
counted
|
||||
? (short || over
|
||||
? Icons.error_outline_rounded
|
||||
: Icons.check_circle_outline_rounded)
|
||||
: Icons.info_outline_rounded,
|
||||
size: 16,
|
||||
color: !counted
|
||||
? AppColors.textTertiary
|
||||
: (short
|
||||
? AppColors.danger
|
||||
: (over ? AppColors.warning : AppColors.success)),
|
||||
),
|
||||
Icon(icon, size: 16, color: tone),
|
||||
const SizedBox(width: AppSpacing.sm),
|
||||
Expanded(
|
||||
child: Text(
|
||||
!counted
|
||||
? 'Count the drawer. The shift cannot be ended until '
|
||||
'it matches the expected amount.'
|
||||
: short
|
||||
? 'The drawer is '
|
||||
'${Formatters.money(variance.abs())} short. '
|
||||
'Recount, or settle the difference — the '
|
||||
'shift cannot be ended while it is off.'
|
||||
: over
|
||||
? 'The drawer is '
|
||||
'${Formatters.money(variance)} over. '
|
||||
'Recount — the shift cannot be ended '
|
||||
'while it is off.'
|
||||
: 'The drawer matches what was rung. Ready to '
|
||||
'end the shift.',
|
||||
message,
|
||||
style: const TextStyle(
|
||||
fontSize: 12.5,
|
||||
color: AppColors.textSecondary,
|
||||
height: 1.4,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -272,25 +276,23 @@ class _EndShiftScreenState extends ConsumerState<EndShiftScreen> {
|
||||
),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
PrimaryButton(
|
||||
label: !balanced
|
||||
? 'Drawer must match to end shift'
|
||||
: pending > 0
|
||||
? 'Upload $pending bill(s) & end shift'
|
||||
: 'End shift',
|
||||
icon: balanced ? Icons.logout_rounded : Icons.lock_outline_rounded,
|
||||
label: pending > 0
|
||||
? 'Upload $pending bill(s) & end shift'
|
||||
: 'End shift',
|
||||
icon: Icons.logout_rounded,
|
||||
large: true,
|
||||
busy: _pushing,
|
||||
// Disabled until the count agrees. There is deliberately no way
|
||||
// past this on the screen: an override that a tired cashier can
|
||||
// press at the end of a long day is not a control.
|
||||
onPressed: (_pushing || !balanced)
|
||||
? null
|
||||
: () => _finish(sync: pending > 0),
|
||||
),
|
||||
if (pending > 0) ...[
|
||||
if (pending > 0 && balanced) ...[
|
||||
const SizedBox(height: AppSpacing.xs),
|
||||
TextButton(
|
||||
// Skipping the upload is still allowed; skipping the count is
|
||||
// not, so this is gated on the same condition.
|
||||
onPressed:
|
||||
(_pushing || !balanced) ? null : () => _finish(sync: false),
|
||||
onPressed: _pushing ? null : () => _finish(sync: false),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: AppColors.textSecondary,
|
||||
),
|
||||
@@ -659,14 +661,10 @@ class _ReviewPanel extends StatelessWidget {
|
||||
'when you end the shift.',
|
||||
),
|
||||
_checkRow(
|
||||
!short && !over && counted > 0,
|
||||
counted == 0
|
||||
? 'Drawer not counted yet — required before the shift can '
|
||||
'be ended.'
|
||||
: (short || over)
|
||||
? 'Drawer does not match the expected amount. The '
|
||||
'shift stays open until it does.'
|
||||
: 'Drawer matches the expected amount.',
|
||||
counted > 0,
|
||||
counted > 0
|
||||
? 'Drawer counted.'
|
||||
: 'Drawer not counted yet.',
|
||||
),
|
||||
_checkRow(
|
||||
false,
|
||||
|
||||
Reference in New Issue
Block a user