source: WineTrackerFinal/WineTrackerWebApi/Controllers/ShipmentController.cs

main
Last change on this file was 17d6948, checked in by Nikola Mishevski <Nikola.Mishevski@…>, 6 days ago

initial commit WineTracker Project

  • Property mode set to 100644
File size: 22.8 KB
Line 
1using Microsoft.AspNetCore.Mvc;
2using Microsoft.EntityFrameworkCore;
3using Npgsql;
4using System.Data;
5using Dapper;
6using WineTrackerWebApi.Models.Shipment;
7using WineTrackerWebApi.Models.Wine;
8using WineTrackerWebApi.Models.Customer;
9using WineTrackerWebApi.Models.Vehicle;
10using WineTrackerWebApi.Models.Employee;
11using WineTrackerWebApi.Models.ErrorHandle;
12using WineTrackerWebApi.Helpers;
13
14namespace WineTrackerWebApi.Controllers
15{
16 [Route("api/[controller]")]
17 [ApiController]
18 public class ShipmentController : ControllerBase
19 {
20 private readonly string _connectionString;
21 public ShipmentController(IConfiguration configuration)
22 {
23 _connectionString = configuration.GetConnectionString("DefaultConnection");
24 }
25
26 // GET: api/Shipment
27 [HttpGet]
28 public async Task<ActionResult<IEnumerable<Shipment>>> GetShipment(string shipmentDate ="", string employeeNameSurname = "", string registration="")
29 {
30 var parameters = new {
31 ShipmentDate = !string.IsNullOrEmpty(shipmentDate) ? DateTime.Parse(shipmentDate) : DateTime.MinValue,
32 employeeNameSurname = employeeNameSurname.Trim().ToLower(),
33 Registration = registration.Trim().ToLower() };
34
35 List<string> whereConditons = new List<string>();
36 if(!string.IsNullOrEmpty(shipmentDate)) whereConditons.Add("s.shipment_date = @ShipmentDate");
37 if(!string.IsNullOrEmpty(employeeNameSurname)) whereConditons.Add("LOWER(CONCAT(e.employee_name, ' ', e.employee_surname)) Like '%' || @employeeNameSurname || '%'");
38 if(!string.IsNullOrEmpty(registration)) whereConditons.Add("LOWER(v.registration) = @Registration");
39
40 string whereClause = whereConditons.Count > 0 ? "where " + string.Join(" and ", whereConditons) : "";
41
42 using (IDbConnection db = new NpgsqlConnection(_connectionString)) // Use NpgsqlConnection for PostgreSQL
43 {
44 string query = $@"
45 select
46 s.shipment_id as ShipmentId,
47 s.payment_id as PaymentId,
48 p.payment_status as PaymentStatus,
49 TO_CHAR(s.shipment_date, 'DD-MM-YYYY') as ShipmentDate,
50 (
51 SELECT STRING_AGG(DISTINCT c.customer_name, ', ')
52 FROM shipment_load sl
53 JOIN customer c ON sl.customer_id = c.customer_id
54 WHERE sl.shipment_id = s.shipment_id
55 ) as Customers,
56 CONCAT(e.employee_name, ' ', e.employee_surname) AS EmployeeFullName,
57 CONCAT(vd.make, ' ', vd.model, ' (', v.registration, ')') AS VehicleInfo
58 FROM shipment s
59 JOIN employee e ON s.employee_id = e.employee_id
60 JOIN vehicle v ON s.vehicle_id = v.vehicle_id
61 join vehicle_details vd on vd.vehicle_details_id = v.vehicle_details_id
62 join payment p on s.payment_id = p.payment_id
63 {whereClause}
64 ORDER BY s.shipment_date desc";
65 var shipments = await db.QueryAsync<Shipment>(query, parameters);
66 return Ok(shipments);
67 }
68 }
69
70 [HttpGet("getCreateShipmentInfo")]
71 public async Task<ActionResult<IEnumerable<Shipment>>> GetCreateShipmentInfo()
72 {
73 using (IDbConnection db = new NpgsqlConnection(_connectionString)) // Use NpgsqlConnection for PostgreSQL
74 {
75 const string getEmployees = $@"
76 select
77 e.employee_id as Id,
78 CONCAT(e.employee_name, ' ', e.employee_surname) AS Name
79 from employee e";
80 var employeeList = await db.QueryAsync<EmployeeListElement>(getEmployees);
81
82 const string getCustomers = $@"
83 select
84 c.customer_id as Id,
85 CONCAT(c.customer_name , ' (', a.city , ' ', a.street ,' ',a.building_number, ' ', a.postcode, ')') AS Name,
86 CONCAT(a.city ,' ', a.street ,' ',a.building_number,' ', a.postcode) AS Address
87 from customer c
88 join address a on c.address_id = a.address_id";
89 var customerList = await db.QueryAsync<CustomerListElement>(getCustomers);
90
91 string getWines = $@"
92 select
93 w.wine_id as Id,
94 CONCAT(w.wine_name , ' (', wt.wine_type_name,', ', wt.wine_type_region,')') AS Name,
95 w.base_price as BasePrice
96 from wine w
97 join wine_type wt on w.wine_type_id = wt.wine_type_id";
98 var wineList = await db.QueryAsync<WineListElement>(getWines);
99
100 string getVehicles = $@"
101 select
102 v.vehicle_id as Id,
103 CONCAT(vd.make, ' ', vd.model, ' (', v.registration, ')') AS Name,
104 edv.employee_id as EmployeeId,
105 vd.capacity as Capacity
106 from employee_drives_vehicle edv
107 join vehicle v on edv.vehicle_id = v.vehicle_id
108 join vehicle_details vd on v.vehicle_details_id = vd.vehicle_details_id";
109 var vehicleList = await db.QueryAsync<VehicleListElement>(getVehicles);
110
111 return Ok(new{ employeeList, customerList, wineList, vehicleList });
112 }
113 }
114
115 [HttpPost]
116 public async Task<IActionResult> CreateShipment([FromBody]AddShipment shipment)
117 {
118 try
119 {
120 using (IDbConnection db = new NpgsqlConnection(_connectionString)) // Use NpgsqlConnection for PostgreSQL
121 {
122
123 db.Open();
124 using (var transaction = db.BeginTransaction()) // Start a transaction
125 {
126 var parameters = new
127 {
128 VehicleId = int.Parse(shipment.VehicleId),
129 ShipmentDate= DateTime.Parse(shipment.ShipmentDate),
130 EmployeeId = int.Parse(shipment.EmployeeId)
131 };
132
133 await ModelValidation.IsVehicleAlreadyBooked((NpgsqlConnection)db, (NpgsqlTransaction)transaction, parameters.VehicleId, parameters.ShipmentDate);
134
135 string addPayment = @"INSERT INTO public.Payment (Payment_Status) VALUES (0) RETURNING Payment_Id;";
136 var paymentId = await db.ExecuteScalarAsync<int>(addPayment, transaction: transaction);
137
138 string addShipment = $@"INSERT INTO public.Shipment (Employee_Id, Payment_Id, Vehicle_Id, Shipment_Date) VALUES (@EmployeeId, {paymentId}, @VehicleId, @ShipmentDate) RETURNING Shipment_Id;";
139 var shipmentId = await db.ExecuteScalarAsync<int>(addShipment, parameters, transaction: transaction);
140
141 //make a regular nested list for each loop to insert the shipment_load rows
142 var updateRows = shipment.Customers.SelectMany(s => s.Wines.Select(x => $"({shipmentId}, {int.Parse(s.CustomerId)}, {x.WineId}, {Math.Round((x.AgreedPrice / x.Quantity), 2)}, {x.BasePrice}, {x.Quantity})")).ToList();
143 string updateRowsString = string.Join(",", updateRows);
144
145 string addShipmentLoad = $@"INSERT INTO public.shipment_load (Shipment_Id, Customer_Id, Wine_Id, Wine_Agreed_Price, Wine_Base_Price, Wine_Quantity) VALUES {updateRowsString};";
146 var shipmentLoadId = await db.ExecuteAsync(addShipmentLoad, transaction: transaction);
147
148 transaction.Commit();
149
150 return Ok();
151 }
152 }
153 }
154 catch (Exception ex)
155 {
156 return BadRequest(new ErrorHandler { Name = "Error", Description = ex.Message });
157 }
158 }
159
160 [HttpGet("getBaseShipmentDetails/{shipmentId}")]
161 public async Task<ActionResult<ShipmentBaseDetails>> GetBaseShipmentDetails(string shipmentId)
162 {
163 using (IDbConnection db = new NpgsqlConnection(_connectionString)) // Use NpgsqlConnection for PostgreSQL
164 {
165 string query = $@"
166 SELECT
167 s.shipment_id AS ShipmentId,
168 p.payment_status AS PaymentStatus,
169 CONCAT(e.employee_name, ' ', e.employee_surname) AS EmployeeFullName,
170 CONCAT(vd.make, ' ', vd.model, ' (', v.registration, ')') AS VehicleInfo,
171 TO_CHAR(s.shipment_date, 'DD-MM-YYYY') AS ShipmentDate,
172 COUNT(DISTINCT sl.customer_id) AS NumberOfCustomers,
173 SUM(sl.wine_quantity) AS WineQuantityShipped,
174 SUM(sl.wine_base_price * sl.wine_quantity) AS ShipmentBasePrice,
175 SUM(sl.wine_agreed_price * sl.wine_quantity) AS ShipmentAgreedPrice,
176 COALESCE(ex.shipment_expenses, 0) AS ShipmentExpenses,
177 ROUND(
178 (SUM(sl.wine_agreed_price * sl.wine_quantity) -
179 SUM(sl.wine_base_price * sl.wine_quantity) -
180 COALESCE(ex.shipment_expenses, 0))::NUMERIC
181 , 2) AS ShipmentProfit
182 FROM shipment s
183 JOIN payment p ON s.payment_id = p.payment_id
184 JOIN employee e ON s.employee_id = e.employee_id
185 JOIN vehicle v ON s.vehicle_id = v.vehicle_id
186 join vehicle_details vd on vd.vehicle_details_id = v.vehicle_details_id
187 JOIN shipment_load sl ON s.shipment_id = sl.shipment_id
188 LEFT JOIN (SELECT shipment_id, SUM(amount) AS shipment_expenses FROM shipment_has_expense_of_expense_type GROUP BY shipment_id) ex ON s.shipment_id = ex.shipment_id
189 WHERE s.shipment_id = {int.Parse(shipmentId)}
190 GROUP BY
191 s.shipment_id,
192 p.payment_status,
193 e.employee_name,
194 e.employee_surname,
195 vd.make,
196 vd.model,
197 v.registration,
198 s.shipment_date,
199 ex.shipment_expenses;";
200
201 var shipmentDetails = await db.QueryFirstAsync<ShipmentBaseDetails>(query);
202 return Ok(shipmentDetails);
203 }
204 }
205
206
207 [HttpGet("getCustomerShipmentDetails/{shipmentId}")]
208 public async Task<ActionResult<IEnumerable<ShipmentCustomerDetails>>> GetCustomerShipmentDetails(string shipmentId)
209 {
210 using (IDbConnection db = new NpgsqlConnection(_connectionString)) // Use NpgsqlConnection for PostgreSQL
211 {
212 string query = $@"
213 select
214 c.customer_name as CustomerName,
215 CONCAT(a.city , ', ', a.street, ', ',a.building_number, ', ', a.postcode) AS CustomerAddress,
216 w.wine_name as WineName,
217 sl.wine_base_price as WineBasePrice,
218 sl.wine_agreed_price as WineAgreedPrice,
219 sl.wine_quantity as WineQuantity
220 from shipment_load sl
221 join customer c on sl.customer_id = c.customer_id
222 join address a on c.address_id = a.address_id
223 join wine w on sl.wine_id = w.wine_id
224 where sl.shipment_id = {int.Parse(shipmentId)}";
225
226 var shipmentCustomerDetails = await db.QueryAsync<ShipmentCustomerDetails>(query);
227 return Ok(shipmentCustomerDetails);
228 }
229 }
230
231 // PUT: api/Shipment/PayShipment/5
232 // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
233 [HttpPut("PayShipment/{paymentId}")]
234 public async Task<IActionResult> PayShipment(int paymentId)
235 {
236 try
237 {
238 using (IDbConnection db = new NpgsqlConnection(_connectionString)) // Use NpgsqlConnection for PostgreSQL
239 {
240 var parameters = new { PaymentId = paymentId };
241
242 string query = $@"update payment set payment_status = 1 where payment_Id = @PaymentId";
243 await db.ExecuteAsync(query, parameters);
244
245 return Ok();
246 }
247 }
248 catch (Exception ex)
249 {
250 return BadRequest(new ErrorHandler { Name = "Error", Description = ex.Message });
251 }
252 }
253
254 // PUT: api/Shipment/AddShipmentExpense
255 // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
256 [HttpPost("AddShipmentExpense")]
257 public async Task<IActionResult> AddShipmentExpense([FromBody] ShipmentExpense shipmentExpense)
258 {
259 try
260 {
261 using (IDbConnection db = new NpgsqlConnection(_connectionString)) // Use NpgsqlConnection for PostgreSQL
262 {
263 var parameters = new { ShipmentId = shipmentExpense.ShipmentId, ExpenseTypeId = shipmentExpense.ExpenseTypeId, Amount = shipmentExpense.Amount };
264
265 string query = $@"insert into shipment_has_expense_of_expense_type (expense_type_id, shipment_id, amount) values (@ExpenseTypeId, @ShipmentId, @Amount)";
266 await db.ExecuteAsync(query, parameters);
267
268 return Ok();
269 }
270 }
271 catch (Exception ex)
272 {
273 return BadRequest(new ErrorHandler { Name = "Error", Description = ex.Message });
274 }
275 }
276
277 // GET: api/Shipment/GetShipmentExpenses/5
278 [HttpGet("GetShipmentExpenses/{shipmentId}")]
279 public async Task<ActionResult<IEnumerable<ShipmentExpense>>> GetShipmentExpenses(int shipmentId)
280 {
281 using (IDbConnection db = new NpgsqlConnection(_connectionString)) // Use NpgsqlConnection for PostgreSQL
282 {
283 var parameters = new { ShipmentId = shipmentId };
284
285 const string query = $@"
286 select
287 sheoet.shipment_id as ShipmentId,
288 sheoet.expense_type_id as ExpenseTypeId,
289 et.expense_type_name as ExpenseTypeName,
290 sheoet.amount as Amount
291 from shipment_has_expense_of_expense_type sheoet
292 join expense_type et on et.expense_type_id = sheoet.expense_type_id
293 where sheoet.shipment_id = @ShipmentId
294 ";
295 var vehicleDetails = await db.QueryAsync<ShipmentExpense>(query, parameters);
296 return Ok(vehicleDetails);
297 }
298 }
299
300 // DELETE: api/Shipment/removeShipmentExpense/5
301 [HttpDelete("removeShipmentExpense/{shipmentId}/{expenseTypeId}")]
302 public async Task<IActionResult> RemoveShipmentExpense(int shipmentId, int expenseTypeId)
303 {
304 try
305 {
306 using (IDbConnection db = new NpgsqlConnection(_connectionString)) // Use NpgsqlConnection for PostgreSQL
307 {
308 var parameters = new { ShipmentId = shipmentId, ExpenseTypeId = expenseTypeId };
309
310 string query = $@"delete from shipment_has_expense_of_expense_type where shipment_id = @ShipmentId and expense_type_id = @ExpenseTypeId";
311 await db.ExecuteAsync(query, parameters);
312 return Ok();
313 }
314 }
315 catch (Exception ex)
316 {
317 return BadRequest(new ErrorHandler { Name = "Error", Description = ex.Message });
318 }
319 }
320
321 // DELETE: api/Shipment/deleteShipment/5
322 [HttpDelete("deleteShipment/{shipmentId}/{paymentId}")]
323 public async Task<IActionResult> DeleteShipment(int shipmentId, int paymentId)
324 {
325 try
326 {
327 using (IDbConnection db = new NpgsqlConnection(_connectionString)) // Use NpgsqlConnection for PostgreSQL
328 {
329 db.Open();
330 using (var transaction = db.BeginTransaction()) // Start a transaction
331 {
332 var parameters = new { ShipmentId = shipmentId, PaymentId = paymentId };
333
334 string deleteShipmentExpenses = $@"delete from shipment_has_expense_of_expense_type where shipment_id = @ShipmentId";
335 await db.ExecuteAsync(deleteShipmentExpenses, parameters, transaction: transaction);
336
337 string deleteShipmentLoads = $@"delete from shipment_load where shipment_id = @ShipmentId";
338 await db.ExecuteAsync(deleteShipmentLoads, parameters, transaction: transaction);
339
340 string deleteShipment = $@"delete from shipment where shipment_id = @ShipmentId";
341 await db.ExecuteAsync(deleteShipment, parameters, transaction: transaction);
342
343 string deletePayment = $@"delete from payment where payment_id = @PaymentId";
344 await db.ExecuteAsync(deletePayment, parameters, transaction: transaction);
345
346 transaction.Commit();
347 return Ok();
348 }
349 }
350 }
351 catch (Exception ex)
352 {
353 return BadRequest(new ErrorHandler { Name = "Error", Description = ex.Message });
354 }
355 }
356
357 // GET: api/Shipment/GetEditableShipment/5
358 [HttpGet("GetEditableShipment/{shipmentId}")]
359 public async Task<ActionResult<IEnumerable<AddShipment>>> GetEditableShipment(int shipmentId)
360 {
361 using (IDbConnection db = new NpgsqlConnection(_connectionString)) // Use NpgsqlConnection for PostgreSQL
362 {
363 var parameters = new { ShipmentId = shipmentId };
364
365 string getShipment = $@"
366 select
367 shipment_id as ShipmentId,
368 TO_CHAR(shipment_date , 'yyyy-MM-dd') as ShipmentDate,
369 employee_id as EmployeeId,
370 vehicle_id as VehicleId
371 from shipment s
372 where shipment_id = @ShipmentId";
373 var editableShipment = await db.QueryFirstAsync<AddShipment>(getShipment, parameters);
374
375 string getCustomersPerShipment = $@"
376 select distinct
377 c.customer_id as CustomerId
378 from shipment_load sl
379 join customer c on sl.customer_id = c.customer_id
380 where sl.shipment_id = @ShipmentId";
381
382 var customersPerShipmentIds = await db.QueryAsync<AddCustomerToShipment>(getCustomersPerShipment, parameters);
383
384 var wineParameters = new { CustomerIds = customersPerShipmentIds.Select(x => int.Parse(x.CustomerId)).ToList(), ShipmentId = shipmentId };
385
386 string getWinesPerCustomer = $@"
387 select
388 sl.wine_id as WineId,
389 customer_id as CustomerId,
390 sl.wine_quantity as Quantity,
391 sl.wine_agreed_price * sl.wine_quantity as AgreedPrice,
392 sl.wine_base_price as BasePrice
393 from shipment_load sl
394 WHERE sl.customer_id = ANY(@CustomerIds) and sl.shipment_id = @ShipmentId";
395 var winesPerCustomersDynamic = await db.QueryAsync<dynamic>(getWinesPerCustomer, wineParameters);
396
397 var customersPerShipmentMapped = winesPerCustomersDynamic.GroupBy(x => (int)x.customerid).Select(x => new AddCustomerToShipment
398 {
399 CustomerId = x.Key.ToString(),
400 Wines = x.Select(y => new AddWineToShipment
401 {
402 WineId = y.wineid.ToString(),
403 Quantity = y.quantity,
404 AgreedPrice = (decimal)y.agreedprice,
405 BasePrice = (decimal)y.baseprice
406 }).ToList()
407 }).ToList();
408
409 editableShipment.Customers = customersPerShipmentMapped;
410
411 return Ok(editableShipment);
412 }
413 }
414
415 [HttpPost("EditShipment")]
416 public async Task<ActionResult<bool>> EditShipment([FromBody] AddShipment shipment)
417 {
418 try
419 {
420 using (IDbConnection db = new NpgsqlConnection(_connectionString)) // Use NpgsqlConnection for PostgreSQL
421 {
422 db.Open();
423 using (var transaction = db.BeginTransaction()) // Start a transaction
424 {
425 var parameters = new {
426 ShipmentId = int.Parse(shipment.ShipmentId),
427 EmployeeId = int.Parse(shipment.EmployeeId),
428 VehicleId = int.Parse(shipment.VehicleId),
429 ShipmentDate = DateTime.Parse(shipment.ShipmentDate)
430 };
431
432 await ModelValidation.IsVehicleAlreadyBooked((NpgsqlConnection)db, (NpgsqlTransaction)transaction, parameters.VehicleId, parameters.ShipmentDate, parameters.ShipmentId);
433
434 string editShipment = $@"
435 update public.Shipment
436 set
437 Employee_Id = @EmployeeId,
438 Vehicle_Id = @VehicleId,
439 Shipment_Date = @ShipmentDate
440 where shipment_id = @ShipmentId";
441
442 await db.ExecuteAsync(editShipment, parameters, transaction: transaction);
443
444 string deleteShipmentLoads = $@"delete from shipment_load where shipment_id = @ShipmentId";
445 await db.ExecuteAsync(deleteShipmentLoads, parameters, transaction: transaction);
446
447 //make a regular nested for each loop to insert the shipment_load rows
448 var updateRows = shipment.Customers.SelectMany(s => s.Wines.Select(x => $"({int.Parse(shipment.ShipmentId)}, {int.Parse(s.CustomerId)}, {x.WineId}, {Math.Round((x.AgreedPrice / x.Quantity), 2)}, {x.BasePrice}, {x.Quantity})")).ToList();
449 string updateRowsString = string.Join(",", updateRows);
450
451 string addShipmentLoad = $@"INSERT INTO public.shipment_load (Shipment_Id, Customer_Id, Wine_Id, Wine_Agreed_Price, Wine_Base_Price, Wine_Quantity) VALUES {updateRowsString};";
452 var shipmentLoadId = await db.ExecuteAsync(addShipmentLoad, transaction: transaction);
453
454 transaction.Commit();
455
456 return Ok(true);
457 }
458 }
459 }
460 catch (Exception ex)
461 {
462 return BadRequest(ex.Message);
463 }
464 }
465 }
466}
Note: See TracBrowser for help on using the repository browser.