@@ -28,6 +28,7 @@ import type {
2828 StoreReference ,
2929 StoreResourceIdentifier ,
3030 Type ,
31+ TypedMoney ,
3132} from "@commercetools/platform-sdk" ;
3233import { Decimal } from "decimal.js/decimal" ;
3334import type { Request } from "express" ;
@@ -109,19 +110,17 @@ export const roundDecimal = (decimal: Decimal, roundingMode: RoundingMode) => {
109110 }
110111} ;
111112
112- export const createCentPrecisionMoney = ( value : _Money ) : CentPrecisionMoney => {
113+ export const getCurrencyFractionDigits = ( currencyCode : string ) : number => {
113114 // Taken from https://docs.adyen.com/development-resources/currency-codes
114- let fractionDigits = 2 ;
115- switch ( value . currencyCode . toUpperCase ( ) ) {
115+ switch ( currencyCode . toUpperCase ( ) ) {
116116 case "BHD" :
117117 case "IQD" :
118118 case "JOD" :
119119 case "KWD" :
120120 case "LYD" :
121121 case "OMR" :
122122 case "TND" :
123- fractionDigits = 3 ;
124- break ;
123+ return 3 ;
125124 case "CVE" :
126125 case "DJF" :
127126 case "GNF" :
@@ -137,29 +136,120 @@ export const createCentPrecisionMoney = (value: _Money): CentPrecisionMoney => {
137136 case "XAF" :
138137 case "XOF" :
139138 case "XPF" :
140- fractionDigits = 0 ;
141- break ;
139+ return 0 ;
142140 default :
143- fractionDigits = 2 ;
141+ return 2 ;
144142 }
143+ } ;
144+
145+ export const calculateCentAmountFromPreciseAmount = (
146+ preciseAmount : number ,
147+ fractionDigits : number ,
148+ currencyCode : string ,
149+ roundingMode : RoundingMode = "HalfEven" ,
150+ ) : number => {
151+ const centFractionDigits = getCurrencyFractionDigits ( currencyCode ) ;
152+ const diff = fractionDigits - centFractionDigits ;
153+ const scale = new Decimal ( 10 ) . pow ( Math . abs ( diff ) ) ;
154+ const decimal =
155+ diff >= 0
156+ ? new Decimal ( preciseAmount ) . div ( scale )
157+ : new Decimal ( preciseAmount ) . mul ( scale ) ;
158+
159+ return roundDecimal ( decimal , roundingMode ) . toNumber ( ) ;
160+ } ;
145161
146- if ( ( value as HighPrecisionMoney & HighPrecisionMoneyDraft ) . preciseAmount ) {
147- throw new Error ( "HighPrecisionMoney not supported" ) ;
162+ export const createCentPrecisionMoney = ( value : _Money ) : CentPrecisionMoney => {
163+ const fractionDigits = getCurrencyFractionDigits ( value . currencyCode ) ;
164+ const preciseValue = value as HighPrecisionMoney & HighPrecisionMoneyDraft ;
165+ let centAmount : number ;
166+
167+ centAmount = value . centAmount ?? 0 ;
168+
169+ if (
170+ preciseValue . preciseAmount !== undefined &&
171+ preciseValue . fractionDigits !== undefined
172+ ) {
173+ centAmount = calculateCentAmountFromPreciseAmount (
174+ preciseValue . preciseAmount ,
175+ preciseValue . fractionDigits ,
176+ value . currencyCode ,
177+ "HalfEven" ,
178+ ) ;
148179 }
149180
150181 return {
151182 type : "centPrecision" ,
152- // centAmont is only optional on HighPrecisionMoney, so this should never
153- // fallback to 0
154- centAmount : value . centAmount ?? 0 ,
183+ centAmount,
155184 currencyCode : value . currencyCode ,
156- fractionDigits : fractionDigits ,
185+ fractionDigits,
157186 } ;
158187} ;
159188
160- export const createTypedMoney = ( value : _Money ) : CentPrecisionMoney => {
161- const result = createCentPrecisionMoney ( value ) ;
162- return result ;
189+ export const createHighPrecisionMoney = (
190+ value : HighPrecisionMoney | HighPrecisionMoneyDraft ,
191+ ) : HighPrecisionMoney => {
192+ if ( value . preciseAmount === undefined ) {
193+ throw new Error ( "HighPrecisionMoney requires preciseAmount" ) ;
194+ }
195+
196+ if ( value . fractionDigits === undefined ) {
197+ throw new Error ( "HighPrecisionMoney requires fractionDigits" ) ;
198+ }
199+
200+ const centAmount =
201+ value . centAmount ??
202+ calculateCentAmountFromPreciseAmount (
203+ value . preciseAmount ,
204+ value . fractionDigits ,
205+ value . currencyCode ,
206+ "HalfEven" ,
207+ ) ;
208+
209+ return {
210+ type : "highPrecision" ,
211+ centAmount,
212+ currencyCode : value . currencyCode ,
213+ fractionDigits : value . fractionDigits ,
214+ preciseAmount : value . preciseAmount ,
215+ } ;
216+ } ;
217+
218+ export const createTypedMoney = ( value : _Money ) : TypedMoney => {
219+ const preciseValue = value as HighPrecisionMoney & HighPrecisionMoneyDraft ;
220+ if (
221+ ( "type" in value && value . type === "highPrecision" ) ||
222+ preciseValue . preciseAmount !== undefined
223+ ) {
224+ return createHighPrecisionMoney (
225+ value as HighPrecisionMoney | HighPrecisionMoneyDraft ,
226+ ) ;
227+ }
228+
229+ return createCentPrecisionMoney ( value ) ;
230+ } ;
231+
232+ export const calculateMoneyTotalCentAmount = (
233+ money : _Money ,
234+ quantity : number ,
235+ roundingMode : RoundingMode = "HalfEven" ,
236+ ) : number => {
237+ const preciseValue = money as HighPrecisionMoney & HighPrecisionMoneyDraft ;
238+
239+ if (
240+ preciseValue . preciseAmount === undefined ||
241+ preciseValue . fractionDigits === undefined
242+ ) {
243+ return ( money . centAmount ?? 0 ) * quantity ;
244+ }
245+
246+ const totalPrecise = new Decimal ( preciseValue . preciseAmount ) . mul ( quantity ) ;
247+ return calculateCentAmountFromPreciseAmount (
248+ totalPrecise . toNumber ( ) ,
249+ preciseValue . fractionDigits ,
250+ money . currencyCode ,
251+ roundingMode ,
252+ ) ;
163253} ;
164254
165255export const resolveStoreReference = (
0 commit comments