Template
Add shop, POS sync, event ticketing, and shipping catalog apps (#6)
## Summary - Closes #5 - Optional `shop`, `pos_sync`, `events`, and `shipping` apps gated by `FEATURE_*` flags - Deps match the catalog: shop/events need email + Stripe; POS/shipping need shop - Portal inventory, POS webhooks, capacity tickets, EasyPost/Pirate Ship shipping ## Test plan - [ ] `manage.py test` (152 passed locally) - [ ] Shop cart + paid order decrements stocked inventory - [ ] POS inbound webhook decrements SKU; paid order queues outbound reserve - [ ] Event capacity blocks overbook; paid order emails ticket codes - [ ] Shipping stub label + Pirate Ship CSV of unshipped paid orders - [ ] `validate-env.sh` rejects shop without email/payments, POS/shipping without shop Reviewed-on: #6
This commit was merged in pull request #6.
This commit is contained in:
1 parent
97b8607bf2
commit
9cdce7a897
82 files changed
+3908
-1
No files matched your search
@@ -0,0 +1,25 @@
|
||||
{% extends "portal_base.html" %}
|
||||
{% block title %}{{ shipment.order.number }} · Shipping{% endblock %}
|
||||
{% block topbar_title %}{{ shipment.order.number }}{% endblock %}
|
||||
{% block portal_content %}
|
||||
<p>{{ shipment.order.email }} · {{ shipment.get_status_display }}</p>
|
||||
{% if shipment.tracking_number %}<p>Tracking: {{ shipment.tracking_number }}</p>{% endif %}
|
||||
{% if shipment.label_url %}<p><a href="{{ shipment.label_url }}">Download label</a></p>{% endif %}
|
||||
{% if shipment.status != 'labeled' %}
|
||||
<form method="post" action="{% url 'shipping:shipment_buy' shipment.pk %}">
|
||||
{% csrf_token %}
|
||||
{% for rate in shipment.rates %}
|
||||
<label>
|
||||
<input type="radio" name="rate_id" value="{{ rate.id }}" {% if forloop.first %}checked{% endif %}>
|
||||
{{ rate.carrier }} {{ rate.service }} — {{ rate.rate }} {{ rate.currency }}
|
||||
</label><br>
|
||||
{% empty %}
|
||||
<p>No rates yet.</p>
|
||||
{% endfor %}
|
||||
{% if shipment.rates %}
|
||||
<button class="btn btn-primary" type="submit">Buy label</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% endif %}
|
||||
<p><a href="{% url 'shipping:shipment_list' %}">← All shipments</a></p>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,43 @@
|
||||
{% extends "portal_base.html" %}
|
||||
{% block title %}Shipping · Portal{% endblock %}
|
||||
{% block topbar_title %}Shipping{% endblock %}
|
||||
{% block portal_content %}
|
||||
<p><a class="btn btn-ghost" href="{% url 'shipping:pirate_ship_export' %}">Pirate Ship CSV</a></p>
|
||||
<h2>Unshipped paid orders</h2>
|
||||
<table class="table">
|
||||
<thead><tr><th>Order</th><th>Email</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
{% for order in unshipped %}
|
||||
<tr>
|
||||
<td>{{ order.number }}</td>
|
||||
<td>{{ order.email }}</td>
|
||||
<td>
|
||||
<form method="post" action="{% url 'shipping:shipment_create' %}">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="order" value="{{ order.pk }}">
|
||||
<input name="weight_oz" type="number" min="1" value="16" style="width:5rem"> oz
|
||||
<button class="btn btn-primary" type="submit">Quote rates</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan="3" class="empty-state">No paid orders waiting to ship.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<h2>Shipments</h2>
|
||||
<table class="table">
|
||||
<thead><tr><th>Order</th><th>Status</th><th>Tracking</th></tr></thead>
|
||||
<tbody>
|
||||
{% for shipment in shipments %}
|
||||
<tr>
|
||||
<td><a href="{% url 'shipping:shipment_detail' shipment.pk %}">{{ shipment.order.number }}</a></td>
|
||||
<td>{{ shipment.get_status_display }}</td>
|
||||
<td>{{ shipment.tracking_number|default:"—" }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan="3" class="empty-state">No shipments yet.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock %}
|
||||
Reference in new issue
Block a user