@@ -59,12 +59,34 @@ function rejectUnknownKeys(
5959 if ( unknownKey ) throw new Error ( `${ fieldName } contains unsupported field "${ unknownKey } "` )
6060}
6161
62- function positiveNumber ( value : unknown , fieldName : string ) : number {
63- const parsed = typeof value === 'number' ? value : Number ( value )
64- if ( ! Number . isFinite ( parsed ) || parsed <= 0 ) {
62+ function positiveQuickBooksAmount (
63+ value : unknown ,
64+ fieldName : string
65+ ) : { decimal : Decimal ; number : number } {
66+ if ( typeof value !== 'number' && typeof value !== 'string' ) {
6567 throw new Error ( `${ fieldName } must be a positive finite number` )
6668 }
67- return parsed
69+ const normalized = typeof value === 'string' ? value . trim ( ) : value
70+ if ( normalized === '' ) throw new Error ( `${ fieldName } must be a positive finite number` )
71+
72+ let decimal : Decimal
73+ try {
74+ decimal = new Decimal ( normalized )
75+ } catch {
76+ throw new Error ( `${ fieldName } must be a positive finite number` )
77+ }
78+ if ( ! decimal . isFinite ( ) || decimal . lte ( 0 ) ) {
79+ throw new Error ( `${ fieldName } must be a positive finite number` )
80+ }
81+ if ( decimal . decimalPlaces ( ) > 2 ) {
82+ throw new Error ( `${ fieldName } cannot have more than two decimal places` )
83+ }
84+
85+ const number = decimal . toNumber ( )
86+ if ( ! Number . isFinite ( number ) || ! new Decimal ( number ) . equals ( decimal ) ) {
87+ throw new Error ( `${ fieldName } is outside the safely supported amount range` )
88+ }
89+ return { decimal, number }
6890}
6991
7092function stringValue ( value : unknown , fieldName : string ) : string {
@@ -97,6 +119,7 @@ export function parseQuickBooksJournalLines(
97119 if ( ! parsed ) return undefined
98120 validateLineCount ( parsed , fieldName , 2 )
99121
122+ const decimalAmounts : Decimal [ ] = [ ]
100123 const lines = parsed . map ( ( rawLine , index ) => {
101124 const itemName = `${ fieldName } [${ index } ]`
102125 const line = requireObject ( rawLine , itemName )
@@ -117,9 +140,11 @@ export function parseQuickBooksJournalLines(
117140 if ( ( entityType === undefined ) !== ( line . entityId === undefined ) ) {
118141 throw new Error ( `${ itemName } .entityType and entityId must be supplied together` )
119142 }
143+ const amount = positiveQuickBooksAmount ( line . amount , `${ itemName } .amount` )
144+ decimalAmounts . push ( amount . decimal )
120145 return {
121146 postingType,
122- amount : positiveNumber ( line . amount , ` ${ itemName } .amount` ) ,
147+ amount : amount . number ,
123148 accountId : stringValue ( line . accountId , `${ itemName } .accountId` ) ,
124149 description : optionalStringValue ( line . description , `${ itemName } .description` ) ,
125150 entityType,
@@ -130,12 +155,14 @@ export function parseQuickBooksJournalLines(
130155 }
131156 } )
132157
133- const debitTotal = lines
134- . filter ( ( line ) => line . postingType === 'debit' )
135- . reduce ( ( sum , line ) => sum . plus ( line . amount ) , new Decimal ( 0 ) )
136- const creditTotal = lines
137- . filter ( ( line ) => line . postingType === 'credit' )
138- . reduce ( ( sum , line ) => sum . plus ( line . amount ) , new Decimal ( 0 ) )
158+ const debitTotal = lines . reduce (
159+ ( sum , line , index ) => ( line . postingType === 'debit' ? sum . plus ( decimalAmounts [ index ] ) : sum ) ,
160+ new Decimal ( 0 )
161+ )
162+ const creditTotal = lines . reduce (
163+ ( sum , line , index ) => ( line . postingType === 'credit' ? sum . plus ( decimalAmounts [ index ] ) : sum ) ,
164+ new Decimal ( 0 )
165+ )
139166 if ( ! debitTotal . equals ( creditTotal ) ) {
140167 throw new Error ( 'Journal entry debit and credit totals must balance' )
141168 }
@@ -182,7 +209,7 @@ export function parseQuickBooksDepositLines(
182209 const line = requireObject ( rawLine , itemName )
183210 rejectUnknownKeys ( line , DEPOSIT_LINE_KEYS , itemName )
184211 return {
185- amount : positiveNumber ( line . amount , `${ itemName } .amount` ) ,
212+ amount : positiveQuickBooksAmount ( line . amount , `${ itemName } .amount` ) . number ,
186213 accountId : stringValue ( line . accountId , `${ itemName } .accountId` ) ,
187214 description : optionalStringValue ( line . description , `${ itemName } .description` ) ,
188215 }
0 commit comments