feat: compute total price for bill #108

Merged
realaravinth merged 5 commits from billing into master 2024-09-18 17:27:22 +05:30
4 changed files with 252 additions and 13 deletions
Showing only changes of commit 39edaead04 - Show all commits

View file

@ -0,0 +1,94 @@
{
"db_name": "PostgreSQL",
"query": "SELECT \n product_name,\n product_id,\n line_item_id,\n quantity_minor_unit,\n quantity_minor_number,\n quantity_major_unit,\n quantity_major_number,\n created_time,\n bill_id,\n price_per_unit_minor,\n price_per_unit_major,\n price_per_unit_currency,\n deleted\n FROM cqrs_billing_line_item_query\n WHERE\n bill_id = $1;",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "product_name",
"type_info": "Text"
},
{
"ordinal": 1,
"name": "product_id",
"type_info": "Uuid"
},
{
"ordinal": 2,
"name": "line_item_id",
"type_info": "Uuid"
},
{
"ordinal": 3,
"name": "quantity_minor_unit",
"type_info": "Text"
},
{
"ordinal": 4,
"name": "quantity_minor_number",
"type_info": "Int4"
},
{
"ordinal": 5,
"name": "quantity_major_unit",
"type_info": "Text"
},
{
"ordinal": 6,
"name": "quantity_major_number",
"type_info": "Int4"
},
{
"ordinal": 7,
"name": "created_time",
"type_info": "Timestamptz"
},
{
"ordinal": 8,
"name": "bill_id",
"type_info": "Uuid"
},
{
"ordinal": 9,
"name": "price_per_unit_minor",
"type_info": "Int4"
},
{
"ordinal": 10,
"name": "price_per_unit_major",
"type_info": "Int4"
},
{
"ordinal": 11,
"name": "price_per_unit_currency",
"type_info": "Text"
},
{
"ordinal": 12,
"name": "deleted",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Uuid"
]
},
"nullable": [
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false
]
},
"hash": "538d43c832702b03da4a51e0b0794785adfb14b4b8ff0ed7c4a7079e711b8ce7"
}

View file

@ -0,0 +1,144 @@
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
use uuid::Uuid;
use super::BillingDBPostgresAdapter;
use crate::billing::{
application::port::output::db::{errors::*, get_line_items_for_bill_id::*},
domain::line_item_aggregate::LineItem,
};
#[async_trait::async_trait]
impl GetLineItemsForBillIDDBPort for BillingDBPostgresAdapter {
async fn get_line_items_for_bill_id(&self, bill_id: Uuid) -> BillingDBResult<Vec<LineItem>> {
let mut res = sqlx::query_as!(
super::line_item_view::LineItemView,
"SELECT
product_name,
product_id,
line_item_id,
quantity_minor_unit,
quantity_minor_number,
quantity_major_unit,
quantity_major_number,
created_time,
bill_id,
price_per_unit_minor,
price_per_unit_major,
price_per_unit_currency,
deleted
FROM cqrs_billing_line_item_query
WHERE
bill_id = $1;",
bill_id
)
.fetch_all(&self.pool)
.await?;
println!("Got len: {}", res.len());
let mut output = Vec::with_capacity(res.len());
res.drain(0..).for_each(|r| output.push(r.into()));
Ok(output)
}
}
#[cfg(test)]
pub mod tests {
use super::*;
// use crate::billing::domain::add_product_command::tests::get_customizations;
use crate::{
billing::{
adapters::output::db::postgres::line_item_view::LineItemView, domain::bill_aggregate::*,
},
types::currency::*,
types::quantity::*,
utils::uuid::{tests::*, *},
};
async fn create_dummy_line_item(
db: &BillingDBPostgresAdapter,
bill_id: Uuid,
line_item_id: Uuid,
) {
let view = LineItemView::default();
let version = 0;
sqlx::query!(
"INSERT INTO cqrs_billing_line_item_query (
version,
product_name,
product_id,
line_item_id,
quantity_minor_unit,
quantity_minor_number,
quantity_major_unit,
quantity_major_number,
created_time,
bill_id,
price_per_unit_minor,
price_per_unit_major,
price_per_unit_currency,
deleted
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14
);",
version,
view.product_name,
view.product_id,
line_item_id,
QuantityUnit::DiscreteNumber.to_string(),
view.quantity_minor_number,
QuantityUnit::DiscreteNumber.to_string(),
view.quantity_major_number,
view.created_time,
bill_id,
view.price_per_unit_minor,
view.price_per_unit_major,
Currency::INR.to_string(),
view.deleted,
)
.execute(&db.pool)
.await
.unwrap();
}
#[actix_rt::test]
async fn test_postgres_get_line_items_for_bill_id() {
let settings = crate::settings::tests::get_settings().await;
settings.create_db().await;
let db = super::BillingDBPostgresAdapter::new(
sqlx::postgres::PgPool::connect(&settings.database.url)
.await
.unwrap(),
);
let bill_id = UUID;
// state doesn't exist
assert!(db
.get_line_items_for_bill_id(bill_id)
.await
.unwrap()
.is_empty());
let u = GenerateUUID;
let li_id_1 = u.get_uuid();
let li_id_2 = u.get_uuid();
create_dummy_line_item(&db, bill_id, li_id_1).await;
create_dummy_line_item(&db, bill_id, li_id_2).await;
// state exists
let res = db.get_line_items_for_bill_id(bill_id).await.unwrap();
assert_eq!(res.len(), 2);
assert!(res
.iter()
.any(|li| *li.bill_id() == bill_id && *li.line_item_id() == li_id_1));
assert!(res
.iter()
.any(|li| *li.bill_id() == bill_id && *li.line_item_id() == li_id_2));
settings.drop_db().await;
}
}

View file

@ -25,23 +25,23 @@ pub const NEW_LINE_ITEM_NON_UUID: &str = "new_line_item_non_uuid-asdfa-billing";
// be designed to reflect the response dto that will be returned to a user. // be designed to reflect the response dto that will be returned to a user.
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct LineItemView { pub struct LineItemView {
product_name: String, pub product_name: String,
product_id: Uuid, pub product_id: Uuid,
bill_id: Uuid, pub bill_id: Uuid,
created_time: OffsetDateTime, pub created_time: OffsetDateTime,
line_item_id: Uuid, pub line_item_id: Uuid,
quantity_major_number: i32, pub quantity_major_number: i32,
quantity_minor_number: i32, pub quantity_minor_number: i32,
quantity_major_unit: String, pub quantity_major_unit: String,
quantity_minor_unit: String, pub quantity_minor_unit: String,
price_per_unit_major: i32, pub price_per_unit_major: i32,
price_per_unit_minor: i32, pub price_per_unit_minor: i32,
price_per_unit_currency: String, pub price_per_unit_currency: String,
deleted: bool, pub deleted: bool,
} }
impl Default for LineItemView { impl Default for LineItemView {

View file

@ -10,6 +10,7 @@ use crate::db::{migrate::RunMigrations, sqlx_postgres::Postgres};
mod bill_id_exists; mod bill_id_exists;
mod bill_view; mod bill_view;
mod errors; mod errors;
mod get_line_items_for_bill_id;
mod line_item_id_exists; mod line_item_id_exists;
mod line_item_view; mod line_item_view;
mod next_token_id; mod next_token_id;