Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions config.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"id": "tpos",
"version": "1.1.2",
"version": "1.2.0",
"name": "TPoS",
"repo": "https://github.com/lnbits/tpos",
"short_description": "A shareable PoS terminal!",
"description": "",
"tile": "/tpos/static/image/tpos.png",
"min_lnbits_version": "1.6.2",
"min_lnbits_version": "1.7.0-rc1",
"contributors": [
{
"name": "Ben Arc",
Expand Down
2 changes: 1 addition & 1 deletion crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ async def set_wrapper_assetlinks_cache(

async def create_tpos(data: CreateTposData) -> Tpos:
tpos_id = urlsafe_short_hash()
data_dict = data.dict()
data_dict = data.model_dump()
data_dict["inventory_tags"] = serialize_inventory_tags(data.inventory_tags)
data_dict["inventory_omit_tags"] = serialize_inventory_tags(
data.inventory_omit_tags
Expand Down
37 changes: 19 additions & 18 deletions models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from typing import Any, Literal

from fastapi import Query
from pydantic import BaseModel, Field, validator
from pydantic import BaseModel, ConfigDict, Field, field_validator


class PayLnurlWData(BaseModel):
Expand All @@ -20,8 +20,7 @@ class CreateWithdrawPay(BaseModel):
class AuthorizeAtm(BaseModel):
password: str = Field(..., min_length=1, max_length=256)

class Config:
extra = "forbid"
model_config = ConfigDict(extra="forbid")


class CreateTposInvoiceTabSettlement(BaseModel):
Expand Down Expand Up @@ -62,15 +61,15 @@ class InventorySale(BaseModel):


class CreateTposData(BaseModel):
wallet: str | None
name: str | None
currency: str | None
wallet: str | None = None
name: str | None = None
currency: str | None = None
use_inventory: bool = Field(False)
inventory_id: str | None = None
inventory_tags: list[str] | None = None
inventory_omit_tags: list[str] | None = None
tax_inclusive: bool = Field(True)
tax_default: float | None = Field(0.0)
tax_default: float | None = Field(0.0, validate_default=True)
tip_options: str = Field("[]")
tip_wallet: str = Field("")
withdraw_time: int = Field(0)
Expand All @@ -82,9 +81,9 @@ class CreateTposData(BaseModel):
lnaddress_cut: int | None = Field(0)
enable_receipt_print: bool = Query(False)
enable_remote: bool = Query(False)
business_name: str | None
business_address: str | None
business_vat_id: str | None
business_name: str | None = None
business_address: str | None = None
business_vat_id: str | None = None
only_show_sats_on_bitcoin: bool = Query(True)
allow_price_adjustment: bool = Field(True)
fiat_provider: str | None = Field(None)
Expand All @@ -97,7 +96,8 @@ class CreateTposData(BaseModel):
tabs_enabled: bool = Field(False)
tabs_allow_create: bool = Field(False)

@validator("tax_default", pre=True, always=True)
@field_validator("tax_default", mode="before")
@classmethod
def default_tax_when_none(cls, v):
return 0.0 if v is None else v

Expand Down Expand Up @@ -235,7 +235,7 @@ class CreateTposTabData(BaseModel):
class CreateTposTabCharge(BaseModel):
amount: float = Field(..., gt=0)
description: str | None = Field(None, max_length=512)
items: list[dict[str, Any]] = Field(default_factory=list, max_items=200)
items: list[dict[str, Any]] = Field(default_factory=list, max_length=200)
notes: dict[str, Any] | None = None
internal_memo: str | None = Field(None, max_length=512)
idempotency_key: str = Field(..., min_length=8, max_length=128)
Expand All @@ -249,15 +249,16 @@ class LnurlCharge(BaseModel):


class Item(BaseModel):
image: str | None
image: str | None = None
price: float
title: str
description: str | None
tax: float | None = Field(0, ge=0.0)
description: str | None = None
tax: float | None = Field(0, ge=0.0, validate_default=True)
disabled: bool = False
categories: list[str] | None = []

@validator("tax", pre=True, always=True)
@field_validator("tax", mode="before")
@classmethod
def set_default_tax(cls, v):
return v or 0

Expand Down Expand Up @@ -404,7 +405,7 @@ def render_text(
return "\n".join(lines)

def to_api_dict(self) -> dict[str, Any]:
data = self.dict()
data = self.model_dump()
details = data.get("extra", {}).get("details", {})
details["exchangeRate"] = details.pop("exchange_rate", 1.0)
details["taxValue"] = details.pop("tax_value", 0.0)
Expand All @@ -424,4 +425,4 @@ class ReceiptPrint(BaseModel):
receipt: dict[str, Any] = Field(default_factory=dict)


CreateTposInvoice.update_forward_refs(InventorySale=InventorySale)
CreateTposInvoice.model_rebuild()
12 changes: 8 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
[project]
name = "lnbits-tpos"
version = "0.0.0"
requires-python = ">=3.10,<3.13"
version = "1.2.0"
requires-python = ">=3.10,<3.14"
description = "LNbits, free and open-source Lightning wallet and accounts system."
authors = [{ name = "Alan Bits", email = "alan@lnbits.com" }]
urls = { Homepage = "https://lnbits.com", Repository = "https://github.com/lnbits/tpos" }
dependencies = [ "lnbits>=1.6.2" ]
dependencies = [ "lnbits>=1.7.0rc1" ]

[dependency-groups]
dev = [
"black",
"pytest-asyncio",
"pytest",
"mypy",
"mypy==1.17.1",
"pre-commit",
"ruff",
]
Expand All @@ -22,6 +22,10 @@ package-mode = false

# Keep UV configuration independent of a parent LNbits checkout.
[tool.uv]
build-constraint-dependencies = ["scikit-build-core<0.10"]

[tool.poetry.dependencies]
python = ">=3.10,<3.13"

[tool.mypy]
exclude = "(nostr/*)"
Expand Down
5 changes: 2 additions & 3 deletions services_inventory.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from lnbits.core.crud import get_wallet
from lnbits.core.models import User
from lnbits.settings import settings
from pydantic import BaseModel, Field, ValidationError
from pydantic import BaseModel, ConfigDict, Field, ValidationError

from .helpers import create_internal_user_access_token, from_csv, inventory_tags_to_list
from .models import CreateTposInvoice, Tpos
Expand All @@ -20,8 +20,7 @@ class InventoryCartItem(BaseModel):
quantity: int = Field(..., ge=1)
price: float = Field(..., ge=0)

class Config:
allow_inf_nan = False
model_config = ConfigDict(allow_inf_nan=False)


async def validate_inventory_sale(tpos: Tpos, data: CreateTposInvoice) -> None:
Expand Down
6 changes: 3 additions & 3 deletions tests/test_security.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ async def pay_during_creation(wallet_id, invoice_data):
monkeypatch.setattr(tasks, "maybe_push_order", order)
# The original event predates the amount binding; the replay is also unprocessed.
for event in [*paid_events, replayed_payment]:
await tasks.on_invoice_paid(event.copy(deep=True))
await tasks.on_invoice_paid(event.model_copy(deep=True))
order.assert_awaited_once()
settlements = await get_tab_settlements(tab["id"])
assert len(settlements) == 1
Expand Down Expand Up @@ -135,7 +135,7 @@ async def test_owner_listing_requires_admin_key_and_stays_in_account(client):
)
assert {row["id"] for row in listing.json()} == {pos["id"], second.json()["id"]}
assert all(set(row) == set(pos) for row in listing.json())
public = TposClean(**pos).dict()
public = TposClean(**pos).model_dump()
assert "wallet" not in public
assert "tip_wallet" not in public
assert "adminkey" not in pos and "inkey" not in pos
Expand Down Expand Up @@ -281,7 +281,7 @@ async def test_tab_settlement_matches_paid_amount_and_deduplicates(client, monke
{"extra": {**payment.extra, "tpos_id": uuid4().hex}},
):
await tasks.maybe_settle_tab(
payment.copy(update=changed), stored_pos, "lightning"
payment.model_copy(update=changed), stored_pos, "lightning"
)
bridge.assert_not_awaited()

Expand Down
Loading
Loading