diff --git a/lnbits/extensions/shop/crud.py b/lnbits/extensions/shop/crud.py index 1986829e2..dceaa203b 100644 --- a/lnbits/extensions/shop/crud.py +++ b/lnbits/extensions/shop/crud.py @@ -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, diff --git a/lnbits/extensions/shop/migrations.py b/lnbits/extensions/shop/migrations.py index 2936695eb..e6592dffb 100644 --- a/lnbits/extensions/shop/migrations.py +++ b/lnbits/extensions/shop/migrations.py @@ -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, diff --git a/lnbits/extensions/shop/models.py b/lnbits/extensions/shop/models.py index 2282cef0f..ed6342f0b 100644 --- a/lnbits/extensions/shop/models.py +++ b/lnbits/extensions/shop/models.py @@ -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 diff --git a/lnbits/extensions/shop/templates/shop/_dialogs.html b/lnbits/extensions/shop/templates/shop/_dialogs.html index a55d76144..3718f38ca 100644 --- a/lnbits/extensions/shop/templates/shop/_dialogs.html +++ b/lnbits/extensions/shop/templates/shop/_dialogs.html @@ -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'" >
{ 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() diff --git a/lnbits/extensions/shop/templates/shop/stall.html b/lnbits/extensions/shop/templates/shop/stall.html index 6e776e86e..18b89f37e 100644 --- a/lnbits/extensions/shop/templates/shop/stall.html +++ b/lnbits/extensions/shop/templates/shop/stall.html @@ -48,7 +48,10 @@ - {{p.price}} sats + + {{unit != 'sat' ? getAmountFormated(p.price) : p.price + + 'sats'}} {% endraw %} @@ -112,13 +115,18 @@
- - {{ item.price }} satsBTC {{ (item.price / 1e8).toFixed(8) }} + + {{ item.price }} satsBTC {{ (item.price / 1e8).toFixed(8) }} + + + {{ getAmountFormated(item.price) }} + ({{ getValueInSats(item.price) }} sats) + {{item.quantity}} left
- {% raw %} Total: {{ finalCost }} {% endraw %} + {% raw %} Total: {{ unit != 'sat' ? getAmountFormated(finalCost) : + finalCost + 'sats' }} + ({{ getValueInSats(finalCost) }} sats) + {% endraw %}
{ 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) } }) diff --git a/lnbits/extensions/shop/views.py b/lnbits/extensions/shop/views.py index d9fbe64b4..50d6357c3 100644 --- a/lnbits/extensions/shop/views.py +++ b/lnbits/extensions/shop/views.py @@ -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}, ) diff --git a/lnbits/extensions/shop/views_api.py b/lnbits/extensions/shop/views_api.py index 86f0a8f56..398fc0b5b 100644 --- a/lnbits/extensions/shop/views_api.py +++ b/lnbits/extensions/shop/views_api.py @@ -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)