diff --git a/lnbits/extensions/splitpayments/crud.py b/lnbits/extensions/splitpayments/crud.py index ef10add4..de4e0822 100644 --- a/lnbits/extensions/splitpayments/crud.py +++ b/lnbits/extensions/splitpayments/crud.py @@ -1,5 +1,7 @@ from typing import List +from lnbits.helpers import urlsafe_short_hash + from . import db from .models import Target @@ -20,8 +22,15 @@ async def set_targets(source_wallet: str, targets: List[Target]): await conn.execute( """ INSERT INTO splitpayments.targets - (source, wallet, percent, alias) - VALUES (?, ?, ?, ?) + (id, source, wallet, percent, tag, alias) + VALUES (?, ?, ?, ?, ?, ?) """, - (source_wallet, target.wallet, target.percent, target.alias), + ( + urlsafe_short_hash(), + source_wallet, + target.wallet, + target.percent, + target.tag, + target.alias, + ), ) diff --git a/lnbits/extensions/splitpayments/migrations.py b/lnbits/extensions/splitpayments/migrations.py index b3921c42..eb72387e 100644 --- a/lnbits/extensions/splitpayments/migrations.py +++ b/lnbits/extensions/splitpayments/migrations.py @@ -1,3 +1,6 @@ +from lnbits.helpers import urlsafe_short_hash + + async def m001_initial(db): """ Initial split payment table. @@ -52,3 +55,45 @@ async def m002_float_percent(db): ) await db.execute("DROP TABLE splitpayments.splitpayments_old") + + +async def m003_add_id_and_tag(db): + """ + Add float percent and migrates the existing data. + """ + await db.execute("ALTER TABLE splitpayments.targets RENAME TO splitpayments_old") + await db.execute( + """ + CREATE TABLE splitpayments.targets ( + id TEXT PRIMARY KEY, + wallet TEXT NOT NULL, + source TEXT NOT NULL, + percent REAL NOT NULL CHECK (percent >= 0 AND percent <= 100), + tag TEXT NOT NULL, + alias TEXT, + + UNIQUE (source, wallet) + ); + """ + ) + + for row in [ + list(row) + for row in await db.fetchall("SELECT * FROM splitpayments.splitpayments_old") + ]: + await db.execute( + """ + INSERT INTO splitpayments.targets ( + id, + wallet, + source, + percent, + tag, + alias + ) + VALUES (?, ?, ?, ?, ?, ?) + """, + (urlsafe_short_hash(), row[0], row[1], row[2], "", row[3]), + ) + + await db.execute("DROP TABLE splitpayments.splitpayments_old") diff --git a/lnbits/extensions/splitpayments/models.py b/lnbits/extensions/splitpayments/models.py index 6338d97f..fc3db2c6 100644 --- a/lnbits/extensions/splitpayments/models.py +++ b/lnbits/extensions/splitpayments/models.py @@ -8,13 +8,15 @@ class Target(BaseModel): wallet: str source: str percent: float + tag: str alias: Optional[str] class TargetPutList(BaseModel): wallet: str = Query(...) alias: str = Query("") - percent: float = Query(..., ge=0.01, lt=100) + percent: float = Query(..., ge=0, lt=100) + tag: str class TargetPut(BaseModel): diff --git a/lnbits/extensions/splitpayments/static/js/index.js b/lnbits/extensions/splitpayments/static/js/index.js index 5d326231..f5f16276 100644 --- a/lnbits/extensions/splitpayments/static/js/index.js +++ b/lnbits/extensions/splitpayments/static/js/index.js @@ -10,7 +10,11 @@ function hashTargets(targets) { } function isTargetComplete(target) { - return target.wallet && target.wallet.trim() !== '' && target.percent > 0 + return ( + target.wallet && + target.wallet.trim() !== '' && + (target.percent > 0 || target.tag != '') + ) } new Vue({ @@ -20,7 +24,11 @@ new Vue({ return { selectedWallet: null, currentHash: '', // a string that must match if the edit data is unchanged - targets: [] + targets: [ + { + method: 'split' + } + ] } }, computed: { @@ -37,6 +45,14 @@ new Vue({ timeout: 500 }) }, + clearTarget(index) { + this.targets.splice(index, 1) + console.log(this.targets) + this.$q.notify({ + message: 'Removed item. You must click to save manually.', + timeout: 500 + }) + }, getTargets() { LNbits.api .request( @@ -50,17 +66,41 @@ new Vue({ .then(response => { this.currentHash = hashTargets(response.data) this.targets = response.data.concat({}) + for (let i = 0; i < this.targets.length; i++) { + if (this.targets[i].tag.length > 0) { + this.targets[i].method = 'tag' + } else if (this.targets[i].percent.length > 0) { + this.targets[i].method = 'split' + } else { + this.targets[i].method = '' + } + } }) }, changedWallet(wallet) { this.selectedWallet = wallet this.getTargets() }, - targetChanged(isPercent, index) { + clearChanged(index) { + if (this.targets[index].method == 'split') { + this.targets[index].tag = null + this.targets[index].method = 'split' + } else { + this.targets[index].percent = null + this.targets[index].method = 'tag' + } + }, + targetChanged(index) { // fix percent min and max range - if (isPercent) { + if (this.targets[index].percent) { if (this.targets[index].percent > 100) this.targets[index].percent = 100 if (this.targets[index].percent < 0) this.targets[index].percent = 0 + this.targets[index].tag = '' + } + + // not percentage + if (!this.targets[index].percent) { + this.targets[index].percent = 0 } // remove empty lines (except last) @@ -70,6 +110,7 @@ new Vue({ if ( (!target.wallet || target.wallet.trim() === '') && (!target.alias || target.alias.trim() === '') && + (!target.tag || target.tag.trim() === '') && !target.percent ) { this.targets.splice(i, 1) @@ -79,7 +120,7 @@ new Vue({ // add a line at the end if the last one is filled let last = this.targets[this.targets.length - 1] - if (last.wallet && last.wallet.trim() !== '' && last.percent > 0) { + if (last.wallet && last.wallet.trim() !== '') { this.targets.push({}) } @@ -108,11 +149,17 @@ new Vue({ if (t !== index) target.percent -= +(diff * target.percent).toFixed(2) }) } - // overwrite so changes appear this.targets = this.targets }, saveTargets() { + for (let i = 0; i < this.targets.length; i++) { + if (this.targets[i].tag != '') { + this.targets[i].percent = 0 + } else { + this.targets[i].tag = '' + } + } LNbits.api .request( 'PUT', @@ -121,7 +168,12 @@ new Vue({ { targets: this.targets .filter(isTargetComplete) - .map(({wallet, percent, alias}) => ({wallet, percent, alias})) + .map(({wallet, percent, tag, alias}) => ({ + wallet, + percent, + tag, + alias + })) } ) .then(response => { diff --git a/lnbits/extensions/splitpayments/tasks.py b/lnbits/extensions/splitpayments/tasks.py index 53378b20..33768805 100644 --- a/lnbits/extensions/splitpayments/tasks.py +++ b/lnbits/extensions/splitpayments/tasks.py @@ -25,7 +25,7 @@ async def on_invoice_paid(payment: Payment) -> None: return targets = await get_targets(payment.wallet_id) - + logger.debug(targets) if not targets: return @@ -35,21 +35,45 @@ async def on_invoice_paid(payment: Payment) -> None: logger.error("splitpayment failure: total percent adds up to more than 100%") return - logger.debug(f"performing split payments to {len(targets)} targets") + logger.debug(f"checking if tagged for {len(targets)} targets") + tagged = False for target in targets: - amount = int(payment.amount * target.percent / 100) # msats - payment_hash, payment_request = await create_invoice( - wallet_id=target.wallet, - amount=int(amount / 1000), # sats - internal=True, - memo=f"split payment: {target.percent}% for {target.alias or target.wallet}", - extra={"tag": "splitpayments"}, - ) - logger.debug(f"created split invoice: {payment_hash}") + if target.tag in payment.extra: + tagged = True + payment_hash, payment_request = await create_invoice( + wallet_id=target.wallet, + amount=int(payment.amount / 1000), # sats + internal=True, + memo=f"Pushed tagged payment to {target.alias}", + extra={"tag": "splitpayments"}, + ) + logger.debug(f"created split invoice: {payment_hash}") - checking_id = await pay_invoice( - payment_request=payment_request, - wallet_id=payment.wallet_id, - extra={"tag": "splitpayments"}, - ) - logger.debug(f"paid split invoice: {checking_id}") + checking_id = await pay_invoice( + payment_request=payment_request, + wallet_id=payment.wallet_id, + extra={"tag": "splitpayments"}, + ) + logger.debug(f"paid split invoice: {checking_id}") + + logger.debug(f"performing split to {len(targets)} targets") + + if tagged == False: + for target in targets: + if target.percent > 0: + amount = int(payment.amount * target.percent / 100) # msats + payment_hash, payment_request = await create_invoice( + wallet_id=target.wallet, + amount=int(amount / 1000), # sats + internal=True, + memo=f"split payment: {target.percent}% for {target.alias or target.wallet}", + extra={"tag": "splitpayments"}, + ) + logger.debug(f"created split invoice: {payment_hash}") + + checking_id = await pay_invoice( + payment_request=payment_request, + wallet_id=payment.wallet_id, + extra={"tag": "splitpayments"}, + ) + logger.debug(f"paid split invoice: {checking_id}") diff --git a/lnbits/extensions/splitpayments/templates/splitpayments/index.html b/lnbits/extensions/splitpayments/templates/splitpayments/index.html index 1cceb7ba..b105bf2c 100644 --- a/lnbits/extensions/splitpayments/templates/splitpayments/index.html +++ b/lnbits/extensions/splitpayments/templates/splitpayments/index.html @@ -31,39 +31,80 @@ style="flex-wrap: nowrap" v-for="(target, t) in targets" > - + + + + + + + - + + Add more + + +
@@ -76,7 +117,7 @@ unelevated color="primary" type="submit" - :disabled="!isDirty" + :disabled="targets.length < 2" > Save Targets diff --git a/lnbits/extensions/splitpayments/views_api.py b/lnbits/extensions/splitpayments/views_api.py index 8b5a282a..41a11c94 100644 --- a/lnbits/extensions/splitpayments/views_api.py +++ b/lnbits/extensions/splitpayments/views_api.py @@ -50,16 +50,15 @@ async def api_targets_set( Target( wallet=wallet.id, source=wal.wallet.id, + tag=entry.tag, percent=entry.percent, alias=entry.alias, ) ) - - percent_sum = sum([target.percent for target in targets]) - if percent_sum > 100: - raise HTTPException( - status_code=HTTPStatus.BAD_REQUEST, detail="Splitting over 100%." - ) - + percent_sum = sum([target.percent for target in targets]) + if percent_sum > 100: + raise HTTPException( + status_code=HTTPStatus.BAD_REQUEST, detail="Splitting over 100%." + ) await set_targets(wal.wallet.id, targets) return ""