fix: shipping cost; update UI on new order
This commit is contained in:
parent
f80a75e90c
commit
547c477ce6
11 changed files with 57 additions and 23 deletions
6
crud.py
6
crud.py
|
|
@ -393,12 +393,13 @@ async def create_order(merchant_id: str, o: Order) -> Order:
|
|||
address,
|
||||
contact_data,
|
||||
extra_data,
|
||||
order_items,
|
||||
order_items,
|
||||
shipping_id,
|
||||
stall_id,
|
||||
invoice_id,
|
||||
total
|
||||
)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(event_id) DO NOTHING
|
||||
""",
|
||||
(
|
||||
|
|
@ -412,6 +413,7 @@ async def create_order(merchant_id: str, o: Order) -> Order:
|
|||
json.dumps(o.contact.dict() if o.contact else {}),
|
||||
json.dumps(o.extra.dict()),
|
||||
json.dumps([i.dict() for i in o.items]),
|
||||
o.shipping_id,
|
||||
o.stall_id,
|
||||
o.invoice_id,
|
||||
o.total,
|
||||
|
|
|
|||
|
|
@ -77,8 +77,6 @@ def copy_x(output, x32, y32, data):
|
|||
def order_from_json(s: str) -> Tuple[Optional[Any], Optional[str]]:
|
||||
try:
|
||||
order = json.loads(s)
|
||||
return (
|
||||
(order, s) if (type(order) is dict) and "items" in order else (None, s)
|
||||
)
|
||||
return (order, s) if (type(order) is dict) and "items" in order else (None, s)
|
||||
except ValueError:
|
||||
return None, s
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ async def m001_initial(db):
|
|||
order_items TEXT NOT NULL,
|
||||
address TEXT,
|
||||
total REAL NOT NULL,
|
||||
shipping_id TEXT NOT NULL,
|
||||
stall_id TEXT NOT NULL,
|
||||
invoice_id TEXT NOT NULL,
|
||||
paid BOOLEAN NOT NULL DEFAULT false,
|
||||
|
|
|
|||
18
models.py
18
models.py
|
|
@ -2,7 +2,7 @@ import json
|
|||
import time
|
||||
from abc import abstractmethod
|
||||
from sqlite3 import Row
|
||||
from typing import List, Optional
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
|
@ -313,6 +313,8 @@ class OrderExtra(BaseModel):
|
|||
products: List[ProductOverview]
|
||||
currency: str
|
||||
btc_price: str
|
||||
shipping_cost: float = 0
|
||||
shipping_cost_sat: float = 0
|
||||
|
||||
@classmethod
|
||||
async def from_products(cls, products: List[Product]):
|
||||
|
|
@ -329,6 +331,7 @@ class PartialOrder(BaseModel):
|
|||
event_created_at: Optional[int]
|
||||
public_key: str
|
||||
merchant_public_key: str
|
||||
shipping_id: str
|
||||
items: List[OrderItem]
|
||||
contact: Optional[OrderContact]
|
||||
address: Optional[str]
|
||||
|
|
@ -356,20 +359,25 @@ class PartialOrder(BaseModel):
|
|||
f"Order ({self.id}) has products from different stalls"
|
||||
)
|
||||
|
||||
async def total_sats(self, products: List[Product]) -> float:
|
||||
async def costs_in_sats(
|
||||
self, products: List[Product], shipping_cost: float
|
||||
) -> Tuple[float, float]:
|
||||
product_prices = {}
|
||||
for p in products:
|
||||
product_prices[p.id] = p
|
||||
|
||||
amount: float = 0 # todo
|
||||
product_cost: float = 0 # todo
|
||||
for item in self.items:
|
||||
price = product_prices[item.product_id].price
|
||||
currency = product_prices[item.product_id].config.currency or "sat"
|
||||
if currency != "sat":
|
||||
price = await fiat_amount_as_satoshis(price, currency)
|
||||
amount += item.quantity * price
|
||||
product_cost += item.quantity * price
|
||||
|
||||
return amount
|
||||
if currency != "sat":
|
||||
shipping_cost = await fiat_amount_as_satoshis(shipping_cost, currency)
|
||||
|
||||
return product_cost, shipping_cost
|
||||
|
||||
|
||||
class Order(PartialOrder):
|
||||
|
|
|
|||
23
services.py
23
services.py
|
|
@ -20,6 +20,7 @@ from .crud import (
|
|||
get_products_by_ids,
|
||||
get_stalls,
|
||||
get_wallet_for_product,
|
||||
get_zone,
|
||||
increment_customer_unread_messages,
|
||||
update_customer_profile,
|
||||
update_order_paid_status,
|
||||
|
|
@ -60,8 +61,12 @@ async def create_new_order(
|
|||
merchant.id, [p.product_id for p in data.items]
|
||||
)
|
||||
data.validate_order_items(products)
|
||||
shipping_zone = await get_zone(merchant.id, data.shipping_id)
|
||||
assert shipping_zone, f"Shipping zone not found for order '{data.id}'"
|
||||
|
||||
total_amount = await data.total_sats(products)
|
||||
product_cost_sat, shipping_cost_sat = await data.costs_in_sats(
|
||||
products, shipping_zone.cost
|
||||
)
|
||||
|
||||
wallet_id = await get_wallet_for_product(data.items[0].product_id)
|
||||
assert wallet_id, "Missing wallet for order `{data.id}`"
|
||||
|
|
@ -75,7 +80,7 @@ async def create_new_order(
|
|||
|
||||
payment_hash, invoice = await create_invoice(
|
||||
wallet_id=wallet_id,
|
||||
amount=round(total_amount),
|
||||
amount=round(product_cost_sat + shipping_cost_sat),
|
||||
memo=f"Order '{data.id}' for pubkey '{data.public_key}'",
|
||||
extra={
|
||||
"tag": "nostrmarket",
|
||||
|
|
@ -84,12 +89,16 @@ async def create_new_order(
|
|||
},
|
||||
)
|
||||
|
||||
extra = await OrderExtra.from_products(products)
|
||||
extra.shipping_cost_sat = shipping_cost_sat
|
||||
extra.shipping_cost = shipping_zone.cost
|
||||
|
||||
order = Order(
|
||||
**data.dict(),
|
||||
stall_id=products[0].stall_id,
|
||||
invoice_id=payment_hash,
|
||||
total=total_amount,
|
||||
extra=await OrderExtra.from_products(products),
|
||||
total=product_cost_sat + shipping_cost_sat,
|
||||
extra=extra,
|
||||
)
|
||||
await create_order(merchant.id, order)
|
||||
await websocketUpdater(
|
||||
|
|
@ -99,6 +108,7 @@ async def create_new_order(
|
|||
"type": "new-order",
|
||||
"stallId": products[0].stall_id,
|
||||
"customerPubkey": data.public_key,
|
||||
"orderId": order.id,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
|
@ -312,6 +322,11 @@ async def _handle_dirrect_message(
|
|||
order["event_created_at"] = event_created_at
|
||||
return await _handle_new_order(PartialOrder(**order))
|
||||
|
||||
await websocketUpdater(
|
||||
merchant_id,
|
||||
json.dumps({"type": "new-direct-message", "customerPubkey": from_pubkey}),
|
||||
)
|
||||
|
||||
return None
|
||||
except Exception as ex:
|
||||
logger.warning(ex)
|
||||
|
|
|
|||
|
|
@ -375,8 +375,9 @@ async function customerStall(path) {
|
|||
this.qrCodeDialog.data.message = json.message
|
||||
return cb()
|
||||
}
|
||||
let payment_request = json.payment_options.find(o => o.type == 'ln')
|
||||
.link
|
||||
let payment_request = json.payment_options.find(
|
||||
o => o.type == 'ln'
|
||||
).link
|
||||
if (!payment_request) return
|
||||
this.loading = false
|
||||
this.qrCodeDialog.data.payment_request = payment_request
|
||||
|
|
|
|||
|
|
@ -83,6 +83,13 @@ async function directMessages(path) {
|
|||
LNbits.utils.notifyApiError(error)
|
||||
}
|
||||
},
|
||||
handleNewMessage: async function (data) {
|
||||
if (data.customerPubkey === this.activePublicKey) {
|
||||
await this.getDirectMessages(this.activePublicKey)
|
||||
} else {
|
||||
await this.getCustomers()
|
||||
}
|
||||
},
|
||||
showClientOrders: function () {
|
||||
this.$emit('customer-selected', this.activePublicKey)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -141,8 +141,11 @@
|
|||
</div>
|
||||
<div class="col-1"></div>
|
||||
</div>
|
||||
<div v-if="props.row.extra.currency !== 'sat'" class="row items-center no-wrap q-mb-md q-mt-md">
|
||||
<div class="col-3 q-pr-lg">Exchange Rate:</div>
|
||||
<div
|
||||
v-if="props.row.extra.currency !== 'sat'"
|
||||
class="row items-center no-wrap q-mb-md q-mt-md"
|
||||
>
|
||||
<div class="col-3 q-pr-lg">Exchange Rate (1 BTC):</div>
|
||||
<div class="col-6 col-sm-8 q-pr-lg">
|
||||
<q-input
|
||||
filled
|
||||
|
|
|
|||
|
|
@ -64,13 +64,13 @@ async function orderList(path) {
|
|||
{
|
||||
name: 'total',
|
||||
align: 'left',
|
||||
label: 'Total',
|
||||
label: 'Total Sats',
|
||||
field: 'total'
|
||||
},
|
||||
{
|
||||
name: 'fiat',
|
||||
align: 'left',
|
||||
label: 'Fiat',
|
||||
label: 'Total Fiat',
|
||||
field: 'fiat'
|
||||
},
|
||||
{
|
||||
|
|
@ -136,7 +136,6 @@ async function orderList(path) {
|
|||
},
|
||||
orderTotal: function (order) {
|
||||
return order.items.reduce((t, item) => {
|
||||
console.log('### t, o', t, item)
|
||||
product = order.extra.products.find(p => p.id === item.product_id)
|
||||
return t + item.quantity * product.price
|
||||
}, 0)
|
||||
|
|
|
|||
|
|
@ -113,9 +113,7 @@ const merchant = async () => {
|
|||
const port = location.port ? `:${location.port}` : ''
|
||||
const wsUrl = `${scheme}://${document.domain}${port}/api/v1/ws/${this.merchant.id}`
|
||||
const wsConnection = new WebSocket(wsUrl)
|
||||
console.log('### waiting for events')
|
||||
wsConnection.onmessage = async e => {
|
||||
console.log('### e', e)
|
||||
const data = JSON.parse(e.data)
|
||||
if (data.type === 'new-order') {
|
||||
this.$q.notify({
|
||||
|
|
@ -126,6 +124,7 @@ const merchant = async () => {
|
|||
await this.$refs.orderListRef.addOrder(data)
|
||||
} else if (data.type === 'new-customer') {
|
||||
} else if (data.type === 'new-direct-message') {
|
||||
await this.$refs.directMessagesRef.handleNewMessage(data)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -148,6 +148,7 @@
|
|||
</div>
|
||||
<div v-if="merchant && merchant.id" class="col-12">
|
||||
<direct-messages
|
||||
ref="directMessagesRef"
|
||||
:inkey="g.user.wallets[0].inkey"
|
||||
:adminkey="g.user.wallets[0].adminkey"
|
||||
:active-chat-customer="activeChatCustomer"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue