diff --git a/server/crates/elerp_common/src/guest_order_module/model/guest_order.rs b/server/crates/elerp_common/src/guest_order_module/model/guest_order.rs index 365dfe8..a63fc44 100644 --- a/server/crates/elerp_common/src/guest_order_module/model/guest_order.rs +++ b/server/crates/elerp_common/src/guest_order_module/model/guest_order.rs @@ -57,6 +57,8 @@ pub struct GuestOrder { pub order_id: i64, #[serde(default)] pub is_record: bool, + #[serde(default)] + pub non_payment: bool, pub order_category_id: i64, #[serde(default)] @@ -79,6 +81,7 @@ pub struct GetGuestOrdersQuery { pub person_in_charge_id: Option, pub order_type: Option, pub is_record: Option, + pub non_payment: Option, pub currency: Option, pub date_start: Option, pub date_end: Option, @@ -111,6 +114,7 @@ impl From for Order { description: value.description, order_type: value.order_type, is_record: value.is_record, + non_payment: value.non_payment, } } } @@ -134,6 +138,7 @@ impl From for GuestOrder { confirmed_date: value.date, order_category_id: value.order_category_id, is_record: value.is_record, + non_payment: value.non_payment, } } } @@ -176,6 +181,10 @@ impl GetGuestOrdersQuery { let eq = eq_or_not(reverse, "is_record"); conditions.push(format!("guest_orders.is_record{eq}{v}")); } + if let Some(v) = &self.non_payment { + let eq = eq_or_not(reverse, "non_payment"); + conditions.push(format!("guest_orders.non_payment{eq}{v}")); + } if let Some(v) = &self.currency { let eq = eq_or_not(reverse, "currency"); conditions.push(format!("guest_orders.currency{eq}'{}'", v.as_ref())); diff --git a/server/crates/elerp_common/src/order_module.rs b/server/crates/elerp_common/src/order_module.rs index 1380ebd..8b35785 100644 --- a/server/crates/elerp_common/src/order_module.rs +++ b/server/crates/elerp_common/src/order_module.rs @@ -6,7 +6,7 @@ use sqlx::{QueryBuilder, Row, SqliteConnection}; use self::model::{ check_order_result::{CheckOrderResult, ItemNotAvailable}, - order::{Order, OrderItem, OrderPaymentStatus, OrderType}, + order::{Order, OrderType}, }; pub mod model; @@ -114,39 +114,20 @@ pub async fn check(order: &Order, fast_check: bool, tx: &mut SqliteConnection) - Ok(CheckOrderResult { items_not_available }) } -fn calc_total_amount(items: &Vec) -> f64 { - let mut total: f64 = 0.0; - for item in items.iter() { - if item.exchanged { - continue; - } - total += (item.quantity as f64) * item.price; - } - total -} - pub async fn add(mut order: Order, tx: &mut SqliteConnection) -> Result { let items = order.items.as_ref(); - let total_amount = if let Some(items) = items { + if let Some(items) = items { if !order.is_record { inventory_module::change(order.warehouse_id, items, order.order_type, tx).await?; } - calc_total_amount(items) - } else { - 0.0 - }; - let order_payment_status = if order.order_type == OrderType::StockOut && total_amount > 0.0 { - OrderPaymentStatus::Unsettled - } else { - OrderPaymentStatus::None - }; - let r = sqlx::query("INSERT INTO orders (from_guest_order_id, created_by_user_id, updated_by_user_id, warehouse_id, currency, total_amount, person_related_id, person_in_charge_id, date, last_updated_date, description, order_type, is_record, order_category_id, total_amount_settled, order_payment_status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") + } + let r = sqlx::query("INSERT INTO orders (from_guest_order_id, created_by_user_id, updated_by_user_id, warehouse_id, currency, total_amount, person_related_id, person_in_charge_id, date, last_updated_date, description, order_type, is_record, non_payment, order_category_id, total_amount_settled, order_payment_status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") .bind(order.from_guest_order_id) .bind(order.created_by_user_id) .bind(order.updated_by_user_id) .bind(order.warehouse_id) .bind(&order.currency) - .bind(total_amount) + .bind(order.total_amount) .bind(order.person_related_id) .bind(order.person_in_charge_id) .bind(order.date) @@ -154,9 +135,10 @@ pub async fn add(mut order: Order, tx: &mut SqliteConnection) -> Result { .bind(&order.description) .bind(&order.order_type) .bind(order.is_record) + .bind(order.non_payment) .bind(order.order_category_id) .bind(0) - .bind(order_payment_status) + .bind(order.order_payment_status) .execute(&mut *tx) .await?; if r.rows_affected() != 1 { @@ -164,9 +146,6 @@ pub async fn add(mut order: Order, tx: &mut SqliteConnection) -> Result { } order.id = sql::try_set_standard_id(r.last_insert_rowid(), "orders", tx).await?; - order.total_amount = total_amount; - order.total_amount_settled = 0.0; - order.order_payment_status = order_payment_status; add_order_items(&order, tx).await?; diff --git a/server/crates/elerp_common/src/order_module/model/order.rs b/server/crates/elerp_common/src/order_module/model/order.rs index 8f37553..84a6059 100644 --- a/server/crates/elerp_common/src/order_module/model/order.rs +++ b/server/crates/elerp_common/src/order_module/model/order.rs @@ -101,6 +101,8 @@ pub struct Order { pub order_type: OrderType, #[serde(default)] pub is_record: bool, + #[serde(default)] + pub non_payment: bool, } #[derive(Debug, Serialize, Deserialize, ToSchema, Clone, IntoParams, FromRow)] @@ -125,6 +127,7 @@ pub struct GetOrdersQuery { pub order_type: Option, pub order_category_id: Option, pub is_record: Option, + pub non_payment: Option, pub currency: Option, pub items: Option>, pub item_categories: Option>, @@ -159,6 +162,7 @@ impl GetOrdersQuery { reverse: None, last_updated_date_start: None, last_updated_date_end: None, + non_payment: None, } } pub fn get_where_condition(&self) -> String { @@ -212,6 +216,10 @@ impl GetOrdersQuery { let eq = eq_or_not(reverse, "is_record"); conditions.push(format!("orders.is_record{eq}{v}")); } + if let Some(v) = &self.non_payment { + let eq = eq_or_not(reverse, "non_payment"); + conditions.push(format!("orders.non_payment{eq}{v}")); + } if let Some(v) = &self.order_payment_status { let eq = in_or_not(reverse, "order_payment_status"); let v = set_to_string(v, "','"); diff --git a/server/crates/guest_order_module/src/lib.rs b/server/crates/guest_order_module/src/lib.rs index 1b9b65a..4dbce02 100644 --- a/server/crates/guest_order_module/src/lib.rs +++ b/server/crates/guest_order_module/src/lib.rs @@ -39,6 +39,7 @@ impl GuestOrderModule { description TEXT NOT NULL, order_type TEXT NOT NULL, is_record BOOLEAN NOT NULL, + non_payment BOOLEAN NOT NULL, guest_order_status TEXT NOT NULL, order_id INT NOT NULL DEFAULT 0, order_category_id INT NOT NULL, @@ -133,7 +134,7 @@ impl GuestOrderModule { pub async fn add(&self, sub_token: &str, mut order: GuestOrder, tx: &mut SqliteConnection) -> Result { let now = self.ps.get_timestamp_seconds() as i64; - let r = sqlx::query("INSERT INTO guest_orders (date, confirmed_date, sub_token, created_by_user_id, warehouse_id, currency, person_related_id, person_in_charge_id, description, order_type, is_record, guest_order_status, order_category_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") + let r = sqlx::query("INSERT INTO guest_orders (date, confirmed_date, sub_token, created_by_user_id, warehouse_id, currency, person_related_id, person_in_charge_id, description, order_type, is_record, non_payment, guest_order_status, order_category_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") .bind(now) .bind(now) .bind(sub_token) @@ -145,6 +146,7 @@ impl GuestOrderModule { .bind(&order.description) .bind(&order.order_type) .bind(order.is_record) + .bind(order.non_payment) .bind(GuestOrderStatus::Pending) .bind(order.order_category_id) .execute(&mut *tx) @@ -235,6 +237,7 @@ impl GuestOrderModule { description: row.get("description"), order_type: row.get("order_type"), is_record: row.get("is_record"), + non_payment: row.get("non_payment"), guest_order_status, order_id, date, @@ -252,6 +255,7 @@ impl GuestOrderModule { guest_orders.created_by_user_id, guest_orders.warehouse_id, guest_orders.is_record, + guest_orders.non_payment, CASE WHEN guest_order_status='Confirmed' THEN orders.currency ELSE guest_orders.currency END AS currency, CASE WHEN guest_order_status='Confirmed' THEN orders.person_related_id ELSE guest_orders.person_related_id END AS person_related_id, CASE WHEN guest_order_status='Confirmed' THEN orders.person_in_charge_id ELSE guest_orders.person_in_charge_id END AS person_in_charge_id, @@ -301,6 +305,7 @@ impl GuestOrderModule { guest_orders.currency, guest_orders.order_type, guest_orders.is_record, + guest_orders.non_payment, guest_orders.guest_order_status, guest_orders.date, guest_orders.confirmed_date, diff --git a/server/crates/order_module/src/lib.rs b/server/crates/order_module/src/lib.rs index fb94c5f..226eee5 100644 --- a/server/crates/order_module/src/lib.rs +++ b/server/crates/order_module/src/lib.rs @@ -47,6 +47,7 @@ impl OrderModule { description TEXT NOT NULL, order_type TEXT NOT NULL, is_record BOOLEAN NOT NULL, + non_payment BOOLEAN NOT NULL, order_category_id INT NOT NULL )", ) @@ -133,6 +134,17 @@ impl OrderModule { Ok(count >= self.ps.get_config().limit.orders) } + fn calc_total_amount(&self, items: &Vec) -> f64 { + let mut total: f64 = 0.0; + for item in items.iter() { + if item.exchanged { + continue; + } + total += (item.quantity as f64) * item.price; + } + total + } + pub fn preprocess(&self, order: &mut Order, user: &UserInfo, initial: bool, person_in_charge_id: i64) { if let Some(items) = order.items.clone() { order.items = Some( @@ -151,6 +163,7 @@ impl OrderModule { .collect(), ); }; + order.total_amount = if let Some(items) = order.items.as_ref() { self.calc_total_amount(items) } else { 0.0 }; let now = self.ps.get_timestamp_seconds() as i64; order.updated_by_user_id = user.id; @@ -161,6 +174,13 @@ impl OrderModule { order.last_updated_date = now; order.person_in_charge_id = person_in_charge_id; order.from_guest_order_id = 0; + order.total_amount_settled = 0.0; + + order.order_payment_status = if !order.non_payment && order.total_amount > 0.0 { + OrderPaymentStatus::Unsettled + } else { + OrderPaymentStatus::None + }; } pub async fn can_access(&self, id: i64, user: &UserInfo, tx: &mut SqliteConnection) -> Result { @@ -345,6 +365,7 @@ impl OrderModule { order_type: row.get("order_type"), order_category_id: row.get("order_category_id"), is_record: row.get("is_record"), + non_payment: row.get("non_payment"), items: None, } } @@ -391,6 +412,7 @@ impl OrderModule { orders.currency, orders.order_type, orders.is_record, + orders.non_payment, orders.order_category_id, orders.total_amount, orders.total_amount_settled, diff --git a/server/tests/guest_order.rs b/server/tests/guest_order.rs index 1713aff..e8c2d77 100644 --- a/server/tests/guest_order.rs +++ b/server/tests/guest_order.rs @@ -23,6 +23,7 @@ async fn test_order_preprocess() { description: "".to_owned(), order_type: OrderType::StockOut, is_record: false, + non_payment: false, guest_order_status: GuestOrderStatus::Expired, order_id: 0, order_category_id: p.order_category1.id, @@ -58,6 +59,7 @@ async fn test_module() { description: "".to_owned(), order_type: OrderType::StockOut, is_record: false, + non_payment: false, guest_order_status: GuestOrderStatus::Expired, order_id: 0, order_category_id: p.order_category1.id, diff --git a/server/tests/order.rs b/server/tests/order.rs index f9ecb24..cdbcf64 100644 --- a/server/tests/order.rs +++ b/server/tests/order.rs @@ -43,6 +43,7 @@ async fn test_order_preprocess() { description: format!("Testing order #1"), order_type: OrderType::StockIn, is_record: false, + non_payment: false, }; c.order.preprocess(&mut order, &p.user1, true, p.person2.id); assert_eq!(order.created_by_user_id, p.user1.id); @@ -55,6 +56,22 @@ async fn test_order_preprocess() { assert_eq!(order.items.as_ref().unwrap()[0].quantity, 250); assert_eq!(order.items.as_ref().unwrap()[0].price, 10.0); assert_eq!(order.items.as_ref().unwrap()[0].exchanged, false); + assert_eq!(order.total_amount, 2500.0); + assert_eq!(order.total_amount_settled, 0.0); + assert_eq!(order.order_payment_status, OrderPaymentStatus::Unsettled); + + order.non_payment = true; + c.order.preprocess(&mut order, &p.user1, true, p.person2.id); + assert_eq!(order.total_amount, 2500.0); + assert_eq!(order.total_amount_settled, 0.0); + assert_eq!(order.order_payment_status, OrderPaymentStatus::None); + + order.items.take(); + order.non_payment = false; + c.order.preprocess(&mut order, &p.user1, true, p.person2.id); + assert_eq!(order.total_amount, 0.0); + assert_eq!(order.total_amount_settled, 0.0); + assert_eq!(order.order_payment_status, OrderPaymentStatus::None); } #[tokio::test] @@ -98,6 +115,7 @@ async fn test_module() { description: format!("Testing order"), order_type: OrderType::StockOut, is_record: true, + non_payment: false, }; c.order.preprocess(&mut order, &p.user1, true, p.person1.person_in_charge_id); @@ -145,6 +163,7 @@ async fn test_module() { description: format!("Testing order #{n}"), order_type: OrderType::StockIn, is_record: false, + non_payment: false, }; c.order.preprocess(&mut order, &p.user1, true, p.person1.person_in_charge_id); @@ -207,6 +226,7 @@ async fn test_module() { description: format!("Testing stock out..."), order_type: OrderType::StockOut, is_record: false, + non_payment: false, }; c.order.preprocess(&mut stock_out_order, &p.user1, true, p.person1.person_in_charge_id); let stock_out_order = c.order.add(stock_out_order, tx.as_mut()).await.unwrap(); @@ -250,6 +270,7 @@ async fn test_module() { description: format!("Testing stock out..."), order_type: OrderType::Exchange, is_record: false, + non_payment: false, }; c.order.preprocess(&mut exchange_order, &p.user1, true, p.person1.person_in_charge_id); let exchange_order = c.order.add(exchange_order, tx.as_mut()).await.unwrap(); @@ -293,6 +314,7 @@ async fn test_module() { description: format!("Testing stock out..."), order_type: OrderType::Calibration, is_record: false, + non_payment: false, }; c.order.preprocess(&mut calibration_order, &p.user1, true, p.person1.person_in_charge_id); let calibration_order = c.order.add(calibration_order, tx.as_mut()).await.unwrap(); @@ -331,6 +353,7 @@ async fn test_module() { description: format!("Testing stock out..."), order_type: OrderType::StockIn, is_record: false, + non_payment: false, }; c.order.preprocess(&mut stock_in_order, &p.user1, true, p.person1.person_in_charge_id); let stock_in_order = c.order.add(stock_in_order, tx.as_mut()).await.unwrap(); @@ -369,6 +392,7 @@ async fn test_module() { description: format!("Testing stock out..."), order_type: OrderType::CalibrationStrict, is_record: false, + non_payment: false, }; c.order.preprocess(&mut calibration_strict_order, &p.user1, true, p.person1.person_in_charge_id); let calibration_strict_order = c.order.add(calibration_strict_order, tx.as_mut()).await.unwrap(); @@ -431,6 +455,7 @@ async fn remove_after_calibration(strict: bool) { description: format!("Testing order #1"), order_type: OrderType::StockIn, is_record: false, + non_payment: false, }; let mut tx = c.ps.begin_tx(true).await.unwrap(); @@ -469,6 +494,7 @@ async fn remove_after_calibration(strict: bool) { description: format!("Testing order #1"), order_type: OrderType::StockOut, is_record: false, + non_payment: false, }; c.order.preprocess(&mut out_order, &p.user1, true, p.person2.id); @@ -509,6 +535,7 @@ async fn remove_after_calibration(strict: bool) { description: format!("Testing order #1"), order_type: if strict { OrderType::CalibrationStrict } else { OrderType::Calibration }, is_record: false, + non_payment: false, }; c.order.preprocess(&mut calibration_order, &p.user1, true, p.person2.id); @@ -536,4 +563,4 @@ async fn test_remove_after_calibration() { #[tokio::test] async fn test_remove_after_calibration_strict() { remove_after_calibration(true).await; -} \ No newline at end of file +} diff --git a/server/tests/order_payment.rs b/server/tests/order_payment.rs index a85d256..0e278e8 100644 --- a/server/tests/order_payment.rs +++ b/server/tests/order_payment.rs @@ -57,6 +57,7 @@ async fn test_module() { description: format!("Testing stock out..."), order_type: OrderType::StockOut, is_record: false, + non_payment: false, }; c.order.preprocess(&mut order, &p.user1, true, p.person1.person_in_charge_id); let order = c.order.add(order, tx.as_mut()).await.unwrap(); diff --git a/server/tests/statistical.rs b/server/tests/statistical.rs index 95f6aeb..fab989d 100644 --- a/server/tests/statistical.rs +++ b/server/tests/statistical.rs @@ -45,6 +45,7 @@ async fn test_module() { description: format!("Testing order #{n}"), order_type: OrderType::StockOut, is_record: true, + non_payment: false, }; c.order.preprocess(&mut order, &p.user1, true, p.person2.id); c.order.add(order, tx.as_mut()).await.unwrap(); diff --git a/web/src/api/erp/model.ts b/web/src/api/erp/model.ts index ade4375..184dd55 100644 --- a/web/src/api/erp/model.ts +++ b/web/src/api/erp/model.ts @@ -197,6 +197,7 @@ export interface Order { order_payment_status: OrderPaymentStatus; order_type: OrderType; is_record?: boolean; + non_payment?: boolean; order_category_id: number; person_related_id: number; warehouse_id: number; @@ -222,6 +223,7 @@ export interface GuestOrder { items?: OrderItem[]; order_type: OrderType; is_record?: boolean; + non_payment?: boolean; person_related_id: number; warehouse_id: number; } @@ -275,6 +277,7 @@ export interface GetOrdersQuery { person_in_charge_id?: number; order_type?: OrderType; is_record?: boolean; + non_payment?: boolean; order_category_id?: number; order_payment_status?: Set; currency?: OrderCurrency; @@ -297,6 +300,7 @@ export interface GetGuestOrdersQuery { person_in_charge_id?: number; order_type?: OrderType; is_record?: boolean; + non_payment?: boolean; currency?: OrderCurrency; order_category_id?: number; date_start?: number; diff --git a/web/src/components/erp/GuestOrders.vue b/web/src/components/erp/GuestOrders.vue index e4b261e..504484f 100644 --- a/web/src/components/erp/GuestOrders.vue +++ b/web/src/components/erp/GuestOrders.vue @@ -95,6 +95,12 @@ const form: FormRow[] = [ noUpdate: true, onlyModal: true, }, + { + key: "non_payment", + type: FormRowType.CheckBox, + onlyModal: true, + noUpdate: true, + }, { key: "order_category_id", type: FormRowType.OrderCategory, @@ -278,8 +284,8 @@ myself.subscribe(async (flag) => { {{ t("common.equalToValue") }} - - {{ t("common.isRecord") }} + + {{ t("common.nonPayment") }} diff --git a/web/src/components/erp/Orders.vue b/web/src/components/erp/Orders.vue index 43a1f61..af76928 100644 --- a/web/src/components/erp/Orders.vue +++ b/web/src/components/erp/Orders.vue @@ -126,6 +126,12 @@ const form: FormRow[] = [ onlyModal: true, noUpdate: true, }, + { + key: "non_payment", + type: FormRowType.CheckBox, + onlyModal: true, + noUpdate: true, + }, { key: "order_category_id", type: FormRowType.OrderCategory, @@ -400,6 +406,9 @@ myself.subscribe(async (flag) => { {{ t("common.isRecord") }} + + {{ t("common.nonPayment") }} + diff --git a/web/src/i18n/lang/cn.ts b/web/src/i18n/lang/cn.ts index da96f3c..368c52f 100644 --- a/web/src/i18n/lang/cn.ts +++ b/web/src/i18n/lang/cn.ts @@ -91,6 +91,7 @@ export default { userConfigure: "用户配置", defaultOption: "默认选项", userStatus: "用户状态", + nonPayment: "无需回款", }, orderType: { stockIn: "进货", diff --git a/web/src/i18n/lang/en.ts b/web/src/i18n/lang/en.ts index 9e13569..5a12936 100644 --- a/web/src/i18n/lang/en.ts +++ b/web/src/i18n/lang/en.ts @@ -91,6 +91,7 @@ export default { fromGuestOrderID: "Guest Order ID", defaultOption: "Default option", userStatus: "User status", + nonPayment: "Non Payment", }, orderType: { stockIn: "Stock in", diff --git a/web/src/i18n/lang/malay.ts b/web/src/i18n/lang/malay.ts index d1091fd..343c736 100644 --- a/web/src/i18n/lang/malay.ts +++ b/web/src/i18n/lang/malay.ts @@ -91,6 +91,7 @@ export default { fromGuestOrderID: "ID Pesanan Tetamu", defaultOption: "Pilihan lalai", userStatus: "Status pengguna", + nonPayment: "Tiada bayar", }, orderType: { stockIn: "Masuk Stok",