added product import and billing integration

This commit is contained in:
2026-08-05 18:15:21 +05:30
parent 33b4337933
commit 6c0266c9c7
32 changed files with 889 additions and 457 deletions

View File

@@ -350,20 +350,41 @@ class _PaymentScreenState extends ConsumerState<PaymentScreen> {
return GlassCard(
padding: const EdgeInsets.all(AppSpacing.lg),
radius: AppRadius.xl,
child: state.activeMethod.needsChange
? _cashTender(controller)
: _referenceTender(controller, state),
child: _amountTender(controller, state),
);
}
Widget _cashTender(PaymentController controller) {
/// Amount entry for whichever method is active. Every method works the
/// same way now — a keypad, an Exact shortcut, and an explicit amount —
/// rather than cash alone asking what was received while card/UPI/wallet
/// silently assumed the full balance. Cash additionally gets denomination
/// chips and a change-due row, since only cash can be over-tendered; a
/// method that captures a reference (card, UPI, gift card) additionally
/// gets that field below the keypad.
Widget _amountTender(PaymentController controller, PaymentState state) {
final cash = state.activeMethod.needsChange;
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'Cash received',
style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600),
Row(
children: [
Text(state.activeMethod.emoji, style: const TextStyle(fontSize: 20)),
const SizedBox(width: AppSpacing.sm),
Expanded(
child: Text(
cash
? 'Cash received'
: '${state.activeMethod.label} amount received',
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
),
),
),
],
),
const SizedBox(height: AppSpacing.md),
Container(
@@ -405,16 +426,20 @@ class _PaymentScreenState extends ConsumerState<PaymentScreen> {
label: const Text('Exact'),
onPressed: () => _setCash(controller.balanceDue),
),
for (final note in const [50, 100, 200, 500, 2000])
ActionChip(
label: Text('$note'),
onPressed: () =>
_setCash((double.tryParse(_cashBuffer) ?? 0) + note),
),
// Denomination shortcuts only make sense for physical notes.
if (cash)
for (final note in const [50, 100, 200, 500, 2000])
ActionChip(
label: Text('$note'),
onPressed: () =>
_setCash((double.tryParse(_cashBuffer) ?? 0) + note),
),
],
),
const SizedBox(height: AppSpacing.md),
_changeRow(controller.changeDue),
if (cash) ...[
const SizedBox(height: AppSpacing.md),
_changeRow(controller.changeDue),
],
const SizedBox(height: AppSpacing.md),
Center(
child: NumericKeypad(
@@ -424,6 +449,21 @@ class _PaymentScreenState extends ConsumerState<PaymentScreen> {
onBackspace: _backspaceCash,
),
),
if (state.activeMethod.needsReference) ...[
const SizedBox(height: AppSpacing.md),
TextField(
onChanged: controller.setReference,
decoration: InputDecoration(
labelText: switch (state.activeMethod) {
PaymentMethod.card => 'Approval code',
PaymentMethod.upi => 'UPI transaction ID',
PaymentMethod.giftCard => 'Gift card number',
_ => 'Reference',
},
prefixIcon: const Icon(Icons.tag_rounded),
),
),
],
const SizedBox(height: AppSpacing.md),
_splitButton(controller, amount: double.tryParse(_cashBuffer) ?? 0),
],
@@ -473,71 +513,6 @@ class _PaymentScreenState extends ConsumerState<PaymentScreen> {
);
}
Widget _referenceTender(PaymentController controller, PaymentState state) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
Text(state.activeMethod.emoji,
style: const TextStyle(fontSize: 20),),
const SizedBox(width: AppSpacing.sm),
Expanded(
child: Text(
'${state.activeMethod.label} payment',
overflow: TextOverflow.ellipsis,
style:
const TextStyle(fontSize: 15, fontWeight: FontWeight.w600),
),
),
],
),
const SizedBox(height: AppSpacing.xxl),
Center(
child: Container(
width: 104,
height: 104,
decoration: const BoxDecoration(
color: AppColors.primarySurface,
borderRadius: AppRadius.brXl,
),
alignment: Alignment.center,
child: Text(state.activeMethod.emoji,
style: const TextStyle(fontSize: 46),),
),
),
const SizedBox(height: AppSpacing.lg),
Text(
'Charge ${Formatters.money(controller.balanceDue)} on the '
'${state.activeMethod.label.toLowerCase()} terminal',
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 13.5,
color: AppColors.textSecondary,
height: 1.5,
),
),
const SizedBox(height: AppSpacing.xxl),
if (state.activeMethod.needsReference)
TextField(
onChanged: controller.setReference,
decoration: InputDecoration(
labelText: switch (state.activeMethod) {
PaymentMethod.card => 'Approval code',
PaymentMethod.upi => 'UPI transaction ID',
PaymentMethod.giftCard => 'Gift card number',
_ => 'Reference',
},
prefixIcon: const Icon(Icons.tag_rounded),
),
),
const SizedBox(height: AppSpacing.lg),
_splitButton(controller),
],
);
}
Widget _splitButton(PaymentController controller, {double? amount}) {
return OutlinedButton.icon(
onPressed: controller.balanceDue > 0
@@ -622,18 +597,17 @@ class _PaymentScreenState extends ConsumerState<PaymentScreen> {
),
),
),
if (state.activeMethod.needsChange)
TextButton(
onPressed: () => _setCash(controller.balanceDue),
style: TextButton.styleFrom(
minimumSize: const Size(0, 30),
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
),
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),),
),
child: const Text('Exact',
style: TextStyle(fontSize: 12.5),),
),
],
),
),