Merge branch 'address' into 'master'
Add, activate, and remove active user addresses. See merge request engineering/make-post-sell/make_post_sell!9
This commit is contained in:
commit
f01ba4458c
11 changed files with 207 additions and 12 deletions
|
|
@ -10,6 +10,7 @@ from .shop import *
|
|||
from .user import *
|
||||
from .user_shop import *
|
||||
from .user_product import *
|
||||
from .user_address import *
|
||||
from .price import *
|
||||
from .cart import *
|
||||
from .cart_coupon import *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import uuid
|
||||
|
||||
from sqlalchemy import Column, BigInteger, Boolean, Integer
|
||||
from sqlalchemy import Column, BigInteger, Boolean, Integer, UnicodeText
|
||||
|
||||
from .meta import Base, RBase, UUIDType, foreign_key, now_timestamp
|
||||
|
||||
|
|
@ -93,6 +93,9 @@ class Invoice(RBase, Base):
|
|||
# the timestamp when the invoice was created.
|
||||
created_timestamp = Column(BigInteger, nullable=False)
|
||||
|
||||
# the user's address to ship the item to.
|
||||
delivery_address = Column(UnicodeText, nullable=True)
|
||||
|
||||
# one to one.
|
||||
user = relationship(argument="User", uselist=False, lazy="joined")
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ UUIDType = TempUUIDType(binary=False)
|
|||
|
||||
|
||||
CLASS_TO_TABLE = {
|
||||
"Address": "mps_address",
|
||||
"Shop": "mps_shop",
|
||||
"Product": "mps_product",
|
||||
"Coupon": "mps_coupon",
|
||||
|
|
@ -23,6 +24,7 @@ CLASS_TO_TABLE = {
|
|||
"User": "mps_user",
|
||||
"UserShop": "mps_user_shop",
|
||||
"UserProduct": "mps_user_product",
|
||||
"UserAddress": "mps_user_address",
|
||||
"Price": "mps_price",
|
||||
"License": "mps_license",
|
||||
"Cart": "mps_cart",
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ from .stripe_user_shop import StripeUserShop
|
|||
|
||||
from .user_product import UserProduct
|
||||
|
||||
from .user_address import UserAddress, get_user_address_by_id
|
||||
|
||||
from .shop import Shop
|
||||
|
||||
from .cart import Cart
|
||||
|
|
@ -70,6 +72,8 @@ class User(RBase, Base):
|
|||
# TODO: remove this column.
|
||||
active_cart_total_in_cents = Column(BigInteger, default=0, nullable=False)
|
||||
|
||||
active_address_id = Column(UUIDType, nullable=True)
|
||||
|
||||
# 0 is dark, 1 is light, could have more themes when h@ckz0rs unite.
|
||||
theme_id = Column(BigInteger, nullable=False, default=1)
|
||||
|
||||
|
|
@ -97,6 +101,8 @@ class User(RBase, Base):
|
|||
# this is ideal when we want to further filter the results.
|
||||
carts = relationship(argument="Cart", lazy="dynamic", back_populates="user")
|
||||
|
||||
addresses = relationship(argument="UserAddress", lazy="dynamic", back_populates="user")
|
||||
|
||||
def __init__(self, email):
|
||||
self.name = unicode("user-{}".format(generate_password(size=8)))
|
||||
self.created_timestamp = now_timestamp()
|
||||
|
|
@ -227,6 +233,18 @@ class User(RBase, Base):
|
|||
def does_not_own_cart(self, cart):
|
||||
return not self.owns_cart(cart)
|
||||
|
||||
def new_address(self, new_address):
|
||||
user_address = UserAddress(new_address)
|
||||
user_address.user = self
|
||||
self.active_address_id = user_address.uuid_str
|
||||
return user_address
|
||||
|
||||
@property
|
||||
def active_address(self):
|
||||
for address in self.addresses:
|
||||
if address.id == self.active_address_id:
|
||||
return address
|
||||
|
||||
|
||||
def _user_by_name_query(dbsession, name):
|
||||
"""query User by case insensitive name."""
|
||||
|
|
|
|||
31
make_post_sell/models/user_address.py
Normal file
31
make_post_sell/models/user_address.py
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import uuid
|
||||
|
||||
from sqlalchemy import Column, BigInteger, UnicodeText
|
||||
|
||||
from .meta import Base, RBase, UUIDType, now_timestamp, foreign_key, get_object_by_id
|
||||
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
|
||||
class UserAddress(RBase, Base):
|
||||
"""This class represents a physical mail address."""
|
||||
|
||||
id = Column(UUIDType, primary_key=True, index=True)
|
||||
user_id = Column(UUIDType, foreign_key("User", "id"), nullable=True)
|
||||
#shop_id = Column(UUIDType, foreign_key("Shop", "id"), nullable=True)
|
||||
data = Column(UnicodeText)
|
||||
|
||||
created_timestamp = Column(BigInteger, nullable=False)
|
||||
|
||||
user = relationship(argument="User", uselist=False, lazy="joined")
|
||||
#shop = relationship(argument="Shop", uselist=False, lazy="joined")
|
||||
|
||||
def __init__(self, data):
|
||||
self.created_timestamp = now_timestamp()
|
||||
self.id = uuid.uuid1()
|
||||
self.data = data
|
||||
|
||||
|
||||
def get_user_address_by_id(dbsession, user_address_id):
|
||||
"""Try to get UserAddress object by id or return None."""
|
||||
return get_object_by_id(dbsession, user_address_id, UserAddress)
|
||||
|
|
@ -28,6 +28,10 @@ def includeme(config):
|
|||
# user routes.
|
||||
config.add_route("user_settings", "/u/settings")
|
||||
config.add_route("user_purchases", "/u/purchases")
|
||||
config.add_route("user_addresses", "/u/addresses")
|
||||
config.add_route("user_address_save", "/u/addresses/save")
|
||||
config.add_route("user_address_delete", "/u/addresses/{address_id}/delete")
|
||||
config.add_route("user_address_activate", "/u/addresses/{address_id}/activate")
|
||||
|
||||
config.add_route("user_shops", "/u/shops")
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
"""address migration
|
||||
|
||||
Revision ID: d38d649cb682
|
||||
Revises: b8be6cd51cbd
|
||||
Create Date: 2022-09-03 12:46:49.118477
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'd38d649cb682'
|
||||
down_revision = 'b8be6cd51cbd'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
from make_post_sell.models.meta import UUIDType
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('mps_invoice', sa.Column('delivery_address', sa.UnicodeText(), nullable=True))
|
||||
op.add_column('mps_user', sa.Column('active_address_id', UUIDType, nullable=True))
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('mps_user', 'active_address_id')
|
||||
op.drop_column('mps_invoice', 'delivery_address')
|
||||
# ### end Alembic commands ###
|
||||
|
|
@ -62,7 +62,7 @@ def main(argv=sys.argv):
|
|||
try:
|
||||
with env['request'].tm:
|
||||
dbsession = env['request'].dbsession
|
||||
setup_models(dbsession, settings)
|
||||
#setup_models(dbsession, settings)
|
||||
except OperationalError:
|
||||
print('''
|
||||
Pyramid is having a problem using your SQL database. The problem
|
||||
|
|
|
|||
53
make_post_sell/templates/user_addresses.j2
Normal file
53
make_post_sell/templates/user_addresses.j2
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
{% extends "base.j2" -%}
|
||||
|
||||
{% block call_to_action %}
|
||||
{% endblock call_to_action %}
|
||||
|
||||
{% block content -%}
|
||||
|
||||
<section class="two-column">
|
||||
<section class="well">
|
||||
|
||||
<h3>Active Address</h3>
|
||||
|
||||
<pre>{{- request.user.active_address.data -}}</pre>
|
||||
|
||||
{% if request.user.addresses.count() >= 2 %}
|
||||
|
||||
<h3>Saved Addresses</h3>
|
||||
|
||||
{% for address in request.user.addresses %}
|
||||
{% if address.id != request.user.active_address_id %}
|
||||
<pre>{{- address.data -}}</pre>
|
||||
<a href="/u/addresses/{{ address.id }}/delete">Delete</a>
|
||||
<a href="/u/addresses/{{ address.id }}/activate">Activate</a>
|
||||
<br/>
|
||||
<br/>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
</section>
|
||||
|
||||
<section class="well">
|
||||
|
||||
<form method="post" action="/u/addresses/save">
|
||||
|
||||
<label for="new_address_input">New Address</label>
|
||||
|
||||
<textarea
|
||||
name = "new_address"
|
||||
id = "new_address_input"
|
||||
class = "mps-billing-address"
|
||||
placeholder = "New Address"
|
||||
required /></textarea>
|
||||
|
||||
<input type="submit" name="submit" class="mps-submit" value="Save Address" />
|
||||
|
||||
<br/>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
{%- endblock -%}
|
||||
|
|
@ -35,9 +35,6 @@
|
|||
placeholder = "Full Name"
|
||||
required />
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<!-- not implemented.
|
||||
<fieldset>
|
||||
|
||||
|
|
@ -52,20 +49,16 @@
|
|||
</fieldset>
|
||||
-->
|
||||
|
||||
<br />
|
||||
|
||||
<input type="submit" name="submit" class="mps-submit" value="Save Settings" />
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
</form>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<br />
|
||||
|
||||
<section class="one-column">
|
||||
<section class="well">
|
||||
|
||||
|
|
@ -75,8 +68,10 @@
|
|||
|
||||
{% if request.shop and request.shop.is_ready %}
|
||||
<a href="/u/purchases" class="product-download-button mps-button">My Purchases</a>
|
||||
<br/><br/>
|
||||
<br/>
|
||||
<a href="/billing" class="product-edit-button mps-button">Payment Preferences</a>
|
||||
<br/>
|
||||
<a href="/u/addresses" class="product-edit-button mps-button">Shipping Addresses</a>
|
||||
{% endif %}
|
||||
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
from pyramid.view import view_config
|
||||
|
||||
from pyramid.httpexceptions import HTTPFound
|
||||
|
||||
from . import (
|
||||
user_required,
|
||||
shop_is_ready_required,
|
||||
|
|
@ -62,3 +64,58 @@ def user_settings(request):
|
|||
"full_name": full_name,
|
||||
}
|
||||
|
||||
|
||||
@view_config(route_name="user_addresses", renderer="user_addresses.j2")
|
||||
@user_required()
|
||||
def user_addresses(request):
|
||||
return {}
|
||||
|
||||
|
||||
@view_config(route_name="user_address_save")
|
||||
@user_required()
|
||||
def user_address_save(request):
|
||||
new_address = request.params.get("new_address", None)
|
||||
if new_address:
|
||||
user_address = request.user.new_address(new_address)
|
||||
request.dbsession.add(user_address)
|
||||
request.dbsession.flush()
|
||||
msg = ("You saved that address.", "success")
|
||||
request.session.flash(msg)
|
||||
|
||||
return HTTPFound("/u/addresses")
|
||||
|
||||
|
||||
@view_config(route_name="user_address_delete")
|
||||
@user_required()
|
||||
def user_address_delete(request):
|
||||
address_id = request.matchdict.get("address_id")
|
||||
|
||||
if request.user.active_address.uuid_str == address_id:
|
||||
msg = ("You may not delete the active address.", "error")
|
||||
request.session.flash(msg)
|
||||
|
||||
else:
|
||||
for address in request.user.addresses:
|
||||
if address.uuid_str == address_id:
|
||||
request.dbsession.delete(address)
|
||||
request.dbsession.flush()
|
||||
msg = ("You deleted that address.", "success")
|
||||
request.session.flash(msg)
|
||||
|
||||
return HTTPFound("/u/addresses")
|
||||
|
||||
|
||||
@view_config(route_name="user_address_activate")
|
||||
@user_required()
|
||||
def user_address_activate(request):
|
||||
address_id = request.matchdict.get("address_id")
|
||||
|
||||
for address in request.user.addresses:
|
||||
if address.uuid_str == address_id:
|
||||
request.user.active_address_id = address_id
|
||||
request.dbsession.add(request.user)
|
||||
request.dbsession.flush()
|
||||
msg = ("You activated that address.", "success")
|
||||
request.session.flash(msg)
|
||||
|
||||
return HTTPFound("/u/addresses")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue