diff --git a/core/payment/src/api/debit_notes.rs b/core/payment/src/api/debit_notes.rs index e37b207732..e1efeb752b 100644 --- a/core/payment/src/api/debit_notes.rs +++ b/core/payment/src/api/debit_notes.rs @@ -341,6 +341,51 @@ async fn accept_debit_note( Ok(None) => return response::server_error(&format!("Activity {} not found", activity_id)), Err(e) => return response::server_error(&e), }; + //check if invoice exists and accepted for this activity + match db + .as_dao::() + .get_by_agreement(activity.agreement_id.clone(), node_id) + .await + { + Ok(Some(invoice)) => match invoice.status { + DocumentStatus::Issued => { + log::error!( + "Wrong status [{}] for invoice [{}] for Activity [{}] and agreement [{}]", + invoice.status, + invoice.invoice_id, + activity_id, + activity.agreement_id + ); + return response::server_error(&"Wrong status for invoice"); + } + DocumentStatus::Received => { + log::warn!("Received debit note [{}] for freshly received invoice [{}] for Activity [{}] and agreement [{}]", + debit_note_id, + invoice.invoice_id, + activity_id, + activity.agreement_id + ); + } + DocumentStatus::Accepted + | DocumentStatus::Rejected + | DocumentStatus::Failed + | DocumentStatus::Settled + | DocumentStatus::Cancelled => { + log::info!("Received debit note [{}] for already existing invoice [{}] with status {} for Activity [{}] and agreement [{}]", + debit_note_id, + invoice.invoice_id, + invoice.status, + activity_id, + activity.agreement_id + ); + return response::ok(Null); + } + }, + Ok(None) => { + //no problem, ignore + } + Err(e) => return response::server_error(&e), + }; let amount_to_pay = &debit_note.total_amount_due - &activity.total_amount_scheduled.0; log::trace!( diff --git a/core/payment/src/dao/invoice.rs b/core/payment/src/dao/invoice.rs index e4463519d8..69cc299878 100644 --- a/core/payment/src/dao/invoice.rs +++ b/core/payment/src/dao/invoice.rs @@ -191,6 +191,36 @@ impl<'c> InvoiceDao<'c> { .await } + /* + * Get invoice by agreement id + * Only one invoice per agreement is allowed (it is enforced by the unique constraint on pay_invoice_x_activity) + */ + pub async fn get_by_agreement( + &self, + agreement_id: String, + owner_id: NodeId, + ) -> DbResult> { + readonly_transaction(self.pool, move |conn| { + let invoice: Option = query!() + .filter(dsl::agreement_id.eq(&agreement_id)) + .filter(dsl::owner_id.eq(owner_id)) + .first(conn) + .optional()?; + match invoice { + Some(invoice) => { + let activity_ids = activity_dsl::pay_invoice_x_activity + .select(activity_dsl::activity_id) + .filter(activity_dsl::invoice_id.eq(invoice.id.clone())) + .filter(activity_dsl::owner_id.eq(owner_id)) + .load(conn)?; + Ok(Some(invoice.into_api_model(activity_ids)?)) + } + None => Ok(None), + } + }) + .await + } + pub async fn get_many( &self, invoice_ids: Vec,