Fix settlement linking to original expense/receivable entries
The frontend now: 1. Fetches unsettled entries when opening settlement dialogs 2. Includes entry links (exp-xxx/rcv-xxx) in settlement payloads 3. Passes settled_entry_links to backend for proper linking This enables the settlement transaction to include links back to the original entries it is settling, making it possible to track which expenses/receivables have been paid. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
1ae5c8c927
commit
7362d6292e
1 changed files with 57 additions and 10 deletions
|
|
@ -1137,7 +1137,7 @@ window.app = Vue.createApp({
|
|||
this.receivableDialog.reference = ''
|
||||
this.receivableDialog.currency = null
|
||||
},
|
||||
showSettleReceivableDialog(userBalance) {
|
||||
async showSettleReceivableDialog(userBalance) {
|
||||
// Only show for users who owe castle (positive balance = receivable)
|
||||
if (userBalance.balance <= 0) return
|
||||
|
||||
|
|
@ -1151,6 +1151,19 @@ window.app = Vue.createApp({
|
|||
const fiatCurrency = Object.keys(fiatBalances)[0] || null // Get first fiat currency (e.g., 'EUR')
|
||||
const fiatAmount = fiatCurrency ? Math.abs(fiatBalances[fiatCurrency]) : 0
|
||||
|
||||
// Fetch unsettled receivable entries for this user
|
||||
let unsettledEntries = []
|
||||
try {
|
||||
const response = await LNbits.api.request(
|
||||
'GET',
|
||||
`/castle/api/v1/users/${userBalance.user_id}/unsettled-entries?entry_type=receivable`,
|
||||
this.g.user.wallets[0].adminkey
|
||||
)
|
||||
unsettledEntries = response.data.unsettled_entries || []
|
||||
} catch (error) {
|
||||
console.warn('Could not fetch unsettled entries:', error)
|
||||
}
|
||||
|
||||
this.settleReceivableDialog = {
|
||||
show: true,
|
||||
user_id: userBalance.user_id,
|
||||
|
|
@ -1168,7 +1181,9 @@ window.app = Vue.createApp({
|
|||
checkWalletKey: null,
|
||||
pollIntervalId: null,
|
||||
exchangeRate: fiatAmount > 0 ? Math.abs(userBalance.balance) / fiatAmount : this.currentExchangeRate, // Calculate rate from actual amounts or use current rate
|
||||
originalCurrency: fiatCurrency || 'BTC'
|
||||
originalCurrency: fiatCurrency || 'BTC',
|
||||
unsettledEntries: unsettledEntries, // Store for linking in settlement
|
||||
entryLinks: unsettledEntries.map(e => e.link).filter(l => l) // Extract rcv-xxx links
|
||||
}
|
||||
},
|
||||
async generateSettlementInvoice() {
|
||||
|
|
@ -1304,6 +1319,11 @@ window.app = Vue.createApp({
|
|||
payload.amount_sats = this.settleReceivableDialog.maxAmount
|
||||
}
|
||||
|
||||
// Include links to entries being settled
|
||||
if (this.settleReceivableDialog.entryLinks && this.settleReceivableDialog.entryLinks.length > 0) {
|
||||
payload.settled_entry_links = this.settleReceivableDialog.entryLinks
|
||||
}
|
||||
|
||||
const response = await LNbits.api.request(
|
||||
'POST',
|
||||
'/castle/api/v1/receivables/settle',
|
||||
|
|
@ -1329,7 +1349,7 @@ window.app = Vue.createApp({
|
|||
this.settleReceivableDialog.loading = false
|
||||
}
|
||||
},
|
||||
showPayUserDialog(userBalance) {
|
||||
async showPayUserDialog(userBalance) {
|
||||
// Only show for users castle owes (negative balance = payable)
|
||||
if (userBalance.balance >= 0) return
|
||||
|
||||
|
|
@ -1342,6 +1362,19 @@ window.app = Vue.createApp({
|
|||
const maxAmountSats = Math.abs(userBalance.balance)
|
||||
const maxAmountFiat = Math.abs(fiatAmount)
|
||||
|
||||
// Fetch unsettled expense entries for this user
|
||||
let unsettledEntries = []
|
||||
try {
|
||||
const response = await LNbits.api.request(
|
||||
'GET',
|
||||
`/castle/api/v1/users/${userBalance.user_id}/unsettled-entries?entry_type=expense`,
|
||||
this.g.user.wallets[0].adminkey
|
||||
)
|
||||
unsettledEntries = response.data.unsettled_entries || []
|
||||
} catch (error) {
|
||||
console.warn('Could not fetch unsettled entries:', error)
|
||||
}
|
||||
|
||||
this.payUserDialog = {
|
||||
show: true,
|
||||
user_id: userBalance.user_id,
|
||||
|
|
@ -1356,7 +1389,9 @@ window.app = Vue.createApp({
|
|||
loading: false,
|
||||
paymentSuccess: false,
|
||||
exchangeRate: maxAmountFiat > 0 ? maxAmountSats / maxAmountFiat : this.currentExchangeRate,
|
||||
originalCurrency: fiatCurrency || 'BTC'
|
||||
originalCurrency: fiatCurrency || 'BTC',
|
||||
unsettledEntries: unsettledEntries, // Store for linking in settlement
|
||||
entryLinks: unsettledEntries.map(e => e.link).filter(l => l) // Extract exp-xxx links
|
||||
}
|
||||
},
|
||||
async sendLightningPayment() {
|
||||
|
|
@ -1395,16 +1430,23 @@ window.app = Vue.createApp({
|
|||
)
|
||||
|
||||
// Record the payment in Castle accounting
|
||||
await LNbits.api.request(
|
||||
'POST',
|
||||
'/castle/api/v1/payables/pay',
|
||||
this.g.user.wallets[0].adminkey,
|
||||
{
|
||||
const payPayload = {
|
||||
user_id: this.payUserDialog.user_id,
|
||||
amount: this.payUserDialog.amount,
|
||||
payment_method: 'lightning',
|
||||
payment_hash: paymentResponse.data.payment_hash
|
||||
}
|
||||
|
||||
// Include links to entries being settled
|
||||
if (this.payUserDialog.entryLinks && this.payUserDialog.entryLinks.length > 0) {
|
||||
payPayload.settled_entry_links = this.payUserDialog.entryLinks
|
||||
}
|
||||
|
||||
await LNbits.api.request(
|
||||
'POST',
|
||||
'/castle/api/v1/payables/pay',
|
||||
this.g.user.wallets[0].adminkey,
|
||||
payPayload
|
||||
)
|
||||
|
||||
this.payUserDialog.paymentSuccess = true
|
||||
|
|
@ -1461,6 +1503,11 @@ window.app = Vue.createApp({
|
|||
payload.amount_sats = this.payUserDialog.maxAmount
|
||||
}
|
||||
|
||||
// Include links to entries being settled
|
||||
if (this.payUserDialog.entryLinks && this.payUserDialog.entryLinks.length > 0) {
|
||||
payload.settled_entry_links = this.payUserDialog.entryLinks
|
||||
}
|
||||
|
||||
const response = await LNbits.api.request(
|
||||
'POST',
|
||||
'/castle/api/v1/payables/pay',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue