third commit
This commit is contained in:
@@ -74,11 +74,31 @@ class PaymentController extends StateNotifier<PaymentState> {
|
||||
return diff > 0 ? diff.asMoney : 0;
|
||||
}
|
||||
|
||||
/// Whether Complete Sale should be enabled.
|
||||
///
|
||||
/// Cash is the strict case: the drawer cannot be reconciled and no change can
|
||||
/// be calculated unless the cashier states what was handed over. Card, UPI
|
||||
/// and wallet settle on the external terminal, so they need no amount here.
|
||||
bool get canConfirm {
|
||||
if (_billTotal <= 0) return false;
|
||||
|
||||
// Staged splits already cover the bill.
|
||||
if (balanceDue <= 0.01) return true;
|
||||
|
||||
if (state.activeMethod.needsChange) {
|
||||
return state.cashTendered >= balanceDue && balanceDue > 0;
|
||||
return state.cashTendered >= balanceDue;
|
||||
}
|
||||
return balanceDue > 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Why the button is disabled, for display next to it.
|
||||
String? get blockedReason {
|
||||
if (_billTotal <= 0) return 'Add at least one item before charging.';
|
||||
if (canConfirm) return null;
|
||||
if (state.activeMethod.needsChange) {
|
||||
return 'Enter the cash received, or tap Exact.';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void selectMethod(PaymentMethod method) {
|
||||
@@ -156,10 +176,10 @@ class PaymentController extends StateNotifier<PaymentState> {
|
||||
|
||||
state = state.copyWith(isProcessing: false, result: result);
|
||||
|
||||
// Fire and forget — printing must never block the next sale.
|
||||
final receipts = _ref.read(receiptServiceProvider);
|
||||
unawaited(receipts.printDirect(result.transaction));
|
||||
unawaited(receipts.openCashDrawer());
|
||||
// No auto-print: with no roll printer attached this silently failed and
|
||||
// looked like a bug. The receipt screen shows the bill on the terminal
|
||||
// and offers Print, WhatsApp and Share explicitly.
|
||||
unawaited(_ref.read(receiptServiceProvider).openCashDrawer());
|
||||
unawaited(_ref.read(soundServiceProvider).saleComplete());
|
||||
|
||||
// Stock changed, so the grid must refresh; the new order changes the
|
||||
|
||||
@@ -125,7 +125,7 @@ class _PaymentScreenState extends ConsumerState<PaymentScreen> {
|
||||
);
|
||||
},
|
||||
),
|
||||
bottomNavigationBar: _bottomBar(cart.grandTotal, state, cart.isEmpty),
|
||||
bottomNavigationBar: _bottomBar(controller, state, cart.grandTotal),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -557,7 +557,15 @@ class _PaymentScreenState extends ConsumerState<PaymentScreen> {
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------ Bottom bar
|
||||
Widget _bottomBar(double total, PaymentState state, bool cartEmpty) {
|
||||
Widget _bottomBar(
|
||||
PaymentController controller,
|
||||
PaymentState state,
|
||||
double total,
|
||||
) {
|
||||
// A cash sale cannot complete until the cashier says what was handed over.
|
||||
final canComplete = controller.canConfirm;
|
||||
final reason = controller.blockedReason;
|
||||
|
||||
return SafeArea(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
@@ -599,13 +607,45 @@ class _PaymentScreenState extends ConsumerState<PaymentScreen> {
|
||||
],
|
||||
),
|
||||
).animate().shake(duration: 300.ms, hz: 3),
|
||||
if (reason != null && state.error == null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: AppSpacing.sm),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.info_outline_rounded,
|
||||
size: 15, color: AppColors.textTertiary),
|
||||
const SizedBox(width: AppSpacing.sm),
|
||||
Expanded(
|
||||
child: Text(
|
||||
reason,
|
||||
style: const TextStyle(
|
||||
fontSize: 12.5,
|
||||
color: AppColors.textTertiary,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (state.activeMethod.needsChange)
|
||||
TextButton(
|
||||
onPressed: () => _setCash(controller.balanceDue),
|
||||
style: TextButton.styleFrom(
|
||||
minimumSize: const Size(0, 30),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: AppSpacing.md,
|
||||
),
|
||||
),
|
||||
child: const Text('Exact',
|
||||
style: TextStyle(fontSize: 12.5)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
PrimaryButton(
|
||||
label: 'Complete Sale',
|
||||
icon: Icons.check_circle_outline_rounded,
|
||||
large: true,
|
||||
tone: ButtonTone.success,
|
||||
busy: state.isProcessing,
|
||||
onPressed: cartEmpty ? null : _confirm,
|
||||
onPressed: canComplete ? _confirm : null,
|
||||
trailing: Text(
|
||||
Formatters.money(total),
|
||||
style: AppTypography.money(19, color: Colors.white),
|
||||
|
||||
Reference in New Issue
Block a user