Wallet polish (#2942)
This commit is contained in:
@@ -397,8 +397,8 @@ async def get_daily_stats(
|
||||
SUM(apipayments.amount - ABS(apipayments.fee)) AS balance,
|
||||
ABS(SUM(apipayments.fee)) as fee,
|
||||
COUNT(*) as payments_count
|
||||
FROM apipayments
|
||||
RIGHT JOIN wallets ON apipayments.wallet_id = wallets.id
|
||||
FROM wallets
|
||||
LEFT JOIN apipayments ON apipayments.wallet_id = wallets.id
|
||||
{clause}
|
||||
AND (wallets.deleted = false OR wallets.deleted is NULL)
|
||||
GROUP BY date
|
||||
|
||||
@@ -125,7 +125,7 @@ class Payment(BaseModel):
|
||||
|
||||
|
||||
class PaymentFilters(FilterModel):
|
||||
__search_fields__ = ["memo", "amount", "wallet_id", "tag"]
|
||||
__search_fields__ = ["memo", "amount", "wallet_id", "tag", "status", "time"]
|
||||
|
||||
__sort_fields__ = ["created_at", "amount", "fee", "memo", "time", "tag"]
|
||||
|
||||
|
||||
@@ -399,14 +399,17 @@ async def calculate_fiat_amounts(
|
||||
amount_sat = int(amount)
|
||||
|
||||
if wallet_currency:
|
||||
if wallet_currency == currency:
|
||||
fiat_amount = amount
|
||||
else:
|
||||
fiat_amount = await satoshis_amount_as_fiat(amount_sat, wallet_currency)
|
||||
fiat_amounts["wallet_fiat_currency"] = wallet_currency
|
||||
fiat_amounts["wallet_fiat_amount"] = round(fiat_amount, ndigits=3)
|
||||
fiat_amounts["wallet_fiat_rate"] = amount_sat / fiat_amount
|
||||
fiat_amounts["wallet_btc_rate"] = (fiat_amount / amount_sat) * 100_000_000
|
||||
try:
|
||||
if wallet_currency == currency:
|
||||
fiat_amount = amount
|
||||
else:
|
||||
fiat_amount = await satoshis_amount_as_fiat(amount_sat, wallet_currency)
|
||||
fiat_amounts["wallet_fiat_currency"] = wallet_currency
|
||||
fiat_amounts["wallet_fiat_amount"] = round(fiat_amount, ndigits=3)
|
||||
fiat_amounts["wallet_fiat_rate"] = amount_sat / fiat_amount
|
||||
fiat_amounts["wallet_btc_rate"] = (fiat_amount / amount_sat) * 100_000_000
|
||||
except Exception as e:
|
||||
logger.error(f"Error calculating fiat amount for wallet '{wallet.id}': {e}")
|
||||
|
||||
logger.debug(
|
||||
f"Calculated fiat amounts {wallet.id=} {amount=} {currency=}: {fiat_amounts=}"
|
||||
|
||||
@@ -199,6 +199,7 @@
|
||||
v-model="formData.lnbits_default_bgimage"
|
||||
label="Background Image"
|
||||
@update:model-value="applyGlobalBgimage"
|
||||
hint="This must be a trusted source. It can change the content and it can log your IP address."
|
||||
>
|
||||
</q-input>
|
||||
</div>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
'q-pt-sm': g.fiatTracking,
|
||||
'q-pt-lg': !g.fiatTracking
|
||||
}"
|
||||
v-if="!isPrioritySwapped || !g.fiatTracking"
|
||||
v-if="!isFiatPriority || !g.fiatTracking"
|
||||
style="height: 100px"
|
||||
>
|
||||
<div class="col-7">
|
||||
@@ -82,7 +82,7 @@
|
||||
|
||||
<div
|
||||
class="column"
|
||||
v-if="isPrioritySwapped && g.fiatTracking"
|
||||
v-if="isFiatPriority && g.fiatTracking"
|
||||
:class="{
|
||||
'q-pt-sm': g.fiatTracking,
|
||||
'q-pt-lg': !g.fiatTracking
|
||||
@@ -189,6 +189,7 @@
|
||||
>
|
||||
<q-card-section>
|
||||
<payment-list
|
||||
@filter-changed="handleFilterChange"
|
||||
:update="updatePayments"
|
||||
:mobile-simple="mobileSimple"
|
||||
:expand-details="expandDetails"
|
||||
@@ -256,114 +257,191 @@
|
||||
|
||||
<q-expansion-item
|
||||
group="extras"
|
||||
icon="settings_cell"
|
||||
:label="$t('export_to_phone')"
|
||||
>
|
||||
<q-card>
|
||||
<q-card-section class="text-center">
|
||||
<p v-text="$t('export_to_phone_desc')"></p>
|
||||
<lnbits-qrcode
|
||||
:value="`${baseUrl}/wallet?usr=${g.user.id}&wal=${g.wallet.id}`"
|
||||
></lnbits-qrcode>
|
||||
</q-card-section>
|
||||
<span v-text="exportWalletQR"></span>
|
||||
<q-card-actions class="flex-center q-pb-md">
|
||||
<q-btn
|
||||
outline
|
||||
color="grey"
|
||||
:label="$t('copy_wallet_url')"
|
||||
@click="copyText(`${baseUrl}/wallet?usr=${g.user.id}&wal=${g.wallet.id}`)"
|
||||
></q-btn>
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-expansion-item>
|
||||
<q-separator></q-separator>
|
||||
<q-expansion-item
|
||||
group="extras"
|
||||
icon="edit"
|
||||
:label="$t('rename_wallet')"
|
||||
icon="phone_android"
|
||||
:label="$t('access_wallet_on_mobile')"
|
||||
>
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<div class="" style="max-width: 320px">
|
||||
<q-input
|
||||
filled
|
||||
v-model.trim="update.name"
|
||||
label="Name"
|
||||
dense
|
||||
/>
|
||||
</div>
|
||||
<q-btn
|
||||
:disable="!update.name.length"
|
||||
unelevated
|
||||
class="q-mt-sm"
|
||||
color="primary"
|
||||
:label="$t('update_name')"
|
||||
@click="updateWallet({ name: update.name })"
|
||||
></q-btn>
|
||||
You can connect to this wallet from a mobile app:
|
||||
<ul>
|
||||
<li>
|
||||
Download
|
||||
<a class="text-secondary" href="https://zeusln.app"
|
||||
>Zeus</a
|
||||
>
|
||||
or
|
||||
<a
|
||||
class="text-secondary"
|
||||
href="https://bluewallet.io/"
|
||||
>BlueWallet</a
|
||||
>
|
||||
from App Store or Google Play
|
||||
</li>
|
||||
<li>
|
||||
Enable the
|
||||
<a class="text-secondary" href="/lndhub">LndHub </a>
|
||||
extension for this account
|
||||
</li>
|
||||
<li>
|
||||
Scan the QR code in the
|
||||
<a class="text-secondary" href="/lndhub">LndHub </a>
|
||||
extensions with your mobile app
|
||||
</li>
|
||||
</ul>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-expansion-item>
|
||||
<q-separator></q-separator>
|
||||
<q-expansion-item
|
||||
group="extras"
|
||||
icon="attach_money"
|
||||
:label="$t('fiat_tracking')"
|
||||
>
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<div style="max-width: 360px">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<q-select
|
||||
filled
|
||||
dense
|
||||
v-model="update.currency"
|
||||
type="text"
|
||||
:disable="g.fiatTracking"
|
||||
:options="receive.units.filter((u) => u !== 'sat')"
|
||||
></q-select>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
Or you can access the wallet directly from your mobile
|
||||
browser using:
|
||||
<q-expansion-item
|
||||
icon="mobile_friendly"
|
||||
:label="$t('export_to_phone')"
|
||||
>
|
||||
<q-card>
|
||||
<q-card-section class="text-center">
|
||||
<p v-text="$t('export_to_phone_desc')"></p>
|
||||
<lnbits-qrcode
|
||||
:value="`${baseUrl}/wallet?usr=${g.user.id}&wal=${g.wallet.id}`"
|
||||
></lnbits-qrcode>
|
||||
</q-card-section>
|
||||
<span v-text="exportWalletQR"></span>
|
||||
<q-card-actions class="flex-center q-pb-md">
|
||||
<q-btn
|
||||
color="primary"
|
||||
@click="handleFiatTracking()"
|
||||
:disable="update.currency == ''"
|
||||
:label="g.fiatTracking ? 'Remove' : 'Add'"
|
||||
outline
|
||||
color="grey"
|
||||
:label="$t('copy_wallet_url')"
|
||||
@click="copyText(`${baseUrl}wallet?usr=${g.user.id}&wal=${g.wallet.id}`)"
|
||||
></q-btn>
|
||||
</div>
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-expansion-item>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-expansion-item>
|
||||
<q-separator></q-separator>
|
||||
<q-expansion-item
|
||||
group="extras"
|
||||
icon="settings"
|
||||
:label="$t('wallet_config')"
|
||||
>
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<q-input
|
||||
filled
|
||||
v-model.trim="update.name"
|
||||
label="Name"
|
||||
dense
|
||||
/>
|
||||
</div>
|
||||
<div class="col-4 q-pl-sm">
|
||||
<q-btn
|
||||
:disable="!update.name.length"
|
||||
unelevated
|
||||
class="q-mt-xs full-width"
|
||||
color="primary"
|
||||
:label="$t('update_name')"
|
||||
dense
|
||||
@click="updateWallet({ name: update.name })"
|
||||
></q-btn>
|
||||
</div>
|
||||
<div class="col-2"></div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-section>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<q-select
|
||||
filled
|
||||
dense
|
||||
v-model="update.currency"
|
||||
type="text"
|
||||
:disable="g.fiatTracking"
|
||||
:options="receive.units.filter((u) => u !== 'sat')"
|
||||
></q-select>
|
||||
</div>
|
||||
<div class="col-4 q-pl-sm">
|
||||
<q-btn
|
||||
dense
|
||||
color="primary"
|
||||
class="q-mt-xs full-width"
|
||||
@click="handleFiatTracking()"
|
||||
:disable="update.currency == ''"
|
||||
:label="g.fiatTracking ? 'Remove' : 'Add'"
|
||||
></q-btn>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<q-btn
|
||||
v-if="g.user.admin"
|
||||
flat
|
||||
round
|
||||
icon="settings"
|
||||
class="float-right q-mb-lg"
|
||||
to="/admin#exchange_providers"
|
||||
><q-tooltip
|
||||
v-text="$t('exchange_providers')"
|
||||
></q-tooltip
|
||||
></q-btn>
|
||||
</div>
|
||||
</div>
|
||||
<q-btn
|
||||
v-if="g.user.admin"
|
||||
class="absolute-top-right"
|
||||
flat
|
||||
round
|
||||
icon="settings"
|
||||
to="/admin#exchange_providers"
|
||||
><q-tooltip
|
||||
v-text="$t('exchange_providers')"
|
||||
></q-tooltip
|
||||
></q-btn>
|
||||
</q-card-section>
|
||||
<q-card-section>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<p v-text="$t('delete_wallet_desc')"></p>
|
||||
</div>
|
||||
<div class="col-4 q-pl-sm">
|
||||
<q-btn
|
||||
unelevated
|
||||
color="red-10"
|
||||
class="full-width"
|
||||
@click="deleteWallet()"
|
||||
:label="$t('delete_wallet')"
|
||||
></q-btn>
|
||||
</div>
|
||||
<div class="col-2"></div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-expansion-item>
|
||||
|
||||
<q-separator></q-separator>
|
||||
<q-expansion-item
|
||||
group="extras"
|
||||
icon="remove_circle"
|
||||
:label="$t('delete_wallet')"
|
||||
group="charts"
|
||||
icon="insights"
|
||||
:label="$t('wallet_charts')"
|
||||
>
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<p v-text="$t('delete_wallet_desc')"></p>
|
||||
<q-btn
|
||||
unelevated
|
||||
color="red-10"
|
||||
@click="deleteWallet()"
|
||||
:label="$t('delete_wallet')"
|
||||
></q-btn>
|
||||
<div class="row">
|
||||
<div class="col-md-4 col-sm-12">
|
||||
<q-checkbox
|
||||
dense
|
||||
@click="saveChartsPreferences"
|
||||
v-model="chartConfig.showBalance"
|
||||
:label="$t('payments_balance_chart')"
|
||||
>
|
||||
</q-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 col-sm-12">
|
||||
<q-checkbox
|
||||
dense
|
||||
@click="saveChartsPreferences"
|
||||
v-model="chartConfig.showBalanceInOut"
|
||||
:label="$t('payments_balance_in_out_chart')"
|
||||
>
|
||||
</q-checkbox>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-12">
|
||||
<q-checkbox
|
||||
dense
|
||||
@click="saveChartsPreferences"
|
||||
v-model="chartConfig.showPaymentCountInOut"
|
||||
:label="$t('payments_count_in_out_chart')"
|
||||
>
|
||||
</q-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-expansion-item>
|
||||
@@ -403,6 +481,27 @@
|
||||
>{% endfor %}
|
||||
</q-card>
|
||||
{% endif %}
|
||||
<q-card v-if="chartConfig.showBalance">
|
||||
<q-card-section class="q-pa-none">
|
||||
<div style="height: 200px" class="q-pa-sm">
|
||||
<canvas ref="walletBalanceChart"></canvas>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
<q-card v-if="chartConfig.showBalanceInOut">
|
||||
<q-card-section class="q-pa-none">
|
||||
<div style="height: 200px" class="q-pa-sm">
|
||||
<canvas ref="walletBalanceInOut"></canvas>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
<q-card v-if="chartConfig.showPaymentCountInOut">
|
||||
<q-card-section class="q-pa-none">
|
||||
<div style="height: 200px" class="q-pa-sm">
|
||||
<canvas ref="walletPaymentsInOut"></canvas>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -35,11 +35,13 @@ from lnbits.core.models import (
|
||||
PaymentWalletStats,
|
||||
Wallet,
|
||||
)
|
||||
from lnbits.core.models.users import User
|
||||
from lnbits.core.services.payments import get_payments_daily_stats
|
||||
from lnbits.db import Filters, Page
|
||||
from lnbits.decorators import (
|
||||
WalletTypeInfo,
|
||||
check_admin,
|
||||
check_user_exists,
|
||||
parse_filters,
|
||||
require_admin_key,
|
||||
require_invoice_key,
|
||||
@@ -135,13 +137,29 @@ async def api_payments_wallets_stats(
|
||||
@payment_router.get(
|
||||
"/stats/daily",
|
||||
name="Get payments history per day",
|
||||
dependencies=[Depends(check_admin)],
|
||||
response_model=List[PaymentDailyStats],
|
||||
openapi_extra=generate_filter_params_openapi(PaymentFilters),
|
||||
)
|
||||
async def api_payments_daily_stats(
|
||||
user: User = Depends(check_user_exists),
|
||||
filters: Filters[PaymentFilters] = Depends(parse_filters(PaymentFilters)),
|
||||
):
|
||||
|
||||
if not user.admin:
|
||||
exc = HTTPException(
|
||||
status_code=HTTPStatus.UNAUTHORIZED,
|
||||
detail="Missing wallet id.",
|
||||
)
|
||||
wallet_filter = next(
|
||||
(f for f in filters.filters if f.field == "wallet_id"), None
|
||||
)
|
||||
if not wallet_filter:
|
||||
raise exc
|
||||
wallet_id = list((wallet_filter.values or {}).values())
|
||||
if len(wallet_id) == 0:
|
||||
raise exc
|
||||
if not user.get_wallet(wallet_id[0]):
|
||||
raise exc
|
||||
return await get_payments_daily_stats(filters)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user