Merge pull request 'Card expiry date and few small fixes' (#244) from SEND023 into main
Some checks failed
ci/woodpecker/push/fx_oracle Pipeline is pending
ci/woodpecker/push/gateway_chain Pipeline is pending
ci/woodpecker/push/gateway_mntx Pipeline is pending
ci/woodpecker/push/gateway_tgsettle Pipeline is pending
ci/woodpecker/push/nats Pipeline is pending
ci/woodpecker/push/db Pipeline is pending
ci/woodpecker/push/discovery Pipeline is pending
ci/woodpecker/push/frontend Pipeline is pending
ci/woodpecker/push/fx_ingestor Pipeline is pending
ci/woodpecker/push/ledger Pipeline is pending
ci/woodpecker/push/notification Pipeline is pending
ci/woodpecker/push/payments_orchestrator Pipeline is pending
ci/woodpecker/push/billing_fees Pipeline failed
ci/woodpecker/push/bff Pipeline failed
Some checks failed
ci/woodpecker/push/fx_oracle Pipeline is pending
ci/woodpecker/push/gateway_chain Pipeline is pending
ci/woodpecker/push/gateway_mntx Pipeline is pending
ci/woodpecker/push/gateway_tgsettle Pipeline is pending
ci/woodpecker/push/nats Pipeline is pending
ci/woodpecker/push/db Pipeline is pending
ci/woodpecker/push/discovery Pipeline is pending
ci/woodpecker/push/frontend Pipeline is pending
ci/woodpecker/push/fx_ingestor Pipeline is pending
ci/woodpecker/push/ledger Pipeline is pending
ci/woodpecker/push/notification Pipeline is pending
ci/woodpecker/push/payments_orchestrator Pipeline is pending
ci/woodpecker/push/billing_fees Pipeline failed
ci/woodpecker/push/bff Pipeline failed
Reviewed-on: #244 Reviewed-by: tech <tech.sendico@proton.me>
This commit was merged in pull request #244.
This commit is contained in:
@@ -16,8 +16,8 @@ class CardPaymentMethod implements PaymentMethodData {
|
|||||||
|
|
||||||
const CardPaymentMethod({
|
const CardPaymentMethod({
|
||||||
required this.pan,
|
required this.pan,
|
||||||
this.expMonth,
|
required this.expMonth,
|
||||||
this.expYear,
|
required this.expYear,
|
||||||
required this.firstName,
|
required this.firstName,
|
||||||
required this.lastName,
|
required this.lastName,
|
||||||
this.country,
|
this.country,
|
||||||
|
|||||||
@@ -165,6 +165,7 @@ class PermissionsProvider extends ChangeNotifier {
|
|||||||
perm.Action? action,
|
perm.Action? action,
|
||||||
Object? objectRef,
|
Object? objectRef,
|
||||||
}) {
|
}) {
|
||||||
|
if (!_organizations.isOrganizationSet) return false;
|
||||||
final orgId = _organizations.current.id;
|
final orgId = _organizations.current.id;
|
||||||
final pd = policyDescriptions.firstWhereOrNull(
|
final pd = policyDescriptions.firstWhereOrNull(
|
||||||
(policy) =>
|
(policy) =>
|
||||||
|
|||||||
@@ -360,6 +360,7 @@
|
|||||||
"enterBik": "Enter BIK",
|
"enterBik": "Enter BIK",
|
||||||
"add": "Add",
|
"add": "Add",
|
||||||
"expiryDate": "Expiry (MM/YY)",
|
"expiryDate": "Expiry (MM/YY)",
|
||||||
|
"enterExpiryDate": "Enter card expiry date",
|
||||||
"firstName": "First Name",
|
"firstName": "First Name",
|
||||||
"enterFirstName": "Enter First Name",
|
"enterFirstName": "Enter First Name",
|
||||||
"lastName": "Last Name",
|
"lastName": "Last Name",
|
||||||
|
|||||||
@@ -360,6 +360,7 @@
|
|||||||
"enterBik": "Введите БИК",
|
"enterBik": "Введите БИК",
|
||||||
"add": "Добавить",
|
"add": "Добавить",
|
||||||
"expiryDate": "Срок действия (ММ/ГГ)",
|
"expiryDate": "Срок действия (ММ/ГГ)",
|
||||||
|
"enterExpiryDate": "Введите срок действия карты",
|
||||||
"firstName": "Имя",
|
"firstName": "Имя",
|
||||||
"enterFirstName": "Введите имя",
|
"enterFirstName": "Введите имя",
|
||||||
"lastName": "Фамилия",
|
"lastName": "Фамилия",
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class _CardFormMinimalState extends State<CardFormMinimal> {
|
|||||||
late TextEditingController _panController;
|
late TextEditingController _panController;
|
||||||
late TextEditingController _firstNameController;
|
late TextEditingController _firstNameController;
|
||||||
late TextEditingController _lastNameController;
|
late TextEditingController _lastNameController;
|
||||||
|
late TextEditingController _expiryController;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -37,17 +38,23 @@ class _CardFormMinimalState extends State<CardFormMinimal> {
|
|||||||
_panController = TextEditingController(text: widget.initialData?.pan ?? '');
|
_panController = TextEditingController(text: widget.initialData?.pan ?? '');
|
||||||
_firstNameController = TextEditingController(text: widget.initialData?.firstName ?? '');
|
_firstNameController = TextEditingController(text: widget.initialData?.firstName ?? '');
|
||||||
_lastNameController = TextEditingController(text: widget.initialData?.lastName ?? '');
|
_lastNameController = TextEditingController(text: widget.initialData?.lastName ?? '');
|
||||||
|
_expiryController = TextEditingController(text: _formatExpiry(widget.initialData));
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) => _emitIfValid());
|
WidgetsBinding.instance.addPostFrameCallback((_) => _emitIfValid());
|
||||||
}
|
}
|
||||||
|
|
||||||
void _emitIfValid() {
|
void _emitIfValid() {
|
||||||
if (_formKey.currentState?.validate() ?? false) {
|
if (_formKey.currentState?.validate() ?? false) {
|
||||||
|
final expiry = _parseExpiry(_expiryController.text);
|
||||||
|
if (expiry == null) return;
|
||||||
|
|
||||||
widget.onChanged(
|
widget.onChanged(
|
||||||
CardPaymentMethod(
|
CardPaymentMethod(
|
||||||
pan: _panController.text.replaceAll(' ', ''),
|
pan: _panController.text.replaceAll(' ', ''),
|
||||||
firstName: _firstNameController.text,
|
firstName: _firstNameController.text,
|
||||||
lastName: _lastNameController.text,
|
lastName: _lastNameController.text,
|
||||||
|
expMonth: expiry.month,
|
||||||
|
expYear: expiry.year,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -63,6 +70,7 @@ class _CardFormMinimalState extends State<CardFormMinimal> {
|
|||||||
_panController.clear();
|
_panController.clear();
|
||||||
_firstNameController.clear();
|
_firstNameController.clear();
|
||||||
_lastNameController.clear();
|
_lastNameController.clear();
|
||||||
|
_expiryController.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,12 +78,14 @@ class _CardFormMinimalState extends State<CardFormMinimal> {
|
|||||||
final hasPanChange = newData.pan != _panController.text;
|
final hasPanChange = newData.pan != _panController.text;
|
||||||
final hasFirstNameChange = newData.firstName != _firstNameController.text;
|
final hasFirstNameChange = newData.firstName != _firstNameController.text;
|
||||||
final hasLastNameChange = newData.lastName != _lastNameController.text;
|
final hasLastNameChange = newData.lastName != _lastNameController.text;
|
||||||
|
final hasExpiryChange = _formatExpiry(newData) != _expiryController.text;
|
||||||
|
|
||||||
if (hasPanChange) _panController.text = newData.pan;
|
if (hasPanChange) _panController.text = newData.pan;
|
||||||
if (hasFirstNameChange) _firstNameController.text = newData.firstName;
|
if (hasFirstNameChange) _firstNameController.text = newData.firstName;
|
||||||
if (hasLastNameChange) _lastNameController.text = newData.lastName;
|
if (hasLastNameChange) _lastNameController.text = newData.lastName;
|
||||||
|
if (hasExpiryChange) _expiryController.text = _formatExpiry(newData);
|
||||||
|
|
||||||
if (hasPanChange || hasFirstNameChange || hasLastNameChange) {
|
if (hasPanChange || hasFirstNameChange || hasLastNameChange || hasExpiryChange) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) => _emitIfValid());
|
WidgetsBinding.instance.addPostFrameCallback((_) => _emitIfValid());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,6 +125,16 @@ class _CardFormMinimalState extends State<CardFormMinimal> {
|
|||||||
style: getTextFieldStyle(context, widget.isEditable),
|
style: getTextFieldStyle(context, widget.isEditable),
|
||||||
validator: (v) => (v == null || v.isEmpty) ? l10n.enterLastName : null,
|
validator: (v) => (v == null || v.isEmpty) ? l10n.enterLastName : null,
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
TextFormField(
|
||||||
|
readOnly: !widget.isEditable,
|
||||||
|
controller: _expiryController,
|
||||||
|
decoration: getInputDecoration(context, l10n.expiryDate, widget.isEditable),
|
||||||
|
style: getTextFieldStyle(context, widget.isEditable),
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
inputFormatters: [CreditCardExpirationDateFormatter()],
|
||||||
|
validator: (v) => _parseExpiry(v ?? '') == null ? l10n.enterExpiryDate : null,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -125,6 +145,35 @@ class _CardFormMinimalState extends State<CardFormMinimal> {
|
|||||||
_panController.dispose();
|
_panController.dispose();
|
||||||
_firstNameController.dispose();
|
_firstNameController.dispose();
|
||||||
_lastNameController.dispose();
|
_lastNameController.dispose();
|
||||||
|
_expiryController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _formatExpiry(CardPaymentMethod? data) {
|
||||||
|
if (data == null) return '';
|
||||||
|
if (data.expMonth == null || data.expYear == null) return '';
|
||||||
|
final month = data.expMonth!.toString().padLeft(2, '0');
|
||||||
|
final year = (data.expYear! % 100).toString().padLeft(2, '0');
|
||||||
|
return '$month/$year';
|
||||||
|
}
|
||||||
|
|
||||||
|
_CardExpiry? _parseExpiry(String value) {
|
||||||
|
final normalized = value.trim();
|
||||||
|
final match = RegExp(r'^(\d{2})\s*/\s*(\d{2})$').firstMatch(normalized);
|
||||||
|
if (match == null) return null;
|
||||||
|
|
||||||
|
final month = int.tryParse(match.group(1)!);
|
||||||
|
final year = int.tryParse(match.group(2)!);
|
||||||
|
if (month == null || year == null || month < 1 || month > 12) return null;
|
||||||
|
|
||||||
|
final normalizedYear = 2000 + year;
|
||||||
|
return _CardExpiry(month: month, year: normalizedYear);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CardExpiry {
|
||||||
|
final int month;
|
||||||
|
final int year;
|
||||||
|
|
||||||
|
const _CardExpiry({required this.month, required this.year});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:pshared/models/payment/wallet.dart';
|
import 'package:pshared/models/payment/wallet.dart';
|
||||||
import 'package:pshared/models/recipient/recipient.dart';
|
import 'package:pshared/models/recipient/recipient.dart';
|
||||||
import 'package:pshared/provider/recipient/provider.dart';
|
import 'package:pshared/provider/recipient/provider.dart';
|
||||||
import 'package:pweb/pages/dashboard/payouts/form.dart';
|
|
||||||
|
|
||||||
|
import 'package:pweb/pages/dashboard/payouts/form.dart';
|
||||||
import 'package:pweb/pages/payment_methods/payment_page/back_button.dart';
|
import 'package:pweb/pages/payment_methods/payment_page/back_button.dart';
|
||||||
import 'package:pweb/pages/payment_methods/payment_page/header.dart';
|
import 'package:pweb/pages/payment_methods/payment_page/header.dart';
|
||||||
import 'package:pweb/pages/payment_methods/payment_page/method_selector.dart';
|
import 'package:pweb/pages/payment_methods/payment_page/method_selector.dart';
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class PaymentMethodDropdown extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => DropdownButtonFormField<Wallet>(
|
Widget build(BuildContext context) => DropdownButtonFormField<Wallet>(
|
||||||
dropdownColor: Theme.of(context).colorScheme.onSecondary,
|
dropdownColor: Theme.of(context).colorScheme.onSecondary,
|
||||||
value: _getSelectedMethod(),
|
initialValue: _getSelectedMethod(),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: AppLocalizations.of(context)!.whereGetMoney,
|
labelText: AppLocalizations.of(context)!.whereGetMoney,
|
||||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
|
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
|
||||||
|
|||||||
Reference in New Issue
Block a user