@@ -5,12 +5,13 @@ import isObject from 'lodash/isObject';
5
5
import type { OnyxCollection } from 'react-native-onyx' ;
6
6
import type { FormInputErrors , FormOnyxKeys , FormOnyxValues , FormValue } from '@components/Form/types' ;
7
7
import CONST from '@src/CONST' ;
8
+ import type { Country } from '@src/CONST' ;
8
9
import type { OnyxFormKey } from '@src/ONYXKEYS' ;
9
10
import type { Report , TaxRates } from '@src/types/onyx' ;
10
- import * as CardUtils from './CardUtils' ;
11
+ import { getMonthFromExpirationDateString , getYearFromExpirationDateString } from './CardUtils' ;
11
12
import DateUtils from './DateUtils' ;
12
- import * as Localize from './Localize' ;
13
- import * as LoginUtils from './LoginUtils' ;
13
+ import { translateLocal } from './Localize' ;
14
+ import { appendCountryCode , getPhoneNumberWithoutSpecialChars } from './LoginUtils' ;
14
15
import { parsePhoneNumber } from './PhoneNumber' ;
15
16
import StringUtils from './StringUtils' ;
16
17
@@ -118,7 +119,7 @@ function getFieldRequiredErrors<TFormID extends OnyxFormKey>(values: FormOnyxVal
118
119
return ;
119
120
}
120
121
121
- errors [ fieldKey ] = Localize . translateLocal ( 'common.error.fieldRequired' ) ;
122
+ errors [ fieldKey ] = translateLocal ( 'common.error.fieldRequired' ) ;
122
123
} ) ;
123
124
124
125
return errors ;
@@ -137,7 +138,7 @@ function isValidExpirationDate(string: string): boolean {
137
138
}
138
139
139
140
// Use the last of the month to check if the expiration date is in the future or not
140
- const expirationDate = `${ CardUtils . getYearFromExpirationDateString ( string ) } -${ CardUtils . getMonthFromExpirationDateString ( string ) } -01` ;
141
+ const expirationDate = `${ getYearFromExpirationDateString ( string ) } -${ getMonthFromExpirationDateString ( string ) } -01` ;
141
142
return isAfter ( new Date ( expirationDate ) , endOfMonth ( new Date ( ) ) ) ;
142
143
}
143
144
@@ -202,7 +203,7 @@ function getAgeRequirementError(date: string, minimumAge: number, maximumAge: nu
202
203
const testDate = parse ( date , CONST . DATE . FNS_FORMAT_STRING , currentDate ) ;
203
204
204
205
if ( ! isValid ( testDate ) ) {
205
- return Localize . translateLocal ( 'common.error.dateInvalid' ) ;
206
+ return translateLocal ( 'common.error.dateInvalid' ) ;
206
207
}
207
208
208
209
const maximalDate = subYears ( currentDate , minimumAge ) ;
@@ -213,10 +214,10 @@ function getAgeRequirementError(date: string, minimumAge: number, maximumAge: nu
213
214
}
214
215
215
216
if ( isSameDay ( testDate , maximalDate ) || isAfter ( testDate , maximalDate ) ) {
216
- return Localize . translateLocal ( 'privatePersonalDetails.error.dateShouldBeBefore' , { dateString : format ( maximalDate , CONST . DATE . FNS_FORMAT_STRING ) } ) ;
217
+ return translateLocal ( 'privatePersonalDetails.error.dateShouldBeBefore' , { dateString : format ( maximalDate , CONST . DATE . FNS_FORMAT_STRING ) } ) ;
217
218
}
218
219
219
- return Localize . translateLocal ( 'privatePersonalDetails.error.dateShouldBeAfter' , { dateString : format ( minimalDate , CONST . DATE . FNS_FORMAT_STRING ) } ) ;
220
+ return translateLocal ( 'privatePersonalDetails.error.dateShouldBeAfter' , { dateString : format ( minimalDate , CONST . DATE . FNS_FORMAT_STRING ) } ) ;
220
221
}
221
222
222
223
/**
@@ -228,14 +229,14 @@ function getDatePassedError(inputDate: string): string {
228
229
229
230
// If input date is not valid, return an error
230
231
if ( ! isValid ( parsedDate ) ) {
231
- return Localize . translateLocal ( 'common.error.dateInvalid' ) ;
232
+ return translateLocal ( 'common.error.dateInvalid' ) ;
232
233
}
233
234
234
235
// Clear time for currentDate so comparison is based solely on the date
235
236
currentDate . setHours ( 0 , 0 , 0 , 0 ) ;
236
237
237
238
if ( parsedDate < currentDate ) {
238
- return Localize . translateLocal ( 'common.error.dateInvalid' ) ;
239
+ return translateLocal ( 'common.error.dateInvalid' ) ;
239
240
}
240
241
241
242
return '' ;
@@ -318,7 +319,7 @@ function isValidTwoFactorCode(code: string): boolean {
318
319
* Checks whether a value is a numeric string including `(`, `)`, `-` and optional leading `+`
319
320
*/
320
321
function isNumericWithSpecialChars ( input : string ) : boolean {
321
- return / ^ \+ ? [ \d \\ + ] * $ / . test ( LoginUtils . getPhoneNumberWithoutSpecialChars ( input ) ) ;
322
+ return / ^ \+ ? [ \d \\ + ] * $ / . test ( getPhoneNumberWithoutSpecialChars ( input ) ) ;
322
323
}
323
324
324
325
/**
@@ -515,7 +516,7 @@ function isValidEmail(email: string): boolean {
515
516
* @param phoneNumber
516
517
*/
517
518
function isValidPhoneInternational ( phoneNumber : string ) : boolean {
518
- const phoneNumberWithCountryCode = LoginUtils . appendCountryCode ( phoneNumber ) ;
519
+ const phoneNumberWithCountryCode = appendCountryCode ( phoneNumber ) ;
519
520
const parsedPhoneNumber = parsePhoneNumber ( phoneNumberWithCountryCode ) ;
520
521
521
522
return parsedPhoneNumber . possible && Str . isValidE164Phone ( parsedPhoneNumber . number ?. e164 ?? '' ) ;
@@ -554,6 +555,91 @@ function isValidOwnershipPercentage(value: string, totalOwnedPercentage: Record<
554
555
return isValidNumber && isTotalSumValid ;
555
556
}
556
557
558
+ /**
559
+ * Validates the given value if it is correct ABN number - https://abr.business.gov.au/Help/AbnFormat
560
+ * @param registrationNumber - number to validate.
561
+ */
562
+ function isValidABN ( registrationNumber : string ) : boolean {
563
+ const cleanedAbn : string = registrationNumber . replaceAll ( / [ _ ] / g, '' ) ;
564
+ if ( cleanedAbn . length !== 11 ) {
565
+ return false ;
566
+ }
567
+
568
+ const weights : number [ ] = [ 10 , 1 , 3 , 5 , 7 , 9 , 11 , 13 , 15 , 17 , 19 ] ;
569
+ const checksum : number = [ ...cleanedAbn ] . reduce ( ( total : number , char : string , index : number ) => {
570
+ let digit = Number ( char ) ;
571
+ if ( index === 0 ) {
572
+ digit -- ;
573
+ } // First digit special rule
574
+ return total + digit * ( weights . at ( index ) ?? 0 ) ; // Using optional chaining for safety
575
+ } , 0 ) ;
576
+
577
+ return checksum % 89 === 0 ;
578
+ }
579
+
580
+ /**
581
+ * Validates the given value if it is correct ACN number - https://asic.gov.au/for-business/registering-a-company/steps-to-register-a-company/australian-company-numbers/australian-company-number-digit-check/
582
+ * @param registrationNumber - number to validate.
583
+ */
584
+ function isValidACN ( registrationNumber : string ) : boolean {
585
+ const cleanedAcn : string = registrationNumber . replaceAll ( / \s | - / g, '' ) ;
586
+ if ( cleanedAcn . length !== 9 || Number . isNaN ( Number ( cleanedAcn ) ) ) {
587
+ return false ;
588
+ }
589
+
590
+ const weights : number [ ] = [ 8 , 7 , 6 , 5 , 4 , 3 , 2 , 1 ] ;
591
+ const tally : number = weights . reduce ( ( total : number , weight : number , index : number ) => {
592
+ return total + Number ( cleanedAcn [ index ] ) * weight ;
593
+ } , 0 ) ;
594
+
595
+ const checkDigit : number = 10 - ( tally % 10 ) ;
596
+ return checkDigit === Number ( cleanedAcn [ 8 ] ) || ( checkDigit === 10 && Number ( cleanedAcn [ 8 ] ) === 0 ) ;
597
+ }
598
+
599
+ /**
600
+ * Validates the given value if it is correct australian registration number.
601
+ * @param registrationNumber
602
+ */
603
+ function isValidAURegistrationNumber ( registrationNumber : string ) : boolean {
604
+ return isValidABN ( registrationNumber ) || isValidACN ( registrationNumber ) ;
605
+ }
606
+
607
+ /**
608
+ * Validates the given value if it is correct british registration number.
609
+ * @param registrationNumber
610
+ */
611
+ function isValidGBRegistrationNumber ( registrationNumber : string ) : boolean {
612
+ return / ^ (?: \d { 8 } | [ A - Z ] { 2 } \d { 6 } ) $ / . test ( registrationNumber ) ;
613
+ }
614
+
615
+ /**
616
+ * Validates the given value if it is correct canadian registration number.
617
+ * @param registrationNumber
618
+ */
619
+ function isValidCARegistrationNumber ( registrationNumber : string ) : boolean {
620
+ return / ^ \d { 9 } (?: [ A - Z ] { 2 } \d { 4 } ) ? $ / . test ( registrationNumber ) ;
621
+ }
622
+
623
+ /**
624
+ * Validates the given value if it is correct registration number for the given country.
625
+ * @param registrationNumber
626
+ * @param country
627
+ */
628
+ function isValidRegistrationNumber ( registrationNumber : string , country : Country | '' ) {
629
+ switch ( country ) {
630
+ case CONST . COUNTRY . AU :
631
+ return isValidAURegistrationNumber ( registrationNumber ) ;
632
+ case CONST . COUNTRY . GB :
633
+ return isValidGBRegistrationNumber ( registrationNumber ) ;
634
+ case CONST . COUNTRY . CA :
635
+ return isValidCARegistrationNumber ( registrationNumber ) ;
636
+ case CONST . COUNTRY . US :
637
+ return isValidTaxID ( registrationNumber ) ;
638
+ default :
639
+ return true ;
640
+ }
641
+ }
642
+
557
643
export {
558
644
meetsMinimumAgeRequirement ,
559
645
meetsMaximumAgeRequirement ,
@@ -602,4 +688,5 @@ export {
602
688
isValidPhoneInternational ,
603
689
isValidZipCodeInternational ,
604
690
isValidOwnershipPercentage ,
691
+ isValidRegistrationNumber ,
605
692
} ;
0 commit comments