feat: compute total price for bill #108
4 changed files with 252 additions and 13 deletions
|
@ -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"
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue