Adds functionality to pay users (Castle pays)
Implements the ability for the super user (Castle) to pay other users for expenses or liabilities. Introduces a new `PayUser` model to represent these payments, along with API endpoints to process and record them. Integrates a "Pay User" button into the user list, allowing the super user to initiate payments through either lightning or manual methods (cash, bank transfer, check). Adds UI elements and logic for handling both lightning payments (generating invoices and paying them) and manual payment recording. This functionality allows Castle to manage and settle debts with its users directly through the application.
This commit is contained in:
parent
f0257e7c7f
commit
60aba90e00
4 changed files with 560 additions and 1 deletions
|
|
@ -192,6 +192,7 @@
|
|||
</template>
|
||||
<template v-slot:body-cell-actions="props">
|
||||
<q-td :props="props">
|
||||
<!-- User owes Castle (negative balance) - Castle receives payment -->
|
||||
<q-btn
|
||||
v-if="props.row.balance < 0"
|
||||
flat
|
||||
|
|
@ -201,7 +202,19 @@
|
|||
icon="payments"
|
||||
@click="showSettleReceivableDialog(props.row)"
|
||||
>
|
||||
<q-tooltip>Settle receivable (user pays)</q-tooltip>
|
||||
<q-tooltip>Settle receivable (user pays castle)</q-tooltip>
|
||||
</q-btn>
|
||||
<!-- Castle owes User (positive balance) - Castle pays user -->
|
||||
<q-btn
|
||||
v-if="props.row.balance > 0"
|
||||
flat
|
||||
dense
|
||||
size="sm"
|
||||
color="positive"
|
||||
icon="send"
|
||||
@click="showPayUserDialog(props.row)"
|
||||
>
|
||||
<q-tooltip>Pay user (castle pays user)</q-tooltip>
|
||||
</q-btn>
|
||||
</q-td>
|
||||
</template>
|
||||
|
|
@ -1254,4 +1267,126 @@
|
|||
</q-card>
|
||||
</q-dialog>
|
||||
|
||||
<!-- Pay User Dialog (Castle pays user - Super User Only) -->
|
||||
<q-dialog v-model="payUserDialog.show" position="top">
|
||||
<q-card class="q-pa-md" style="min-width: 400px">
|
||||
<q-form @submit="submitPayUser">
|
||||
<h6 class="q-my-none q-mb-md">Pay User</h6>
|
||||
|
||||
<div class="q-mb-md">
|
||||
<div class="text-subtitle2">User</div>
|
||||
<div>{% raw %}{{ payUserDialog.username }}{% endraw %}</div>
|
||||
<div class="text-caption text-grey">{% raw %}{{ payUserDialog.user_id }}{% endraw %}</div>
|
||||
</div>
|
||||
|
||||
<div class="q-mb-md">
|
||||
<div class="text-subtitle2">Amount Castle Owes</div>
|
||||
<div class="text-positive text-h6">
|
||||
{% raw %}{{ formatSats(payUserDialog.maxAmount) }}{% endraw %} sats
|
||||
</div>
|
||||
<div v-if="payUserDialog.fiatCurrency && payUserDialog.maxAmountFiat" class="text-caption">
|
||||
{% raw %}{{ formatFiat(payUserDialog.maxAmountFiat, payUserDialog.fiatCurrency) }}{% endraw %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.number="payUserDialog.amount"
|
||||
type="number"
|
||||
:label="paymentAmountLabel"
|
||||
hint="Amount castle is paying (max: owed amount)"
|
||||
:max="paymentMaxAmount"
|
||||
:step="paymentAmountStep"
|
||||
:rules="[
|
||||
val => val !== null && val !== undefined && val !== '' || 'Amount is required',
|
||||
val => val > 0 || 'Amount must be positive',
|
||||
val => val <= paymentMaxAmount || 'Cannot exceed owed amount'
|
||||
]"
|
||||
></q-input>
|
||||
|
||||
<q-select
|
||||
filled
|
||||
dense
|
||||
v-model="payUserDialog.payment_method"
|
||||
:options="[
|
||||
{label: 'Lightning Payment', value: 'lightning'},
|
||||
{label: 'Cash', value: 'cash'},
|
||||
{label: 'Bank Transfer', value: 'bank_transfer'},
|
||||
{label: 'Check', value: 'check'},
|
||||
{label: 'Other', value: 'other'}
|
||||
]"
|
||||
option-label="label"
|
||||
option-value="value"
|
||||
emit-value
|
||||
map-options
|
||||
label="Payment Method *"
|
||||
:rules="[val => !!val || 'Payment method is required']"
|
||||
></q-select>
|
||||
|
||||
<q-input
|
||||
v-if="payUserDialog.payment_method !== 'lightning'"
|
||||
filled
|
||||
dense
|
||||
v-model="payUserDialog.description"
|
||||
type="text"
|
||||
label="Description (optional)"
|
||||
:placeholder="payUserDialog.payment_method === 'cash' ?
|
||||
`Cash payment to ${payUserDialog.username}` :
|
||||
payUserDialog.payment_method === 'bank_transfer' ?
|
||||
`Bank transfer to ${payUserDialog.username}` :
|
||||
payUserDialog.payment_method === 'check' ?
|
||||
`Check payment to ${payUserDialog.username}` :
|
||||
`Payment to ${payUserDialog.username}`"
|
||||
hint="Auto-generated if left empty"
|
||||
></q-input>
|
||||
|
||||
<q-input
|
||||
v-if="payUserDialog.payment_method !== 'lightning'"
|
||||
filled
|
||||
dense
|
||||
v-model="payUserDialog.reference"
|
||||
type="text"
|
||||
label="Reference (optional)"
|
||||
hint="Receipt number, transaction ID, etc."
|
||||
></q-input>
|
||||
|
||||
<!-- Show success message if lightning payment was made -->
|
||||
<div v-if="payUserDialog.paymentSuccess" class="q-mt-md q-pa-md bg-positive text-white rounded-borders">
|
||||
<q-icon name="check_circle" size="md" class="q-mr-sm"></q-icon>
|
||||
Payment sent successfully!
|
||||
</div>
|
||||
|
||||
<div class="row q-mt-md q-gutter-sm">
|
||||
<!-- For lightning: send payment button -->
|
||||
<q-btn
|
||||
v-if="payUserDialog.payment_method === 'lightning'"
|
||||
unelevated
|
||||
color="positive"
|
||||
@click="sendLightningPayment"
|
||||
:loading="payUserDialog.loading"
|
||||
:disable="payUserDialog.paymentSuccess"
|
||||
>
|
||||
Send Lightning Payment
|
||||
</q-btn>
|
||||
|
||||
<!-- For non-lightning: record manual payment button -->
|
||||
<q-btn
|
||||
v-if="payUserDialog.payment_method !== 'lightning'"
|
||||
unelevated
|
||||
color="positive"
|
||||
type="submit"
|
||||
:loading="payUserDialog.loading"
|
||||
>
|
||||
Record Payment
|
||||
</q-btn>
|
||||
|
||||
<q-btn v-close-popup flat color="grey">
|
||||
{% raw %}{{ payUserDialog.paymentSuccess ? 'Close' : 'Cancel' }}{% endraw %}
|
||||
</q-btn>
|
||||
</div>
|
||||
</q-form>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue