source: StockMaster/Migrations/20260222001854_InitialSetup.cs@ dfe03b8

main
Last change on this file since dfe03b8 was dfe03b8, checked in by Ceyda <ceyda.huseini@…>, 3 days ago

Initialize StockMaster project

  • Property mode set to 100644
File size: 25.8 KB
Line 
1using System;
2using Microsoft.EntityFrameworkCore.Migrations;
3using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
4
5#nullable disable
6
7namespace StockMaster.Migrations
8{
9 /// <inheritdoc />
10 public partial class InitialSetup : Migration
11 {
12 /// <inheritdoc />
13 protected override void Up(MigrationBuilder migrationBuilder)
14 {
15 migrationBuilder.EnsureSchema(
16 name: "stock_management");
17
18 migrationBuilder.CreateTable(
19 name: "category",
20 schema: "stock_management",
21 columns: table => new
22 {
23 category_id = table.Column<int>(type: "integer", nullable: false)
24 .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
25 name = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
26 description = table.Column<string>(type: "text", nullable: false),
27 created_at = table.Column<DateTime>(type: "timestamp without time zone", nullable: false)
28 },
29 constraints: table =>
30 {
31 table.PrimaryKey("PK_category", x => x.category_id);
32 });
33
34 migrationBuilder.CreateTable(
35 name: "customer",
36 schema: "stock_management",
37 columns: table => new
38 {
39 customer_id = table.Column<int>(type: "integer", nullable: false)
40 .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
41 name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
42 email = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
43 phone = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
44 address = table.Column<string>(type: "text", nullable: false),
45 created_at = table.Column<DateTime>(type: "timestamp without time zone", nullable: false)
46 },
47 constraints: table =>
48 {
49 table.PrimaryKey("PK_customer", x => x.customer_id);
50 });
51
52 migrationBuilder.CreateTable(
53 name: "product_price_log",
54 schema: "stock_management",
55 columns: table => new
56 {
57 log_id = table.Column<int>(type: "integer", nullable: false)
58 .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
59 product_id = table.Column<int>(type: "integer", nullable: false),
60 product_name = table.Column<string>(type: "text", nullable: false),
61 old_price = table.Column<decimal>(type: "numeric", nullable: false),
62 new_price = table.Column<decimal>(type: "numeric", nullable: false),
63 changed_by = table.Column<string>(type: "text", nullable: false),
64 changed_at = table.Column<DateTime>(type: "timestamp without time zone", nullable: false)
65 },
66 constraints: table =>
67 {
68 table.PrimaryKey("PK_product_price_log", x => x.log_id);
69 });
70
71 migrationBuilder.CreateTable(
72 name: "supplier",
73 schema: "stock_management",
74 columns: table => new
75 {
76 supplier_id = table.Column<int>(type: "integer", nullable: false)
77 .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
78 name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
79 contact_person = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
80 phone = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
81 email = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
82 address = table.Column<string>(type: "text", nullable: false),
83 created_at = table.Column<DateTime>(type: "timestamp without time zone", nullable: false)
84 },
85 constraints: table =>
86 {
87 table.PrimaryKey("PK_supplier", x => x.supplier_id);
88 });
89
90 migrationBuilder.CreateTable(
91 name: "users",
92 schema: "stock_management",
93 columns: table => new
94 {
95 user_id = table.Column<int>(type: "integer", nullable: false)
96 .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
97 username = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
98 password = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false),
99 full_name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
100 email = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
101 role = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
102 is_active = table.Column<bool>(type: "boolean", nullable: false),
103 created_at = table.Column<DateTime>(type: "timestamp without time zone", nullable: false)
104 },
105 constraints: table =>
106 {
107 table.PrimaryKey("PK_users", x => x.user_id);
108 });
109
110 migrationBuilder.CreateTable(
111 name: "warehouse",
112 schema: "stock_management",
113 columns: table => new
114 {
115 warehouse_id = table.Column<int>(type: "integer", nullable: false)
116 .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
117 name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
118 location = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false),
119 capacity = table.Column<int>(type: "integer", nullable: false),
120 created_at = table.Column<DateTime>(type: "timestamp without time zone", nullable: false)
121 },
122 constraints: table =>
123 {
124 table.PrimaryKey("PK_warehouse", x => x.warehouse_id);
125 });
126
127 migrationBuilder.CreateTable(
128 name: "product",
129 schema: "stock_management",
130 columns: table => new
131 {
132 product_id = table.Column<int>(type: "integer", nullable: false)
133 .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
134 name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
135 description = table.Column<string>(type: "text", nullable: false),
136 sku = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
137 unit_price = table.Column<decimal>(type: "numeric(12,2)", precision: 12, scale: 2, nullable: false),
138 reorder_level = table.Column<int>(type: "integer", nullable: false),
139 category_id = table.Column<int>(type: "integer", nullable: true),
140 supplier_id = table.Column<int>(type: "integer", nullable: true),
141 is_active = table.Column<bool>(type: "boolean", nullable: false),
142 created_at = table.Column<DateTime>(type: "timestamp without time zone", nullable: false)
143 },
144 constraints: table =>
145 {
146 table.PrimaryKey("PK_product", x => x.product_id);
147 table.ForeignKey(
148 name: "FK_product_category_category_id",
149 column: x => x.category_id,
150 principalSchema: "stock_management",
151 principalTable: "category",
152 principalColumn: "category_id");
153 table.ForeignKey(
154 name: "FK_product_supplier_supplier_id",
155 column: x => x.supplier_id,
156 principalSchema: "stock_management",
157 principalTable: "supplier",
158 principalColumn: "supplier_id");
159 });
160
161 migrationBuilder.CreateTable(
162 name: "purchase_order",
163 schema: "stock_management",
164 columns: table => new
165 {
166 po_id = table.Column<int>(type: "integer", nullable: false)
167 .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
168 order_date = table.Column<DateTime>(type: "timestamp without time zone", nullable: false),
169 expected_delivery_date = table.Column<DateTime>(type: "timestamp without time zone", nullable: false),
170 actual_delivery_date = table.Column<DateTime>(type: "timestamp without time zone", nullable: true),
171 status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
172 supplier_id = table.Column<int>(type: "integer", nullable: true),
173 warehouse_id = table.Column<int>(type: "integer", nullable: false),
174 created_at = table.Column<DateTime>(type: "timestamp without time zone", nullable: false)
175 },
176 constraints: table =>
177 {
178 table.PrimaryKey("PK_purchase_order", x => x.po_id);
179 table.ForeignKey(
180 name: "FK_purchase_order_supplier_supplier_id",
181 column: x => x.supplier_id,
182 principalSchema: "stock_management",
183 principalTable: "supplier",
184 principalColumn: "supplier_id");
185 table.ForeignKey(
186 name: "FK_purchase_order_warehouse_warehouse_id",
187 column: x => x.warehouse_id,
188 principalSchema: "stock_management",
189 principalTable: "warehouse",
190 principalColumn: "warehouse_id",
191 onDelete: ReferentialAction.Cascade);
192 });
193
194 migrationBuilder.CreateTable(
195 name: "sale",
196 schema: "stock_management",
197 columns: table => new
198 {
199 sale_id = table.Column<int>(type: "integer", nullable: false)
200 .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
201 date_time = table.Column<DateTime>(type: "timestamp without time zone", nullable: false),
202 total_amount = table.Column<decimal>(type: "numeric(15,2)", precision: 15, scale: 2, nullable: false),
203 user_id = table.Column<int>(type: "integer", nullable: true),
204 customer_id = table.Column<int>(type: "integer", nullable: true),
205 warehouse_id = table.Column<int>(type: "integer", nullable: false)
206 },
207 constraints: table =>
208 {
209 table.PrimaryKey("PK_sale", x => x.sale_id);
210 table.ForeignKey(
211 name: "FK_sale_customer_customer_id",
212 column: x => x.customer_id,
213 principalSchema: "stock_management",
214 principalTable: "customer",
215 principalColumn: "customer_id");
216 table.ForeignKey(
217 name: "FK_sale_users_user_id",
218 column: x => x.user_id,
219 principalSchema: "stock_management",
220 principalTable: "users",
221 principalColumn: "user_id");
222 table.ForeignKey(
223 name: "FK_sale_warehouse_warehouse_id",
224 column: x => x.warehouse_id,
225 principalSchema: "stock_management",
226 principalTable: "warehouse",
227 principalColumn: "warehouse_id",
228 onDelete: ReferentialAction.Cascade);
229 });
230
231 migrationBuilder.CreateTable(
232 name: "warehouse_stock",
233 schema: "stock_management",
234 columns: table => new
235 {
236 warehouse_id = table.Column<int>(type: "integer", nullable: false),
237 product_id = table.Column<int>(type: "integer", nullable: false),
238 quantity_on_hand = table.Column<int>(type: "integer", nullable: false),
239 last_updated = table.Column<DateTime>(type: "timestamp without time zone", nullable: false)
240 },
241 constraints: table =>
242 {
243 table.PrimaryKey("PK_warehouse_stock", x => new { x.warehouse_id, x.product_id });
244 table.ForeignKey(
245 name: "FK_warehouse_stock_product_product_id",
246 column: x => x.product_id,
247 principalSchema: "stock_management",
248 principalTable: "product",
249 principalColumn: "product_id",
250 onDelete: ReferentialAction.Cascade);
251 table.ForeignKey(
252 name: "FK_warehouse_stock_warehouse_warehouse_id",
253 column: x => x.warehouse_id,
254 principalSchema: "stock_management",
255 principalTable: "warehouse",
256 principalColumn: "warehouse_id",
257 onDelete: ReferentialAction.Cascade);
258 });
259
260 migrationBuilder.CreateTable(
261 name: "purchase_order_item",
262 schema: "stock_management",
263 columns: table => new
264 {
265 po_id = table.Column<int>(type: "integer", nullable: false),
266 product_id = table.Column<int>(type: "integer", nullable: false),
267 quantity = table.Column<int>(type: "integer", nullable: false),
268 unit_cost = table.Column<decimal>(type: "numeric(12,2)", precision: 12, scale: 2, nullable: false),
269 received_quantity = table.Column<int>(type: "integer", nullable: false)
270 },
271 constraints: table =>
272 {
273 table.PrimaryKey("PK_purchase_order_item", x => new { x.po_id, x.product_id });
274 table.ForeignKey(
275 name: "FK_purchase_order_item_product_product_id",
276 column: x => x.product_id,
277 principalSchema: "stock_management",
278 principalTable: "product",
279 principalColumn: "product_id",
280 onDelete: ReferentialAction.Cascade);
281 table.ForeignKey(
282 name: "FK_purchase_order_item_purchase_order_po_id",
283 column: x => x.po_id,
284 principalSchema: "stock_management",
285 principalTable: "purchase_order",
286 principalColumn: "po_id",
287 onDelete: ReferentialAction.Cascade);
288 });
289
290 migrationBuilder.CreateTable(
291 name: "sale_item",
292 schema: "stock_management",
293 columns: table => new
294 {
295 sale_id = table.Column<int>(type: "integer", nullable: false),
296 product_id = table.Column<int>(type: "integer", nullable: false),
297 quantity = table.Column<int>(type: "integer", nullable: false),
298 unit_price_at_sale = table.Column<decimal>(type: "numeric(12,2)", precision: 12, scale: 2, nullable: false)
299 },
300 constraints: table =>
301 {
302 table.PrimaryKey("PK_sale_item", x => new { x.sale_id, x.product_id });
303 table.ForeignKey(
304 name: "FK_sale_item_product_product_id",
305 column: x => x.product_id,
306 principalSchema: "stock_management",
307 principalTable: "product",
308 principalColumn: "product_id",
309 onDelete: ReferentialAction.Cascade);
310 table.ForeignKey(
311 name: "FK_sale_item_sale_sale_id",
312 column: x => x.sale_id,
313 principalSchema: "stock_management",
314 principalTable: "sale",
315 principalColumn: "sale_id",
316 onDelete: ReferentialAction.Cascade);
317 });
318
319 migrationBuilder.CreateIndex(
320 name: "IX_product_category_id",
321 schema: "stock_management",
322 table: "product",
323 column: "category_id");
324
325 migrationBuilder.CreateIndex(
326 name: "IX_product_supplier_id",
327 schema: "stock_management",
328 table: "product",
329 column: "supplier_id");
330
331 migrationBuilder.CreateIndex(
332 name: "IX_purchase_order_supplier_id",
333 schema: "stock_management",
334 table: "purchase_order",
335 column: "supplier_id");
336
337 migrationBuilder.CreateIndex(
338 name: "IX_purchase_order_warehouse_id",
339 schema: "stock_management",
340 table: "purchase_order",
341 column: "warehouse_id");
342
343 migrationBuilder.CreateIndex(
344 name: "IX_purchase_order_item_product_id",
345 schema: "stock_management",
346 table: "purchase_order_item",
347 column: "product_id");
348
349 migrationBuilder.CreateIndex(
350 name: "IX_sale_customer_id",
351 schema: "stock_management",
352 table: "sale",
353 column: "customer_id");
354
355 migrationBuilder.CreateIndex(
356 name: "IX_sale_user_id",
357 schema: "stock_management",
358 table: "sale",
359 column: "user_id");
360
361 migrationBuilder.CreateIndex(
362 name: "IX_sale_warehouse_id",
363 schema: "stock_management",
364 table: "sale",
365 column: "warehouse_id");
366
367 migrationBuilder.CreateIndex(
368 name: "IX_sale_item_product_id",
369 schema: "stock_management",
370 table: "sale_item",
371 column: "product_id");
372
373 migrationBuilder.CreateIndex(
374 name: "IX_warehouse_stock_product_id",
375 schema: "stock_management",
376 table: "warehouse_stock",
377 column: "product_id");
378
379 // TABLOLAR OLUŞTUKTAN SONRA TRIGGER VE VIEWLARI OLUŞTURUYORUZ
380 migrationBuilder.Sql(@"
381 SET search_path TO stock_management, public;
382
383 -- LOG TABLOSU
384 CREATE TABLE IF NOT EXISTS product_price_log (
385 log_id SERIAL PRIMARY KEY,
386 product_id INT NOT NULL,
387 product_name VARCHAR(100),
388 old_price DECIMAL(12,2),
389 new_price DECIMAL(12,2),
390 changed_by VARCHAR(50),
391 changed_at TIMESTAMP DEFAULT NOW()
392 );
393
394 -- FONKSIYONLAR VE TRIGGERLAR
395 CREATE OR REPLACE FUNCTION trg_fn_recalculate_sale_total() RETURNS TRIGGER LANGUAGE plpgsql AS $$
396 DECLARE v_sale_id INT;
397 BEGIN
398 IF TG_OP = 'DELETE' THEN v_sale_id := OLD.sale_id; ELSE v_sale_id := NEW.sale_id; END IF;
399 UPDATE sale SET total_amount = (SELECT COALESCE(SUM(quantity * unit_price_at_sale), 0) FROM sale_item WHERE sale_id = v_sale_id) WHERE sale_id = v_sale_id;
400 RETURN NULL;
401 END; $$;
402
403 CREATE OR REPLACE TRIGGER trg_recalculate_sale_total AFTER INSERT OR UPDATE OR DELETE ON sale_item FOR EACH ROW EXECUTE FUNCTION trg_fn_recalculate_sale_total();
404
405 CREATE OR REPLACE FUNCTION trg_fn_prevent_negative_stock() RETURNS TRIGGER LANGUAGE plpgsql AS $$
406 BEGIN
407 IF NEW.quantity_on_hand < 0 THEN RAISE EXCEPTION 'Stock cannot be negative!'; END IF;
408 RETURN NEW;
409 END; $$;
410
411 CREATE OR REPLACE TRIGGER trg_prevent_negative_stock BEFORE INSERT OR UPDATE ON warehouse_stock FOR EACH ROW EXECUTE FUNCTION trg_fn_prevent_negative_stock();
412
413 CREATE OR REPLACE FUNCTION prevent_self_delete() RETURNS TRIGGER AS $$
414 BEGIN
415 IF OLD.username = current_setting('app.current_user', true) THEN RAISE EXCEPTION 'You cannot delete your own account.'; END IF;
416 RETURN OLD;
417 END; $$ LANGUAGE plpgsql;
418
419 CREATE OR REPLACE TRIGGER trg_prevent_self_delete BEFORE DELETE ON users FOR EACH ROW EXECUTE FUNCTION prevent_self_delete();
420
421 CREATE OR REPLACE FUNCTION log_price_change() RETURNS TRIGGER AS $$
422 BEGIN
423 IF NEW.unit_price <> OLD.unit_price THEN
424 INSERT INTO product_price_log (product_id, product_name, old_price, new_price, changed_by)
425 VALUES (OLD.product_id, OLD.name, OLD.unit_price, NEW.unit_price, current_setting('app.current_user', true));
426 END IF;
427 RETURN NEW;
428 END; $$ LANGUAGE plpgsql;
429
430 CREATE OR REPLACE TRIGGER trg_price_change BEFORE UPDATE ON product FOR EACH ROW EXECUTE FUNCTION log_price_change();
431
432 -- VIEWS
433 CREATE OR REPLACE VIEW vw_sales_by_day_of_week AS
434 SELECT EXTRACT(ISODOW FROM s.date_time)::INT AS day_number,
435 CASE EXTRACT(ISODOW FROM s.date_time)::INT WHEN 1 THEN 'Monday' WHEN 2 THEN 'Tuesday' WHEN 3 THEN 'Wednesday' WHEN 4 THEN 'Thursday' WHEN 5 THEN 'Friday' WHEN 6 THEN 'Saturday' WHEN 7 THEN 'Sunday' END AS day_name,
436 COUNT(DISTINCT s.sale_id) AS total_sales, COALESCE(SUM(s.total_amount), 0) AS total_revenue, ROUND(COALESCE(AVG(s.total_amount), 0), 2) AS avg_sale_value, COALESCE(SUM(si.quantity), 0) AS total_items_sold
437 FROM sale s JOIN sale_item si ON s.sale_id = si.sale_id GROUP BY EXTRACT(ISODOW FROM s.date_time) ORDER BY day_number;
438
439 CREATE OR REPLACE VIEW vw_employee_sales_ranking AS
440 SELECT u.user_id, u.full_name, u.role, COUNT(DISTINCT s.sale_id) AS total_sales, COALESCE(SUM(s.total_amount), 0) AS total_revenue, ROUND(COALESCE(AVG(s.total_amount), 0), 2) AS avg_sale_value, COUNT(DISTINCT s.customer_id) AS unique_customers, RANK() OVER (ORDER BY COALESCE(SUM(s.total_amount), 0) DESC) AS revenue_rank
441 FROM users u LEFT JOIN sale s ON u.user_id = s.user_id GROUP BY u.user_id, u.full_name, u.role HAVING COUNT(s.sale_id) > 0;
442
443 CREATE OR REPLACE VIEW vw_todays_sales_summary AS
444 SELECT COUNT(DISTINCT s.sale_id) AS total_transactions_today, COALESCE(SUM(s.total_amount), 0) AS total_revenue_today, COALESCE(SUM(si.quantity), 0) AS total_items_sold_today, COUNT(DISTINCT s.customer_id) AS unique_customers_today, COUNT(DISTINCT s.warehouse_id) AS active_warehouses_today
445 FROM sale s JOIN sale_item si ON s.sale_id = si.sale_id WHERE s.date_time::DATE = CURRENT_DATE;
446 ");
447 }
448
449 /// <inheritdoc />
450 protected override void Down(MigrationBuilder migrationBuilder)
451 {
452 migrationBuilder.DropTable(
453 name: "product_price_log",
454 schema: "stock_management");
455
456 migrationBuilder.DropTable(
457 name: "purchase_order_item",
458 schema: "stock_management");
459
460 migrationBuilder.DropTable(
461 name: "sale_item",
462 schema: "stock_management");
463
464 migrationBuilder.DropTable(
465 name: "warehouse_stock",
466 schema: "stock_management");
467
468 migrationBuilder.DropTable(
469 name: "purchase_order",
470 schema: "stock_management");
471
472 migrationBuilder.DropTable(
473 name: "sale",
474 schema: "stock_management");
475
476 migrationBuilder.DropTable(
477 name: "product",
478 schema: "stock_management");
479
480 migrationBuilder.DropTable(
481 name: "customer",
482 schema: "stock_management");
483
484 migrationBuilder.DropTable(
485 name: "users",
486 schema: "stock_management");
487
488 migrationBuilder.DropTable(
489 name: "warehouse",
490 schema: "stock_management");
491
492 migrationBuilder.DropTable(
493 name: "category",
494 schema: "stock_management");
495
496 migrationBuilder.DropTable(
497 name: "supplier",
498 schema: "stock_management");
499 }
500 }
501}
Note: See TracBrowser for help on using the repository browser.