source: src-tauri/src/commands/report.rs

main
Last change on this file was a4ca79c, checked in by Georgi Paunkov <paunkovgeorgi@…>, 4 days ago

Initial commit

  • Property mode set to 100644
File size: 6.0 KB
Line 
1use sqlx::{PgPool, Row};
2use tauri::State;
3use rust_decimal::Decimal;
4
5use crate::commands::auth::{require_manager_session, ActiveSessions};
6
7#[tauri::command]
8pub async fn today_sales(
9 pool: State<'_, PgPool>
10) -> Result<Decimal, String> {
11 let total: Decimal = sqlx::query_scalar(
12 "
13 SELECT COALESCE(SUM(amount), 0)
14 FROM payment
15 WHERE payment_date::date = CURRENT_DATE
16 "
17 )
18 .fetch_one(pool.inner())
19 .await
20 .map_err(|e| e.to_string())?;
21
22 Ok(total)
23}
24
25#[tauri::command]
26pub async fn today_orders(
27 pool: State<'_, PgPool>
28) -> Result<i64, String> {
29 let count: i64 = sqlx::query_scalar(
30 "
31 SELECT COUNT(*)
32 FROM orders
33 WHERE created_at::date = CURRENT_DATE
34 "
35 )
36 .fetch_one(pool.inner())
37 .await
38 .map_err(|e| e.to_string())?;
39
40 Ok(count)
41}
42
43#[tauri::command]
44pub async fn best_product(
45 pool: State<'_, PgPool>
46) -> Result<String, String> {
47 let product: Option<String> = sqlx::query_scalar(
48 "
49 SELECT p.name
50 FROM order_item oi
51 JOIN product p ON p.product_id = oi.product_id
52 JOIN orders o ON o.order_id = oi.order_id
53 WHERE o.status = 'ПЛАТЕНА'
54 GROUP BY p.name
55 ORDER BY SUM(oi.quantity) DESC
56 LIMIT 1
57 "
58 )
59 .fetch_optional(pool.inner())
60 .await
61 .map_err(|e| e.to_string())?;
62
63 Ok(product.unwrap_or_else(|| "-".to_string()))
64}
65
66#[tauri::command]
67pub async fn z_report(
68 token: String,
69 pool: State<'_, PgPool>,
70 sessions: State<'_, ActiveSessions>,
71) -> Result<serde_json::Value, String> {
72 let session = require_manager_session(&token, sessions.inner()).await?;
73 let admin_id = session.user_id;
74
75 let mut tx = pool.begin()
76 .await
77 .map_err(|e| e.to_string())?;
78
79 let last_close: Option<chrono::NaiveDateTime> = sqlx::query_scalar(
80 "SELECT MAX(closed_at) FROM shift_close"
81 )
82 .fetch_one(&mut *tx)
83 .await
84 .map_err(|e| e.to_string())?;
85
86 let since = last_close.unwrap_or_else(|| {
87 chrono::NaiveDate::from_ymd_opt(2000, 1, 1)
88 .unwrap()
89 .and_hms_opt(0, 0, 0)
90 .unwrap()
91 });
92
93 let row = sqlx::query(
94 "
95 SELECT
96 COALESCE(SUM(amount), 0) as total,
97 COUNT(*) as order_count
98 FROM payment
99 WHERE payment_date > $1
100 "
101 )
102 .bind(since)
103 .fetch_one(&mut *tx)
104 .await
105 .map_err(|e| e.to_string())?;
106
107 let total: Decimal = row.get("total");
108 let order_count: i64 = row.get("order_count");
109
110 sqlx::query(
111 "
112 INSERT INTO shift_close (closed_by, total, order_count)
113 VALUES ($1, $2, $3)
114 "
115 )
116 .bind(admin_id)
117 .bind(total)
118 .bind(order_count as i32)
119 .execute(&mut *tx)
120 .await
121 .map_err(|e| e.to_string())?;
122
123 tx.commit()
124 .await
125 .map_err(|e| e.to_string())?;
126
127 Ok(serde_json::json!({
128 "total": total.to_string(),
129 "order_count": order_count,
130 "since": since.to_string()
131 }))
132}
133
134#[tauri::command]
135pub async fn employee_performance(
136 token: String,
137 pool: State<'_, PgPool>,
138 sessions: State<'_, ActiveSessions>,
139) -> Result<Vec<serde_json::Value>, String> {
140 require_manager_session(&token, sessions.inner()).await?;
141
142 let rows = sqlx::query(
143 "
144 SELECT
145 u.first_name,
146 u.last_name,
147 COALESCE(SUM(pay.amount), 0) as total,
148 COUNT(pay.payment_id) as order_count
149 FROM app_user u
150 LEFT JOIN orders o ON o.user_id = u.user_id
151 LEFT JOIN payment pay ON pay.order_id = o.order_id
152 GROUP BY u.user_id, u.first_name, u.last_name
153 ORDER BY total DESC
154 "
155 )
156 .fetch_all(pool.inner())
157 .await
158 .map_err(|e| e.to_string())?;
159
160 let mut result = Vec::new();
161 for row in rows {
162 result.push(serde_json::json!({
163 "name": format!("{} {}", row.get::<String, _>("first_name"), row.get::<String, _>("last_name")),
164 "total": row.get::<Decimal, _>("total").to_string(),
165 "order_count": row.get::<i64, _>("order_count")
166 }));
167 }
168 Ok(result)
169}
170
171#[tauri::command]
172pub async fn search_orders(
173 token: String,
174 query: String,
175 start_date: Option<String>,
176 end_date: Option<String>,
177 pool: State<'_, PgPool>,
178 sessions: State<'_, ActiveSessions>,
179) -> Result<Vec<serde_json::Value>, String> {
180 require_manager_session(&token, sessions.inner()).await?;
181
182 if query.trim().is_empty() && start_date.is_none() && end_date.is_none() {
183 return Ok(Vec::new());
184 }
185
186 let pattern = format!("%{}%", query.trim());
187
188 let rows = sqlx::query(
189 "
190 SELECT
191 i.invoice_number,
192 i.issued_at,
193 pay.amount,
194 u.first_name,
195 u.last_name
196 FROM invoice i
197 JOIN payment pay ON i.payment_id = pay.payment_id
198 JOIN orders o ON pay.order_id = o.order_id
199 JOIN app_user u ON o.user_id = u.user_id
200 WHERE ($1 = '%%' OR i.invoice_number ILIKE $1 OR (u.first_name || ' ' || u.last_name) ILIKE $1)
201 AND ($2::text IS NULL OR $2 = '' OR i.issued_at >= $2::timestamp)
202 AND ($3::text IS NULL OR $3 = '' OR i.issued_at <= $3::timestamp)
203 ORDER BY i.issued_at DESC
204 LIMIT 100
205 "
206 )
207 .bind(pattern)
208 .bind(start_date)
209 .bind(end_date)
210 .fetch_all(pool.inner())
211 .await
212 .map_err(|e| e.to_string())?;
213
214 let mut result = Vec::new();
215 for row in rows {
216 result.push(serde_json::json!({
217 "invoice": row.get::<String, _>("invoice_number"),
218 "date": row.get::<chrono::DateTime<chrono::Utc>, _>("issued_at").to_string(),
219 "amount": row.get::<Decimal, _>("amount").to_string(),
220 "employee": format!("{} {}", row.get::<String, _>("first_name"), row.get::<String, _>("last_name"))
221 }));
222 }
223
224 Ok(result)
225}
Note: See TracBrowser for help on using the repository browser.