diff --git a/lnbits/extensions/offlineshop/crud.py b/lnbits/extensions/offlineshop/crud.py index bc4f3f14..896842d8 100644 --- a/lnbits/extensions/offlineshop/crud.py +++ b/lnbits/extensions/offlineshop/crud.py @@ -52,14 +52,20 @@ async def set_method(shop: int, method: str, wordlist: str = "") -> Optional[Sho async def add_item( - shop: int, name: str, description: str, image: Optional[str], price: int, unit: str + shop: int, + name: str, + description: str, + image: Optional[str], + price: int, + unit: str, + fiat_base_multiplier: int, ) -> int: result = await db.execute( """ - INSERT INTO offlineshop.items (shop, name, description, image, price, unit) - VALUES (?, ?, ?, ?, ?, ?) + INSERT INTO offlineshop.items (shop, name, description, image, price, unit, fiat_base_multiplier) + VALUES (?, ?, ?, ?, ?, ?, ?) """, - (shop, name, description, image, price, unit), + (shop, name, description, image, price, unit, fiat_base_multiplier), ) return result._result_proxy.lastrowid @@ -72,6 +78,7 @@ async def update_item( image: Optional[str], price: int, unit: str, + fiat_base_multiplier: int, ) -> int: await db.execute( """ @@ -80,10 +87,11 @@ async def update_item( description = ?, image = ?, price = ?, - unit = ? + unit = ?, + fiat_base_multiplier = ? WHERE shop = ? AND id = ? """, - (name, description, image, price, unit, shop, item_id), + (name, description, image, price, unit, fiat_base_multiplier, shop, item_id), ) return item_id @@ -92,12 +100,12 @@ async def get_item(id: int) -> Optional[Item]: row = await db.fetchone( "SELECT * FROM offlineshop.items WHERE id = ? LIMIT 1", (id,) ) - return Item(**dict(row)) if row else None + return Item.from_row(row) if row else None async def get_items(shop: int) -> List[Item]: rows = await db.fetchall("SELECT * FROM offlineshop.items WHERE shop = ?", (shop,)) - return [Item(**dict(row)) for row in rows] + return [Item.from_row(row) for row in rows] async def delete_item_from_shop(shop: int, item_id: int): diff --git a/lnbits/extensions/offlineshop/migrations.py b/lnbits/extensions/offlineshop/migrations.py index f7c2dfec..84aea27e 100644 --- a/lnbits/extensions/offlineshop/migrations.py +++ b/lnbits/extensions/offlineshop/migrations.py @@ -27,3 +27,13 @@ async def m001_initial(db): ); """ ) + + +async def m002_fiat_base_multiplier(db): + """ + Store the multiplier for fiat prices. We store the price in cents and + remember to multiply by 100 when we use it to convert to Dollars. + """ + await db.execute( + "ALTER TABLE offlineshop.items ADD COLUMN fiat_base_multiplier INTEGER DEFAULT 1;" + ) diff --git a/lnbits/extensions/offlineshop/models.py b/lnbits/extensions/offlineshop/models.py index 0128fdb8..ca5c73a5 100644 --- a/lnbits/extensions/offlineshop/models.py +++ b/lnbits/extensions/offlineshop/models.py @@ -2,6 +2,7 @@ import base64 import hashlib import json from collections import OrderedDict +from sqlite3 import Row from typing import Dict, List, Optional from lnurl import encode as lnurl_encode # type: ignore @@ -87,8 +88,16 @@ class Item(BaseModel): description: str image: Optional[str] enabled: bool - price: int + price: float unit: str + fiat_base_multiplier: int + + @classmethod + def from_row(cls, row: Row) -> "Item": + data = dict(row) + if data["unit"] != "sat" and data["fiat_base_multiplier"]: + data["price"] /= data["fiat_base_multiplier"] + return cls(**data) def lnurl(self, req: Request) -> str: return lnurl_encode(req.url_for("offlineshop.lnurl_response", item_id=self.id)) diff --git a/lnbits/extensions/offlineshop/static/js/index.js b/lnbits/extensions/offlineshop/static/js/index.js index 00e93241..c0390609 100644 --- a/lnbits/extensions/offlineshop/static/js/index.js +++ b/lnbits/extensions/offlineshop/static/js/index.js @@ -124,7 +124,8 @@ new Vue({ description, image, price, - unit + unit, + fiat_base_multiplier: unit == 'sat' ? 1 : 100 } try { diff --git a/lnbits/extensions/offlineshop/views_api.py b/lnbits/extensions/offlineshop/views_api.py index 5ced4351..1bd045dd 100644 --- a/lnbits/extensions/offlineshop/views_api.py +++ b/lnbits/extensions/offlineshop/views_api.py @@ -1,6 +1,7 @@ from http import HTTPStatus from typing import Optional +from fastapi import Query from fastapi.params import Depends from lnurl.exceptions import InvalidUrl as LnurlInvalidUrl from pydantic.main import BaseModel @@ -34,7 +35,6 @@ async def api_shop_from_wallet( ): shop = await get_or_create_shop_by_wallet(wallet.wallet.id) items = await get_items(shop.id) - try: return { **shop.dict(), @@ -51,8 +51,9 @@ class CreateItemsData(BaseModel): name: str description: str image: Optional[str] - price: int + price: float unit: str + fiat_base_multiplier: int = Query(100, ge=1) @offlineshop_ext.post("/api/v1/offlineshop/items") @@ -61,9 +62,18 @@ async def api_add_or_update_item( data: CreateItemsData, item_id=None, wallet: WalletTypeInfo = Depends(get_key_type) ): shop = await get_or_create_shop_by_wallet(wallet.wallet.id) + if data.unit != "sat": + data.price = data.price * 100 if item_id == None: + await add_item( - shop.id, data.name, data.description, data.image, data.price, data.unit + shop.id, + data.name, + data.description, + data.image, + data.price, + data.unit, + data.fiat_base_multiplier, ) return HTMLResponse(status_code=HTTPStatus.CREATED) else: @@ -75,6 +85,7 @@ async def api_add_or_update_item( data.image, data.price, data.unit, + data.fiat_base_multiplier, ) diff --git a/tools/conv.py b/tools/conv.py index a4c94a94..555e23dc 100644 --- a/tools/conv.py +++ b/tools/conv.py @@ -539,8 +539,8 @@ def migrate_ext(sqlite_db_file, schema, ignore_missing=True): # ITEMS res = sq.execute("SELECT * FROM items;") q = f""" - INSERT INTO offlineshop.items (shop, id, name, description, image, enabled, price, unit) - VALUES (%s, %s, %s, %s, %s, %s::boolean, %s, %s); + INSERT INTO offlineshop.items (shop, id, name, description, image, enabled, price, unit, fiat_base_multiplier) + VALUES (%s, %s, %s, %s, %s, %s::boolean, %s, %s, %s); """ items = res.fetchall() insert_to_pg(q, items)