-
Notifications
You must be signed in to change notification settings - Fork 16
84 track orders #137
base: master
Are you sure you want to change the base?
84 track orders #137
Conversation
Wow! Great entrance, @michkoz ! I can review this today I think, but it should also be reviewed by at least one more person so we have a somewhat distributed understanding of the changes introduced!? Let's try pinging people to see if they're game for some review action @valberg @jacobandresen @kraenhansen @sepow :) |
I've skimmed it and will have time to take a deeper look tomorrow. The only thing that really jumped out to me was the change from current.update() to current.save(). |
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.
A couple of small things, hoping you have time to look at it :)
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
migrations.swappable_dependency(settings.AUTH_USER_MODEL), |
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.
oh nice, did you add this manually?
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.
This was done autocratically
from .payment import Payment, PaymentItem | ||
from django.db.models import Sum, F, Count, Max | ||
from django.core.exceptions import ValidationError | ||
from moneyed import Money |
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.
The sorting is off, try doing this:
pip install pre-commit
pre-commit install
pre-commit run --all-files
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.
My bad, I expected that it works as pre commit hook.
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.
When installed, it will fire, but you have to run pre-commit install
, after that it lints changed files before committing.
# sanity check for the currencies | ||
|
||
if 1 != self.items.all().values('product__price_currency').annotate(total=Count('product__price_currency')).count(): | ||
raise ValidationError('The products have different currencies') |
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.
If the count() is 0, the ValidationError
won't make sense. But it might as well generate an exception below.
You are right that it should be fired another place.
raise ValidationError('The products have different currencies') | ||
|
||
total = self.items.all().aggregate(total=Sum(F('product__price') * F('quantity')), cur=Max('product__price_currency')) | ||
return Money(total['total'], total['cur']) |
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.
This doesn't work for me, I get the following:
site-packages/django/db/models/expressions.py in _resolve_output_field(self)
268 if source is not None and not isinstance(self._output_field, source.__class__):
269 raise FieldError(
--> 270 "Expression contains mixed types. You must set output_field")
271
272 def convert_value(self, value, expression, connection, context):
FieldError: Expression contains mixed types. You must set output_field
I think it has to do with the F('product__price') * F('quantity')
expression.
I'm guessing it somehow works for you? Are you running Python 3.4 or 3.5?
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.
Strange, I tested it on sqlite and it worked. It looks like a difference between psql and sqlite in types. I will rewrite it as a loop. With pre-fetching and selecting related data the performance would be almost the same, more readable and work on every database engine.
Well it works for me, and it generates a correct query:
SELECT MAX("market_product"."price_currency") AS "cur", CAST(SUM(("market_product"."price" * "market_basketitem"."quantity")) AS NUMERIC) AS "total" FROM "market_basketitem" INNER JOIN "market_product" ON ("market_basketitem"."product_id" = "market_product"."id") WHERE "market_basketitem"."basket_id" = '19
And I use python 3.4.5
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.
Yes, there shouldn't be any real performance concerns as the contents of a basket are quite limited :)
amount=self.get_total_amount(), | ||
# FIXME: get valid account |
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.
This means that we don't know the account that pays at the moment of checking out and creating the payment? Maybe if we start by assuming users always have exactly 1 account and always use that?
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.
Well in ideal case: a user should be created with a personal account (better name would be organization). It should be a trigger and account model should have one more field - personal.
So every user has own default account. Later on the user can join other non personal accounts.
And somewhere in navigation bar he can select which account is he using (probably a session variable). Basket should be based on account_id. This would allow to share the basket for the whole organization. Anyway it has nothing to do with what I have done.
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.
Sounds interesting to add a default_account
fk and make it a personal account - so we can guarantee that a user is always associated to at least 1 account.
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 would add more consistency to the shopping logic. Everything is based on the account
. It it actually quite nice task and it shouldn’t be very difficult. Only someone has to decide where the selector for the active account will be. If someone less experience decide to do, I'm happy to assist him/her.
When we talk about shopping logic the payment model it is not optimal as well. It should be called an order
. Order can have many payments
and many orderItems
. As it is now it has many limitation for real-life situations.
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.
Are you suggesting a separate issue for this? I can create the issue and try to describe it...
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 the issue is quite complex and has noting to do with the payment the logic.
The issue could have following sub-task:
- Add personal field to the
account
. - Add trigger when user is created that an
account
with personal field set to true. User can have only one personal account. - Create personal accounts for all existing users.
- Create drop down where all users accounts are listed. It can be called for example `Active account'. And user can choose which account he is using for shopping. Of course if he has only one account no drop down is visible.
- The active account ID should be added to session as variable. Or is there any other way how to know what account is the user using? When user login in he shops as personal/last used account.
Link to the payments:
- Remove user field from basket and add account
- Order history should be filtered by account id not user id
- Payment should keep the user field - it is important to know who made the checkout/payment.
Also #130 should be removed - it doesn’t solve the problem. It is up to consideration if this should be part of the MVP.
What do you think? Also the issues should be revisited.
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 don't think the account needs to be a personal account, as long as all users have at least 1 default_account
, then that account can be deleted or shared to other users at a later time. The important part is only that we automatically create a new "personal" account every time a new user is created.
I'll create a new issue, thanks for fleshing it out!!
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.
Moved to #139
to a secure page at ePay.dk, where you will enter your payment information | ||
and accept the payment.</p> | ||
{% comment %} | ||
<p>We accept the following payment methods: Dankort, Visa/Dankort.</p> |
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.
Fine to just delete this :)
|
||
<div class="row"> | ||
<div class="col-sm-12"> | ||
<strong>Your order Summary:</strong> |
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.
Mark for translation! :)
<th>Order id</th> | ||
<th>Order date</th> | ||
<th>payment status</th> | ||
<th>total amount</th> |
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.
Mark for translation! :)
</div> | ||
{% else %} | ||
<div class="row"> | ||
<div class="col-md-5 col-span-4">Wrong order</div> |
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.
Mark for translation! :)
@@ -4,40 +4,69 @@ | |||
|
|||
<div class="row"> | |||
<div class="col-sm-12"> | |||
<h1 class="page-header">Your payments:</h1> |
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.
This file also needs to be marked for translation! :)
I had to force fix |
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.
A couple of small things, not sure they need to be addressed, I leave it to your judgement, great work @michkoz !
# FIXME this check should be removed, it is better to check for the currenices when an item is added | ||
# sanity check for the currencies | ||
if self.get_items_count() == 0: | ||
return Money(0, getattr(settings, 'DEFAULT_CURRENCY')) |
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.
👍
total = self.test_basket.get_total_amount() | ||
|
||
self.assertEqual(total, Money(Decimal('40'), 'DKK')) | ||
|
||
with self.assertRaisesRegex(ValidationError, 'The products have different currencies'): | ||
self.test_basket.add_to_items(product=self.test_product_2, quantity=2, delivery_date=None) | ||
self.test_basket.get_total_amount() | ||
|
||
def test_backet_get_currency(self): |
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.
little typo in "backet" :)
@@ -55,6 +55,8 @@ def dispatch(self, request, *args, **kwargs): | |||
return super(PaymentView, self).dispatch(request, *args, **kwargs) | |||
|
|||
def get_queryset(self): | |||
# FIX ME: add proper request | |||
request = None | |||
return Payment.objects.filter(user=request.user) |
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.
You should be able to use self.request
here?
Major changes:
Should be fixed at some point:
What is not done:
Please feel free to change anything in my code!