Merge pull request #791 from lnbits/fix/offlineshop_fiat_decimals
Offlineshop - allow for decimals in fiat unit
This commit is contained in:
commit
2ef9548b05
7 changed files with 57 additions and 19 deletions
|
|
@ -52,14 +52,20 @@ async def set_method(shop: int, method: str, wordlist: str = "") -> Optional[Sho
|
||||||
|
|
||||||
|
|
||||||
async def add_item(
|
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:
|
) -> int:
|
||||||
result = await db.execute(
|
result = await db.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO offlineshop.items (shop, name, description, image, price, unit)
|
INSERT INTO offlineshop.items (shop, name, description, image, price, unit, fiat_base_multiplier)
|
||||||
VALUES (?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||||
""",
|
""",
|
||||||
(shop, name, description, image, price, unit),
|
(shop, name, description, image, price, unit, fiat_base_multiplier),
|
||||||
)
|
)
|
||||||
return result._result_proxy.lastrowid
|
return result._result_proxy.lastrowid
|
||||||
|
|
||||||
|
|
@ -72,6 +78,7 @@ async def update_item(
|
||||||
image: Optional[str],
|
image: Optional[str],
|
||||||
price: int,
|
price: int,
|
||||||
unit: str,
|
unit: str,
|
||||||
|
fiat_base_multiplier: int,
|
||||||
) -> int:
|
) -> int:
|
||||||
await db.execute(
|
await db.execute(
|
||||||
"""
|
"""
|
||||||
|
|
@ -80,10 +87,11 @@ async def update_item(
|
||||||
description = ?,
|
description = ?,
|
||||||
image = ?,
|
image = ?,
|
||||||
price = ?,
|
price = ?,
|
||||||
unit = ?
|
unit = ?,
|
||||||
|
fiat_base_multiplier = ?
|
||||||
WHERE shop = ? AND id = ?
|
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
|
return item_id
|
||||||
|
|
||||||
|
|
@ -92,12 +100,12 @@ async def get_item(id: int) -> Optional[Item]:
|
||||||
row = await db.fetchone(
|
row = await db.fetchone(
|
||||||
"SELECT * FROM offlineshop.items WHERE id = ? LIMIT 1", (id,)
|
"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]:
|
async def get_items(shop: int) -> List[Item]:
|
||||||
rows = await db.fetchall("SELECT * FROM offlineshop.items WHERE shop = ?", (shop,))
|
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):
|
async def delete_item_from_shop(shop: int, item_id: int):
|
||||||
|
|
|
||||||
|
|
@ -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;"
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import base64
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
from sqlite3 import Row
|
||||||
from typing import Dict, List, Optional
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
from lnurl import encode as lnurl_encode # type: ignore
|
from lnurl import encode as lnurl_encode # type: ignore
|
||||||
|
|
@ -87,8 +88,16 @@ class Item(BaseModel):
|
||||||
description: str
|
description: str
|
||||||
image: Optional[str]
|
image: Optional[str]
|
||||||
enabled: bool
|
enabled: bool
|
||||||
price: int
|
price: float
|
||||||
unit: str
|
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:
|
def lnurl(self, req: Request) -> str:
|
||||||
return lnurl_encode(req.url_for("offlineshop.lnurl_response", item_id=self.id))
|
return lnurl_encode(req.url_for("offlineshop.lnurl_response", item_id=self.id))
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,8 @@ new Vue({
|
||||||
description,
|
description,
|
||||||
image,
|
image,
|
||||||
price,
|
price,
|
||||||
unit
|
unit,
|
||||||
|
fiat_base_multiplier: unit == 'sat' ? 1 : 100
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
from fastapi import Query
|
||||||
from fastapi.params import Depends
|
from fastapi.params import Depends
|
||||||
from lnurl.exceptions import InvalidUrl as LnurlInvalidUrl
|
from lnurl.exceptions import InvalidUrl as LnurlInvalidUrl
|
||||||
from pydantic.main import BaseModel
|
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)
|
shop = await get_or_create_shop_by_wallet(wallet.wallet.id)
|
||||||
items = await get_items(shop.id)
|
items = await get_items(shop.id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return {
|
return {
|
||||||
**shop.dict(),
|
**shop.dict(),
|
||||||
|
|
@ -51,8 +51,9 @@ class CreateItemsData(BaseModel):
|
||||||
name: str
|
name: str
|
||||||
description: str
|
description: str
|
||||||
image: Optional[str]
|
image: Optional[str]
|
||||||
price: int
|
price: float
|
||||||
unit: str
|
unit: str
|
||||||
|
fiat_base_multiplier: int = Query(100, ge=1)
|
||||||
|
|
||||||
|
|
||||||
@offlineshop_ext.post("/api/v1/offlineshop/items")
|
@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)
|
data: CreateItemsData, item_id=None, wallet: WalletTypeInfo = Depends(get_key_type)
|
||||||
):
|
):
|
||||||
shop = await get_or_create_shop_by_wallet(wallet.wallet.id)
|
shop = await get_or_create_shop_by_wallet(wallet.wallet.id)
|
||||||
|
if data.unit != "sat":
|
||||||
|
data.price = data.price * 100
|
||||||
if item_id == None:
|
if item_id == None:
|
||||||
|
|
||||||
await add_item(
|
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)
|
return HTMLResponse(status_code=HTTPStatus.CREATED)
|
||||||
else:
|
else:
|
||||||
|
|
@ -75,6 +85,7 @@ async def api_add_or_update_item(
|
||||||
data.image,
|
data.image,
|
||||||
data.price,
|
data.price,
|
||||||
data.unit,
|
data.unit,
|
||||||
|
data.fiat_base_multiplier,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,9 +1,8 @@
|
||||||
import psycopg2
|
|
||||||
import sqlite3
|
|
||||||
import os
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import os
|
||||||
|
import sqlite3
|
||||||
|
|
||||||
|
import psycopg2
|
||||||
from environs import Env # type: ignore
|
from environs import Env # type: ignore
|
||||||
|
|
||||||
env = Env()
|
env = Env()
|
||||||
|
|
@ -540,8 +539,8 @@ def migrate_ext(sqlite_db_file, schema, ignore_missing=True):
|
||||||
# ITEMS
|
# ITEMS
|
||||||
res = sq.execute("SELECT * FROM items;")
|
res = sq.execute("SELECT * FROM items;")
|
||||||
q = f"""
|
q = f"""
|
||||||
INSERT INTO offlineshop.items (shop, id, name, description, image, enabled, price, unit)
|
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);
|
VALUES (%s, %s, %s, %s, %s, %s::boolean, %s, %s, %s);
|
||||||
"""
|
"""
|
||||||
items = res.fetchall()
|
items = res.fetchall()
|
||||||
insert_to_pg(q, items)
|
insert_to_pg(q, items)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue