user purchase page upgraded with invoice links
This commit is contained in:
parent
d9ef51031d
commit
d4bada52a2
11 changed files with 132 additions and 150 deletions
|
|
@ -108,6 +108,8 @@ class User(RBase, Base):
|
|||
argument="UserAddress", lazy="dynamic", back_populates="user"
|
||||
)
|
||||
|
||||
invoices = relationship(argument="Invoice", lazy="dynamic", back_populates="user")
|
||||
|
||||
def __init__(self, email):
|
||||
self.name = unicode(f"user-{generate_password(size=8)}")
|
||||
self.created_timestamp = now_timestamp()
|
||||
|
|
|
|||
|
|
@ -61,7 +61,8 @@ def includeme(config):
|
|||
|
||||
config.add_route("cart_handling_option", "/cart/{cart_id}/handling-option")
|
||||
|
||||
config.add_route("view_invoice", "/invoices/{invoice_id}")
|
||||
config.add_route("view_invoice", "/invoice/{invoice_id}")
|
||||
config.add_route("view_invoice2", "/i/{invoice_id}")
|
||||
|
||||
# coupon routes.
|
||||
config.add_route("coupon_new1", "/coupon/new")
|
||||
|
|
@ -89,7 +90,6 @@ def includeme(config):
|
|||
config.add_route("shop_products", "/s/{shop_id}/products")
|
||||
|
||||
config.add_route("shop_sales", "/s/{shop_id}/sales")
|
||||
config.add_route("shop_sales_details", "/s/{shop_id}/sales/{invoice_id}")
|
||||
|
||||
config.add_route("shop_settings", "/s/{shop_id}/settings")
|
||||
|
||||
|
|
|
|||
|
|
@ -19,8 +19,24 @@ h1 {
|
|||
margin-bottom: .4em;
|
||||
}
|
||||
|
||||
tr, th {
|
||||
text-align: left;
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table tr:nth-child(even) {
|
||||
/* Light gray background for even rows */
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
table tr:nth-child(odd) {
|
||||
/* White background for odd rows (optional, for contrast) */
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
tr, th, td {
|
||||
padding: 8px;
|
||||
border: 0px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
label {
|
||||
|
|
|
|||
|
|
@ -1,40 +1,35 @@
|
|||
{% extends "base.j2" -%}
|
||||
{% extends "base.j2" %}
|
||||
|
||||
{% block content -%}
|
||||
|
||||
<h1>Invoice Details</h1>
|
||||
|
||||
<section class="invoice-details">
|
||||
<h2>Invoice ID: {{ invoice.id }}</h2>
|
||||
<p><strong>User:</strong> {{ invoice.user.name }}</p>
|
||||
<p><strong>Shop:</strong> {{ invoice.shop.name }}</p>
|
||||
<p><strong>Created:</strong> {{ invoice.human_created_timestamp }}</p>
|
||||
{% block content %}
|
||||
<h1>Invoice Identifier<br>{{ invoice.id }}</h1>
|
||||
<p><strong>Customer Name:</strong> {{ invoice.user.name }}</p>
|
||||
<p><strong>Date:</strong> {{ invoice.human_created_timestamp }}</p>
|
||||
<p><strong>Total:</strong> ${{ '%0.2f' % invoice.total }}</p>
|
||||
<p><strong>Shipping/Pickup:</strong> {{ invoice.handling_option }}</p>
|
||||
<p><strong>Handling Cost:</strong> ${{ '%0.2f' % ((invoice.handling_cost_in_cents or 0) / 100) }}</p>
|
||||
{% if invoice.delivery_address %}
|
||||
<p><strong>Delivery Address:</strong> {{ invoice.delivery_address }}</p>
|
||||
<p><strong>Handling Option:</strong> {{ invoice.handling_option }}</p>
|
||||
<p><strong>Handling Cost:</strong> ${{ '%0.2f' % (invoice.handling_cost_in_cents / 100) }}</p>
|
||||
<p><strong>Total:</strong> ${{ '%0.2f' % invoice.total }}</p>
|
||||
|
||||
<h3>Line Items</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Product</th>
|
||||
<th>Quantity</th>
|
||||
<th>Price</th>
|
||||
<th>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in line_items %}
|
||||
<tr>
|
||||
<td>{{ item.product.title }}</td>
|
||||
<td>{{ item.quantity }}</td>
|
||||
<td>${{ '%0.2f' % (item.price.price_in_cents / 100) }}</td>
|
||||
<td>${{ '%0.2f' % ((item.price.price_in_cents * item.quantity) / 100) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
<h2>Line Items</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Product</th>
|
||||
<th>Quantity</th>
|
||||
<th>Price</th>
|
||||
<th>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in invoice.line_items %}
|
||||
<tr>
|
||||
<td><a href="/p/{{ item.product.id }}">{{ item.product.title }}</a></td>
|
||||
<td>{{ item.quantity }}</td>
|
||||
<td>${{ '%0.2f' % (item.price.price_in_cents / 100) }}</td>
|
||||
<td>${{ '%0.2f' % ((item.price.price_in_cents * item.quantity) / 100) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
{% extends "base.j2" -%}
|
||||
|
||||
{% block content -%}
|
||||
|
||||
{% if the_title %}
|
||||
<center>
|
||||
<h1>
|
||||
{{ the_title }}
|
||||
</h1>
|
||||
</center>
|
||||
{% endif %}
|
||||
|
||||
<section class="serp">
|
||||
{% if products %}
|
||||
{% for product in products %}
|
||||
|
||||
<div class="serp-item">
|
||||
{% if "thumbnail1" in product.extensions %}
|
||||
<a href="{{ product.absolute_url(request) }}" rel="nofollow">
|
||||
<img src="{{ request.app["bucket.secure_uploads.get_endpoint"] }}/{{ product.s3_path }}/thumbnail1?ts={{ product.updated_timestamp }}" class="serp-thumbnail" />
|
||||
</a>
|
||||
{% endif %}
|
||||
<b><a href="{{ product.absolute_url(request) }}" class="shop_theme_link_color">{{ product.title }}</a></b>
|
||||
{% if product.is_sellable %}
|
||||
<br />
|
||||
<a href="{{ product.absolute_url(request) }}" rel="nofollow">${{ '{:,.2f}'.format(product.price) }}</a>
|
||||
{% endif %}
|
||||
<br>
|
||||
<a href="{{ product.absolute_url(request) }}" rel="nofollow">{{ product.shop.name }}</a>
|
||||
</div>
|
||||
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
{%- endblock -%}
|
||||
|
|
@ -3,13 +3,13 @@
|
|||
{% block content %}
|
||||
<h1>Sales for {{ shop.name }}</h1>
|
||||
|
||||
<table style="width:100%;">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Customer Name</th>
|
||||
<th>Date</th>
|
||||
<th>Total</th>
|
||||
<th>Details</th>
|
||||
<th>Invoice</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
<td>{{ invoice.user.name }}</td>
|
||||
<td>{{ invoice.human_created_timestamp }}</td>
|
||||
<td>${{ '%0.2f' % invoice.total }}</td>
|
||||
<td><a href="{{ request.route_url('shop_sales_details', shop_id=shop.id, invoice_id=invoice.id) }}">Invoice Details</a></td>
|
||||
<td><a href="/i/{{ invoice.id }}">Invoice</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
{% extends "base.j2" %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Invoice Identifier<br>{{ invoice.id }}</h1>
|
||||
<p><strong>Customer Name:</strong> {{ invoice.user.name }}</p>
|
||||
<p><strong>Date:</strong> {{ invoice.human_created_timestamp }}</p>
|
||||
<p><strong>Total:</strong> ${{ '%0.2f' % invoice.total }}</p>
|
||||
<p><strong>Shipping/Pickup:</strong> {{ invoice.handling_option }}</p>
|
||||
<p><strong>Handling Cost:</strong> ${{ '%0.2f' % ((invoice.handling_cost_in_cents or 0) / 100) }}</p>
|
||||
{% if invoice.delivery_address %}
|
||||
<p><strong>Delivery Address:</strong> {{ invoice.delivery_address }}</p>
|
||||
{% endif %}
|
||||
|
||||
<h2>Line Items</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Product</th>
|
||||
<th>Quantity</th>
|
||||
<th>Price</th>
|
||||
<th>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in invoice.line_items %}
|
||||
<tr">
|
||||
<td><a href="/p/{{ item.product.id }}">{{ item.product.title }}</a></td>
|
||||
<td>{{ item.quantity }}</td>
|
||||
<td>${{ '%0.2f' % (item.price.price_in_cents / 100) }}</td>
|
||||
<td>${{ '%0.2f' % ((item.price.price_in_cents * item.quantity) / 100) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock %}
|
||||
55
make_post_sell/templates/user_purchases.j2
Normal file
55
make_post_sell/templates/user_purchases.j2
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
{% extends "base.j2" %}
|
||||
|
||||
{% block content %}
|
||||
<h1>My Purchases</h1>
|
||||
|
||||
{% if invoices %}
|
||||
<h2>My Invoices</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Total</th>
|
||||
<th>Invoice</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for invoice in invoices %}
|
||||
<tr>
|
||||
<td>{{ invoice.human_created_timestamp }}</td>
|
||||
<td>${{ '%0.2f' % invoice.total }}</td>
|
||||
<td><a href="/i/{{ invoice.id }}">Invoice</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p>You have no purchases yet.</p>
|
||||
{% endif %}
|
||||
|
||||
<br>
|
||||
|
||||
<h2>My Products</h2>
|
||||
<section class="serp">
|
||||
{% if products %}
|
||||
{% for product in products %}
|
||||
<div class="serp-item">
|
||||
{% if "thumbnail1" in product.extensions %}
|
||||
<a href="{{ product.absolute_url(request) }}" rel="nofollow">
|
||||
<img src="{{ request.app["bucket.secure_uploads.get_endpoint"] }}/{{ product.s3_path }}/thumbnail1?ts={{ product.updated_timestamp }}" class="serp-thumbnail" />
|
||||
</a>
|
||||
{% endif %}
|
||||
<b><a href="{{ product.absolute_url(request) }}" class="shop_theme_link_color">{{ product.title }}</a></b>
|
||||
{% if product.is_sellable %}
|
||||
<br />
|
||||
<a href="{{ product.absolute_url(request) }}" rel="nofollow">${{ '{:,.2f}'.format(product.price) }}</a>
|
||||
{% endif %}
|
||||
<br>
|
||||
<a href="{{ product.absolute_url(request) }}" rel="nofollow">{{ product.shop.name }}</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p>You have no products yet.</p>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
|
@ -2,13 +2,11 @@ from pyramid.view import view_config
|
|||
from pyramid.httpexceptions import HTTPFound
|
||||
from ..models.invoice import get_invoice_by_id
|
||||
|
||||
from . import (
|
||||
user_required,
|
||||
get_referer_or_home,
|
||||
)
|
||||
from . import user_required, get_referer_or_home
|
||||
|
||||
|
||||
@view_config(route_name="view_invoice", renderer="invoice.j2")
|
||||
@view_config(route_name="view_invoice2", renderer="invoice.j2")
|
||||
@user_required()
|
||||
def view_invoice(request):
|
||||
invoice_id = request.matchdict.get("invoice_id")
|
||||
|
|
@ -18,7 +16,7 @@ def view_invoice(request):
|
|||
request.session.flash(("Invoice not found.", "error"))
|
||||
return HTTPFound(get_referer_or_home(request))
|
||||
|
||||
# Check if the user is the owner of the invoice or a shop editor
|
||||
# check if the user is the owner of the invoice or a shop editor.
|
||||
if request.user != invoice.user and not request.user.can_edit_shop(invoice.shop):
|
||||
request.session.flash(
|
||||
("You do not have permission to view this invoice.", "error")
|
||||
|
|
@ -27,5 +25,4 @@ def view_invoice(request):
|
|||
|
||||
return {
|
||||
"invoice": invoice,
|
||||
"line_items": invoice.line_items.all(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,4 @@
|
|||
from pyramid.view import view_config
|
||||
from pyramid.httpexceptions import HTTPForbidden
|
||||
from ..models.invoice import get_invoice_by_id
|
||||
from ..models.user import get_user_by_id
|
||||
from ..models.shop import get_shop_by_id
|
||||
from ..models.invoice import Invoice
|
||||
from . import shop_editor_required
|
||||
from sqlalchemy import desc
|
||||
|
|
@ -14,17 +10,3 @@ def shop_sales(request):
|
|||
shop = request.shop
|
||||
invoices = shop.invoices.order_by(desc(Invoice.created_timestamp)).all()
|
||||
return {"invoices": invoices, "shop": shop}
|
||||
|
||||
|
||||
@view_config(route_name="shop_sales_details", renderer="sales_details.j2")
|
||||
@shop_editor_required()
|
||||
def shop_sales_details(request):
|
||||
shop_id = request.matchdict["shop_id"]
|
||||
invoice_id = request.matchdict["invoice_id"]
|
||||
shop = get_shop_by_id(request.dbsession, shop_id)
|
||||
invoice = get_invoice_by_id(request.dbsession, invoice_id)
|
||||
|
||||
if not invoice or invoice.shop != shop:
|
||||
raise HTTPForbidden("Access denied.")
|
||||
|
||||
return {"invoice": invoice}
|
||||
|
|
|
|||
|
|
@ -2,15 +2,11 @@ from pyramid.view import view_config
|
|||
|
||||
from pyramid.httpexceptions import HTTPFound
|
||||
|
||||
from . import (
|
||||
user_required,
|
||||
shop_is_ready_required,
|
||||
)
|
||||
from . import user_required, shop_is_ready_required
|
||||
|
||||
from ..models.user import (
|
||||
is_user_name_available,
|
||||
is_user_name_valid,
|
||||
)
|
||||
from ..models.user import is_user_name_available, is_user_name_valid
|
||||
|
||||
from ..models.invoice import Invoice
|
||||
|
||||
|
||||
@view_config(route_name="user_shops", renderer="shops.j2")
|
||||
|
|
@ -21,14 +17,24 @@ def user_shops(request):
|
|||
}
|
||||
|
||||
|
||||
@view_config(route_name="user_purchases", renderer="products.j2")
|
||||
@view_config(route_name="user_purchases", renderer="user_purchases.j2")
|
||||
@user_required()
|
||||
@shop_is_ready_required()
|
||||
def user_products(request):
|
||||
# only display products for the request's shop.
|
||||
products = [p for p in request.user.products if p.shop == request.shop]
|
||||
def user_purchases(request):
|
||||
user = request.user
|
||||
shop = request.shop # Get the current shop from the request
|
||||
|
||||
products = [p for p in request.user.products if p.shop == shop]
|
||||
|
||||
invoices = (
|
||||
user.invoices.filter(Invoice.shop == shop)
|
||||
.order_by(Invoice.created_timestamp.desc())
|
||||
.all()
|
||||
)
|
||||
|
||||
return {
|
||||
"products": products,
|
||||
"invoices": invoices,
|
||||
"the_title": "My Purchases",
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue