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.reference = ''
|
||||||
this.receivableDialog.currency = null
|
this.receivableDialog.currency = null
|
||||||
},
|
},
|
||||||
showSettleReceivableDialog(userBalance) {
|
async showSettleReceivableDialog(userBalance) {
|
||||||
// Only show for users who owe castle (positive balance = receivable)
|
// Only show for users who owe castle (positive balance = receivable)
|
||||||
if (userBalance.balance <= 0) return
|
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 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
|
||||||
|
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 = {
|
this.settleReceivableDialog = {
|
||||||
show: true,
|
show: true,
|
||||||
user_id: userBalance.user_id,
|
user_id: userBalance.user_id,
|
||||||
|
|
@ -1168,7 +1181,9 @@ window.app = Vue.createApp({
|
||||||
checkWalletKey: null,
|
checkWalletKey: null,
|
||||||
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: unsettledEntries.map(e => e.link).filter(l => l) // Extract rcv-xxx links
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async generateSettlementInvoice() {
|
async generateSettlementInvoice() {
|
||||||
|
|
@ -1304,6 +1319,11 @@ window.app = Vue.createApp({
|
||||||
payload.amount_sats = this.settleReceivableDialog.maxAmount
|
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(
|
const response = await LNbits.api.request(
|
||||||
'POST',
|
'POST',
|
||||||
'/castle/api/v1/receivables/settle',
|
'/castle/api/v1/receivables/settle',
|
||||||
|
|
@ -1329,7 +1349,7 @@ window.app = Vue.createApp({
|
||||||
this.settleReceivableDialog.loading = false
|
this.settleReceivableDialog.loading = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
showPayUserDialog(userBalance) {
|
async showPayUserDialog(userBalance) {
|
||||||
// Only show for users castle owes (negative balance = payable)
|
// Only show for users castle owes (negative balance = payable)
|
||||||
if (userBalance.balance >= 0) return
|
if (userBalance.balance >= 0) return
|
||||||
|
|
||||||
|
|
@ -1342,6 +1362,19 @@ 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
|
||||||
|
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 = {
|
this.payUserDialog = {
|
||||||
show: true,
|
show: true,
|
||||||
user_id: userBalance.user_id,
|
user_id: userBalance.user_id,
|
||||||
|
|
@ -1356,7 +1389,9 @@ window.app = Vue.createApp({
|
||||||
loading: false,
|
loading: false,
|
||||||
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: unsettledEntries.map(e => e.link).filter(l => l) // Extract exp-xxx links
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async sendLightningPayment() {
|
async sendLightningPayment() {
|
||||||
|
|
@ -1395,16 +1430,23 @@ window.app = Vue.createApp({
|
||||||
)
|
)
|
||||||
|
|
||||||
// Record the payment in Castle accounting
|
// Record the payment in Castle accounting
|
||||||
|
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(
|
await LNbits.api.request(
|
||||||
'POST',
|
'POST',
|
||||||
'/castle/api/v1/payables/pay',
|
'/castle/api/v1/payables/pay',
|
||||||
this.g.user.wallets[0].adminkey,
|
this.g.user.wallets[0].adminkey,
|
||||||
{
|
payPayload
|
||||||
user_id: this.payUserDialog.user_id,
|
|
||||||
amount: this.payUserDialog.amount,
|
|
||||||
payment_method: 'lightning',
|
|
||||||
payment_hash: paymentResponse.data.payment_hash
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
this.payUserDialog.paymentSuccess = true
|
this.payUserDialog.paymentSuccess = true
|
||||||
|
|
@ -1461,6 +1503,11 @@ window.app = Vue.createApp({
|
||||||
payload.amount_sats = this.payUserDialog.maxAmount
|
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(
|
const response = await LNbits.api.request(
|
||||||
'POST',
|
'POST',
|
||||||
'/castle/api/v1/payables/pay',
|
'/castle/api/v1/payables/pay',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue