diff --git a/crud.py b/crud.py index 94c1d20..326352c 100644 --- a/crud.py +++ b/crud.py @@ -8,7 +8,7 @@ from lnbits.helpers import urlsafe_short_hash from .models import ( CreateDcaClientData, DcaClient, UpdateDcaClientData, - CreateDepositData, DcaDeposit, UpdateDepositStatusData, + CreateDepositData, DcaDeposit, UpdateDepositData, UpdateDepositStatusData, CreateDcaPaymentData, DcaPayment, ClientBalanceSummary, CreateLamassuConfigData, LamassuConfig, UpdateLamassuConfigData, @@ -153,6 +153,28 @@ async def update_deposit_status(deposit_id: str, data: UpdateDepositStatusData) return await get_deposit(deposit_id) +async def update_deposit(deposit_id: str, data: UpdateDepositData) -> Optional[DcaDeposit]: + update_data = {k: v for k, v in data.dict().items() if v is not None} + if not update_data: + return await get_deposit(deposit_id) + + set_clause = ", ".join([f"{k} = :{k}" for k in update_data.keys()]) + update_data["id"] = deposit_id + + await db.execute( + f"UPDATE satoshimachine.dca_deposits SET {set_clause} WHERE id = :id", + update_data + ) + return await get_deposit(deposit_id) + + +async def delete_deposit(deposit_id: str) -> None: + await db.execute( + "DELETE FROM satoshimachine.dca_deposits WHERE id = :id", + {"id": deposit_id} + ) + + # DCA Payment CRUD Operations async def create_dca_payment(data: CreateDcaPaymentData) -> DcaPayment: payment_id = urlsafe_short_hash() diff --git a/models.py b/models.py index e4bf64d..2fb60a6 100644 --- a/models.py +++ b/models.py @@ -60,6 +60,18 @@ class DcaDeposit(BaseModel): confirmed_at: Optional[datetime] +class UpdateDepositData(BaseModel): + amount: Optional[float] = None + currency: Optional[str] = None + notes: Optional[str] = None + + @validator('amount') + def round_amount_to_cents(cls, v): + if v is not None: + return round(float(v), 2) + return v + + class UpdateDepositStatusData(BaseModel): status: str notes: Optional[str] = None diff --git a/static/js/index.js b/static/js/index.js index 610ec0a..50b0fce 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -389,12 +389,12 @@ window.app = Vue.createApp({ } if (this.depositFormDialog.data.id) { - // Update existing deposit (mainly for notes/status) + // Update existing pending deposit const { data: updatedDeposit } = await LNbits.api.request( 'PUT', `/satmachineadmin/api/v1/dca/deposits/${this.depositFormDialog.data.id}`, null, - { status: this.depositFormDialog.data.status, notes: data.notes } + { amount: data.amount, currency: data.currency, notes: data.notes } ) const index = this.deposits.findIndex(d => d.id === updatedDeposit.id) if (index !== -1) { @@ -460,6 +460,28 @@ window.app = Vue.createApp({ this.depositFormDialog.show = true }, + async deleteDeposit(deposit) { + try { + await LNbits.utils + .confirmDialog('Are you sure you want to delete this pending deposit?') + .onOk(async () => { + await LNbits.api.request( + 'DELETE', + `/satmachineadmin/api/v1/dca/deposits/${deposit.id}`, + null + ) + this.deposits = this.deposits.filter(d => d.id !== deposit.id) + this.$q.notify({ + type: 'positive', + message: 'Deposit deleted successfully', + timeout: 5000 + }) + }) + } catch (error) { + LNbits.utils.notifyApiError(error) + } + }, + // Export Methods async exportClientsCSV() { await LNbits.utils.exportCSV(this.clientsTable.columns, this.dcaClients) diff --git a/templates/satmachineadmin/index.html b/templates/satmachineadmin/index.html index 83de8c7..9c0f3a9 100644 --- a/templates/satmachineadmin/index.html +++ b/templates/satmachineadmin/index.html @@ -192,12 +192,21 @@ Confirm Deposit Edit Deposit + + Delete Deposit + diff --git a/views_api.py b/views_api.py index a4a8b94..eb38da8 100644 --- a/views_api.py +++ b/views_api.py @@ -19,7 +19,9 @@ from .crud import ( create_deposit, get_all_deposits, get_deposit, + update_deposit, update_deposit_status, + delete_deposit, get_client_balance_summary, # Lamassu config CRUD operations create_lamassu_config, @@ -39,6 +41,7 @@ from .models import ( UpdateDcaClientData, CreateDepositData, DcaDeposit, + UpdateDepositData, UpdateDepositStatusData, ClientBalanceSummary, CreateLamassuConfigData, @@ -161,6 +164,56 @@ async def api_update_deposit_status( return updated_deposit +@satmachineadmin_api_router.put("/api/v1/dca/deposits/{deposit_id}") +async def api_update_deposit( + deposit_id: str, + data: UpdateDepositData, + user: User = Depends(check_super_user), +) -> DcaDeposit: + """Update deposit fields (amount, currency, notes). Only pending deposits can be edited.""" + deposit = await get_deposit(deposit_id) + if not deposit: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="Deposit not found." + ) + + if deposit.status != "pending": + raise HTTPException( + status_code=HTTPStatus.BAD_REQUEST, + detail="Only pending deposits can be edited.", + ) + + updated_deposit = await update_deposit(deposit_id, data) + if not updated_deposit: + raise HTTPException( + status_code=HTTPStatus.INTERNAL_SERVER_ERROR, + detail="Failed to update deposit.", + ) + return updated_deposit + + +@satmachineadmin_api_router.delete("/api/v1/dca/deposits/{deposit_id}") +async def api_delete_deposit( + deposit_id: str, + user: User = Depends(check_super_user), +): + """Delete a deposit. Only pending deposits (not yet inserted into the machine) can be deleted.""" + deposit = await get_deposit(deposit_id) + if not deposit: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="Deposit not found." + ) + + if deposit.status != "pending": + raise HTTPException( + status_code=HTTPStatus.BAD_REQUEST, + detail="Only pending deposits can be deleted. Confirmed deposits have already been inserted into the machine.", + ) + + await delete_deposit(deposit_id) + return {"message": "Deposit deleted successfully"} + + # Transaction Polling Endpoints