fix: shipping cost; update UI on new order

This commit is contained in:
Vlad Stan 2023-04-03 11:57:55 +03:00
parent f80a75e90c
commit 547c477ce6
11 changed files with 57 additions and 23 deletions

View file

@ -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,

View file

@ -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

View file

@ -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,

View file

@ -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):

View file

@ -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)

View file

@ -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

View file

@ -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)
},

View file

@ -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

View file

@ -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)

View file

@ -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) {

View file

@ -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"