From 6aca905ef735d4b8ad3e44ed42311b4596e22e1b Mon Sep 17 00:00:00 2001 From: lissavxo Date: Sun, 2 Mar 2025 15:22:20 -0300 Subject: [PATCH 1/3] fix: button name csv notes --- .../download/transactions/[paybuttonId].ts | 2 +- pages/api/payments/download/index.ts | 2 +- utils/files.ts | 37 ++++++++++++++----- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/pages/api/paybutton/download/transactions/[paybuttonId].ts b/pages/api/paybutton/download/transactions/[paybuttonId].ts index b631c6203..eab379399 100644 --- a/pages/api/paybutton/download/transactions/[paybuttonId].ts +++ b/pages/api/paybutton/download/transactions/[paybuttonId].ts @@ -52,7 +52,7 @@ export default async (req: any, res: any): Promise => { }; const transactions = await fetchTransactionsByPaybuttonId(paybutton.id, networkIdArray) res.setHeader('Content-Type', 'text/csv') - await downloadTxsFile(res, quoteSlug, timezone, transactions) + await downloadTxsFile(res, quoteSlug, timezone, transactions, userId) } catch (error: any) { switch (error.message) { case RESPONSE_MESSAGES.PAYBUTTON_ID_NOT_PROVIDED_400.message: diff --git a/pages/api/payments/download/index.ts b/pages/api/payments/download/index.ts index 3b2e90011..b1707d5f0 100644 --- a/pages/api/payments/download/index.ts +++ b/pages/api/payments/download/index.ts @@ -44,7 +44,7 @@ export default async (req: any, res: any): Promise => { networkIdArray = [networkId] }; const transactions = await fetchAllPaymentsByUserId(userId, networkIdArray) - await downloadTxsFile(res, quoteSlug, timezone, transactions) + await downloadTxsFile(res, quoteSlug, timezone, transactions, userId) } catch (error: any) { switch (error.message) { case RESPONSE_MESSAGES.METHOD_NOT_ALLOWED.message: diff --git a/utils/files.ts b/utils/files.ts index f8bf4943c..5b4f07057 100644 --- a/utils/files.ts +++ b/utils/files.ts @@ -123,7 +123,8 @@ export const collapseSmallPayments = ( payments: TransactionsWithPaybuttonsAndPrices[], currency: SupportedQuotesType, timezone: string, - collapseThreshold: number + collapseThreshold: number, + userId: string ): TransactionFileData[] => { const treatedPayments: TransactionFileData[] = [] const tempTxGroups: Record = {} @@ -133,7 +134,7 @@ export const collapseSmallPayments = ( const tempTxGroup = tempTxGroups[groupKey] if (tempTxGroup === undefined || tempTxGroup.length === 0) return if (tempTxGroup.length === 1) { - pushTx(tempTxGroup[0]) + pushTx(tempTxGroup[0], groupKey) tempTxGroups[groupKey] = [] return } @@ -164,7 +165,7 @@ export const collapseSmallPayments = ( ) } const rate = new Prisma.Decimal(uniquePrices.values().next().value as number) - const buttonName = tempTxGroup[0].address.paybuttons[0].paybutton.name + const buttonName = groupKey.split('_')[2] const notes = `${buttonName} - ${tempTxGroup.length.toString()} transactions` totalPaymentsTreated += tempTxGroup.length @@ -184,11 +185,12 @@ export const collapseSmallPayments = ( tempTxGroups[groupKey] = [] } - const pushTx = (tx: TransactionsWithPaybuttonsAndPrices): void => { + const pushTx = (tx: TransactionsWithPaybuttonsAndPrices, groupKey: string): void => { const { timestamp, hash, address, amount } = tx const values = getTransactionValue(tx) const value = Number(values[currency]) const rate = tx.prices.find(p => p.price.quoteId === QUOTE_IDS[currency.toUpperCase()])!.price.value + const buttonName = groupKey.split('_')[2] treatedPayments.push({ amount, @@ -198,7 +200,7 @@ export const collapseSmallPayments = ( rate, currency, address: address.address, - notes: '', + notes: buttonName, newtworkId: address.networkId } as TransactionFileData) totalPaymentsTreated += 1 @@ -210,19 +212,35 @@ export const collapseSmallPayments = ( const value = Number(values[currency]) const dateKey = moment.tz(timestamp * 1000, timezone).format('YYYY-MM-DD') const dateKeyUTC = moment.utc(timestamp * 1000).format('YYYY-MM-DD') - const groupKey = `${dateKey}_${dateKeyUTC}` + const uniqueButtonName = new Set( + tx.address.paybuttons + .filter(pb => pb.paybutton.providerUserId === userId) + .map(pb => pb.paybutton.name) + ) + if (uniqueButtonName.size > 1) { + console.error('ERROR WHEN GENERATING CSV, A TX BELONGS TO MORE THEN ONE BUTTON:', { tx, buttons: tx.address.paybuttons.filter(pb => pb.paybutton.providerUserId === userId) }) + } + const groupKey = `${dateKey}_${dateKeyUTC}_${uniqueButtonName.values().next().value as string}` + // console.log('groupKey', groupKey) const nextPayment = payments[index + 1] const nextDateKey = nextPayment === undefined ? null : moment.tz(nextPayment.timestamp * 1000, timezone).format('YYYY-MM-DD') const nextDateKeyUTC = nextPayment === undefined ? null : moment.utc(nextPayment.timestamp * 1000).format('YYYY-MM-DD') - const nextGroupKey = nextDateKey === null || nextDateKeyUTC === null ? null : `${nextDateKey}_${nextDateKeyUTC}` + const nextUniqueButtonName = nextPayment === undefined + ? null + : new Set( + nextPayment.address.paybuttons + .filter(pb => pb.paybutton.providerUserId === userId) + .map(pb => pb.paybutton.name) + ) + const nextGroupKey = nextDateKey === null || nextDateKeyUTC === null || nextUniqueButtonName === null ? null : `${nextDateKey}_${nextDateKeyUTC}_${nextUniqueButtonName.values().next().value as string}` if (value < collapseThreshold) { if (tempTxGroups[groupKey] === undefined) tempTxGroups[groupKey] = [] tempTxGroups[groupKey].push(tx) } else { Object.keys(tempTxGroups).forEach(pushTempGroup) - pushTx(tx) + pushTx(tx, groupKey) } if (nextGroupKey !== groupKey) { @@ -282,12 +300,13 @@ export const downloadTxsFile = async ( currency: SupportedQuotesType, timezone: string, transactions: TransactionsWithPaybuttonsAndPrices[], + userId: string, collapseTransactions: boolean = true, collapseThreshold: number = DEFAULT_CSV_COLLAPSE_THRESHOLD): Promise => { const sortedPayments = sortPaymentsByNetworkId(transactions) let treatedPayments: TransactionFileData[] = [] if (collapseTransactions) { - treatedPayments = collapseSmallPayments(sortedPayments, currency, timezone, collapseThreshold) + treatedPayments = collapseSmallPayments(sortedPayments, currency, timezone, collapseThreshold, userId) } else { treatedPayments = getPaybuttonTransactionsFileData(transactions, currency, timezone) } From 132bacdf27ede2e82d08654acded372d62961ffc Mon Sep 17 00:00:00 2001 From: lissavxo Date: Sun, 2 Mar 2025 15:41:36 -0300 Subject: [PATCH 2/3] tests: fix file util --- tests/unittests/utils/files.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/unittests/utils/files.test.ts b/tests/unittests/utils/files.test.ts index 16ef3cf5f..8afec5fa9 100644 --- a/tests/unittests/utils/files.test.ts +++ b/tests/unittests/utils/files.test.ts @@ -192,20 +192,20 @@ describe('collapseSmallPayments', () => { it('should collapse small payments correctly', () => { - const result = collapseSmallPayments(mockedPayments, currencyUsd, timezone, 1); + const result = collapseSmallPayments(mockedPayments, currencyUsd, timezone, 1, 'dev2-uid'); expect(result).toHaveLength(3); }); it('should collapse small payments threshold 2 USD', () => { - const result = collapseSmallPayments(mockedPayments, currencyUsd, timezone, 2); + const result = collapseSmallPayments(mockedPayments, currencyUsd, timezone, 2, 'dev2-uid'); expect(result).toHaveLength(1); }); it('amount should be the sum of colapsed tx amounts', () => { - const result = collapseSmallPayments(mockedPayments, currencyUsd, timezone, 1); + const result = collapseSmallPayments(mockedPayments, currencyUsd, timezone, 1, 'dev2-uid'); const sumOfSmallPaymentsAmount = Number(mockedSmallerThen1UsdPayments.reduce((sum, payment) => sum.plus(payment.amount), new Decimal(0))); const collapsedPayment = result[1]; @@ -214,7 +214,7 @@ describe('collapseSmallPayments', () => { }); it('value should be the sum of colapsed tx values - USD', () => { - const result = collapseSmallPayments(mockedPayments, currencyUsd, timezone, 1); + const result = collapseSmallPayments(mockedPayments, currencyUsd, timezone, 1, 'dev2-uid'); const sumOfSmallPaymentsAmount = Number(mockedSmallerThen1UsdPayments.reduce((sum, payment) => sum.plus(Number(getTransactionValue(payment)[currencyUsd])), new Decimal(0))); const collapsedPayment = result[1]; @@ -223,7 +223,7 @@ describe('collapseSmallPayments', () => { }); it('value should be the sum of colapsed tx values - CAD', () => { - const result = collapseSmallPayments(mockedPayments, currencyCad, timezone, 1); + const result = collapseSmallPayments(mockedPayments, currencyCad, timezone, 1, 'dev2-uid'); const sumOfSmallPaymentsAmount = Number(mockedSmallerThen1UsdPayments.reduce((sum, payment) => sum.plus(Number(getTransactionValue(payment)[currencyCad])), new Decimal(0))); const collapsedPayment = result[1]; From e8ecd1290941d8c76d6877237473f2c6b71d33f8 Mon Sep 17 00:00:00 2001 From: lissavxo Date: Thu, 6 Mar 2025 16:07:29 -0300 Subject: [PATCH 3/3] fix: csv button names --- .../download/transactions/[paybuttonId].ts | 2 +- utils/files.ts | 61 +++++++++++++------ 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/pages/api/paybutton/download/transactions/[paybuttonId].ts b/pages/api/paybutton/download/transactions/[paybuttonId].ts index eab379399..035296dc1 100644 --- a/pages/api/paybutton/download/transactions/[paybuttonId].ts +++ b/pages/api/paybutton/download/transactions/[paybuttonId].ts @@ -52,7 +52,7 @@ export default async (req: any, res: any): Promise => { }; const transactions = await fetchTransactionsByPaybuttonId(paybutton.id, networkIdArray) res.setHeader('Content-Type', 'text/csv') - await downloadTxsFile(res, quoteSlug, timezone, transactions, userId) + await downloadTxsFile(res, quoteSlug, timezone, transactions, userId, paybuttonId) } catch (error: any) { switch (error.message) { case RESPONSE_MESSAGES.PAYBUTTON_ID_NOT_PROVIDED_400.message: diff --git a/utils/files.ts b/utils/files.ts index 5b4f07057..bcf19088f 100644 --- a/utils/files.ts +++ b/utils/files.ts @@ -124,7 +124,8 @@ export const collapseSmallPayments = ( currency: SupportedQuotesType, timezone: string, collapseThreshold: number, - userId: string + userId: string, + paybuttonId?: string ): TransactionFileData[] => { const treatedPayments: TransactionFileData[] = [] const tempTxGroups: Record = {} @@ -165,8 +166,8 @@ export const collapseSmallPayments = ( ) } const rate = new Prisma.Decimal(uniquePrices.values().next().value as number) - const buttonName = groupKey.split('_')[2] - const notes = `${buttonName} - ${tempTxGroup.length.toString()} transactions` + const buttonNames = groupKey.split('_').slice(2).join(' ; ') + const notes = `${buttonNames} - ${tempTxGroup.length.toString()} transactions` totalPaymentsTreated += tempTxGroup.length @@ -190,7 +191,7 @@ export const collapseSmallPayments = ( const values = getTransactionValue(tx) const value = Number(values[currency]) const rate = tx.prices.find(p => p.price.quoteId === QUOTE_IDS[currency.toUpperCase()])!.price.value - const buttonName = groupKey.split('_')[2] + const buttonNames = groupKey.split('_').slice(2).join(' ; ') treatedPayments.push({ amount, @@ -200,7 +201,7 @@ export const collapseSmallPayments = ( rate, currency, address: address.address, - notes: buttonName, + notes: buttonNames, newtworkId: address.networkId } as TransactionFileData) totalPaymentsTreated += 1 @@ -212,29 +213,49 @@ export const collapseSmallPayments = ( const value = Number(values[currency]) const dateKey = moment.tz(timestamp * 1000, timezone).format('YYYY-MM-DD') const dateKeyUTC = moment.utc(timestamp * 1000).format('YYYY-MM-DD') - const uniqueButtonName = new Set( + const uniqueButtonNames = new Set( tx.address.paybuttons .filter(pb => pb.paybutton.providerUserId === userId) .map(pb => pb.paybutton.name) ) - if (uniqueButtonName.size > 1) { - console.error('ERROR WHEN GENERATING CSV, A TX BELONGS TO MORE THEN ONE BUTTON:', { tx, buttons: tx.address.paybuttons.filter(pb => pb.paybutton.providerUserId === userId) }) + let buttonNamesKey: string | undefined = '' + if (uniqueButtonNames.size > 1) { + if (paybuttonId !== undefined) { + buttonNamesKey = tx.address.paybuttons.find(pb => pb.paybutton.id === paybuttonId)?.paybutton.name ?? '' + } else { + buttonNamesKey = [...uniqueButtonNames].join('_') + } + } else { + buttonNamesKey = uniqueButtonNames.values().next().value ?? '' } - const groupKey = `${dateKey}_${dateKeyUTC}_${uniqueButtonName.values().next().value as string}` - // console.log('groupKey', groupKey) + const groupKey = `${dateKey}_${dateKeyUTC}_${buttonNamesKey}` + let nextGroupKey: string | null = '' + const nextPayment = payments[index + 1] - const nextDateKey = nextPayment === undefined ? null : moment.tz(nextPayment.timestamp * 1000, timezone).format('YYYY-MM-DD') - const nextDateKeyUTC = nextPayment === undefined ? null : moment.utc(nextPayment.timestamp * 1000).format('YYYY-MM-DD') - const nextUniqueButtonName = nextPayment === undefined - ? null - : new Set( + if (nextPayment !== undefined) { + const nextDateKey = moment.tz(nextPayment.timestamp * 1000, timezone).format('YYYY-MM-DD') + const nextDateKeyUTC = moment.utc(nextPayment.timestamp * 1000).format('YYYY-MM-DD') + const nextUniqueButtonName = new Set( nextPayment.address.paybuttons .filter(pb => pb.paybutton.providerUserId === userId) .map(pb => pb.paybutton.name) ) + let nextButtonName: string | undefined = '' + if (nextUniqueButtonName.size > 1) { + if (paybuttonId !== undefined) { + nextButtonName = nextPayment.address.paybuttons.find(pb => pb.paybutton.id === paybuttonId)?.paybutton.name ?? '' + } else { + nextButtonName = [...nextUniqueButtonName].join('_') + } + } else { + nextButtonName = uniqueButtonNames.values().next().value ?? '' + } + nextGroupKey = `${nextDateKey}_${nextDateKeyUTC}_${nextButtonName}` + } else { + nextGroupKey = null + } - const nextGroupKey = nextDateKey === null || nextDateKeyUTC === null || nextUniqueButtonName === null ? null : `${nextDateKey}_${nextDateKeyUTC}_${nextUniqueButtonName.values().next().value as string}` if (value < collapseThreshold) { if (tempTxGroups[groupKey] === undefined) tempTxGroups[groupKey] = [] tempTxGroups[groupKey].push(tx) @@ -301,14 +322,16 @@ export const downloadTxsFile = async ( timezone: string, transactions: TransactionsWithPaybuttonsAndPrices[], userId: string, + paybuttonId?: string, collapseTransactions: boolean = true, - collapseThreshold: number = DEFAULT_CSV_COLLAPSE_THRESHOLD): Promise => { + collapseThreshold: number = DEFAULT_CSV_COLLAPSE_THRESHOLD +): Promise => { const sortedPayments = sortPaymentsByNetworkId(transactions) let treatedPayments: TransactionFileData[] = [] if (collapseTransactions) { - treatedPayments = collapseSmallPayments(sortedPayments, currency, timezone, collapseThreshold, userId) + treatedPayments = collapseSmallPayments(sortedPayments, currency, timezone, collapseThreshold, userId, paybuttonId) } else { - treatedPayments = getPaybuttonTransactionsFileData(transactions, currency, timezone) + treatedPayments = getPaybuttonTransactionsFileData(sortedPayments, currency, timezone) } const mappedPaymentsData = treatedPayments.map(payment => formatPaybuttonTransactionsFileData(payment))