add currency to stalls show products in fiat

This commit is contained in:
Tiago Vasconcelos 2022-12-27 11:12:29 +00:00
parent b1473fc1a4
commit 11b015e516
8 changed files with 141 additions and 31 deletions

View File

@ -148,16 +148,18 @@ async def create_shop_stall(data: createStalls) -> Stalls:
id,
wallet,
name,
currency,
publickey,
relays,
shippingzones
)
VALUES (?, ?, ?, ?, ?, ?)
VALUES (?, ?, ?, ?, ?, ?, ?)
""",
(
stall_id,
data.wallet,
data.name,
data.currency,
data.publickey,
data.relays,
data.shippingzones,
@ -447,17 +449,31 @@ async def get_shop_chat_by_merchant(ids: List[str]) -> List[ChatMessage]:
async def get_shop_settings(user) -> Optional[ShopSettings]:
row = await db.fetchone("SELECT * FROM shop.settings WHERE 'user = ?", (user,))
row = await db.fetchone("""SELECT * FROM shop.settings WHERE "user" = ?""", (user,))
return ShopSettings(**row) if row else None
async def set_shop_settings(user: str, data) -> Optional[ShopSettings]:
async def create_shop_settings(user: str, data):
await db.execute(
f"""
"""
INSERT INTO shop.settings ("user", currency, fiat_base_multiplier)
VALUES (?, ?, ?)
""",
(
user,
data.currency,
data.fiat_base_multiplier,
),
)
async def set_shop_settings(user: str, data):
await db.execute(
"""
UPDATE shop.settings
SET currency = ?, fiat_base_multiplier = ?
WHERE 'user' = ?;
WHERE "user" = ?;
""",
(
data.currency,

View File

@ -21,6 +21,7 @@ async def m001_initial(db):
id TEXT PRIMARY KEY,
wallet TEXT NOT NULL,
name TEXT NOT NULL,
currency TEXT,
publickey TEXT,
relays TEXT,
shippingzones TEXT NOT NULL,

View File

@ -19,6 +19,7 @@ class Stalls(BaseModel):
id: str
wallet: str
name: str
currency: str
publickey: Optional[str]
relays: Optional[str]
shippingzones: str
@ -27,6 +28,7 @@ class Stalls(BaseModel):
class createStalls(BaseModel):
wallet: str = Query(...)
name: str = Query(...)
currency: str = Query("sat")
publickey: str = Query(None)
relays: str = Query(None)
shippingzones: str = Query(...)
@ -38,7 +40,7 @@ class createProduct(BaseModel):
categories: str = Query(None)
description: str = Query(None)
image: str = Query(None)
price: int = Query(0, ge=0)
price: float = Query(0, ge=0)
quantity: int = Query(0, ge=0)
@ -49,19 +51,19 @@ class Products(BaseModel):
categories: Optional[str]
description: Optional[str]
image: Optional[str]
price: int
price: float
quantity: int
class createZones(BaseModel):
cost: int = Query(0, ge=0)
cost: float = Query(0, ge=0)
countries: str = Query(...)
class Zones(BaseModel):
id: str
user: str
cost: int
cost: float
countries: str

View File

@ -84,7 +84,11 @@
dense
v-model.number="productDialog.data.price"
type="number"
label="Price"
:label="'Price (' + currencies.unit + ') *'"
:mask="currencies.unit != 'sat' ? '#.##' : '#'"
fill-mask="0"
reverse-fill-mask
:step="currencies.unit != 'sat' ? '0.01' : '1'"
></q-input>
<q-input
filled
@ -142,9 +146,13 @@
<q-input
filled
dense
:label="'Amount (' + currencies.unit + ') *'"
:mask="currencies.unit != 'sat' ? '#.##' : '#'"
fill-mask="0"
reverse-fill-mask
:step="currencies.unit != 'sat' ? '0.01' : '1'"
type="number"
v-model.trim="zoneDialog.data.cost"
label="Cost (sats)"
></q-input>
<div class="row q-mt-lg">
<q-btn

View File

@ -188,6 +188,9 @@
}
const mapProducts = obj => {
obj._data = _.clone(obj)
if ('{{ currency }}' != 'sat') {
obj.price /= 100
}
return obj
}
const mapZone = obj => {
@ -545,7 +548,7 @@
LNbits.api
.request(
'PUT',
'/shop/api/v1/settings',
'/shop/api/v1/settings/' + this.g.user.id,
this.g.user.wallets[0].adminkey,
data
)
@ -680,6 +683,7 @@
name: this.stallDialog.data.name,
wallet: this.stallDialog.data.wallet,
publickey: this.stallDialog.data.publickey || this.keys.pubkey,
currency: this.currencies.unit,
relays: this.stallDialog.data.relays,
shippingzones: this.stallDialog.data.shippingzones
.map(z => z.split('-')[0].trim())
@ -1376,6 +1380,8 @@
this.onboarding.showAgain = showOnboard || false
this.diagonAlley =
this.$q.localStorage.getItem('lnbits.DAmode') || false
this.currencies.unit = '{{ currency }}'
console.log(this.currencies.unit, '{{currency}}')
await this.getCurrencies()
this.getStalls()
this.getProducts()

View File

@ -48,7 +48,10 @@
</q-item-section>
<q-item-section side>
<span> {{p.price}} sats</span>
<span>
{{unit != 'sat' ? getAmountFormated(p.price) : p.price +
'sats'}}</span
>
</q-item-section>
</q-item>
{% endraw %}
@ -112,13 +115,18 @@
<q-card-section class="q-py-sm">
<div>
<!-- <div class="text-caption text-green-8 text-weight-bolder">
{{ stall.name }}
</div> -->
<span class="text-h6">{{ item.price }} sats</span
><span class="q-ml-sm text-grey-6"
>BTC {{ (item.price / 1e8).toFixed(8) }}</span
>
<span v-if="unit == 'sat'">
<span class="text-h6">{{ item.price }} sats</span
><span class="q-ml-sm text-grey-6"
>BTC {{ (item.price / 1e8).toFixed(8) }}</span
>
</span>
<span v-else>
<span class="text-h6">{{ getAmountFormated(item.price) }}</span>
<span v-if="exchangeRate" class="q-ml-sm text-grey-6"
>({{ getValueInSats(item.price) }} sats)</span
>
</span>
<span
class="q-ml-md text-caption text-green-8 text-weight-bolder q-mt-md"
>{{item.quantity}} left</span
@ -203,7 +211,12 @@
/>
</div>
<div class="row q-mt-lg">
{% raw %} Total: {{ finalCost }} {% endraw %}
{% raw %} Total: {{ unit != 'sat' ? getAmountFormated(finalCost) :
finalCost + 'sats' }}
<span v-if="unit != 'sat'" class="q-ml-sm text-grey-6"
>({{ getValueInSats(finalCost) }} sats)</span
>
{% endraw %}
</div>
<div class="row q-mt-lg">
<q-btn
@ -281,6 +294,8 @@
products: [],
searchText: null,
diagonalley: false,
unit: 'sat',
exchangeRate: 0,
cart: {
total: 0,
size: 0,
@ -334,6 +349,28 @@
products: new Map()
}
},
getAmountFormated(amount) {
return LNbits.utils.formatCurrency(amount.toFixed(2), this.unit)
},
async getRates() {
if (this.unit == 'sat') return
try {
let rate = (
await LNbits.api.request('POST', '/api/v1/conversion', null, {
amount: 1e8,
to: this.unit
})
).data
this.exchangeRate = rate[this.unit]
console.log(this.exchangeRate)
} catch (error) {
LNbits.utils.notifyApiError(error)
}
},
getValueInSats(amount) {
if (!this.exchangeRate) return 0
return Math.ceil((amount / this.exchangeRate) * 1e8)
},
addToCart(item) {
let prod = this.cart.products
if (prod.has(item.id)) {
@ -382,7 +419,10 @@
let data = {
...this.checkoutDialog.data,
wallet: this.stall.wallet,
total: this.finalCost,
total:
this.unit != 'sat'
? this.getValueInSats(this.finalCost)
: this.finalCost, // maybe this is better made in Python to allow API ordering?!
products: Array.from(this.cart.products, p => {
return {product_id: p[0], quantity: p[1].quantity}
})
@ -448,9 +488,12 @@
})
}
},
created() {
async created() {
this.stall = JSON.parse('{{ stall | tojson }}')
this.products = JSON.parse('{{ products | tojson }}')
this.unit = this.stall.currency
await this.getRates()
setInterval(this.getRates, 300000)
}
})
</script>

View File

@ -12,7 +12,7 @@ from starlette.responses import HTMLResponse
from lnbits.core.models import User
from lnbits.decorators import check_user_exists # type: ignore
from lnbits.extensions.shop import shop_ext, shop_renderer
from lnbits.extensions.shop.models import CreateChatMessage
from lnbits.extensions.shop.models import CreateChatMessage, SetSettings
from lnbits.extensions.shop.notifier import Notifier
from .crud import (
@ -26,6 +26,8 @@ from .crud import (
get_shop_zone,
get_shop_zones,
update_shop_product_stock,
get_shop_settings,
create_shop_settings,
)
templates = Jinja2Templates(directory="templates")
@ -33,8 +35,16 @@ templates = Jinja2Templates(directory="templates")
@shop_ext.get("/", response_class=HTMLResponse)
async def index(request: Request, user: User = Depends(check_user_exists)):
settings = await get_shop_settings(user=user.id)
if not settings:
await create_shop_settings(
user=user.id, data=SetSettings(currency="sat", fiat_base_multiplier=1)
)
settings = await get_shop_settings(user.id)
return shop_renderer().TemplateResponse(
"shop/index.html", {"request": request, "user": user.dict()}
"shop/index.html",
{"request": request, "user": user.dict(), "currency": settings.currency},
)

View File

@ -33,6 +33,7 @@ from .crud import (
create_shop_product,
create_shop_stall,
create_shop_zone,
create_shop_settings,
delete_shop_order,
delete_shop_product,
delete_shop_stall,
@ -102,15 +103,21 @@ async def api_shop_product_create(
product_id=None,
wallet: WalletTypeInfo = Depends(require_invoice_key),
):
# For fiat currencies,
# we multiply by data.fiat_base_multiplier (usually 100) to save the value in cents.
settings = await get_shop_settings(user=wallet.wallet.user)
stall = await get_shop_stall(stall_id=data.stall)
if stall.currency != "sat":
data.price *= settings.fiat_base_multiplier
if product_id:
product = await get_shop_product(product_id)
if not product:
return {"message": "Withdraw product does not exist."}
return {"message": "Product does not exist."}
stall = await get_shop_stall(stall_id=product.stall)
# stall = await get_shop_stall(stall_id=product.stall)
if stall.wallet != wallet.wallet.id:
return {"message": "Not your withdraw product."}
return {"message": "Not your product."}
product = await update_shop_product(product_id, **data.dict())
else:
@ -250,6 +257,8 @@ async def api_shop_orders(
wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
orders = await get_shop_orders(wallet_ids)
if not orders:
return
orders_with_details = []
for order in orders:
order = order.dict()
@ -472,9 +481,24 @@ async def api_get_settings(wallet: WalletTypeInfo = Depends(require_admin_key)):
return settings
@shop_ext.put("/api/v1/settings")
@shop_ext.post("/api/v1/settings")
@shop_ext.put("/api/v1/settings/{usr}")
async def api_set_settings(
data: SetSettings, wallet: WalletTypeInfo = Depends(require_admin_key)
data: SetSettings,
usr: str = None,
wallet: WalletTypeInfo = Depends(require_admin_key),
):
if usr:
if usr != wallet.wallet.user:
return {"message": "Not your Shop."}
settings = await get_shop_settings(user=usr)
if settings.user != wallet.wallet.user:
return {"message": "Not your Shop."}
return await set_shop_settings(usr, data)
user = wallet.wallet.user
return await set_shop_settings(user, data)
return await create_shop_settings(user, data)