-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Use currencyConvert
function when calculating WA revenue
#29680
base: master
Are you sure you want to change the base?
feat: Use currencyConvert
function when calculating WA revenue
#29680
Conversation
733ecf1
to
040440e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: Gotta add some tests to web_overview
as well, but you can see on revenue_example_events
how this is working, just need to add some tests + add the original rate/currency to the UI on the revenue debugging table
5f6795b
to
230c7a2
Compare
e837dfd
to
5e97500
Compare
5db74e6
to
8cd2ca8
Compare
Let's now actually use the `currencyConvert` function when computing Revenue on the Web Analytics panel. We make a lot of effort to only actually try converting it if there's something to convert.
8cd2ca8
to
111361f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR Summary
This PR adds currency conversion functionality to the Web Analytics revenue tracking system, leveraging the new currencyConvert
function for accurate multi-currency revenue calculations.
- Added feature flag
web-analytics-revenue-tracking-conversion
to control currency conversion functionality across cloud and self-hosted environments - Implemented comprehensive currency conversion tests in
test_web_overview.py
covering various scenarios like USD/BRL to EUR conversions - Added
original_revenue
andoriginal_currency
fields to preserve pre-conversion values in revenue calculations - Removed
revenue.py
in favor of new currency conversion implementation inexchange_rate.py
- Updated revenue sum expressions to handle currency conversion while maintaining backward compatibility
6 file(s) reviewed, 2 comment(s)
Edit PR Review Bot Settings | Greptile
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Took me a while to be able to test it locally because it was out of my usual context, but LGTM. Only one nit comment.
One thing that is not related to this PR is: what happens when someone uses a invalid currency code? Maybe it was discussed when adding the hogql function, I just missed it.
convert_currency_call( | ||
ast.Field(chain=["events", "properties", event.revenueProperty]), | ||
ast.Field(chain=["events", "properties", event.revenueCurrencyProperty]), | ||
ast.Constant(value=(config.baseCurrency or CurrencyCode.USD).value), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: We have this fallback in two places here, it is not much yet, but I wonder if it will happen more. What I would usually do is create a @property
to centralize the fallback handler, I see there are places in the codebase where we do that but I don't see it being done on posthog/models/schema.py
so maybe you'll know how to approach this if you think it's valid :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome question. There's also a slightly better way where we make this non-optional, and add a @default
to our pydantic models and it will handle that for us. I'm considering changing that, I'm checking with @robbie-c in a separate PR if that's the best way to do it actually - it'll also help with the new config I'm adding to RevenueConfig
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah that's cool, I didn't know that you could set @default
in pydantic. How does this work with creating an object in JS, I'm guessing it just doesn't know about the default there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it just doesn't exist in JS, you have to satisfy the types yourself. I think this is only working when sending data from the backend to the frontend, though, not the opposite, and it's not doing what I want. I'm testing this in a separate PR and will report back in our Slack channel with what I found out
@lricoy It won't exist in the dictionary, and we'll return 0 by default. This means that we'll just ignore events with invalid currencies. One thing I'm worried of is people using lowercase currencies (I think Stripe might be doing that) so I'll need to change that function to capitalize the currencies - I'll cross that bridge once we get there :) |
else: | ||
value_expr = ast.Call( | ||
name="toDecimal", | ||
args=[ast.Field(chain=["events", "properties", event.revenueProperty]), ast.Constant(value=10)], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
10 is the scale? As we use Decimal64 everywhere?
Can this go in a helpfully named constant please :D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
10 is the amount of decimal points we have (after the comma). And yeah, this can go to a helpfully named constant :)
convert_currency_call( | ||
ast.Field(chain=["events", "properties", event.revenueProperty]), | ||
ast.Field(chain=["events", "properties", event.revenueCurrencyProperty]), | ||
ast.Constant(value=(config.baseCurrency or CurrencyCode.USD).value), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah that's cool, I didn't know that you could set @default
in pydantic. How does this work with creating an object in JS, I'm guessing it just doesn't know about the default there?
name="toDecimal", | ||
args=[ | ||
ast.Field(chain=["events", "properties", event.revenueProperty]), | ||
ast.Constant(value=10), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
10
@@ -1,95 +0,0 @@ | |||
from typing import Union |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's a shame that git didn't notice that this was a moved file rather than deleted :(
|
||
def __init__(self, *args, **kwargs): | ||
super().__init__(*args, **kwargs) | ||
self.paginator = HogQLHasMorePaginator.from_limit_context( | ||
limit_context=LimitContext.QUERY, limit=self.query.limit if self.query.limit else None | ||
) | ||
|
||
self.do_currency_conversion = posthoganalytics.feature_enabled( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
might be easier to just include this with the request and do it as a front end feature flag
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this will go away soon, so I'd rather keep it here for now. Some places need to do currency conversion, some don't, so this might be easier :)
This makes the hogql generation much more understandable
Let's now actually use the
currencyConvert
function when computing Revenue on the Web Analytics panel. We make a lot of effort to only actually try converting it if there's something to convert.Does this work well for both Cloud and self-hosted?
Yes
How did you test this code?
Unit tests, gotta test it in prod