Bug: [FEATURE] Sorting POC



Allow sorting on amount and date in tables under Operations

If user clicks on the arrow to sort information being on a page which is not the first one, we will sort the data via backend and redirect them to first page

Performance POC to be done for the same
diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs
index 8433ac43d0c..eb04fe8300e 100644
--- a/crates/api_models/src/payments.rs
+++ b/crates/api_models/src/payments.rs
@@ -4063,6 +4063,9 @@ pub struct PaymentListFilterConstraints {
     pub authentication_type: Option<Vec<enums::AuthenticationType>>,
     /// The list of merchant connector ids to filter payments list for selected label
     pub merchant_connector_id: Option<Vec<String>>,
+    /// The order in which payments list should be sorted
+    #[serde(default)]
+    pub order: Order,
 }
 #[derive(Clone, Debug, serde::Serialize)]
 pub struct PaymentListFilters {
@@ -4102,6 +4105,34 @@ pub struct AmountFilter {
     pub end_amount: Option<i64>,
 }
 
+#[derive(Clone, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize, ToSchema)]
+pub struct Order {
+    /// The field to sort, such as Amount or Created etc.
+    pub on: SortOn,
+    /// The order in which to sort the items, either Ascending or Descending
+    pub by: SortBy,
+}
+
+#[derive(Clone, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize, ToSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum SortOn {
+    /// Sort by the amount field
+    Amount,
+    /// Sort by the created_at field
+    #[default]
+    Created,
+}
+
+#[derive(Clone, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize, ToSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum SortBy {
+    /// Sort in ascending order
+    Asc,
+    /// Sort in descending order
+    #[default]
+    Desc,
+}
+
 #[derive(
     Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash, ToSchema,
 )]
diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs
index 3d305656797..8033ec7706c 100644
--- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs
+++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs
@@ -737,6 +737,7 @@ pub struct PaymentIntentListParams {
     pub starting_after_id: Option<String>,
     pub ending_before_id: Option<String>,
     pub limit: Option<u32>,
+    pub order: api_models::payments::Order,
 }
 
 impl From<api_models::payments::PaymentListConstraints> for PaymentIntentFetchConstraints {
@@ -758,6 +759,7 @@ impl From<api_models::payments::PaymentListConstraints> for PaymentIntentFetchCo
             starting_after_id: value.starting_after,
             ending_before_id: value.ending_before,
             limit: Some(std::cmp::min(value.limit, PAYMENTS_LIST_MAX_LIMIT_V1)),
+            order: Default::default(),
         }))
     }
 }
@@ -781,6 +783,7 @@ impl From<api_models::payments::TimeRange> for PaymentIntentFetchConstraints {
             starting_after_id: None,
             ending_before_id: None,
             limit: None,
+            order: Default::default(),
         }))
     }
 }
@@ -807,6 +810,7 @@ impl From<api_models::payments::PaymentListFilterConstraints> for PaymentIntentF
                 starting_after_id: None,
                 ending_before_id: None,
                 limit: Some(std::cmp::min(value.limit, PAYMENTS_LIST_MAX_LIMIT_V2)),
+                order: value.order,
             }))
         }
     }
diff --git a/crates/storage_impl/src/payments/payment_intent.rs b/crates/storage_impl/src/payments/payment_intent.rs
index ea91732631d..c8dc011b814 100644
--- a/crates/storage_impl/src/payments/payment_intent.rs
+++ b/crates/storage_impl/src/payments/payment_intent.rs
@@ -1,5 +1,5 @@
 #[cfg(feature = "olap")]
-use api_models::payments::AmountFilter;
+use api_models::payments::{AmountFilter, Order, SortBy, SortOn};
 #[cfg(feature = "olap")]
 use async_bb8_diesel::{AsyncConnection, AsyncRunQueryDsl};
 #[cfg(feature = "olap")]
@@ -674,7 +674,6 @@ impl<T: DatabaseStore> PaymentIntentInterface for crate::RouterStore<T> {
                 payment_attempt_schema::table.on(pa_dsl::attempt_id.eq(pi_dsl::active_attempt_id)),
             )
             .filter(pi_dsl::merchant_id.eq(merchant_id.to_owned()))
-            .order(pi_dsl::created_at.desc())
             .into_boxed();
 
         query = match constraints {
@@ -682,6 +681,25 @@ impl<T: DatabaseStore> PaymentIntentInterface for crate::RouterStore<T> {
                 query.filter(pi_dsl::payment_id.eq(payment_intent_id.to_owned()))
             }
             PaymentIntentFetchConstraints::List(params) => {
+                query = match params.order {
+                    Order {
+                        on: SortOn::Amount,
+                        by: SortBy::Asc,
+                    } => query.order(pi_dsl::amount.asc()),
+                    Order {
+                        on: SortOn::Amount,
+                        by: SortBy::Desc,
+                    } => query.order(pi_dsl::amount.desc()),
+                    Order {
+                        on: SortOn::Created,
+                        by: SortBy::Asc,
+                    } => query.order(pi_dsl::created_at.asc()),
+                    Order {
+                        on: SortOn::Created,
+                        by: SortBy::Desc,
+                    } => query.order(pi_dsl::created_at.desc()),
+                };
+
                 if let Some(limit) = params.limit {
                     query = query.limit(limit.into());
                 }

## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [ ] Bugfix
- [x] New feature
- [x] Enhancement
- [ ] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
Support sort criterial for payments list:
- sort on basis of amount (ascending or descending)
- sort on basis of created at time (ascending or descending)

### Additional Changes

- [ ] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [ ] This PR modifies application configuration/environment variables

<!--
Provide links to the files with corresponding changes.

Following are the paths where you can find config files:
1. `config`
2. `crates/router/src/configs`
3. `loadtest/config`
-->


## Motivation and Context
Closes [#5364](https://github.com/juspay/hyperswitch/issues/5364)



## How did you test it?
Use the curl to sort the list by following order. Order takes `on` (the field which need to be considered) and `by`( ascending or descending)
```
curl --location 'http://localhost:8080/payments/list' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer JWT' \
--data '{  
    "order": {
        "on": "created",
        "by": "asc" 
    }   
}'
```
Or
```
curl --location 'http://localhost:8080/payments/list' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer JWT' \
--data '{  
    "order": {
        "on": "amount",
        "by": "desc" 
    }   
}'
```

Response, here the payments will be sorted in decreasing order of amount:
```
{
    "count": 2,
    "total_count": 2,
    "data": [
        {
            "payment_id": "pay_gBlmx0AlNp55baW0ATDt",
            "merchant_id": "merchant_1721657037",
            "status": "succeeded",
            "amount": 7000,
            "net_amount": 0,
            "amount_capturable": 7000,
            "amount_received": null,
            "connector": "stripe",
            "client_secret": "pay_gBlmx0AlNp55baW0ATDt_secret_Ay9Hs88sJ24Md4CRkL0v",
            "created": "2024-07-23T09:32:56.212Z",
            "currency": "EUR",
            "customer_id": "uiuiuiui",
            "customer": {
                "id": "uiuiuiui",
                "name": null,
                "email": "guest@ehjhjxample.com",
                "phone": null,
                "phone_country_code": null
            },
            "description": "Its my first payment request",
            "refunds": null,
            "disputes": null,
            "mandate_id": null,
            "mandate_data": null,
            "setup_future_usage": "on_session",
            "off_session": null,
            "capture_on": null,
            "capture_method": "automatic",
            "payment_method": "card",
            "payment_method_data": {
                "card": {
                    "last4": "0003",
                    "card_type": null,
                    "card_network": null,
                    "card_issuer": null,
                    "card_issuing_country": null,
                    "card_isin": "400000",
                    "card_extended_bin": null,
                    "card_exp_month": "10",
                    "card_exp_year": "25",
                    "card_holder_name": null,
                    "payment_checks": {
                        "cvc_check": "pass",
                        "address_line1_check": "pass",
                        "address_postal_code_check": "pass"
                    },
                    "authentication_data": null
                },
                "billing": null
            },
            "payment_token": null,
            "shipping": {
                "address": {
                    "city": "San Fransico",
                    "country": "US",
                    "line1": "1467",
                    "line2": "Harrison Street",
                    "line3": "Harrison Street",
                    "zip": "94122",
                    "state": "California",
                    "first_name": "joseph",
                    "last_name": "Doe"
                },
                "phone": {
                    "number": "8056594427",
                    "country_code": "+91"
                },
                "email": null
            },
            "billing": {
                "address": {
                    "city": "San Fransico",
                    "country": "NL",
                    "line1": "1467",
                    "line2": "Harrison Street",
                    "line3": "Harrison Street",
                    "zip": "94122",
                    "state": "California",
                    "first_name": "joseph",
                    "last_name": "Doe"
                },
                "phone": {
                    "number": "8056594427",
                    "country_code": "+91"
                },
                "email": "example@example.com"
            },
            "order_details": null,
            "email": null,
            "name": null,
            "phone": null,
            "return_url": null,
            "authentication_type": "no_three_ds",
            "statement_descriptor_name": null,
            "statement_descriptor_suffix": null,
            "next_action": null,
            "cancellation_reason": null,
            "error_code": null,
            "error_message": null,
            "unified_code": null,
            "unified_message": null,
            "payment_experience": null,
            "payment_method_type": null,
            "connector_label": null,
            "business_country": null,
            "business_label": "default",
            "business_sub_label": null,
            "allowed_payment_method_types": null,
            "ephemeral_key": null,
            "manual_retry_allowed": null,
            "connector_transaction_id": "pi_3Pff5ZD5R7gDAGff0gTXIJJX",
            "frm_message": null,
            "metadata": {
                "udf1": "value1",
                "login_date": "2019-09-10T10:11:12Z",
                "new_customer": "true"
            },
            "connector_metadata": null,
            "feature_metadata": null,
            "reference_id": null,
            "payment_link": null,
            "profile_id": "pro_YbnmkkPd2qkewT96ujee",
            "surcharge_details": null,
            "attempt_count": 1,
            "merchant_decision": null,
            "merchant_connector_id": "mca_f3JtTVYi3pElw14sKngo",
            "incremental_authorization_allowed": null,
            "authorization_count": null,
            "incremental_authorizations": null,
            "external_authentication_details": null,
            "external_3ds_authentication_attempted": null,
            "expires_on": null,
            "fingerprint": null,
            "browser_info": null,
            "payment_method_id": null,
            "payment_method_status": null,
            "updated": null,
            "charges": null,
            "frm_metadata": null,
            "merchant_order_reference_id": null
        },
        {
            "payment_id": "pay_SqPYBFChdnejflFdVXm0",
            "merchant_id": "merchant_1721657037",
            "status": "succeeded",
            "amount": 6540,
            "net_amount": 0,
            "amount_capturable": 6540,
            "amount_received": null,
            "connector": "stripe",
            "client_secret": "pay_SqPYBFChdnejflFdVXm0_secret_VTAtKjSlI7fnCEICo9v3",
            "created": "2024-07-22T14:12:15.554Z",
            "currency": "EUR",
            "customer_id": "uiuiuiui",
            "customer": {
                "id": "uiuiuiui",
                "name": null,
                "email": "guest@ehjhjxample.com",
                "phone": null,
                "phone_country_code": null
            },
            "description": "Its my first payment request",
            "refunds": null,
            "disputes": null,
            "mandate_id": null,
            "mandate_data": null,
            "setup_future_usage": "on_session",
            "off_session": null,
            "capture_on": null,
            "capture_method": "automatic",
            "payment_method": "card",
            "payment_method_data": {
                "card": {
                    "last4": "0003",
                    "card_type": null,
                    "card_network": null,
                    "card_issuer": null,
                    "card_issuing_country": null,
                    "card_isin": "400000",
                    "card_extended_bin": null,
                    "card_exp_month": "10",
                    "card_exp_year": "25",
                    "card_holder_name": null,
                    "payment_checks": {
                        "cvc_check": "pass",
                        "address_line1_check": "pass",
                        "address_postal_code_check": "pass"
                    },
                    "authentication_data": null
                },
                "billing": null
            },
            "payment_token": null,
            "shipping": {
                "address": {
                    "city": "San Fransico",
                    "country": "US",
                    "line1": "1467",
                    "line2": "Harrison Street",
                    "line3": "Harrison Street",
                    "zip": "94122",
                    "state": "California",
                    "first_name": "joseph",
                    "last_name": "Doe"
                },
                "phone": {
                    "number": "8056594427",
                    "country_code": "+91"
                },
                "email": null
            },
            "billing": {
                "address": {
                    "city": "San Fransico",
                    "country": "NL",
                    "line1": "1467",
                    "line2": "Harrison Street",
                    "line3": "Harrison Street",
                    "zip": "94122",
                    "state": "California",
                    "first_name": "joseph",
                    "last_name": "Doe"
                },
                "phone": {
                    "number": "8056594427",
                    "country_code": "+91"
                },
                "email": "example@example.com"
            },
            "order_details": null,
            "email": null,
            "name": null,
            "phone": null,
            "return_url": null,
            "authentication_type": "no_three_ds",
            "statement_descriptor_name": null,
            "statement_descriptor_suffix": null,
            "next_action": null,
            "cancellation_reason": null,
            "error_code": null,
            "error_message": null,
            "unified_code": null,
            "unified_message": null,
            "payment_experience": null,
            "payment_method_type": null,
            "connector_label": null,
            "business_country": null,
            "business_label": "default",
            "business_sub_label": null,
            "allowed_payment_method_types": null,
            "ephemeral_key": null,
            "manual_retry_allowed": null,
            "connector_transaction_id": "pi_3PfMyLD5R7gDAGff1mamZiPY",
            "frm_message": null,
            "metadata": {
                "udf1": "value1",
                "login_date": "2019-09-10T10:11:12Z",
                "new_customer": "true"
            },
            "connector_metadata": null,
            "feature_metadata": null,
            "reference_id": null,
            "payment_link": null,
            "profile_id": "pro_YbnmkkPd2qkewT96ujee",
            "surcharge_details": null,
            "attempt_count": 1,
            "merchant_decision": null,
            "merchant_connector_id": "mca_f3JtTVYi3pElw14sKngo",
            "incremental_authorization_allowed": null,
            "authorization_count": null,
            "incremental_authorizations": null,
            "external_authentication_details": null,
            "external_3ds_authentication_attempted": null,
            "expires_on": null,
            "fingerprint": null,
            "browser_info": null,
            "payment_method_id": null,
            "payment_method_status": null,
            "updated": null,
            "charges": null,
            "frm_metadata": null,
            "merchant_order_reference_id": null
        }
    ]
}
```


## Checklist
<!-- Put an `x` in the boxes that apply -->

- [x] I formatted the code `cargo +nightly fmt --all`
- [x] I addressed lints thrown by `cargo clippy`
- [x] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible
