Expose live display currency for transactions
Build and deploy Docker image / build (push) Successful in 1m25s

This commit is contained in:
Ruslan Bakiev
2026-08-24 21:16:42 +07:00
parent 952253dceb
commit e709c332a6
3 changed files with 100 additions and 15 deletions
+39
View File
@@ -25,6 +25,18 @@ export type LockedCurrencyPrice = {
pricingCountryCode: string
}
export type DisplayCurrencyConversion = {
sourceAmount: number
sourceCurrency: string
displayAmount: number
displayCurrency: string
exchangeRate: number
rateSnapshotUuid: string | null
rateEffectiveDate: string
rateQuotedAt: string
provider: string
}
const kycM2mEndpoint =
process.env.KYC_M2M_GRAPHQL_ENDPOINT ?? 'https://kyc.optovia.ru/graphql/m2m'
const exchangeM2mEndpoint =
@@ -131,3 +143,30 @@ export async function lockCurrencyPrice(input: {
)
return data.lockCurrencyPrice
}
export async function convertCurrencyForDisplay(input: {
amount: number
sourceCurrency: string
displayCurrency: string
}): Promise<DisplayCurrencyConversion> {
const data = await requestGraphql<{
convertCurrencyForDisplay: DisplayCurrencyConversion
}>(
exchangeM2mEndpoint,
`query OrdersConvertCurrencyForDisplay($input: ConvertCurrencyForDisplayInput!) {
convertCurrencyForDisplay(input: $input) {
sourceAmount
sourceCurrency
displayAmount
displayCurrency
exchangeRate
rateSnapshotUuid
rateEffectiveDate
rateQuotedAt
provider
}
}`,
{ input },
)
return data.convertCurrencyForDisplay
}
+60 -14
View File
@@ -11,7 +11,9 @@ import { prisma } from '../db.js'
import { ensureDemoWorkspaceData } from '../demo-workspace.js'
import {
lockCurrencyPrice,
convertCurrencyForDisplay,
resolveKycPricingContext,
type DisplayCurrencyConversion,
type KycPricingContext,
type LockedCurrencyPrice,
} from '../pricing-context.js'
@@ -112,6 +114,8 @@ export const teamTypeDefs = `#graphql
sourceCurrency: String
clientAmount: Float!
clientCurrency: String!
displayAmount: Float!
displayCurrency: String!
exchangeRate: Float
exchangeRateSnapshotUuid: String
exchangeRateProvider: String
@@ -216,6 +220,8 @@ export const teamTypeDefs = `#graphql
sourceCurrency: String
clientAmount: Float!
clientCurrency: String!
displayAmount: Float!
displayCurrency: String!
exchangeRate: Float
exchangeRateSnapshotUuid: String
exchangeRateProvider: String
@@ -362,10 +368,10 @@ export const teamTypeDefs = `#graphql
}
type Query {
getTeamOrders: [Order]
getOrder(orderUuid: String!): Order
quotations(status: String): [Quotation!]!
quotation(quotationUuid: String!): Quotation
getTeamOrders(displayCurrency: String!): [Order]
getOrder(orderUuid: String!, displayCurrency: String!): Order
quotations(status: String, displayCurrency: String!): [Quotation!]!
quotation(quotationUuid: String!, displayCurrency: String!): Quotation
tariffReferences(status: String): [TariffReference!]!
tariffReference(tariffReferenceUuid: String!): TariffReference
quotationTariffMatches(quotationUuid: String!): [QuotationTariffMatch!]!
@@ -547,7 +553,12 @@ function mapQuotationChange(item: PrismaQuotationChange) {
}
}
function mapQuotation(item: QuotationWithRelations) {
function mapQuotation(
item: QuotationWithRelations,
display?: DisplayCurrencyConversion,
) {
const displayAmount = display?.displayAmount ?? Number(item.clientAmount)
const displayCurrency = display?.displayCurrency ?? item.clientCurrency
return {
uuid: item.uuid,
teamUuid: item.teamUuid,
@@ -582,6 +593,8 @@ function mapQuotation(item: QuotationWithRelations) {
sourceCurrency: item.sourceCurrency,
clientAmount: Number(item.clientAmount),
clientCurrency: item.clientCurrency,
displayAmount,
displayCurrency,
exchangeRate: numberFromDecimal(item.exchangeRate),
exchangeRateSnapshotUuid: item.exchangeRateSnapshotUuid,
exchangeRateProvider: item.exchangeRateProvider,
@@ -600,7 +613,12 @@ function mapQuotation(item: QuotationWithRelations) {
}
}
function mapLocalOrder(item: OrderWithRelations) {
function mapLocalOrder(
item: OrderWithRelations,
display?: DisplayCurrencyConversion,
) {
const displayAmount = display?.displayAmount ?? Number(item.clientAmount)
const displayCurrency = display?.displayCurrency ?? item.clientCurrency
const progress = orderLocationProgress(item.status)
const locationLatitude = interpolateCoordinate(item.sourceLatitude, item.destinationLatitude, progress)
const locationLongitude = interpolateCoordinate(item.sourceLongitude, item.destinationLongitude, progress)
@@ -615,6 +633,8 @@ function mapLocalOrder(item: OrderWithRelations) {
sourceCurrency: item.sourceCurrency,
clientAmount: Number(item.clientAmount),
clientCurrency: item.clientCurrency,
displayAmount,
displayCurrency,
exchangeRate: numberFromDecimal(item.exchangeRate),
exchangeRateSnapshotUuid: item.exchangeRateSnapshotUuid,
exchangeRateProvider: item.exchangeRateProvider,
@@ -1133,7 +1153,7 @@ async function refreshTeamQuotations(teamUuid: string, actor: { userId: string;
export const teamResolvers = {
Query: {
getTeamOrders: async (_: unknown, __: unknown, ctx: AuthContext) => {
getTeamOrders: async (_: unknown, args: { displayCurrency: string }, ctx: AuthContext) => {
const access = assertTeamAccess(ctx)
await ensureClientWorkspaceData(access)
@@ -1147,10 +1167,17 @@ export const teamResolvers = {
},
})
return localOrders.map(mapLocalOrder)
return Promise.all(localOrders.map(async item => mapLocalOrder(
item,
await convertCurrencyForDisplay({
amount: Number(item.clientAmount),
sourceCurrency: item.clientCurrency,
displayCurrency: args.displayCurrency,
}),
)))
},
getOrder: async (_: unknown, args: { orderUuid: string }, ctx: AuthContext) => {
getOrder: async (_: unknown, args: { orderUuid: string; displayCurrency: string }, ctx: AuthContext) => {
const access = assertTeamAccess(ctx)
const order = await prisma.order.findFirst({
@@ -1161,10 +1188,16 @@ export const teamResolvers = {
include: orderInclude,
})
return order ? mapLocalOrder(order) : null
if (order === null) return null
const display = await convertCurrencyForDisplay({
amount: Number(order.clientAmount),
sourceCurrency: order.clientCurrency,
displayCurrency: args.displayCurrency,
})
return mapLocalOrder(order, display)
},
quotations: async (_: unknown, args: { status?: string | null }, ctx: AuthContext) => {
quotations: async (_: unknown, args: { status?: string | null; displayCurrency: string }, ctx: AuthContext) => {
const access = assertTeamAccess(ctx)
await ensureClientWorkspaceData(access)
@@ -1179,10 +1212,17 @@ export const teamResolvers = {
},
})
return items.map(mapQuotation)
return Promise.all(items.map(async item => mapQuotation(
item,
await convertCurrencyForDisplay({
amount: Number(item.clientAmount),
sourceCurrency: item.clientCurrency,
displayCurrency: args.displayCurrency,
}),
)))
},
quotation: async (_: unknown, args: { quotationUuid: string }, ctx: AuthContext) => {
quotation: async (_: unknown, args: { quotationUuid: string; displayCurrency: string }, ctx: AuthContext) => {
const access = assertTeamAccess(ctx)
const item = await prisma.quotation.findFirst({
where: {
@@ -1191,7 +1231,13 @@ export const teamResolvers = {
},
include: quotationInclude,
})
return item ? mapQuotation(item) : null
if (item === null) return null
const display = await convertCurrencyForDisplay({
amount: Number(item.clientAmount),
sourceCurrency: item.clientCurrency,
displayCurrency: args.displayCurrency,
})
return mapQuotation(item, display)
},
tariffReferences: async (_: unknown, args: { status?: string | null }, ctx: AuthContext) => {