diff --git a/.github/workflows/regtest.yml b/.github/workflows/regtest.yml
index 4e6cfafc5..bcb83de53 100644
--- a/.github/workflows/regtest.yml
+++ b/.github/workflows/regtest.yml
@@ -36,7 +36,6 @@ jobs:
run: |
git clone https://github.com/lnbits/legend-regtest-enviroment.git docker
cd docker
- git checkout cln-24-11
chmod +x ./tests
./tests
sudo chmod -R a+rwx .
diff --git a/docs/guide/installation.md b/docs/guide/installation.md
index d71d54ed5..db763bd6a 100644
--- a/docs/guide/installation.md
+++ b/docs/guide/installation.md
@@ -12,11 +12,15 @@ Note that by default LNbits uses SQLite as its database, which is simple and eff
## Option 1 (recommended): Poetry
-It is recommended to use the latest version of Poetry. Make sure you have Python version 3.9 or higher installed.
+It is recommended to use the latest version of Poetry. Make sure you have Python version `3.12` installed.
-### Verify Python version
+### Install Python 3.12
```sh
+sudo add-apt-repository ppa:deadsnakes/ppa
+sudo apt update
+sudo apt install python3.12
+sudo apt-get install python3.12-dev # ensure correct headers needed for secp256k1
python3 --version
```
@@ -28,16 +32,16 @@ curl -sSL https://install.python-poetry.org | python3 -
export PATH="/home/user/.local/bin:$PATH"
```
+### install LNbits
+
```sh
git clone https://github.com/lnbits/lnbits.git
cd lnbits
+poetry env use 3.12
git checkout main
-
poetry install --only main
-
cp .env.example .env
-# set funding source amongst other options
-nano .env
+# Optional: to set funding source amongst other options via the env `nano .env`
```
#### Running the server
@@ -49,9 +53,16 @@ poetry run lnbits
# Note that you have to add the line DEBUG=true in your .env file, too.
```
+#### LNbits-cli
+
+```sh
+# A very useful terminal client for getting the supersuer ID, updating extensions, etc
+poetry run lnbits-cli --help
+```
+
#### Updating the server
-```
+```sh
cd lnbits
# Stop LNbits with `ctrl + x`
git pull
diff --git a/lnbits/core/crud/payments.py b/lnbits/core/crud/payments.py
index 979c7b027..205282972 100644
--- a/lnbits/core/crud/payments.py
+++ b/lnbits/core/crud/payments.py
@@ -123,15 +123,20 @@ async def get_payments_paginated(
clause.append("wallet_id = :wallet_id")
if complete and pending:
- pass
+ clause.append(
+ f"(status = '{PaymentState.SUCCESS}' OR status = '{PaymentState.PENDING}')"
+ )
elif complete:
clause.append(
- f"((amount > 0 AND status = '{PaymentState.SUCCESS}') OR amount < 0)"
+ f"""
+ (
+ status = '{PaymentState.SUCCESS}'
+ OR (amount < 0 AND status = '{PaymentState.PENDING}')
+ )
+ """
)
elif pending:
clause.append(f"status = '{PaymentState.PENDING}'")
- else:
- pass
if outgoing and incoming:
pass
@@ -289,8 +294,14 @@ async def get_payments_history(
values = {
"wallet_id": wallet_id,
}
+ # count outgoing payments if they are still pending
where = [
- f"wallet_id = :wallet_id AND (status = '{PaymentState.SUCCESS}' OR amount < 0)"
+ f"""
+ wallet_id = :wallet_id AND (
+ status = '{PaymentState.SUCCESS}'
+ OR (amount < 0 AND status = '{PaymentState.PENDING}')
+ )
+ """
]
transactions: list[dict] = await db.fetchall(
f"""
diff --git a/lnbits/core/models/audit.py b/lnbits/core/models/audit.py
index 263662a56..8e251a3b3 100644
--- a/lnbits/core/models/audit.py
+++ b/lnbits/core/models/audit.py
@@ -51,8 +51,8 @@ class AuditFilters(FilterModel):
class AuditCountStat(BaseModel):
- field: str
- total: float
+ field: str = ""
+ total: float = 0
class AuditStats(BaseModel):
diff --git a/lnbits/core/templates/admin/index.html b/lnbits/core/templates/admin/index.html
index 9970d6cd1..224c3e917 100644
--- a/lnbits/core/templates/admin/index.html
+++ b/lnbits/core/templates/admin/index.html
@@ -1,7 +1,7 @@
{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
%} {% block page %}
-
+
+
Components
diff --git a/lnbits/core/templates/core/extensions.html b/lnbits/core/templates/core/extensions.html
index 209cfe146..b4f2f2551 100644
--- a/lnbits/core/templates/core/extensions.html
+++ b/lnbits/core/templates/core/extensions.html
@@ -1,21 +1,5 @@
{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
%} {{ window_vars(user, extensions) }}{% block page %}
-
@@ -36,6 +20,25 @@
v-text="$t('only_admins_can_install')"
>
+
+
+
+
+
+
+
+
+
{% endif %}
+
+
+
+
+
+
+
+
+ {{LNBITS_DENOMINATION}}
+
+
+
+
+
+
dict[str, float]:
- rate, price = await get_fiat_rate_satoshis(currency)
+ rate, price = await get_fiat_rate_and_price_satoshis(currency)
return {"rate": rate, "price": price}
diff --git a/lnbits/core/views/payment_api.py b/lnbits/core/views/payment_api.py
index e27773a9d..98e1a8bb5 100644
--- a/lnbits/core/views/payment_api.py
+++ b/lnbits/core/views/payment_api.py
@@ -108,8 +108,6 @@ async def api_payments_paginated(
await update_pending_payments(key_info.wallet.id)
page = await get_payments_paginated(
wallet_id=key_info.wallet.id,
- pending=True,
- complete=True,
filters=filters,
)
return page
diff --git a/lnbits/static/js/wallet.js b/lnbits/static/js/wallet.js
index aa0fffeab..a9fd62932 100644
--- a/lnbits/static/js/wallet.js
+++ b/lnbits/static/js/wallet.js
@@ -58,7 +58,8 @@ window.app = Vue.createApp({
inkeyHidden: true,
adminkeyHidden: true,
hasNfc: false,
- nfcReaderAbortController: null
+ nfcReaderAbortController: null,
+ primaryColor: this.$q.localStorage.getItem('lnbits.primaryColor')
}
},
computed: {
@@ -672,6 +673,11 @@ window.app = Vue.createApp({
watch: {
updatePayments() {
this.updateFiatBalance()
+ },
+ '$q.screen.gt.sm'(value) {
+ if (value == true) {
+ this.mobileSimple = false
+ }
}
},
mounted() {
diff --git a/lnbits/utils/exchange_rates.py b/lnbits/utils/exchange_rates.py
index cc8d46d49..a71ebbd46 100644
--- a/lnbits/utils/exchange_rates.py
+++ b/lnbits/utils/exchange_rates.py
@@ -247,7 +247,7 @@ async def btc_price(currency: str) -> float:
return sum(rates_values) / len(rates_values)
-async def get_fiat_rate_satoshis(currency: str) -> tuple[float, float]:
+async def get_fiat_rate_and_price_satoshis(currency: str) -> tuple[float, float]:
price = await cache.save_result(
lambda: btc_price(currency),
f"btc-price-{currency}",
@@ -256,11 +256,16 @@ async def get_fiat_rate_satoshis(currency: str) -> tuple[float, float]:
return float(100_000_000 / price), price
+async def get_fiat_rate_satoshis(currency: str) -> float:
+ rate, _ = await get_fiat_rate_and_price_satoshis(currency)
+ return rate
+
+
async def fiat_amount_as_satoshis(amount: float, currency: str) -> int:
- rate, _ = await get_fiat_rate_satoshis(currency)
+ rate = await get_fiat_rate_satoshis(currency)
return int(amount * (rate))
async def satoshis_amount_as_fiat(amount: float, currency: str) -> float:
- rate, _ = await get_fiat_rate_satoshis(currency)
+ rate = await get_fiat_rate_satoshis(currency)
return float(amount / rate)
diff --git a/pyproject.toml b/pyproject.toml
index e8ea7d889..46152190f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "lnbits"
-version = "1.0.0-rc6"
+version = "1.0.0-rc7"
description = "LNbits, free and open-source Lightning wallet and accounts system."
authors = ["Alan Bits "]
readme = "README.md"
diff --git a/tests/unit/test_crud_payments.py b/tests/unit/test_crud_payments.py
new file mode 100644
index 000000000..b177ed730
--- /dev/null
+++ b/tests/unit/test_crud_payments.py
@@ -0,0 +1,66 @@
+import pytest
+
+from lnbits.core.crud import create_wallet, get_payments, update_payment
+from lnbits.core.models import PaymentState
+from lnbits.core.services import create_user_account, update_wallet_balance
+
+
+async def update_payments(payments):
+ payments[0].status = PaymentState.FAILED
+ await update_payment(payments[0])
+ payments[1].status = PaymentState.PENDING
+ await update_payment(payments[1])
+ payments[2].status = PaymentState.PENDING
+ await update_payment(payments[2])
+
+
+@pytest.mark.anyio
+async def test_crud_get_payments(app):
+
+ user = await create_user_account()
+ wallet = await create_wallet(user_id=user.id)
+
+ for _ in range(11):
+ await update_wallet_balance(wallet, 10)
+ wallet.balance_msat += 10 * 1000
+ await update_wallet_balance(wallet, -10)
+ wallet.balance_msat += -10 * 1000
+
+ payments = await get_payments(wallet_id=wallet.id)
+ assert len(payments) == 22, "should return 22 successful payments"
+
+ payments = await get_payments(wallet_id=wallet.id, incoming=True)
+ assert len(payments) == 11, "should return 11 successful incoming payments"
+ await update_payments(payments)
+
+ payments = await get_payments(wallet_id=wallet.id, outgoing=True)
+ assert len(payments) == 11, "should return 11 successful outgoing payments"
+ await update_payments(payments)
+
+ payments = await get_payments(wallet_id=wallet.id, pending=True)
+ assert len(payments) == 4, "should return 4 pending payments"
+
+ # function signature should have Optional[bool] for complete and pending to make
+ # this distinction possible
+ payments = await get_payments(wallet_id=wallet.id, pending=False)
+ assert len(payments) == 22, "should return all payments"
+
+ payments = await get_payments(wallet_id=wallet.id, complete=True, pending=True)
+ assert len(payments) == 20, "should return 4 pending and 16 complete payments"
+
+ payments = await get_payments(wallet_id=wallet.id, complete=True, outgoing=True)
+ assert (
+ len(payments) == 10
+ ), "should return 8 complete outgoing payments and 2 pending outgoing payments"
+
+ payments = await get_payments(wallet_id=wallet.id)
+ assert len(payments) == 22, "should return all payments"
+
+ payments = await get_payments(wallet_id=wallet.id, complete=True)
+ assert (
+ len(payments) == 18
+ ), "should return 14 successful payment and 4 pending payments"
+
+ # both false should return failed payments
+ # payments = await get_payments(wallet_id=wallet.id, complete=False, pending=False)
+ # assert len(payments) == 2, "should return 2 failed payment"