Include both expense and receivable links in net settlements

Both settlement dialogs now fetch BOTH expense and receivable entries
to properly link all entries being netted in a settlement.

This ensures that when a user has:
- 2 expenses (80 EUR - castle owes user)
- 1 receivable (1000 EUR - user owes castle)

The net settlement (920 EUR) includes links to all three entries:
^exp-xxx ^exp-xxx ^rcv-xxx

This allows proper tracking of which specific entries were settled.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
padreug 2025-12-15 01:40:12 +01:00
parent 7362d6292e
commit f8af54f90b

View file

@ -1151,15 +1151,26 @@ window.app = Vue.createApp({
const fiatCurrency = Object.keys(fiatBalances)[0] || null // Get first fiat currency (e.g., 'EUR') const fiatCurrency = Object.keys(fiatBalances)[0] || null // Get first fiat currency (e.g., 'EUR')
const fiatAmount = fiatCurrency ? Math.abs(fiatBalances[fiatCurrency]) : 0 const fiatAmount = fiatCurrency ? Math.abs(fiatBalances[fiatCurrency]) : 0
// Fetch unsettled receivable entries for this user // Fetch unsettled entries for this user (BOTH receivables AND expenses for net settlement)
let unsettledEntries = [] let allEntryLinks = []
try { try {
const response = await LNbits.api.request( // Fetch receivable entries (user owes castle)
const receivableResponse = await LNbits.api.request(
'GET', 'GET',
`/castle/api/v1/users/${userBalance.user_id}/unsettled-entries?entry_type=receivable`, `/castle/api/v1/users/${userBalance.user_id}/unsettled-entries?entry_type=receivable`,
this.g.user.wallets[0].adminkey this.g.user.wallets[0].adminkey
) )
unsettledEntries = response.data.unsettled_entries || [] const receivableEntries = receivableResponse.data.unsettled_entries || []
allEntryLinks.push(...receivableEntries.map(e => e.link).filter(l => l))
// Also fetch expense entries (castle owes user) - these are netted in the settlement
const expenseResponse = await LNbits.api.request(
'GET',
`/castle/api/v1/users/${userBalance.user_id}/unsettled-entries?entry_type=expense`,
this.g.user.wallets[0].adminkey
)
const expenseEntries = expenseResponse.data.unsettled_entries || []
allEntryLinks.push(...expenseEntries.map(e => e.link).filter(l => l))
} catch (error) { } catch (error) {
console.warn('Could not fetch unsettled entries:', error) console.warn('Could not fetch unsettled entries:', error)
} }
@ -1182,8 +1193,7 @@ window.app = Vue.createApp({
pollIntervalId: null, pollIntervalId: null,
exchangeRate: fiatAmount > 0 ? Math.abs(userBalance.balance) / fiatAmount : this.currentExchangeRate, // Calculate rate from actual amounts or use current rate 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: allEntryLinks // Include BOTH rcv-xxx AND exp-xxx links for net settlement
entryLinks: unsettledEntries.map(e => e.link).filter(l => l) // Extract rcv-xxx links
} }
}, },
async generateSettlementInvoice() { async generateSettlementInvoice() {
@ -1362,15 +1372,26 @@ window.app = Vue.createApp({
const maxAmountSats = Math.abs(userBalance.balance) const maxAmountSats = Math.abs(userBalance.balance)
const maxAmountFiat = Math.abs(fiatAmount) const maxAmountFiat = Math.abs(fiatAmount)
// Fetch unsettled expense entries for this user // Fetch unsettled entries for this user (BOTH expenses AND receivables for net settlement)
let unsettledEntries = [] let allEntryLinks = []
try { try {
const response = await LNbits.api.request( // Fetch expense entries (castle owes user)
const expenseResponse = await LNbits.api.request(
'GET', 'GET',
`/castle/api/v1/users/${userBalance.user_id}/unsettled-entries?entry_type=expense`, `/castle/api/v1/users/${userBalance.user_id}/unsettled-entries?entry_type=expense`,
this.g.user.wallets[0].adminkey this.g.user.wallets[0].adminkey
) )
unsettledEntries = response.data.unsettled_entries || [] const expenseEntries = expenseResponse.data.unsettled_entries || []
allEntryLinks.push(...expenseEntries.map(e => e.link).filter(l => l))
// Also fetch receivable entries (user owes castle) - these are netted in the settlement
const receivableResponse = await LNbits.api.request(
'GET',
`/castle/api/v1/users/${userBalance.user_id}/unsettled-entries?entry_type=receivable`,
this.g.user.wallets[0].adminkey
)
const receivableEntries = receivableResponse.data.unsettled_entries || []
allEntryLinks.push(...receivableEntries.map(e => e.link).filter(l => l))
} catch (error) { } catch (error) {
console.warn('Could not fetch unsettled entries:', error) console.warn('Could not fetch unsettled entries:', error)
} }
@ -1390,8 +1411,7 @@ window.app = Vue.createApp({
paymentSuccess: false, paymentSuccess: false,
exchangeRate: maxAmountFiat > 0 ? maxAmountSats / maxAmountFiat : this.currentExchangeRate, exchangeRate: maxAmountFiat > 0 ? maxAmountSats / maxAmountFiat : this.currentExchangeRate,
originalCurrency: fiatCurrency || 'BTC', originalCurrency: fiatCurrency || 'BTC',
unsettledEntries: unsettledEntries, // Store for linking in settlement entryLinks: allEntryLinks // Include BOTH exp-xxx AND rcv-xxx links for net settlement
entryLinks: unsettledEntries.map(e => e.link).filter(l => l) // Extract exp-xxx links
} }
}, },
async sendLightningPayment() { async sendLightningPayment() {