Manufacturing Data Tracking App
創建於:2025年1月6日
創建於:2025年1月6日
Crafting an innovative application
Working on a user-friendly Windows application for manufacturing, focusing on interfaces for operators, technicians, and team leaders, using C# .NET Framework, DevExpress, and SQL Server database.
Crafting a personalized solution
I’m developing a comprehensive design approach to leverage C#, DevExpress, and SQL Server for a shift management application, including detailed workflows, data handling, and user role management.
Designing the data schema
I’m detailing a SQL Server data schema with tables for users, machines, shifts, and production orders, emphasizing real-time feedback and best practices like normalization and DevExpress controls.
Crafting interfaces and tracking
I’m working on a versatile UI, integrating badge scanning for login, and a streamlined team leader interface for shift management and tracking production, consumption, and troubleshooting insights.
Thought about production data tracking system pendant 18 secondes
Below is a comprehensive proposal and high-level design for an innovative, simple, and practical Windows application to track production data in a manufacturing environment. The solution uses C# (.NET Framework), DevExpress UI components, and a SQL Server database. The focus is on an operator/technician input interface and team leader configuration interface, both kept straightforward and efficient.
XtraForm
, GridControl
, ChartControl
, LayoutControl
) for a polished and user-friendly UI.Below is a simplified depiction of the core tables needed (additional fields may be added as required):
Users
UserID
(PK)FullName
UserRole
(Operator, Technician, TeamLeader, Manager)BadgeID
(for scanning)Active
(bool)Machines
MachineID
(PK)MachineName
MachineType
(Printing, Laminating, Complexing, etc.)Active
(bool)Shifts
ShiftID
(PK)ShiftName
(Morning, Day, Night)StartTime
EndTime
TeamLeaderID
(FK to Users)Active
(bool)ProductionOrders
OrderID
(PK)OrderNumber
TargetQuantity
Active
(bool)ProductionRecords
ProductionRecordID
(PK)MachineID
(FK)ShiftID
(FK)OrderID
(FK)OperatorID
(FK)StartTime
EndTime
(nullable if still running)ProducedQuantity
LotNumber
(auto-generated)RecordStatus
(Running, Stopped, Completed, etc.)DowntimeRecords
DowntimeID
(PK)MachineID
(FK)ShiftID
(FK)OperatorID
(FK)StartTime
EndTime
DowntimeCause
Duration
ConsumptionRecords
ConsumptionID
(PK)OrderID
(FK)MachineID
(FK)MaterialCode
ConsumedQuantity
ConsumptionTime
Below is a typical daily workflow highlighting how each role (Team Leader, Operator/Technician) interacts with the application:
Team Leader Login
Assign Resources
OrderID
to MachineID
).Authorize Declarations
Check Tools / Inventory (optional)
Shift Start Confirmation
ShiftID
as active, logs the timestamp in the database, and opens up data entry for Operators/Technicians.Operator/Technician Badge Scan
Machine Status Selection
Production Start
StartTime
in ProductionRecords
for that machine/order, status = “Running”.Production Stop
ProductionRecords
status to “Stopped” or “Completed”.Downtime
DowntimeRecords
with start time.Consumption Declaration
MaterialCode
, ConsumedQuantity
, ConsumptionTime
.ProductionOrder
.Shift Transition
Data Validation
Approval
Reporting and Handover
Operator/Technician Interface
XtraTabControl
or NavigationFrame
for different actions (Production Start/Stop, Downtime, Consumption).Team Leader Interface
Responsiveness and Scaling
Error Prevention and Best Practices
MachineID
, ShiftID
, OrderID
).BadgeID
carefully with assigned roles.Order #1234
. The system logs “Production Start” (Machine 1, 08:05).This proposal outlines a simple yet comprehensive approach to building a production tracking application using C# .NET (WinForms), DevExpress for UI, and SQL Server as a robust database backend. It focuses on:
By following these workflows, designs, and best practices, you can deliver an innovative, practical manufacturing application that boosts productivity, ensures accurate data collection, and provides valuable insights into production efficiency and downtime causes.
Crafting production reporting
I'm tasked with designing SQL queries and procedures for a production reporting system. This involves creating 8 tables, including Users and ProductionRecords, and developing various stored procedures for data operations like InsertProductionRecord and StartProduction.
Focusing on key tables
I’m pinpointing essential tables such as Users, Machines, and ProductionOrders, ensuring clarity and brevity with precise T-SQL syntax and concise comments.
Crafting SQL solutions
I’m compiling a comprehensive SQL script for 8 tables and 13 procedures, including sample usage and indexing best practices, to facilitate effective production reporting scenario handling.
Thought about production reporting SQL design pendant 28 secondes
Below is an illustrative SQL Server script that demonstrates how you might implement the core tables and stored procedures for the proposed production tracking application. This includes the creation of tables, constraints, indexes, and essential stored procedures (for starting/stopping production, recording downtime, etc.). Feel free to adapt the schema and procedures to your organization’s specific naming conventions and requirements.
Note: This script is meant as a foundation or starting template. In a real-world system, you would add more validations, error handling, security measures, and transaction logic as needed.
Below are the core tables needed to capture users, shifts, machines, orders, production records, downtime events, and consumption.
-------------------------------------------------------------------------------- -- 1) Create Tables -------------------------------------------------------------------------------- -- USERS TABLE IF OBJECT_ID('[dbo].[Users]', 'U') IS NOT NULL DROP TABLE [dbo].[Users]; GO CREATE TABLE [dbo].[Users] ( UserID INT IDENTITY(1,1) PRIMARY KEY, FullName VARCHAR(100) NOT NULL, UserRole VARCHAR(50) NOT NULL, -- e.g., 'Operator', 'Technician', 'TeamLeader', 'Manager' BadgeID VARCHAR(50) NOT NULL, -- Unique badge or employee ID Active BIT NOT NULL DEFAULT (1), -- 1=Active, 0=Inactive CreatedDate DATETIME NOT NULL DEFAULT (GETDATE()) ); GO -- MACHINES TABLE IF OBJECT_ID('[dbo].[Machines]', 'U') IS NOT NULL DROP TABLE [dbo].[Machines]; GO CREATE TABLE [dbo].[Machines] ( MachineID INT IDENTITY(1,1) PRIMARY KEY, MachineName VARCHAR(100) NOT NULL, MachineType VARCHAR(50) NOT NULL, -- e.g., 'Printing', 'Laminating', 'Complexing' Active BIT NOT NULL DEFAULT (1), CreatedDate DATETIME NOT NULL DEFAULT (GETDATE()) ); GO -- SHIFTS TABLE IF OBJECT_ID('[dbo].[Shifts]', 'U') IS NOT NULL DROP TABLE [dbo].[Shifts]; GO CREATE TABLE [dbo].[Shifts] ( ShiftID INT IDENTITY(1,1) PRIMARY KEY, ShiftName VARCHAR(50) NOT NULL, -- e.g., 'Morning', 'Day', 'Night' StartTime DATETIME NOT NULL, EndTime DATETIME NOT NULL, TeamLeaderID INT NOT NULL, -- FK to Users table Active BIT NOT NULL DEFAULT (1), CreatedDate DATETIME NOT NULL DEFAULT (GETDATE()) ); GO -- FK: Shifts -> Users ALTER TABLE [dbo].[Shifts] ADD CONSTRAINT FK_Shifts_TeamLeader FOREIGN KEY (TeamLeaderID) REFERENCES [dbo].[Users](UserID); GO -- PRODUCTION ORDERS IF OBJECT_ID('[dbo].[ProductionOrders]', 'U') IS NOT NULL DROP TABLE [dbo].[ProductionOrders]; GO CREATE TABLE [dbo].[ProductionOrders] ( OrderID INT IDENTITY(1,1) PRIMARY KEY, OrderNumber VARCHAR(50) NOT NULL, -- e.g., 'MO-1001' TargetQuantity INT NOT NULL DEFAULT (0), Active BIT NOT NULL DEFAULT (1), CreatedDate DATETIME NOT NULL DEFAULT (GETDATE()) ); GO -- PRODUCTION RECORDS IF OBJECT_ID('[dbo].[ProductionRecords]', 'U') IS NOT NULL DROP TABLE [dbo].[ProductionRecords]; GO CREATE TABLE [dbo].[ProductionRecords] ( ProductionRecordID INT IDENTITY(1,1) PRIMARY KEY, MachineID INT NOT NULL, -- FK to Machines ShiftID INT NOT NULL, -- FK to Shifts OrderID INT NOT NULL, -- FK to ProductionOrders OperatorID INT NOT NULL, -- FK to Users StartTime DATETIME NOT NULL, EndTime DATETIME NULL, ProducedQuantity INT NOT NULL DEFAULT (0), LotNumber VARCHAR(50) NOT NULL, -- e.g., auto-generated RecordStatus VARCHAR(20) NOT NULL, -- e.g., 'Running', 'Stopped', 'Completed' CreatedDate DATETIME NOT NULL DEFAULT (GETDATE()) ); GO -- FKs: ProductionRecords -> Machines, Shifts, Orders, Users ALTER TABLE [dbo].[ProductionRecords] ADD CONSTRAINT FK_ProdRecords_Machines FOREIGN KEY (MachineID) REFERENCES [dbo].[Machines](MachineID); ALTER TABLE [dbo].[ProductionRecords] ADD CONSTRAINT FK_ProdRecords_Shifts FOREIGN KEY (ShiftID) REFERENCES [dbo].[Shifts](ShiftID); ALTER TABLE [dbo].[ProductionRecords] ADD CONSTRAINT FK_ProdRecords_Orders FOREIGN KEY (OrderID) REFERENCES [dbo].[ProductionOrders](OrderID); ALTER TABLE [dbo].[ProductionRecords] ADD CONSTRAINT FK_ProdRecords_Users FOREIGN KEY (OperatorID) REFERENCES [dbo].[Users](UserID); GO -- DOWNTIME RECORDS IF OBJECT_ID('[dbo].[DowntimeRecords]', 'U') IS NOT NULL DROP TABLE [dbo].[DowntimeRecords]; GO CREATE TABLE [dbo].[DowntimeRecords] ( DowntimeID INT IDENTITY(1,1) PRIMARY KEY, MachineID INT NOT NULL, -- FK to Machines ShiftID INT NOT NULL, -- FK to Shifts OperatorID INT NOT NULL, -- FK to Users StartTime DATETIME NOT NULL, EndTime DATETIME NULL, DowntimeCause VARCHAR(100) NOT NULL, -- e.g., 'Breakdown', 'Maintenance', 'Changeover' Duration INT NULL, -- in minutes (or seconds) CreatedDate DATETIME NOT NULL DEFAULT (GETDATE()) ); GO -- FKs: DowntimeRecords -> Machines, Shifts, Users ALTER TABLE [dbo].[DowntimeRecords] ADD CONSTRAINT FK_DowntimeRecords_Machines FOREIGN KEY (MachineID) REFERENCES [dbo].[Machines](MachineID); ALTER TABLE [dbo].[DowntimeRecords] ADD CONSTRAINT FK_DowntimeRecords_Shifts FOREIGN KEY (ShiftID) REFERENCES [dbo].[Shifts](ShiftID); ALTER TABLE [dbo].[DowntimeRecords] ADD CONSTRAINT FK_DowntimeRecords_Users FOREIGN KEY (OperatorID) REFERENCES [dbo].[Users](UserID); GO -- CONSUMPTION RECORDS IF OBJECT_ID('[dbo].[ConsumptionRecords]', 'U') IS NOT NULL DROP TABLE [dbo].[ConsumptionRecords]; GO CREATE TABLE [dbo].[ConsumptionRecords] ( ConsumptionID INT IDENTITY(1,1) PRIMARY KEY, OrderID INT NOT NULL, -- FK to ProductionOrders MachineID INT NOT NULL, -- FK to Machines MaterialCode VARCHAR(50) NOT NULL, ConsumedQuantity INT NOT NULL DEFAULT (0), ConsumptionTime DATETIME NOT NULL, CreatedDate DATETIME NOT NULL DEFAULT (GETDATE()) ); GO -- FKs: ConsumptionRecords -> ProductionOrders, Machines ALTER TABLE [dbo].[ConsumptionRecords] ADD CONSTRAINT FK_ConsumptionRecords_Orders FOREIGN KEY (OrderID) REFERENCES [dbo].[ProductionOrders](OrderID); ALTER TABLE [dbo].[ConsumptionRecords] ADD CONSTRAINT FK_ConsumptionRecords_Machines FOREIGN KEY (MachineID) REFERENCES [dbo].[Machines](MachineID); GO -------------------------------------------------------------------------------- -- 2) Recommended Indexes -------------------------------------------------------------------------------- -- Add indexes to improve common query performance. Adjust as needed. CREATE INDEX IDX_ProductionRecords_MachineID ON [dbo].[ProductionRecords](MachineID); CREATE INDEX IDX_ProductionRecords_ShiftID ON [dbo].[ProductionRecords](ShiftID); CREATE INDEX IDX_ProductionRecords_OrderID ON [dbo].[ProductionRecords](OrderID); CREATE INDEX IDX_DowntimeRecords_MachineID ON [dbo].[DowntimeRecords](MachineID); CREATE INDEX IDX_DowntimeRecords_ShiftID ON [dbo].[DowntimeRecords](ShiftID); CREATE INDEX IDX_ConsumptionRecords_OrderID ON [dbo].[ConsumptionRecords](OrderID); GO
Below are examples of the main procedures you might need for a typical production workflow.
usp_Shift_Start
Marks a shift as started and sets it to active. Typically, the shift is already pre-defined with StartTime
and EndTime
, so this procedure might just confirm or update the shift’s status.
-------------------------------------------------------------------------------- -- Start Shift -------------------------------------------------------------------------------- IF OBJECT_ID('[dbo].[usp_Shift_Start]', 'P') IS NOT NULL DROP PROCEDURE [dbo].[usp_Shift_Start]; GO CREATE PROCEDURE [dbo].[usp_Shift_Start] ( @ShiftID INT ) AS BEGIN SET NOCOUNT ON; UPDATE [dbo].[Shifts] SET Active = 1 WHERE ShiftID = @ShiftID; -- Optional: Log any shift start event if needed SELECT 'Shift ' + CAST(@ShiftID AS VARCHAR) + ' started successfully.' AS Result; END; GO
usp_Shift_End
Finalizes or ends a shift. Ensures no open production or downtime records remain active for that shift (in a real system, you’d enforce or check that logic here).
-------------------------------------------------------------------------------- -- End Shift -------------------------------------------------------------------------------- IF OBJECT_ID('[dbo].[usp_Shift_End]', 'P') IS NOT NULL DROP PROCEDURE [dbo].[usp_Shift_End]; GO CREATE PROCEDURE [dbo].[usp_Shift_End] ( @ShiftID INT ) AS BEGIN SET NOCOUNT ON; -- Example check: ensure no 'Running' ProductionRecords remain for this shift IF EXISTS( SELECT 1 FROM [dbo].[ProductionRecords] WHERE ShiftID = @ShiftID AND RecordStatus = 'Running' ) BEGIN RAISERROR('Cannot end shift: There are active production records.', 16, 1); RETURN; END -- Mark shift as inactive UPDATE [dbo].[Shifts] SET Active = 0 WHERE ShiftID = @ShiftID; SELECT 'Shift ' + CAST(@ShiftID AS VARCHAR) + ' ended successfully.' AS Result; END; GO
usp_Production_Start
Starts a production record for a given machine, shift, order, and operator. Generates a lot number automatically (simple example).
-------------------------------------------------------------------------------- -- Start Production -------------------------------------------------------------------------------- IF OBJECT_ID('[dbo].[usp_Production_Start]', 'P') IS NOT NULL DROP PROCEDURE [dbo].[usp_Production_Start]; GO CREATE PROCEDURE [dbo].[usp_Production_Start] ( @MachineID INT, @ShiftID INT, @OrderID INT, @OperatorID INT ) AS BEGIN SET NOCOUNT ON; -- Validate if there is already an active production for this machine IF EXISTS( SELECT 1 FROM [dbo].[ProductionRecords] WHERE MachineID = @MachineID AND RecordStatus = 'Running' ) BEGIN RAISERROR('This machine is already in Running status. Stop previous production first.', 16, 1); RETURN; END DECLARE @LotNumber VARCHAR(50); SET @LotNumber = 'LOT-' + CONVERT(VARCHAR(8), GETDATE(), 112) + '-' + CAST(@MachineID AS VARCHAR) + '-' + CAST(@OrderID AS VARCHAR); INSERT INTO [dbo].[ProductionRecords] ( MachineID, ShiftID, OrderID, OperatorID, StartTime, EndTime, ProducedQuantity, LotNumber, RecordStatus ) VALUES ( @MachineID, @ShiftID, @OrderID, @OperatorID, GETDATE(), NULL, 0, @LotNumber, 'Running' ); SELECT 'Production started on Machine ' + CAST(@MachineID AS VARCHAR) + ' with LotNumber: ' + @LotNumber + '.' AS Result; END; GO
usp_Production_Stop
Stops the active production record, calculates total produced quantity, and closes the record.
-------------------------------------------------------------------------------- -- Stop Production -------------------------------------------------------------------------------- IF OBJECT_ID('[dbo].[usp_Production_Stop]', 'P') IS NOT NULL DROP PROCEDURE [dbo].[usp_Production_Stop]; GO CREATE PROCEDURE [dbo].[usp_Production_Stop] ( @ProductionRecordID INT, @ProducedQuantity INT ) AS BEGIN SET NOCOUNT ON; -- Validate if record is in Running status IF NOT EXISTS( SELECT 1 FROM [dbo].[ProductionRecords] WHERE ProductionRecordID = @ProductionRecordID AND RecordStatus = 'Running' ) BEGIN RAISERROR('No active production found for the provided ID.', 16, 1); RETURN; END UPDATE [dbo].[ProductionRecords] SET EndTime = GETDATE(), ProducedQuantity = @ProducedQuantity, RecordStatus = 'Stopped' WHERE ProductionRecordID = @ProductionRecordID; SELECT 'Production record ' + CAST(@ProductionRecordID AS VARCHAR) + ' has been stopped successfully.' AS Result; END; GO
Tip: You could also have a separate “Completed” status if “Stopped” is for partial stops, or combine them as needed.
usp_Downtime_Start
Records the start of a downtime event for a given machine, operator, and shift.
-------------------------------------------------------------------------------- -- Start Downtime -------------------------------------------------------------------------------- IF OBJECT_ID('[dbo].[usp_Downtime_Start]', 'P') IS NOT NULL DROP PROCEDURE [dbo].[usp_Downtime_Start]; GO CREATE PROCEDURE [dbo].[usp_Downtime_Start] ( @MachineID INT, @ShiftID INT, @OperatorID INT, @DowntimeCause VARCHAR(100) ) AS BEGIN SET NOCOUNT ON; INSERT INTO [dbo].[DowntimeRecords] ( MachineID, ShiftID, OperatorID, StartTime, EndTime, DowntimeCause, Duration ) VALUES ( @MachineID, @ShiftID, @OperatorID, GETDATE(), NULL, @DowntimeCause, NULL ); SELECT 'Downtime started for Machine ' + CAST(@MachineID AS VARCHAR) + ' due to ' + @DowntimeCause + '.' AS Result; END; GO
usp_Downtime_Stop
Stops the downtime record by providing the downtime ID, calculating the duration, and setting the end time.
-------------------------------------------------------------------------------- -- Stop Downtime -------------------------------------------------------------------------------- IF OBJECT_ID('[dbo].[usp_Downtime_Stop]', 'P') IS NOT NULL DROP PROCEDURE [dbo].[usp_Downtime_Stop]; GO CREATE PROCEDURE [dbo].[usp_Downtime_Stop] ( @DowntimeID INT ) AS BEGIN SET NOCOUNT ON; IF NOT EXISTS( SELECT 1 FROM [dbo].[DowntimeRecords] WHERE DowntimeID = @DowntimeID AND EndTime IS NULL ) BEGIN RAISERROR('No active downtime found for the provided ID.', 16, 1); RETURN; END UPDATE [dbo].[DowntimeRecords] SET EndTime = GETDATE(), Duration = DATEDIFF(MINUTE, StartTime, GETDATE()) WHERE DowntimeID = @DowntimeID; SELECT 'Downtime record ' + CAST(@DowntimeID AS VARCHAR) + ' has been closed successfully.' AS Result; END; GO
usp_Consumption_Declare
Records the consumption of raw materials for a given production order and machine.
-------------------------------------------------------------------------------- -- Declare Consumption -------------------------------------------------------------------------------- IF OBJECT_ID('[dbo].[usp_Consumption_Declare]', 'P') IS NOT NULL DROP PROCEDURE [dbo].[usp_Consumption_Declare]; GO CREATE PROCEDURE [dbo].[usp_Consumption_Declare] ( @OrderID INT, @MachineID INT, @MaterialCode VARCHAR(50), @ConsumedQuantity INT ) AS BEGIN SET NOCOUNT ON; INSERT INTO [dbo].[ConsumptionRecords] ( OrderID, MachineID, MaterialCode, ConsumedQuantity, ConsumptionTime ) VALUES ( @OrderID, @MachineID, @MaterialCode, @ConsumedQuantity, GETDATE() ); SELECT 'Consumption declared: ' + CAST(@ConsumedQuantity AS VARCHAR) + ' units of ' + @MaterialCode + ' for OrderID ' + CAST(@OrderID AS VARCHAR) + '.' AS Result; END; GO
Below are a few sample SELECT
queries that demonstrate how to retrieve relevant production data for dashboards or shift summaries.
-------------------------------------------------------------------------------- -- Production Summary by Shift and Machine -------------------------------------------------------------------------------- SELECT s.ShiftName, m.MachineName, p.OrderID, SUM(p.ProducedQuantity) AS TotalProduced, COUNT(*) AS ProductionRuns FROM [dbo].[ProductionRecords] p INNER JOIN [dbo].[Machines] m ON p.MachineID = m.MachineID INNER JOIN [dbo].[Shifts] s ON p.ShiftID = s.ShiftID GROUP BY s.ShiftName, m.MachineName, p.OrderID ORDER BY s.ShiftName, m.MachineName;
-------------------------------------------------------------------------------- -- Downtime by Cause and Duration -------------------------------------------------------------------------------- SELECT d.DowntimeCause, m.MachineName, s.ShiftName, SUM(d.Duration) AS TotalDowntimeMinutes, COUNT(*) AS DowntimeEvents FROM [dbo].[DowntimeRecords] d INNER JOIN [dbo].[Machines] m ON d.MachineID = m.MachineID INNER JOIN [dbo].[Shifts] s ON d.ShiftID = s.ShiftID WHERE d.Duration IS NOT NULL GROUP BY d.DowntimeCause, m.MachineName, s.ShiftName ORDER BY TotalDowntimeMinutes DESC;
-------------------------------------------------------------------------------- -- Consumption Report by Order -------------------------------------------------------------------------------- SELECT c.OrderID, p.OrderNumber, c.MaterialCode, SUM(c.ConsumedQuantity) AS TotalConsumed, COUNT(*) AS ConsumptionDeclarations FROM [dbo].[ConsumptionRecords] c INNER JOIN [dbo].[ProductionOrders] p ON c.OrderID = p.OrderID GROUP BY c.OrderID, p.OrderNumber, c.MaterialCode ORDER BY c.OrderID, c.MaterialCode;
Below is a sample scenario showing how you might call these stored procedures in sequence:
EXEC [dbo].[usp_Shift_Start] @ShiftID = 1;
EXEC [dbo].[usp_Production_Start] @MachineID = 1, @ShiftID = 1, @OrderID = 100, @OperatorID = 10; -- (User ID for Operator)
-- Suppose the newly created ProductionRecordID = 500 from the step above EXEC [dbo].[usp_Production_Stop] @ProductionRecordID = 500, @ProducedQuantity = 2000;
EXEC [dbo].[usp_Downtime_Start] @MachineID = 2, @ShiftID = 1, @OperatorID = 11, @DowntimeCause = 'Maintenance'; -- ... later ... -- Suppose the DowntimeID = 300 EXEC [dbo].[usp_Downtime_Stop] @DowntimeID = 300;
EXEC [dbo].[usp_Consumption_Declare] @OrderID = 100, @MachineID = 1, @MaterialCode = 'FILM-001', @ConsumedQuantity = 10;
EXEC [dbo].[usp_Shift_End] @ShiftID = 1;
With these scripts and procedures, you have a robust starting point for implementing a production reporting and tracking system in SQL Server. The schema is adaptable, and you can expand it with additional tables, fields, and procedures (e.g., for scheduling, tool management, or operator assignment) as your requirements evolve.
Gathering SQL insights
I’m honing in on creating comprehensive SQL queries and procedures for production reporting. Seeking user feedback to refine or offer advanced options.
Refining my approach
I'm working on scaling SQL queries to manage assignments, shifts, access, production summaries, downtime analysis, data cleansing, and user management. This is a solid step forward.
Refining SQL queries
I'm focusing on refining SQL queries for production reporting, ensuring they are labeled, documented, and include advanced procedures and roles for clarity and efficiency.
Thought about production reporting design in SQL pendant 12 secondes
Below is a comprehensive SQL Server script that creates the core database schema (tables, foreign keys, indexes) and stored procedures to manage the typical production reporting operations described previously. This script is intended as a starting template—you’ll likely extend, adapt, or refine it for your specific manufacturing environment (e.g., additional columns, advanced validations, security settings, etc.).
-------------------------------------------------------------------------------- -- 1) Create or Recreate Core Tables -------------------------------------------------------------------------------- -- USERS TABLE IF OBJECT_ID('[dbo].[Users]', 'U') IS NOT NULL DROP TABLE [dbo].[Users]; GO CREATE TABLE [dbo].[Users] ( UserID INT IDENTITY(1,1) PRIMARY KEY, FullName VARCHAR(100) NOT NULL, UserRole VARCHAR(50) NOT NULL, -- e.g.: Operator, Technician, TeamLeader, Manager BadgeID VARCHAR(50) NOT NULL, -- For scanning / identification Active BIT NOT NULL DEFAULT (1), CreatedDate DATETIME NOT NULL DEFAULT (GETDATE()) ); GO -- MACHINES TABLE IF OBJECT_ID('[dbo].[Machines]', 'U') IS NOT NULL DROP TABLE [dbo].[Machines]; GO CREATE TABLE [dbo].[Machines] ( MachineID INT IDENTITY(1,1) PRIMARY KEY, MachineName VARCHAR(100) NOT NULL, MachineType VARCHAR(50) NOT NULL, -- e.g.: Printing, Laminating, Complexing Active BIT NOT NULL DEFAULT (1), CreatedDate DATETIME NOT NULL DEFAULT (GETDATE()) ); GO -- SHIFTS TABLE IF OBJECT_ID('[dbo].[Shifts]', 'U') IS NOT NULL DROP TABLE [dbo].[Shifts]; GO CREATE TABLE [dbo].[Shifts] ( ShiftID INT IDENTITY(1,1) PRIMARY KEY, ShiftName VARCHAR(50) NOT NULL, -- e.g.: Morning, Day, Night StartTime DATETIME NOT NULL, EndTime DATETIME NOT NULL, TeamLeaderID INT NOT NULL, -- FK to Users table (TeamLeader) Active BIT NOT NULL DEFAULT (1), CreatedDate DATETIME NOT NULL DEFAULT (GETDATE()) ); GO -- Foreign key: Shifts -> Users ALTER TABLE [dbo].[Shifts] ADD CONSTRAINT FK_Shifts_TeamLeader FOREIGN KEY (TeamLeaderID) REFERENCES [dbo].[Users](UserID); GO -- PRODUCTION ORDERS TABLE IF OBJECT_ID('[dbo].[ProductionOrders]', 'U') IS NOT NULL DROP TABLE [dbo].[ProductionOrders]; GO CREATE TABLE [dbo].[ProductionOrders] ( OrderID INT IDENTITY(1,1) PRIMARY KEY, OrderNumber VARCHAR(50) NOT NULL, -- e.g.: MO-1001 TargetQuantity INT NOT NULL DEFAULT (0), Active BIT NOT NULL DEFAULT (1), CreatedDate DATETIME NOT NULL DEFAULT (GETDATE()) ); GO -- PRODUCTION RECORDS TABLE IF OBJECT_ID('[dbo].[ProductionRecords]', 'U') IS NOT NULL DROP TABLE [dbo].[ProductionRecords]; GO CREATE TABLE [dbo].[ProductionRecords] ( ProductionRecordID INT IDENTITY(1,1) PRIMARY KEY, MachineID INT NOT NULL, -- FK to Machines ShiftID INT NOT NULL, -- FK to Shifts OrderID INT NOT NULL, -- FK to ProductionOrders OperatorID INT NOT NULL, -- FK to Users StartTime DATETIME NOT NULL, EndTime DATETIME NULL, ProducedQuantity INT NOT NULL DEFAULT (0), LotNumber VARCHAR(50) NOT NULL, -- Auto-generated RecordStatus VARCHAR(20) NOT NULL, -- e.g.: Running, Stopped, Completed CreatedDate DATETIME NOT NULL DEFAULT (GETDATE()) ); GO -- Foreign keys for ProductionRecords ALTER TABLE [dbo].[ProductionRecords] ADD CONSTRAINT FK_ProdRecords_Machines FOREIGN KEY (MachineID) REFERENCES [dbo].[Machines](MachineID); ALTER TABLE [dbo].[ProductionRecords] ADD CONSTRAINT FK_ProdRecords_Shifts FOREIGN KEY (ShiftID) REFERENCES [dbo].[Shifts](ShiftID); ALTER TABLE [dbo].[ProductionRecords] ADD CONSTRAINT FK_ProdRecords_Orders FOREIGN KEY (OrderID) REFERENCES [dbo].[ProductionOrders](OrderID); ALTER TABLE [dbo].[ProductionRecords] ADD CONSTRAINT FK_ProdRecords_Users FOREIGN KEY (OperatorID) REFERENCES [dbo].[Users](UserID); GO -- DOWNTIME RECORDS TABLE IF OBJECT_ID('[dbo].[DowntimeRecords]', 'U') IS NOT NULL DROP TABLE [dbo].[DowntimeRecords]; GO CREATE TABLE [dbo].[DowntimeRecords] ( DowntimeID INT IDENTITY(1,1) PRIMARY KEY, MachineID INT NOT NULL, -- FK to Machines ShiftID INT NOT NULL, -- FK to Shifts OperatorID INT NOT NULL, -- FK to Users StartTime DATETIME NOT NULL, EndTime DATETIME NULL, DowntimeCause VARCHAR(100) NOT NULL, -- e.g.: Breakdown, Maintenance, Changeover Duration INT NULL, -- in minutes (or seconds) CreatedDate DATETIME NOT NULL DEFAULT (GETDATE()) ); GO -- Foreign keys for DowntimeRecords ALTER TABLE [dbo].[DowntimeRecords] ADD CONSTRAINT FK_DowntimeRecords_Machines FOREIGN KEY (MachineID) REFERENCES [dbo].[Machines](MachineID); ALTER TABLE [dbo].[DowntimeRecords] ADD CONSTRAINT FK_DowntimeRecords_Shifts FOREIGN KEY (ShiftID) REFERENCES [dbo].[Shifts](ShiftID); ALTER TABLE [dbo].[DowntimeRecords] ADD CONSTRAINT FK_DowntimeRecords_Users FOREIGN KEY (OperatorID) REFERENCES [dbo].[Users](UserID); GO -- CONSUMPTION RECORDS TABLE IF OBJECT_ID('[dbo].[ConsumptionRecords]', 'U') IS NOT NULL DROP TABLE [dbo].[ConsumptionRecords]; GO CREATE TABLE [dbo].[ConsumptionRecords] ( ConsumptionID INT IDENTITY(1,1) PRIMARY KEY, OrderID INT NOT NULL, -- FK to ProductionOrders MachineID INT NOT NULL, -- FK to Machines MaterialCode VARCHAR(50) NOT NULL, -- e.g.: Ink-001, Film-ABC ConsumedQuantity INT NOT NULL DEFAULT (0), ConsumptionTime DATETIME NOT NULL, CreatedDate DATETIME NOT NULL DEFAULT (GETDATE()) ); GO -- Foreign keys for ConsumptionRecords ALTER TABLE [dbo].[ConsumptionRecords] ADD CONSTRAINT FK_ConsumptionRecords_Orders FOREIGN KEY (OrderID) REFERENCES [dbo].[ProductionOrders](OrderID); ALTER TABLE [dbo].[ConsumptionRecords] ADD CONSTRAINT FK_ConsumptionRecords_Machines FOREIGN KEY (MachineID) REFERENCES [dbo].[Machines](MachineID); GO -------------------------------------------------------------------------------- -- 2) Recommended Indexes (Adjust or add more based on usage) -------------------------------------------------------------------------------- CREATE INDEX IDX_ProductionRecords_MachineID ON [dbo].[ProductionRecords](MachineID); CREATE INDEX IDX_ProductionRecords_ShiftID ON [dbo].[ProductionRecords](ShiftID); CREATE INDEX IDX_ProductionRecords_OrderID ON [dbo].[ProductionRecords](OrderID); CREATE INDEX IDX_DowntimeRecords_MachineID ON [dbo].[DowntimeRecords](MachineID); CREATE INDEX IDX_DowntimeRecords_ShiftID ON [dbo].[DowntimeRecords](ShiftID); CREATE INDEX IDX_ConsumptionRecords_OrderID ON [dbo].[ConsumptionRecords](OrderID); GO
Below are the core stored procedures to manage shifts, start/stop production, record downtime, and declare consumptions. Each includes basic validations and error handling.
usp_Shift_Start
Marks a shift as started by setting it active. You could enhance this with more checks (e.g., verifying that the shift times are correct, ensuring the team leader is valid, etc.).
-------------------------------------------------------------------------------- -- Start Shift -------------------------------------------------------------------------------- IF OBJECT_ID('[dbo].[usp_Shift_Start]', 'P') IS NOT NULL DROP PROCEDURE [dbo].[usp_Shift_Start]; GO CREATE PROCEDURE [dbo].[usp_Shift_Start] ( @ShiftID INT ) AS BEGIN SET NOCOUNT ON; UPDATE [dbo].[Shifts] SET Active = 1 WHERE ShiftID = @ShiftID; SELECT 'Shift ' + CAST(@ShiftID AS VARCHAR) + ' started successfully.' AS Result; END; GO
usp_Shift_End
Ends a shift by marking it inactive. We also verify that no production records are still in “Running” status. In a real system, you might automatically stop them or prompt the user.
-------------------------------------------------------------------------------- -- End Shift -------------------------------------------------------------------------------- IF OBJECT_ID('[dbo].[usp_Shift_End]', 'P') IS NOT NULL DROP PROCEDURE [dbo].[usp_Shift_End]; GO CREATE PROCEDURE [dbo].[usp_Shift_End] ( @ShiftID INT ) AS BEGIN SET NOCOUNT ON; -- Example check: Ensure no active production is still running on this shift IF EXISTS ( SELECT 1 FROM [dbo].[ProductionRecords] WHERE ShiftID = @ShiftID AND RecordStatus = 'Running' ) BEGIN RAISERROR('Cannot end shift: There are still active production records.', 16, 1); RETURN; END UPDATE [dbo].[Shifts] SET Active = 0 WHERE ShiftID = @ShiftID; SELECT 'Shift ' + CAST(@ShiftID AS VARCHAR) + ' ended successfully.' AS Result; END; GO
usp_Production_Start
Creates a new row in ProductionRecords
to indicate that production is starting. It automatically generates a lot number (in a simple format) for traceability. Also checks that the machine isn’t already in a “Running” state.
-------------------------------------------------------------------------------- -- Start Production -------------------------------------------------------------------------------- IF OBJECT_ID('[dbo].[usp_Production_Start]', 'P') IS NOT NULL DROP PROCEDURE [dbo].[usp_Production_Start]; GO CREATE PROCEDURE [dbo].[usp_Production_Start] ( @MachineID INT, @ShiftID INT, @OrderID INT, @OperatorID INT ) AS BEGIN SET NOCOUNT ON; -- Validate if there's already an active production on this machine IF EXISTS ( SELECT 1 FROM [dbo].[ProductionRecords] WHERE MachineID = @MachineID AND RecordStatus = 'Running' ) BEGIN RAISERROR('Machine is already in Running status. Stop previous production first.', 16, 1); RETURN; END DECLARE @LotNumber VARCHAR(50); SET @LotNumber = 'LOT-' + CONVERT(VARCHAR(8), GETDATE(), 112) + '-' + CAST(@MachineID AS VARCHAR) + '-' + CAST(@OrderID AS VARCHAR); INSERT INTO [dbo].[ProductionRecords] ( MachineID, ShiftID, OrderID, OperatorID, StartTime, EndTime, ProducedQuantity, LotNumber, RecordStatus ) VALUES ( @MachineID, @ShiftID, @OrderID, @OperatorID, GETDATE(), NULL, 0, @LotNumber, 'Running' ); SELECT 'Production started on Machine ' + CAST(@MachineID AS VARCHAR) + ', LotNumber: ' + @LotNumber + '.' AS Result; END; GO
usp_Production_Stop
Updates the relevant ProductionRecords
row to mark the production as stopped. You supply the ProductionRecordID and the final ProducedQuantity.
-------------------------------------------------------------------------------- -- Stop Production -------------------------------------------------------------------------------- IF OBJECT_ID('[dbo].[usp_Production_Stop]', 'P') IS NOT NULL DROP PROCEDURE [dbo].[usp_Production_Stop]; GO CREATE PROCEDURE [dbo].[usp_Production_Stop] ( @ProductionRecordID INT, @ProducedQuantity INT ) AS BEGIN SET NOCOUNT ON; -- Check the record is indeed in a Running status IF NOT EXISTS ( SELECT 1 FROM [dbo].[ProductionRecords] WHERE ProductionRecordID = @ProductionRecordID AND RecordStatus = 'Running' ) BEGIN RAISERROR('No active (Running) production found for the provided ID.', 16, 1); RETURN; END UPDATE [dbo].[ProductionRecords] SET EndTime = GETDATE(), ProducedQuantity = @ProducedQuantity, RecordStatus = 'Stopped' WHERE ProductionRecordID = @ProductionRecordID; SELECT 'Production record ' + CAST(@ProductionRecordID AS VARCHAR) + ' has been stopped with quantity ' + CAST(@ProducedQuantity AS VARCHAR) + '.' AS Result; END; GO
You could introduce a “Completed” status if your process differentiates between “Stopped” and “Fully Completed.” Adjust as needed.
usp_Downtime_Start
Inserts a new downtime record for the specified machine, shift, and operator, with a given downtime cause.
-------------------------------------------------------------------------------- -- Start Downtime -------------------------------------------------------------------------------- IF OBJECT_ID('[dbo].[usp_Downtime_Start]', 'P') IS NOT NULL DROP PROCEDURE [dbo].[usp_Downtime_Start]; GO CREATE PROCEDURE [dbo].[usp_Downtime_Start] ( @MachineID INT, @ShiftID INT, @OperatorID INT, @DowntimeCause VARCHAR(100) ) AS BEGIN SET NOCOUNT ON; INSERT INTO [dbo].[DowntimeRecords] ( MachineID, ShiftID, OperatorID, StartTime, EndTime, DowntimeCause, Duration ) VALUES ( @MachineID, @ShiftID, @OperatorID, GETDATE(), NULL, @DowntimeCause, NULL ); SELECT 'Downtime started for Machine ' + CAST(@MachineID AS VARCHAR) + ' due to ' + @DowntimeCause + '.' AS Result; END; GO
usp_Downtime_Stop
Completes the downtime event by updating the EndTime and calculating the Duration in minutes.
-------------------------------------------------------------------------------- -- Stop Downtime -------------------------------------------------------------------------------- IF OBJECT_ID('[dbo].[usp_Downtime_Stop]', 'P') IS NOT NULL DROP PROCEDURE [dbo].[usp_Downtime_Stop]; GO CREATE PROCEDURE [dbo].[usp_Downtime_Stop] ( @DowntimeID INT ) AS BEGIN SET NOCOUNT ON; -- Check that downtime record is still open (EndTime is NULL) IF NOT EXISTS ( SELECT 1 FROM [dbo].[DowntimeRecords] WHERE DowntimeID = @DowntimeID AND EndTime IS NULL ) BEGIN RAISERROR('No active downtime found for the provided ID.', 16, 1); RETURN; END UPDATE [dbo].[DowntimeRecords] SET EndTime = GETDATE(), Duration = DATEDIFF(MINUTE, StartTime, GETDATE()) WHERE DowntimeID = @DowntimeID; SELECT 'Downtime record ' + CAST(@DowntimeID AS VARCHAR) + ' has been closed.' AS Result; END; GO
usp_Consumption_Declare
Records consumption of materials for a particular production order (linked to a specific machine). It logs the consumed quantity and a timestamp.
-------------------------------------------------------------------------------- -- Declare Consumption -------------------------------------------------------------------------------- IF OBJECT_ID('[dbo].[usp_Consumption_Declare]', 'P') IS NOT NULL DROP PROCEDURE [dbo].[usp_Consumption_Declare]; GO CREATE PROCEDURE [dbo].[usp_Consumption_Declare] ( @OrderID INT, @MachineID INT, @MaterialCode VARCHAR(50), @ConsumedQuantity INT ) AS BEGIN SET NOCOUNT ON; INSERT INTO [dbo].[ConsumptionRecords] ( OrderID, MachineID, MaterialCode, ConsumedQuantity, ConsumptionTime ) VALUES ( @OrderID, @MachineID, @MaterialCode, @ConsumedQuantity, GETDATE() ); SELECT 'Consumption declared: ' + CAST(@ConsumedQuantity AS VARCHAR) + ' of ' + @MaterialCode + ' for OrderID ' + CAST(@OrderID AS VARCHAR) + '.' AS Result; END; GO
Below is a short demonstration of how one might use these procedures sequentially in a typical production shift scenario:
-------------------------------------------------------------------------------- -- EXAMPLE USAGE -------------------------------------------------------------------------------- -- 1. Team Leader starts the shift (ShiftID=1) EXEC [dbo].[usp_Shift_Start] @ShiftID = 1; -- 2. Operator starts production on MachineID=1 for OrderID=100 EXEC [dbo].[usp_Production_Start] @MachineID = 1, @ShiftID = 1, @OrderID = 100, @OperatorID = 10; -- (Example Operator) -- 3. Operator declares consumption EXEC [dbo].[usp_Consumption_Declare] @OrderID = 100, @MachineID = 1, @MaterialCode = 'INK-BLUE', @ConsumedQuantity = 5; -- 4. Operator stops production (assume ProductionRecordID=501 was returned or known) EXEC [dbo].[usp_Production_Stop] @ProductionRecordID = 501, @ProducedQuantity = 1500; -- 5. Technician/Operator starts downtime on MachineID=2 EXEC [dbo].[usp_Downtime_Start] @MachineID = 2, @ShiftID = 1, @OperatorID = 11, @DowntimeCause = 'Maintenance'; -- (Later, after maintenance finished, say DowntimeID=300) EXEC [dbo].[usp_Downtime_Stop] @DowntimeID = 300; -- 6. Team Leader ends the shift EXEC [dbo].[usp_Shift_End] @ShiftID = 1;
Below are a few example queries (not procedures) that showcase how to generate typical reports:
SELECT s.ShiftName, m.MachineName, p.OrderID, SUM(p.ProducedQuantity) AS TotalProduced, COUNT(*) AS ProductionRuns FROM [dbo].[ProductionRecords] p INNER JOIN [dbo].[Machines] m ON p.MachineID = m.MachineID INNER JOIN [dbo].[Shifts] s ON p.ShiftID = s.ShiftID GROUP BY s.ShiftName, m.MachineName, p.OrderID ORDER BY s.ShiftName, m.MachineName;
SELECT d.DowntimeCause, m.MachineName, s.ShiftName, SUM(d.Duration) AS TotalDowntimeMinutes, COUNT(*) AS DowntimeEvents FROM [dbo].[DowntimeRecords] d INNER JOIN [dbo].[Machines] m ON d.MachineID = m.MachineID INNER JOIN [dbo].[Shifts] s ON d.ShiftID = s.ShiftID WHERE d.Duration IS NOT NULL -- Only completed downtimes GROUP BY d.DowntimeCause, m.MachineName, s.ShiftName ORDER BY TotalDowntimeMinutes DESC;
SELECT c.OrderID, po.OrderNumber, c.MaterialCode, SUM(c.ConsumedQuantity) AS TotalConsumed, COUNT(*) AS DeclarationsCount FROM [dbo].[ConsumptionRecords] c INNER JOIN [dbo].[ProductionOrders] po ON c.OrderID = po.OrderID GROUP BY c.OrderID, po.OrderNumber, c.MaterialCode ORDER BY c.OrderID, c.MaterialCode;
TRY...CATCH
blocks in T-SQL to handle exceptions gracefully and log them for auditing or debugging.AuditLogs
table.This end-to-end SQL script provides a robust baseline for a production tracking system. It covers essential tables, foreign keys, stored procedures for shift management, production start/stop, downtime, and consumption declarations. Coupled with the example reporting queries, this design ensures:
Use these examples as a starting point to build out a full production reporting and monitoring application in your manufacturing environment.
Crafting UI mockups
I’m developing a detailed textual representation of user interfaces for users, team leaders, and managers, considering frameworks like DevExpress and WinForms for implementation.
Thought about UI design for production system pendant 9 secondes
Below is a conceptual UI design and mockup outlining the look, feel, and workflow for all user roles—Operators, Team Leaders, and Managers—in a Windows desktop application built with C# .NET and DevExpress. The primary goal is to keep interfaces intuitive, role-based, and fast to operate in a busy manufacturing environment.
+--------------------------------------+
| [Operator Production Screen] |
+--------------------------------------+
| [Badge Scan Box] [Operator: John D.]|
|--------------------------------------|
| MACHINE LIST / STATUS |
| +----------------------------------+
| | Machine: #1 [RUNNING] |
| | Order: MO-1234 |
| | Start Time: 08:05 |
| | Lot Number: LOT-YYYY-MMDD-1-1234 |
| | Produced: 1500 units |
| +----------------------------------+
| | Machine: #2 [STOPPED] |
| +----------------------------------+
| | Machine: #3 [DOWNTIME] |
| +----------------------------------+
|--------------------------------------|
| [ACTION BUTTONS] |
| [Start Prod] [Stop Prod] [Downtime] |
| [Consumption] |
|--------------------------------------|
| [Real-Time Feedback] e.g. "Machine 1
| production is running for MO-1234" |
+--------------------------------------+
Badge Scan Text Field (at the top or as a popup):
Machine Status Panel (center of the window):
Action Buttons (large, color-coded):
Real-Time Feedback:
+--------------------------------------------------------------+
| [Team Leader Dashboard] |
+--------------------------------------------------------------+
| [Ribbon/Nav Bar] [Active Shift: Morning Shift] [User: TL1] |
|--------------------------------------------------------------|
| TABS OR PANELS: |
| 1) SHIFT SETUP 2) REAL-TIME MONITORING |
| 3) DATA VALIDATION 4) REPORTS |
+--------------------------------------------------------------+
-- SHIFT SETUP TAB --
+--------------------------------------------------------------+
| [Shift Setup Panel] |
| - SHIFT DETAILS: (Shift Name, StartTime, EndTime) |
| - TEAM LEADER: [TeamLeaderID / FullName] |
| - ASSIGNMENTS: (Drag & drop operators to machines) |
| ------------------------------------------------------- |
| | Machine 1 - Operator(s): [OpA, OpB] | |
| | Machine 2 - Operator(s): [OpC, OpD] | |
| | ... | |
| ------------------------------------------------------- |
| [START SHIFT] [SAVE CHANGES] |
+--------------------------------------------------------------+
-- REAL-TIME MONITORING TAB --
+--------------------------------------------------------------+
| [GridControl or CardView with Machine Statuses] |
| MACHINE | STATUS | CURRENT ORDER | PRODUCED QTY | ... |
| Machine 1 | RUNNING | MO-1234 | 1500 | ... |
| Machine 2 | DOWNTIME | MO-1235 | 0 | ... |
| ... | ... | ... | ... | ... |
|--------------------------------------------------------------|
| [Live chart or gauge to show overall progress per shift] |
+--------------------------------------------------------------+
-- DATA VALIDATION TAB --
+--------------------------------------------------------------+
| [Table of ProductionRecords / DowntimeRecords to review] |
| ProductionRecordID | Machine | StartTime | EndTime | QTY ...|
| 101 | #1 | 08:05 | 10:20 | 1500 ...|
| ...
|--------------------------------------------------------------|
| [Edit / Approve / Reject Buttons]
+--------------------------------------------------------------+
-- REPORTS TAB --
+--------------------------------------------------------------+
| [Filters: Date Range, Shift, Machine] [Generate Report] |
| [Preview / Export / Print Buttons] |
+--------------------------------------------------------------+
Ribbon / Navigation Tabs:
Shift Management Panel:
Machine Monitoring Grid:
Data Validation Panel:
Reports Panel:
+---------------------------------------------+
| [Manager Executive Dashboard] |
+---------------------------------------------+
| [Navigation Pane: 1) Overview 2) Trends |
| 3) Reports 4) Admin? |
|---------------------------------------------|
| [MAIN PANEL] |
| => KPI CARDS: |
| - Overall Equipment Effectiveness |
| - Total Downtime in last 7 days |
| - Production vs. Target (Monthly) |
|---------------------------------------------|
| [CHARTS / GRAPHS] |
| => Barchart by Machine (Downtime) |
| => Piechart by Downtime Cause |
| => Trends over time |
+---------------------------------------------+
KPI Cards or Tile Controls at the top:
Charts & Graphs:
ChartControl
in various chart types: bar, pie, line.Filters / Drilldown:
Reports & Export:
Color Coding:
Iconography:
DevExpress Skins / Themes:
Responsive / Scalable Layout:
Multi-Language Support (if needed):
Real-Time Refresh:
Error Handling / Pop-ups:
Operators & Technicians
Team Leaders
Managers / Executives
Log-In Flow:
Consistent Look:
User-Centric Layout:
By adhering to these guidelines, the application will be both visually coherent and highly usable for all roles, thus streamlining production data capture and real-time monitoring within your manufacturing environment.
Crafting the framework
I’m assembling a Windows app with DevExpress integration, covering DB connections, DevExpress UI creates, and layers such as Entities, DataAccess, Business logic, and UI layer. Progress and refining citations are continuous.
Organizing forms and classes
I’m piecing together a minimal set of classes for forms like OperatorForm, TeamLeaderForm, and ManagerForm, while integrating stored procedure calls and a streamlined "DAL" folder.
Structuring the solution
I’m outlining the solution structure, which includes Program.cs, various folders, and minimal code snippets. Error handling with try/catch and DevExpress integration are also discussed.
Clarifying application scope
I’m noting that a comprehensive application would be more extensive than this structured skeleton. This helps set realistic expectations for the project's complexity and complexity.
Réflexion pendant 33 secondes
Below is a sample end-to-end C# WinForms application that demonstrates how you could implement the production tracking system described: it uses DevExpress controls, calls SQL Server stored procedures, and includes separate forms and classes for Operators, Team Leaders, and Managers. This example is simplified for illustration—in a real production app, you’d want to:
Despite its length, this code will give you a solid foundation: from App.config to Program.cs
, through UI forms that integrate DevExpress controls (e.g., RibbonControl, GridControl, XtraForms), and calling stored procedures for production/downtime/consumption.
A typical directory layout might look like this (one-project approach):
MyProductionApp/
├─ App.config
├─ Program.cs
├─ DbHelper.cs (SQL connection logic)
├─ Services/
│ ├─ ProductionService.cs
│ ├─ DowntimeService.cs
│ └─ ShiftService.cs
├─ Models/
│ ├─ ProductionRecord.cs
│ ├─ DowntimeRecord.cs
│ ├─ Shift.cs
│ └─ ...
├─ Forms/
│ ├─ MainForm.cs (initial login / role selection)
│ ├─ OperatorForm.cs
│ ├─ TeamLeaderForm.cs
│ └─ ManagerForm.cs
├─ Properties/
│ └─ AssemblyInfo.cs
└─ (etc.)
Below, we’ll provide condensed but representative code for these files.
Ensure you have your connectionString set in App.config
. DevExpress references usually get installed via NuGet or the DevExpress installer. In App.config
, add:
<?xml version="1.0" encoding="utf-8"?> <configuration> <connectionStrings> <add name="ProductionDb" connectionString="Data Source=YOUR_SERVER;Initial Catalog=YOUR_DB;Integrated Security=True;" providerName="System.Data.SqlClient" /> </connectionStrings> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2"/> </startup> </configuration>
using System; using System.Windows.Forms; using DevExpress.Skins; using DevExpress.UserSkins; using DevExpress.XtraEditors; namespace MyProductionApp { static class Program { [STAThread] static void Main() { // Enable DevExpress Skins BonusSkins.Register(); SkinManager.EnableFormSkins(); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); // Start with a MainForm that handles login or user role selection Application.Run(new MainForm()); } } }
A simple static class to retrieve a connection. In a real app, you might add try...catch around connections or use a factory pattern.
using System.Configuration; using System.Data; using System.Data.SqlClient; namespace MyProductionApp { public static class DbHelper { public static SqlConnection GetConnection() { string connStr = ConfigurationManager.ConnectionStrings["ProductionDb"].ConnectionString; return new SqlConnection(connStr); } public static SqlCommand CreateCommand(SqlConnection conn, string storedProcName, CommandType cmdType = CommandType.StoredProcedure) { SqlCommand cmd = new SqlCommand(storedProcName, conn); cmd.CommandType = cmdType; return cmd; } } }
You can create data model classes to store retrieved information from your DB calls.
namespace MyProductionApp.Models { public class ProductionRecord { public int ProductionRecordID { get; set; } public int MachineID { get; set; } public int ShiftID { get; set; } public int OrderID { get; set; } public int OperatorID { get; set; } public string LotNumber { get; set; } public int ProducedQuantity { get; set; } public string RecordStatus { get; set; } public System.DateTime StartTime { get; set; } public System.DateTime? EndTime { get; set; } } }
You’d have similar classes for DowntimeRecord, ConsumptionRecord, Shift, etc., containing only the fields you plan to display or manipulate in the UI.
Each service class calls stored procedures (like usp_Production_Start
, usp_Production_Stop
) and returns data or success messages.
using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using MyProductionApp.Models; namespace MyProductionApp.Services { public static class ProductionService { public static string StartProduction(int machineId, int shiftId, int orderId, int operatorId) { try { using (var conn = DbHelper.GetConnection()) { conn.Open(); using (var cmd = DbHelper.CreateCommand(conn, "usp_Production_Start")) { cmd.Parameters.AddWithValue("@MachineID", machineId); cmd.Parameters.AddWithValue("@ShiftID", shiftId); cmd.Parameters.AddWithValue("@OrderID", orderId); cmd.Parameters.AddWithValue("@OperatorID", operatorId); var result = cmd.ExecuteScalar(); // the SP might SELECT a message return result?.ToString() ?? "Production started successfully."; } } } catch (Exception ex) { // Log exception as needed return $"Error: {ex.Message}"; } } public static string StopProduction(int productionRecordId, int producedQuantity) { try { using (var conn = DbHelper.GetConnection()) { conn.Open(); using (var cmd = DbHelper.CreateCommand(conn, "usp_Production_Stop")) { cmd.Parameters.AddWithValue("@ProductionRecordID", productionRecordId); cmd.Parameters.AddWithValue("@ProducedQuantity", producedQuantity); var result = cmd.ExecuteScalar(); return result?.ToString() ?? "Production stopped successfully."; } } } catch (Exception ex) { return $"Error: {ex.Message}"; } } // Example: get a list of running production records public static List<ProductionRecord> GetRunningProductions() { var list = new List<ProductionRecord>(); try { using (var conn = DbHelper.GetConnection()) { conn.Open(); using (var cmd = DbHelper.CreateCommand(conn, "SELECT * FROM ProductionRecords WHERE RecordStatus = 'Running'", CommandType.Text)) { using (var reader = cmd.ExecuteReader()) { while (reader.Read()) { var record = new ProductionRecord { ProductionRecordID = reader.GetInt32(reader.GetOrdinal("ProductionRecordID")), MachineID = reader.GetInt32(reader.GetOrdinal("MachineID")), ShiftID = reader.GetInt32(reader.GetOrdinal("ShiftID")), OrderID = reader.GetInt32(reader.GetOrdinal("OrderID")), OperatorID = reader.GetInt32(reader.GetOrdinal("OperatorID")), LotNumber = reader.GetString(reader.GetOrdinal("LotNumber")), ProducedQuantity = reader.GetInt32(reader.GetOrdinal("ProducedQuantity")), RecordStatus = reader.GetString(reader.GetOrdinal("RecordStatus")), StartTime = reader.GetDateTime(reader.GetOrdinal("StartTime")), EndTime = reader.IsDBNull(reader.GetOrdinal("EndTime")) ? (DateTime?)null : reader.GetDateTime(reader.GetOrdinal("EndTime")) }; list.Add(record); } } } } } catch (Exception ex) { // log error } return list; } } }
using System; using System.Data; using System.Data.SqlClient; namespace MyProductionApp.Services { public static class DowntimeService { public static string StartDowntime(int machineId, int shiftId, int operatorId, string cause) { try { using (var conn = DbHelper.GetConnection()) { conn.Open(); using (var cmd = DbHelper.CreateCommand(conn, "usp_Downtime_Start")) { cmd.Parameters.AddWithValue("@MachineID", machineId); cmd.Parameters.AddWithValue("@ShiftID", shiftId); cmd.Parameters.AddWithValue("@OperatorID", operatorId); cmd.Parameters.AddWithValue("@DowntimeCause", cause); var result = cmd.ExecuteScalar(); return result?.ToString() ?? "Downtime started."; } } } catch (Exception ex) { return $"Error: {ex.Message}"; } } public static string StopDowntime(int downtimeId) { try { using (var conn = DbHelper.GetConnection()) { conn.Open(); using (var cmd = DbHelper.CreateCommand(conn, "usp_Downtime_Stop")) { cmd.Parameters.AddWithValue("@DowntimeID", downtimeId); var result = cmd.ExecuteScalar(); return result?.ToString() ?? "Downtime stopped."; } } } catch (Exception ex) { return $"Error: {ex.Message}"; } } } }
You would continue similarly with ShiftService
(for usp_Shift_Start
, usp_Shift_End
) and ConsumptionService
(for usp_Consumption_Declare
), etc.
Below, we provide four sample forms. In real projects, you’d likely have more specialized forms or might combine some features. We’ll keep them minimal to illustrate DevExpress usage.
using DevExpress.XtraEditors; using System; using System.Windows.Forms; namespace MyProductionApp { public partial class MainForm : XtraForm { public MainForm() { InitializeComponent(); } private void MainForm_Load(object sender, EventArgs e) { // Example: combo box with roles comboRole.Properties.Items.Add("Operator"); comboRole.Properties.Items.Add("TeamLeader"); comboRole.Properties.Items.Add("Manager"); } private void btnLogin_Click(object sender, EventArgs e) { string selectedRole = comboRole.SelectedItem?.ToString() ?? ""; if (string.IsNullOrWhiteSpace(selectedRole)) { XtraMessageBox.Show("Please select a role."); return; } // For brevity, skip actual user password checks switch (selectedRole) { case "Operator": new OperatorForm().Show(); break; case "TeamLeader": new TeamLeaderForm().Show(); break; case "Manager": new ManagerForm().Show(); break; default: XtraMessageBox.Show("Unknown role"); break; } } } }
Designer partial (MainForm.Designer.cs) snippet:
namespace MyProductionApp { partial class MainForm { private DevExpress.XtraEditors.SimpleButton btnLogin; private DevExpress.XtraEditors.ComboBoxEdit comboRole; private void InitializeComponent() { this.btnLogin = new DevExpress.XtraEditors.SimpleButton(); this.comboRole = new DevExpress.XtraEditors.ComboBoxEdit(); ((System.ComponentModel.ISupportInitialize)(this.comboRole.Properties)).BeginInit(); this.SuspendLayout(); // // comboRole // this.comboRole.Location = new System.Drawing.Point(30, 30); this.comboRole.Name = "comboRole"; this.comboRole.Properties.Buttons.AddRange(new DevExpress.XtraEditors.Controls.EditorButton[] { new DevExpress.XtraEditors.Controls.EditorButton( DevExpress.XtraEditors.Controls.ButtonPredefines.Combo)}); this.comboRole.Size = new System.Drawing.Size(200, 20); this.comboRole.TabIndex = 0; // // btnLogin // this.btnLogin.Location = new System.Drawing.Point(30, 70); this.btnLogin.Name = "btnLogin"; this.btnLogin.Size = new System.Drawing.Size(200, 30); this.btnLogin.TabIndex = 1; this.btnLogin.Text = "Login"; this.btnLogin.Click += new System.EventHandler(this.btnLogin_Click); // // MainForm // this.ClientSize = new System.Drawing.Size(268, 130); this.Controls.Add(this.btnLogin); this.Controls.Add(this.comboRole); this.Name = "MainForm"; this.Text = "Production App - Login"; this.Load += new System.EventHandler(this.MainForm_Load); ((System.ComponentModel.ISupportInitialize)(this.comboRole.Properties)).EndInit(); this.ResumeLayout(false); } } }
using DevExpress.XtraEditors; using DevExpress.XtraGrid; using DevExpress.XtraGrid.Views.Grid; using MyProductionApp.Services; using System; using System.Windows.Forms; namespace MyProductionApp { public partial class OperatorForm : XtraForm { public OperatorForm() { InitializeComponent(); } private void OperatorForm_Load(object sender, EventArgs e) { RefreshRunningProductions(); } private void btnStartProd_Click(object sender, EventArgs e) { // In a real system, operator would select MachineID, ShiftID, OrderID from combos int machineId = Convert.ToInt32(txtMachineID.Text); int shiftId = 1; // for demo int orderId = Convert.ToInt32(txtOrderID.Text); int operatorId = 10; // for demo string result = ProductionService.StartProduction(machineId, shiftId, orderId, operatorId); XtraMessageBox.Show(result); RefreshRunningProductions(); } private void btnStopProd_Click(object sender, EventArgs e) { // The user might select the record from the grid int productionRecordId = Convert.ToInt32(txtProductionRecID.Text); int producedQty = Convert.ToInt32(txtProducedQty.Text); string result = ProductionService.StopProduction(productionRecordId, producedQty); XtraMessageBox.Show(result); RefreshRunningProductions(); } private void btnStartDowntime_Click(object sender, EventArgs e) { int machineId = Convert.ToInt32(txtMachineID.Text); int shiftId = 1; // for demo int operatorId = 10; // for demo string cause = txtDowntimeCause.Text; string result = DowntimeService.StartDowntime(machineId, shiftId, operatorId, cause); XtraMessageBox.Show(result); } private void btnDeclareConsumption_Click(object sender, EventArgs e) { // Create a consumption service if needed // For demonstration, let's just call a hypothetical service method // e.g. ConsumptionService.DeclareConsumption(orderId, machineId, materialCode, consumedQty) XtraMessageBox.Show("Consumption declared (fake call)."); } private void RefreshRunningProductions() { gridControl1.DataSource = null; var data = ProductionService.GetRunningProductions(); gridControl1.DataSource = data; gridView1.BestFitColumns(); } } }
Designer partial (OperatorForm.Designer.cs) snippet:
namespace MyProductionApp { partial class OperatorForm { private DevExpress.XtraEditors.SimpleButton btnStartProd; private DevExpress.XtraEditors.SimpleButton btnStopProd; private DevExpress.XtraEditors.SimpleButton btnStartDowntime; private DevExpress.XtraEditors.SimpleButton btnDeclareConsumption; private DevExpress.XtraEditors.TextEdit txtMachineID; private DevExpress.XtraEditors.TextEdit txtOrderID; private DevExpress.XtraEditors.TextEdit txtProductionRecID; private DevExpress.XtraEditors.TextEdit txtProducedQty; private DevExpress.XtraEditors.TextEdit txtDowntimeCause; private DevExpress.XtraGrid.GridControl gridControl1; private GridView gridView1; private void InitializeComponent() { this.btnStartProd = new DevExpress.XtraEditors.SimpleButton(); this.btnStopProd = new DevExpress.XtraEditors.SimpleButton(); this.btnStartDowntime = new DevExpress.XtraEditors.SimpleButton(); this.btnDeclareConsumption = new DevExpress.XtraEditors.SimpleButton(); this.txtMachineID = new DevExpress.XtraEditors.TextEdit(); this.txtOrderID = new DevExpress.XtraEditors.TextEdit(); this.txtProductionRecID = new DevExpress.XtraEditors.TextEdit(); this.txtProducedQty = new DevExpress.XtraEditors.TextEdit(); this.txtDowntimeCause = new DevExpress.XtraEditors.TextEdit(); this.gridControl1 = new DevExpress.XtraGrid.GridControl(); this.gridView1 = new DevExpress.XtraGrid.Views.Grid.GridView(); ((System.ComponentModel.ISupportInitialize)(this.txtMachineID.Properties)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtOrderID.Properties)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtProductionRecID.Properties)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtProducedQty.Properties)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtDowntimeCause.Properties)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.gridControl1)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.gridView1)).BeginInit(); this.SuspendLayout(); // // txtMachineID // this.txtMachineID.Location = new System.Drawing.Point(20, 20); this.txtMachineID.Name = "txtMachineID"; this.txtMachineID.Size = new System.Drawing.Size(100, 20); this.txtMachineID.TabIndex = 0; this.txtMachineID.ToolTip = "Machine ID"; // // txtOrderID // this.txtOrderID.Location = new System.Drawing.Point(20, 50); this.txtOrderID.Name = "txtOrderID"; this.txtOrderID.Size = new System.Drawing.Size(100, 20); this.txtOrderID.TabIndex = 1; this.txtOrderID.ToolTip = "Order ID"; // // btnStartProd // this.btnStartProd.Location = new System.Drawing.Point(130, 20); this.btnStartProd.Name = "btnStartProd"; this.btnStartProd.Size = new System.Drawing.Size(100, 23); this.btnStartProd.TabIndex = 2; this.btnStartProd.Text = "Start Prod"; this.btnStartProd.Click += new System.EventHandler(this.btnStartProd_Click); // // btnStopProd // this.btnStopProd.Location = new System.Drawing.Point(130, 50); this.btnStopProd.Name = "btnStopProd"; this.btnStopProd.Size = new System.Drawing.Size(100, 23); this.btnStopProd.TabIndex = 3; this.btnStopProd.Text = "Stop Prod"; this.btnStopProd.Click += new System.EventHandler(this.btnStopProd_Click); // // txtProductionRecID // this.txtProductionRecID.Location = new System.Drawing.Point(20, 80); this.txtProductionRecID.Name = "txtProductionRecID"; this.txtProductionRecID.Size = new System.Drawing.Size(100, 20); this.txtProductionRecID.TabIndex = 4; this.txtProductionRecID.ToolTip = "Production Record ID"; // // txtProducedQty // this.txtProducedQty.Location = new System.Drawing.Point(130, 80); this.txtProducedQty.Name = "txtProducedQty"; this.txtProducedQty.Size = new System.Drawing.Size(100, 20); this.txtProducedQty.TabIndex = 5; this.txtProducedQty.ToolTip = "Produced Quantity"; // // btnStartDowntime // this.btnStartDowntime.Location = new System.Drawing.Point(20, 140); this.btnStartDowntime.Name = "btnStartDowntime"; this.btnStartDowntime.Size = new System.Drawing.Size(100, 23); this.btnStartDowntime.TabIndex = 6; this.btnStartDowntime.Text = "Start Downtime"; this.btnStartDowntime.Click += new System.EventHandler(this.btnStartDowntime_Click); // // txtDowntimeCause // this.txtDowntimeCause.Location = new System.Drawing.Point(130, 140); this.txtDowntimeCause.Name = "txtDowntimeCause"; this.txtDowntimeCause.Size = new System.Drawing.Size(100, 20); this.txtDowntimeCause.TabIndex = 7; this.txtDowntimeCause.ToolTip = "Downtime Cause"; // // btnDeclareConsumption // this.btnDeclareConsumption.Location = new System.Drawing.Point(20, 170); this.btnDeclareConsumption.Name = "btnDeclareConsumption"; this.btnDeclareConsumption.Size = new System.Drawing.Size(210, 23); this.btnDeclareConsumption.TabIndex = 8; this.btnDeclareConsumption.Text = "Declare Consumption"; this.btnDeclareConsumption.Click += new System.EventHandler(this.btnDeclareConsumption_Click); // // gridControl1 // this.gridControl1.Location = new System.Drawing.Point(250, 20); this.gridControl1.MainView = this.gridView1; this.gridControl1.Size = new System.Drawing.Size(400, 200); this.gridControl1.TabIndex = 9; this.gridControl1.ViewCollection.AddRange(new DevExpress.XtraGrid.Views.Base.BaseView[] { this.gridView1}); // // gridView1 // this.gridView1.GridControl = this.gridControl1; this.gridView1.Name = "gridView1"; // // OperatorForm // this.ClientSize = new System.Drawing.Size(670, 240); this.Controls.Add(this.gridControl1); this.Controls.Add(this.btnDeclareConsumption); this.Controls.Add(this.txtDowntimeCause); this.Controls.Add(this.btnStartDowntime); this.Controls.Add(this.txtProducedQty); this.Controls.Add(this.txtProductionRecID); this.Controls.Add(this.btnStopProd); this.Controls.Add(this.btnStartProd); this.Controls.Add(this.txtOrderID); this.Controls.Add(this.txtMachineID); this.Name = "OperatorForm"; this.Text = "Operator Interface"; this.Load += new System.EventHandler(this.OperatorForm_Load); ((System.ComponentModel.ISupportInitialize)(this.txtMachineID.Properties)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtOrderID.Properties)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtProductionRecID.Properties)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtProducedQty.Properties)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtDowntimeCause.Properties)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.gridControl1)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.gridView1)).EndInit(); this.ResumeLayout(false); } } }
using DevExpress.XtraEditors; using DevExpress.XtraGrid; using DevExpress.XtraGrid.Views.Grid; using MyProductionApp.Services; using System; using System.Windows.Forms; namespace MyProductionApp { public partial class TeamLeaderForm : XtraForm { public TeamLeaderForm() { InitializeComponent(); } private void TeamLeaderForm_Load(object sender, EventArgs e) { LoadData(); } private void btnStartShift_Click(object sender, EventArgs e) { // Example usage: ShiftService.StartShift(shiftId) // For now, assume shiftId=1 for demonstration: string result = ShiftService.StartShift(1); XtraMessageBox.Show(result); } private void btnEndShift_Click(object sender, EventArgs e) { string result = ShiftService.EndShift(1); XtraMessageBox.Show(result); } private void btnRefresh_Click(object sender, EventArgs e) { LoadData(); } private void LoadData() { // Example: we might show all production records from a certain shift var data = ShiftService.GetProductionRecordsForShift(1); gridControl1.DataSource = data; gridView1.BestFitColumns(); } } }
Designer partial (TeamLeaderForm.Designer.cs) with a RibbonControl or simple Buttons:
namespace MyProductionApp { partial class TeamLeaderForm { private DevExpress.XtraBars.Ribbon.RibbonControl ribbonControl1; private DevExpress.XtraBars.BarButtonItem btnStartShift; private DevExpress.XtraBars.BarButtonItem btnEndShift; private DevExpress.XtraBars.BarButtonItem btnRefresh; private DevExpress.XtraGrid.GridControl gridControl1; private DevExpress.XtraGrid.Views.Grid.GridView gridView1; private void InitializeComponent() { this.ribbonControl1 = new DevExpress.XtraBars.Ribbon.RibbonControl(); this.btnStartShift = new DevExpress.XtraBars.BarButtonItem(); this.btnEndShift = new DevExpress.XtraBars.BarButtonItem(); this.btnRefresh = new DevExpress.XtraBars.BarButtonItem(); this.gridControl1 = new DevExpress.XtraGrid.GridControl(); this.gridView1 = new DevExpress.XtraGrid.Views.Grid.GridView(); ((System.ComponentModel.ISupportInitialize)(this.ribbonControl1)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.gridControl1)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.gridView1)).BeginInit(); this.SuspendLayout(); // ribbonControl1 this.ribbonControl1.Items.AddRange(new DevExpress.XtraBars.BarItem[] { this.btnStartShift, this.btnEndShift, this.btnRefresh }); this.ribbonControl1.Location = new System.Drawing.Point(0, 0); this.ribbonControl1.Name = "ribbonControl1"; this.ribbonControl1.Pages.AddRange(new DevExpress.XtraBars.Ribbon.RibbonPage[] { new DevExpress.XtraBars.Ribbon.RibbonPage("Shift Management") { Groups = { new DevExpress.XtraBars.Ribbon.RibbonPageGroup("Operations") { ItemLinks = { btnStartShift, btnEndShift, btnRefresh } } } } }); this.ribbonControl1.Size = new System.Drawing.Size(800, 143); // btnStartShift this.btnStartShift.Caption = "Start Shift"; this.btnStartShift.Id = 1; this.btnStartShift.ItemClick += (s,e) => btnStartShift_Click(s,e); // btnEndShift this.btnEndShift.Caption = "End Shift"; this.btnEndShift.Id = 2; this.btnEndShift.ItemClick += (s,e) => btnEndShift_Click(s,e); // btnRefresh this.btnRefresh.Caption = "Refresh Data"; this.btnRefresh.Id = 3; this.btnRefresh.ItemClick += (s,e) => btnRefresh_Click(s,e); // gridControl1 this.gridControl1.Location = new System.Drawing.Point(0, 143); this.gridControl1.MainView = this.gridView1; this.gridControl1.Size = new System.Drawing.Size(800, 357); this.gridControl1.TabIndex = 1; this.gridControl1.ViewCollection.AddRange(new DevExpress.XtraGrid.Views.Base.BaseView[] { this.gridView1 }); // gridView1 this.gridView1.GridControl = this.gridControl1; this.gridView1.Name = "gridView1"; // TeamLeaderForm this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 16F); this.ClientSize = new System.Drawing.Size(800, 500); this.Controls.Add(this.gridControl1); this.Controls.Add(this.ribbonControl1); this.Name = "TeamLeaderForm"; this.Text = "Team Leader Dashboard"; this.Load += new System.EventHandler(this.TeamLeaderForm_Load); ((System.ComponentModel.ISupportInitialize)(this.ribbonControl1)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.gridControl1)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.gridView1)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); } } }
Note: This code references a ShiftService
with methods like StartShift
, EndShift
, and GetProductionRecordsForShift(shiftId)
—you’d implement them similarly to how we did ProductionService
.
using DevExpress.XtraEditors; using DevExpress.XtraCharts; using System; using System.Collections.Generic; using System.Windows.Forms; namespace MyProductionApp { public partial class ManagerForm : XtraForm { public ManagerForm() { InitializeComponent(); } private void ManagerForm_Load(object sender, EventArgs e) { LoadKpiData(); } private void LoadKpiData() { // Hypothetically fetch KPI data from a service // e.g. var downtimeByCause = ReportingService.GetDowntimeByCause(); // Then bind it to a DevExpress ChartControl } } }
Designer partial (ManagerForm.Designer.cs) with a ChartControl:
namespace MyProductionApp { partial class ManagerForm { private DevExpress.XtraCharts.ChartControl chartControl1; private void InitializeComponent() { this.chartControl1 = new DevExpress.XtraCharts.ChartControl(); ((System.ComponentModel.ISupportInitialize)(this.chartControl1)).BeginInit(); this.SuspendLayout(); // chartControl1 this.chartControl1.Dock = System.Windows.Forms.DockStyle.Fill; this.chartControl1.Legend.Name = "Default Legend"; this.chartControl1.Location = new System.Drawing.Point(0, 0); this.chartControl1.Name = "chartControl1"; this.chartControl1.Size = new System.Drawing.Size(800, 450); this.chartControl1.TabIndex = 0; // ManagerForm this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 16F); this.ClientSize = new System.Drawing.Size(800, 450); this.Controls.Add(this.chartControl1); this.Name = "ManagerForm"; this.Text = "Manager Dashboard"; this.Load += new System.EventHandler(this.ManagerForm_Load); ((System.ComponentModel.ISupportInitialize)(this.chartControl1)).EndInit(); this.ResumeLayout(false); } } }
Role-Based Access
Security & Error Handling
try...catch
and display meaningful error messages.Data Binding
Transactions & Concurrency
Production Scalability
DevExpress Layout
LayoutControl
for a dynamic/responsive layout.This comprehensive sample demonstrates how to:
You can expand this design by adding more forms, advanced validations, or combining these forms into a single app with role-based menus. Remember, the code snippets here are for demonstration—you’ll want to adapt them for production use, including robust security checks, error handling, and alignment with your organization’s coding standards.
Clarifying technical requirements
The task involves a comprehensive approach: SQL scripts for database schema and stored procedures, structured C# code, and clear compilation instructions.
Thought about rapid deployment elements pendant 5 secondes
Below is a comprehensive “starter kit” that includes all the critical technical elements—from SQL scripts (for database objects and stored procedures) to C# WinForms code (with DevExpress integration)—needed for rapid deployment of the production reporting application. The structure is broken down into four main parts:
You can copy-paste each section into your environment, adapt connection strings and namespaces as needed, and be up and running quickly.
The following script will create the necessary tables, foreign keys, indexes, and stored procedures. Run this in your SQL Server instance (e.g., via SQL Server Management Studio) in a new database named ProductionDB
(or any name you prefer).
------------------------------------------------------------------------------- -- 1. CREATE SCHEMA: TABLES & RELATIONSHIPS ------------------------------------------------------------------------------- -- USERS TABLE IF OBJECT_ID('[dbo].[Users]', 'U') IS NOT NULL DROP TABLE [dbo].[Users]; GO CREATE TABLE [dbo].[Users] ( UserID INT IDENTITY(1,1) PRIMARY KEY, FullName VARCHAR(100) NOT NULL, UserRole VARCHAR(50) NOT NULL, -- e.g.: Operator, Technician, TeamLeader, Manager BadgeID VARCHAR(50) NOT NULL, -- Unique employee/badge ID Active BIT NOT NULL DEFAULT (1), CreatedDate DATETIME NOT NULL DEFAULT (GETDATE()) ); GO -- MACHINES TABLE IF OBJECT_ID('[dbo].[Machines]', 'U') IS NOT NULL DROP TABLE [dbo].[Machines]; GO CREATE TABLE [dbo].[Machines] ( MachineID INT IDENTITY(1,1) PRIMARY KEY, MachineName VARCHAR(100) NOT NULL, MachineType VARCHAR(50) NOT NULL, -- e.g.: Printing, Laminating, Complexing Active BIT NOT NULL DEFAULT (1), CreatedDate DATETIME NOT NULL DEFAULT (GETDATE()) ); GO -- SHIFTS TABLE IF OBJECT_ID('[dbo].[Shifts]', 'U') IS NOT NULL DROP TABLE [dbo].[Shifts]; GO CREATE TABLE [dbo].[Shifts] ( ShiftID INT IDENTITY(1,1) PRIMARY KEY, ShiftName VARCHAR(50) NOT NULL, -- e.g.: Morning, Day, Night StartTime DATETIME NOT NULL, EndTime DATETIME NOT NULL, TeamLeaderID INT NOT NULL, -- FK to Users table Active BIT NOT NULL DEFAULT (1), CreatedDate DATETIME NOT NULL DEFAULT (GETDATE()) ); GO -- FK: Shifts -> Users ALTER TABLE [dbo].[Shifts] ADD CONSTRAINT FK_Shifts_TeamLeader FOREIGN KEY (TeamLeaderID) REFERENCES [dbo].[Users](UserID); GO -- PRODUCTION ORDERS IF OBJECT_ID('[dbo].[ProductionOrders]', 'U') IS NOT NULL DROP TABLE [dbo].[ProductionOrders]; GO CREATE TABLE [dbo].[ProductionOrders] ( OrderID INT IDENTITY(1,1) PRIMARY KEY, OrderNumber VARCHAR(50) NOT NULL, -- e.g.: MO-1001 TargetQuantity INT NOT NULL DEFAULT (0), Active BIT NOT NULL DEFAULT (1), CreatedDate DATETIME NOT NULL DEFAULT (GETDATE()) ); GO -- PRODUCTION RECORDS IF OBJECT_ID('[dbo].[ProductionRecords]', 'U') IS NOT NULL DROP TABLE [dbo].[ProductionRecords]; GO CREATE TABLE [dbo].[ProductionRecords] ( ProductionRecordID INT IDENTITY(1,1) PRIMARY KEY, MachineID INT NOT NULL, -- FK to Machines ShiftID INT NOT NULL, -- FK to Shifts OrderID INT NOT NULL, -- FK to ProductionOrders OperatorID INT NOT NULL, -- FK to Users StartTime DATETIME NOT NULL, EndTime DATETIME NULL, ProducedQuantity INT NOT NULL DEFAULT (0), LotNumber VARCHAR(50) NOT NULL, -- e.g.: auto-generated RecordStatus VARCHAR(20) NOT NULL, -- e.g.: Running, Stopped, Completed CreatedDate DATETIME NOT NULL DEFAULT (GETDATE()) ); GO -- FKs: ProductionRecords -> Machines, Shifts, Orders, Users ALTER TABLE [dbo].[ProductionRecords] ADD CONSTRAINT FK_ProdRecords_Machines FOREIGN KEY (MachineID) REFERENCES [dbo].[Machines](MachineID); ALTER TABLE [dbo].[ProductionRecords] ADD CONSTRAINT FK_ProdRecords_Shifts FOREIGN KEY (ShiftID) REFERENCES [dbo].[Shifts](ShiftID); ALTER TABLE [dbo].[ProductionRecords] ADD CONSTRAINT FK_ProdRecords_Orders FOREIGN KEY (OrderID) REFERENCES [dbo].[ProductionOrders](OrderID); ALTER TABLE [dbo].[ProductionRecords] ADD CONSTRAINT FK_ProdRecords_Users FOREIGN KEY (OperatorID) REFERENCES [dbo].[Users](UserID); GO -- DOWNTIME RECORDS IF OBJECT_ID('[dbo].[DowntimeRecords]', 'U') IS NOT NULL DROP TABLE [dbo].[DowntimeRecords]; GO CREATE TABLE [dbo].[DowntimeRecords] ( DowntimeID INT IDENTITY(1,1) PRIMARY KEY, MachineID INT NOT NULL, -- FK to Machines ShiftID INT NOT NULL, -- FK to Shifts OperatorID INT NOT NULL, -- FK to Users StartTime DATETIME NOT NULL, EndTime DATETIME NULL, DowntimeCause VARCHAR(100) NOT NULL, -- e.g.: Breakdown, Maintenance, Changeover Duration INT NULL, -- in minutes (or seconds) CreatedDate DATETIME NOT NULL DEFAULT (GETDATE()) ); GO -- FKs: DowntimeRecords -> Machines, Shifts, Users ALTER TABLE [dbo].[DowntimeRecords] ADD CONSTRAINT FK_DowntimeRecords_Machines FOREIGN KEY (MachineID) REFERENCES [dbo].[Machines](MachineID); ALTER TABLE [dbo].[DowntimeRecords] ADD CONSTRAINT FK_DowntimeRecords_Shifts FOREIGN KEY (ShiftID) REFERENCES [dbo].[Shifts](ShiftID); ALTER TABLE [dbo].[DowntimeRecords] ADD CONSTRAINT FK_DowntimeRecords_Users FOREIGN KEY (OperatorID) REFERENCES [dbo].[Users](UserID); GO -- CONSUMPTION RECORDS IF OBJECT_ID('[dbo].[ConsumptionRecords]', 'U') IS NOT NULL DROP TABLE [dbo].[ConsumptionRecords]; GO CREATE TABLE [dbo].[ConsumptionRecords] ( ConsumptionID INT IDENTITY(1,1) PRIMARY KEY, OrderID INT NOT NULL, -- FK to ProductionOrders MachineID INT NOT NULL, -- FK to Machines MaterialCode VARCHAR(50) NOT NULL, ConsumedQuantity INT NOT NULL DEFAULT (0), ConsumptionTime DATETIME NOT NULL, CreatedDate DATETIME NOT NULL DEFAULT (GETDATE()) ); GO -- FKs: ConsumptionRecords -> ProductionOrders, Machines ALTER TABLE [dbo].[ConsumptionRecords] ADD CONSTRAINT FK_ConsumptionRecords_Orders FOREIGN KEY (OrderID) REFERENCES [dbo].[ProductionOrders](OrderID); ALTER TABLE [dbo].[ConsumptionRecords] ADD CONSTRAINT FK_ConsumptionRecords_Machines FOREIGN KEY (MachineID) REFERENCES [dbo].[Machines](MachineID); GO ------------------------------------------------------------------------------- -- 2. RECOMMENDED INDEXES ------------------------------------------------------------------------------- CREATE INDEX IDX_ProductionRecords_MachineID ON [dbo].[ProductionRecords](MachineID); CREATE INDEX IDX_ProductionRecords_ShiftID ON [dbo].[ProductionRecords](ShiftID); CREATE INDEX IDX_ProductionRecords_OrderID ON [dbo].[ProductionRecords](OrderID); CREATE INDEX IDX_DowntimeRecords_MachineID ON [dbo].[DowntimeRecords](MachineID); CREATE INDEX IDX_DowntimeRecords_ShiftID ON [dbo].[DowntimeRecords](ShiftID); CREATE INDEX IDX_ConsumptionRecords_OrderID ON [dbo].[ConsumptionRecords](OrderID); GO ------------------------------------------------------------------------------- -- 3. STORED PROCEDURES ------------------------------------------------------------------------------- /*************************** * SHIFT MANAGEMENT ***************************/ IF OBJECT_ID('[dbo].[usp_Shift_Start]', 'P') IS NOT NULL DROP PROCEDURE [dbo].[usp_Shift_Start]; GO CREATE PROCEDURE [dbo].[usp_Shift_Start] ( @ShiftID INT ) AS BEGIN SET NOCOUNT ON; UPDATE [dbo].[Shifts] SET Active = 1 WHERE ShiftID = @ShiftID; SELECT 'Shift ' + CAST(@ShiftID AS VARCHAR) + ' started successfully.' AS Result; END; GO IF OBJECT_ID('[dbo].[usp_Shift_End]', 'P') IS NOT NULL DROP PROCEDURE [dbo].[usp_Shift_End]; GO CREATE PROCEDURE [dbo].[usp_Shift_End] ( @ShiftID INT ) AS BEGIN SET NOCOUNT ON; IF EXISTS( SELECT 1 FROM [dbo].[ProductionRecords] WHERE ShiftID = @ShiftID AND RecordStatus = 'Running' ) BEGIN RAISERROR('Cannot end shift: There are active production records.', 16, 1); RETURN; END UPDATE [dbo].[Shifts] SET Active = 0 WHERE ShiftID = @ShiftID; SELECT 'Shift ' + CAST(@ShiftID AS VARCHAR) + ' ended successfully.' AS Result; END; GO /*************************** * PRODUCTION MANAGEMENT ***************************/ IF OBJECT_ID('[dbo].[usp_Production_Start]', 'P') IS NOT NULL DROP PROCEDURE [dbo].[usp_Production_Start]; GO CREATE PROCEDURE [dbo].[usp_Production_Start] ( @MachineID INT, @ShiftID INT, @OrderID INT, @OperatorID INT ) AS BEGIN SET NOCOUNT ON; IF EXISTS( SELECT 1 FROM [dbo].[ProductionRecords] WHERE MachineID = @MachineID AND RecordStatus = 'Running' ) BEGIN RAISERROR('This machine is already in Running status. Stop previous production first.', 16, 1); RETURN; END DECLARE @LotNumber VARCHAR(50); SET @LotNumber = 'LOT-' + CONVERT(VARCHAR(8), GETDATE(), 112) + '-' + CAST(@MachineID AS VARCHAR) + '-' + CAST(@OrderID AS VARCHAR); INSERT INTO [dbo].[ProductionRecords] ( MachineID, ShiftID, OrderID, OperatorID, StartTime, EndTime, ProducedQuantity, LotNumber, RecordStatus ) VALUES ( @MachineID, @ShiftID, @OrderID, @OperatorID, GETDATE(), NULL, 0, @LotNumber, 'Running' ); SELECT 'Production started on Machine ' + CAST(@MachineID AS VARCHAR) + ' with LotNumber: ' + @LotNumber + '.' AS Result; END; GO IF OBJECT_ID('[dbo].[usp_Production_Stop]', 'P') IS NOT NULL DROP PROCEDURE [dbo].[usp_Production_Stop]; GO CREATE PROCEDURE [dbo].[usp_Production_Stop] ( @ProductionRecordID INT, @ProducedQuantity INT ) AS BEGIN SET NOCOUNT ON; IF NOT EXISTS( SELECT 1 FROM [dbo].[ProductionRecords] WHERE ProductionRecordID = @ProductionRecordID AND RecordStatus = 'Running' ) BEGIN RAISERROR('No active production found for the provided ID.', 16, 1); RETURN; END UPDATE [dbo].[ProductionRecords] SET EndTime = GETDATE(), ProducedQuantity = @ProducedQuantity, RecordStatus = 'Stopped' WHERE ProductionRecordID = @ProductionRecordID; SELECT 'Production record ' + CAST(@ProductionRecordID AS VARCHAR) + ' has been stopped successfully.' AS Result; END; GO /*************************** * DOWNTIME MANAGEMENT ***************************/ IF OBJECT_ID('[dbo].[usp_Downtime_Start]', 'P') IS NOT NULL DROP PROCEDURE [dbo].[usp_Downtime_Start]; GO CREATE PROCEDURE [dbo].[usp_Downtime_Start] ( @MachineID INT, @ShiftID INT, @OperatorID INT, @DowntimeCause VARCHAR(100) ) AS BEGIN SET NOCOUNT ON; INSERT INTO [dbo].[DowntimeRecords] ( MachineID, ShiftID, OperatorID, StartTime, EndTime, DowntimeCause, Duration ) VALUES ( @MachineID, @ShiftID, @OperatorID, GETDATE(), NULL, @DowntimeCause, NULL ); SELECT 'Downtime started for Machine ' + CAST(@MachineID AS VARCHAR) + ' due to ' + @DowntimeCause + '.' AS Result; END; GO IF OBJECT_ID('[dbo].[usp_Downtime_Stop]', 'P') IS NOT NULL DROP PROCEDURE [dbo].[usp_Downtime_Stop]; GO CREATE PROCEDURE [dbo].[usp_Downtime_Stop] ( @DowntimeID INT ) AS BEGIN SET NOCOUNT ON; IF NOT EXISTS( SELECT 1 FROM [dbo].[DowntimeRecords] WHERE DowntimeID = @DowntimeID AND EndTime IS NULL ) BEGIN RAISERROR('No active downtime found for the provided ID.', 16, 1); RETURN; END UPDATE [dbo].[DowntimeRecords] SET EndTime = GETDATE(), Duration = DATEDIFF(MINUTE, StartTime, GETDATE()) WHERE DowntimeID = @DowntimeID; SELECT 'Downtime record ' + CAST(@DowntimeID AS VARCHAR) + ' has been closed successfully.' AS Result; END; GO /*************************** * CONSUMPTION DECLARATION ***************************/ IF OBJECT_ID('[dbo].[usp_Consumption_Declare]', 'P') IS NOT NULL DROP PROCEDURE [dbo].[usp_Consumption_Declare]; GO CREATE PROCEDURE [dbo].[usp_Consumption_Declare] ( @OrderID INT, @MachineID INT, @MaterialCode VARCHAR(50), @ConsumedQuantity INT ) AS BEGIN SET NOCOUNT ON; INSERT INTO [dbo].[ConsumptionRecords] ( OrderID, MachineID, MaterialCode, ConsumedQuantity, ConsumptionTime ) VALUES ( @OrderID, @MachineID, @MaterialCode, @ConsumedQuantity, GETDATE() ); SELECT 'Consumption declared: ' + CAST(@ConsumedQuantity AS VARCHAR) + ' units of ' + @MaterialCode + ' for OrderID ' + CAST(@OrderID AS VARCHAR) + '.' AS Result; END; GO ------------------------------------------------------------------------------- -- END OF SCRIPT -------------------------------------------------------------------------------
NOTE: Adjust the
Data Source
,Initial Catalog
, and security in your connection string (below in the C# project) to match the server where you create these objects.
Below is a skeleton for a single-project WinForms app using .NET Framework (4.7.2 or higher) with DevExpress. You can name your project MyProductionApp
or any other name.
<?xml version="1.0" encoding="utf-8"?> <configuration> <connectionStrings> <!-- Adjust Data Source, Initial Catalog, user/password accordingly --> <add name="ProductionDb" connectionString="Data Source=YOUR_SQL_SERVER;Initial Catalog=ProductionDB;Integrated Security=True;" providerName="System.Data.SqlClient" /> </connectionStrings> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2"/> </startup> </configuration>
using System; using System.Windows.Forms; using DevExpress.Skins; using DevExpress.UserSkins; using DevExpress.XtraEditors; namespace MyProductionApp { static class Program { [STAThread] static void Main() { BonusSkins.Register(); SkinManager.EnableFormSkins(); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); // Start with a main form that handles user login or role selection Application.Run(new MainForm()); } } }
using System.Configuration; using System.Data; using System.Data.SqlClient; namespace MyProductionApp { public static class DbHelper { public static SqlConnection GetConnection() { string connStr = ConfigurationManager.ConnectionStrings["ProductionDb"].ConnectionString; return new SqlConnection(connStr); } public static SqlCommand CreateCommand(SqlConnection conn, string storedProcName, CommandType cmdType = CommandType.StoredProcedure) { var cmd = new SqlCommand(storedProcName, conn); cmd.CommandType = cmdType; return cmd; } } }
Here we provide examples of how to call the stored procedures from the C# code. You would typically create one service class per functional area (Production, Downtime, Shift, Consumption, etc.).
using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; namespace MyProductionApp.Services { public static class ProductionService { public static string StartProduction(int machineId, int shiftId, int orderId, int operatorId) { try { using (var conn = DbHelper.GetConnection()) { conn.Open(); using (var cmd = DbHelper.CreateCommand(conn, "usp_Production_Start")) { cmd.Parameters.AddWithValue("@MachineID", machineId); cmd.Parameters.AddWithValue("@ShiftID", shiftId); cmd.Parameters.AddWithValue("@OrderID", orderId); cmd.Parameters.AddWithValue("@OperatorID", operatorId); // The stored procedure returns a SELECT message var result = cmd.ExecuteScalar(); return result?.ToString() ?? "Production started successfully."; } } } catch (Exception ex) { // Log or rethrow as needed return "Error: " + ex.Message; } } public static string StopProduction(int productionRecordId, int producedQuantity) { try { using (var conn = DbHelper.GetConnection()) { conn.Open(); using (var cmd = DbHelper.CreateCommand(conn, "usp_Production_Stop")) { cmd.Parameters.AddWithValue("@ProductionRecordID", productionRecordId); cmd.Parameters.AddWithValue("@ProducedQuantity", producedQuantity); var result = cmd.ExecuteScalar(); return result?.ToString() ?? "Production stopped successfully."; } } } catch (Exception ex) { return "Error: " + ex.Message; } } // Example to get all running productions (query from ProductionRecords) public static List<dynamic> GetRunningProductions() { var list = new List<dynamic>(); try { using (var conn = DbHelper.GetConnection()) { conn.Open(); using (var cmd = DbHelper.CreateCommand(conn, "SELECT ProductionRecordID, MachineID, OrderID, OperatorID, LotNumber, ProducedQuantity, StartTime, EndTime, RecordStatus " + "FROM ProductionRecords WHERE RecordStatus = 'Running'", CommandType.Text)) { using (var reader = cmd.ExecuteReader()) { while (reader.Read()) { list.Add(new { ProductionRecordID = reader.GetInt32(0), MachineID = reader.GetInt32(1), OrderID = reader.GetInt32(2), OperatorID = reader.GetInt32(3), LotNumber = reader.GetString(4), ProducedQuantity = reader.GetInt32(5), StartTime = reader.GetDateTime(6), EndTime = reader.IsDBNull(7) ? (DateTime?)null : reader.GetDateTime(7), RecordStatus = reader.GetString(8) }); } } } } } catch (Exception ex) { // handle or log error } return list; } } }
using System; using System.Data; using System.Data.SqlClient; namespace MyProductionApp.Services { public static class DowntimeService { public static string StartDowntime(int machineId, int shiftId, int operatorId, string cause) { try { using (var conn = DbHelper.GetConnection()) { conn.Open(); using (var cmd = DbHelper.CreateCommand(conn, "usp_Downtime_Start")) { cmd.Parameters.AddWithValue("@MachineID", machineId); cmd.Parameters.AddWithValue("@ShiftID", shiftId); cmd.Parameters.AddWithValue("@OperatorID", operatorId); cmd.Parameters.AddWithValue("@DowntimeCause", cause); var result = cmd.ExecuteScalar(); return result?.ToString() ?? "Downtime started."; } } } catch (Exception ex) { return "Error: " + ex.Message; } } public static string StopDowntime(int downtimeId) { try { using (var conn = DbHelper.GetConnection()) { conn.Open(); using (var cmd = DbHelper.CreateCommand(conn, "usp_Downtime_Stop")) { cmd.Parameters.AddWithValue("@DowntimeID", downtimeId); var result = cmd.ExecuteScalar(); return result?.ToString() ?? "Downtime stopped."; } } } catch (Exception ex) { return "Error: " + ex.Message; } } } }
using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; namespace MyProductionApp.Services { public static class ShiftService { public static string StartShift(int shiftId) { try { using (var conn = DbHelper.GetConnection()) { conn.Open(); using (var cmd = DbHelper.CreateCommand(conn, "usp_Shift_Start")) { cmd.Parameters.AddWithValue("@ShiftID", shiftId); var result = cmd.ExecuteScalar(); return result?.ToString() ?? "Shift started."; } } } catch (Exception ex) { return "Error: " + ex.Message; } } public static string EndShift(int shiftId) { try { using (var conn = DbHelper.GetConnection()) { conn.Open(); using (var cmd = DbHelper.CreateCommand(conn, "usp_Shift_End")) { cmd.Parameters.AddWithValue("@ShiftID", shiftId); var result = cmd.ExecuteScalar(); return result?.ToString() ?? "Shift ended."; } } } catch (Exception ex) { return "Error: " + ex.Message; } } // Example: get production records for a shift public static List<dynamic> GetProductionRecordsForShift(int shiftId) { var list = new List<dynamic>(); try { using (var conn = DbHelper.GetConnection()) { conn.Open(); using (var cmd = DbHelper.CreateCommand(conn, "SELECT ProductionRecordID, MachineID, OrderID, ProducedQuantity, LotNumber, StartTime, EndTime, RecordStatus " + "FROM ProductionRecords WHERE ShiftID = @ShiftID", CommandType.Text)) { cmd.Parameters.AddWithValue("@ShiftID", shiftId); using (var reader = cmd.ExecuteReader()) { while (reader.Read()) { list.Add(new { ProductionRecordID = reader.GetInt32(0), MachineID = reader.GetInt32(1), OrderID = reader.GetInt32(2), ProducedQuantity = reader.GetInt32(3), LotNumber = reader.GetString(4), StartTime = reader.GetDateTime(5), EndTime = reader.IsDBNull(6) ? (DateTime?)null : reader.GetDateTime(6), RecordStatus = reader.GetString(7) }); } } } } } catch (Exception ex) { // handle or log error } return list; } } }
You can create a similar ConsumptionService that calls
usp_Consumption_Declare
.
Below are four forms:
using DevExpress.XtraEditors; using System; using System.Windows.Forms; namespace MyProductionApp { public partial class MainForm : XtraForm { public MainForm() { InitializeComponent(); } private void MainForm_Load(object sender, EventArgs e) { // For demonstration, just add roles manually comboRole.Properties.Items.Add("Operator"); comboRole.Properties.Items.Add("TeamLeader"); comboRole.Properties.Items.Add("Manager"); } private void btnLogin_Click(object sender, EventArgs e) { string selectedRole = comboRole.SelectedItem?.ToString() ?? ""; if (string.IsNullOrWhiteSpace(selectedRole)) { XtraMessageBox.Show("Please select a role."); return; } switch (selectedRole) { case "Operator": new OperatorForm().Show(); break; case "TeamLeader": new TeamLeaderForm().Show(); break; case "Manager": new ManagerForm().Show(); break; default: XtraMessageBox.Show("Unknown role."); break; } } } }
MainForm.Designer.cs (simplified):
namespace MyProductionApp { partial class MainForm { private DevExpress.XtraEditors.SimpleButton btnLogin; private DevExpress.XtraEditors.ComboBoxEdit comboRole; private void InitializeComponent() { this.btnLogin = new DevExpress.XtraEditors.SimpleButton(); this.comboRole = new DevExpress.XtraEditors.ComboBoxEdit(); ((System.ComponentModel.ISupportInitialize)(this.comboRole.Properties)).BeginInit(); this.SuspendLayout(); // // comboRole // this.comboRole.Location = new System.Drawing.Point(30, 30); this.comboRole.Name = "comboRole"; this.comboRole.Properties.Buttons.AddRange(new DevExpress.XtraEditors.Controls.EditorButton[] { new DevExpress.XtraEditors.Controls.EditorButton(DevExpress.XtraEditors.Controls.ButtonPredefines.Combo)}); this.comboRole.Size = new System.Drawing.Size(200, 20); this.comboRole.TabIndex = 0; // // btnLogin // this.btnLogin.Location = new System.Drawing.Point(30, 70); this.btnLogin.Name = "btnLogin"; this.btnLogin.Size = new System.Drawing.Size(200, 30); this.btnLogin.TabIndex = 1; this.btnLogin.Text = "Login"; this.btnLogin.Click += new System.EventHandler(this.btnLogin_Click); // // MainForm // this.ClientSize = new System.Drawing.Size(268, 130); this.Controls.Add(this.btnLogin); this.Controls.Add(this.comboRole); this.Text = "Production App - Login"; this.Load += new System.EventHandler(this.MainForm_Load); ((System.ComponentModel.ISupportInitialize)(this.comboRole.Properties)).EndInit(); this.ResumeLayout(false); } } }
using DevExpress.XtraEditors; using DevExpress.XtraGrid.Views.Grid; using MyProductionApp.Services; using System; using System.Windows.Forms; namespace MyProductionApp { public partial class OperatorForm : XtraForm { public OperatorForm() { InitializeComponent(); } private void OperatorForm_Load(object sender, EventArgs e) { RefreshRunningProductions(); } private void btnStartProd_Click(object sender, EventArgs e) { int machineId = Convert.ToInt32(txtMachineID.Text); int shiftId = 1; // Example int orderId = Convert.ToInt32(txtOrderID.Text); int operatorId = 10; // Example string result = ProductionService.StartProduction(machineId, shiftId, orderId, operatorId); XtraMessageBox.Show(result); RefreshRunningProductions(); } private void btnStopProd_Click(object sender, EventArgs e) { int productionRecordId = Convert.ToInt32(txtProductionRecID.Text); int producedQty = Convert.ToInt32(txtProducedQty.Text); string result = ProductionService.StopProduction(productionRecordId, producedQty); XtraMessageBox.Show(result); RefreshRunningProductions(); } private void btnStartDowntime_Click(object sender, EventArgs e) { int machineId = Convert.ToInt32(txtMachineID.Text); int shiftId = 1; // Example int operatorId = 10; string cause = txtDowntimeCause.Text; string result = DowntimeService.StartDowntime(machineId, shiftId, operatorId, cause); XtraMessageBox.Show(result); } private void btnDeclareConsumption_Click(object sender, EventArgs e) { // Implement similarly with a ConsumptionService XtraMessageBox.Show("Consumption declared (placeholder)."); } private void RefreshRunningProductions() { var data = ProductionService.GetRunningProductions(); gridControl1.DataSource = data; gridView1.BestFitColumns(); } } }
OperatorForm.Designer.cs (simplified):
namespace MyProductionApp { partial class OperatorForm { private DevExpress.XtraEditors.SimpleButton btnStartProd; private DevExpress.XtraEditors.SimpleButton btnStopProd; private DevExpress.XtraEditors.SimpleButton btnStartDowntime; private DevExpress.XtraEditors.SimpleButton btnDeclareConsumption; private DevExpress.XtraEditors.TextEdit txtMachineID; private DevExpress.XtraEditors.TextEdit txtOrderID; private DevExpress.XtraEditors.TextEdit txtProductionRecID; private DevExpress.XtraEditors.TextEdit txtProducedQty; private DevExpress.XtraEditors.TextEdit txtDowntimeCause; private DevExpress.XtraGrid.GridControl gridControl1; private DevExpress.XtraGrid.Views.Grid.GridView gridView1; private void InitializeComponent() { this.btnStartProd = new DevExpress.XtraEditors.SimpleButton(); this.btnStopProd = new DevExpress.XtraEditors.SimpleButton(); this.btnStartDowntime = new DevExpress.XtraEditors.SimpleButton(); this.btnDeclareConsumption = new DevExpress.XtraEditors.SimpleButton(); this.txtMachineID = new DevExpress.XtraEditors.TextEdit(); this.txtOrderID = new DevExpress.XtraEditors.TextEdit(); this.txtProductionRecID = new DevExpress.XtraEditors.TextEdit(); this.txtProducedQty = new DevExpress.XtraEditors.TextEdit(); this.txtDowntimeCause = new DevExpress.XtraEditors.TextEdit(); this.gridControl1 = new DevExpress.XtraGrid.GridControl(); this.gridView1 = new DevExpress.XtraGrid.Views.Grid.GridView(); ((System.ComponentModel.ISupportInitialize)(this.txtMachineID.Properties)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtOrderID.Properties)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtProductionRecID.Properties)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtProducedQty.Properties)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtDowntimeCause.Properties)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.gridControl1)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.gridView1)).BeginInit(); this.SuspendLayout(); // txtMachineID this.txtMachineID.Location = new System.Drawing.Point(20, 20); this.txtMachineID.Name = "txtMachineID"; this.txtMachineID.Size = new System.Drawing.Size(100, 20); this.txtMachineID.ToolTip = "Machine ID"; // txtOrderID this.txtOrderID.Location = new System.Drawing.Point(20, 50); this.txtOrderID.Name = "txtOrderID"; this.txtOrderID.Size = new System.Drawing.Size(100, 20); this.txtOrderID.ToolTip = "Order ID"; // btnStartProd this.btnStartProd.Location = new System.Drawing.Point(130, 20); this.btnStartProd.Name = "btnStartProd"; this.btnStartProd.Size = new System.Drawing.Size(100, 23); this.btnStartProd.Text = "Start Prod"; this.btnStartProd.Click += new System.EventHandler(this.btnStartProd_Click); // btnStopProd this.btnStopProd.Location = new System.Drawing.Point(130, 50); this.btnStopProd.Name = "btnStopProd"; this.btnStopProd.Size = new System.Drawing.Size(100, 23); this.btnStopProd.Text = "Stop Prod"; this.btnStopProd.Click += new System.EventHandler(this.btnStopProd_Click); // txtProductionRecID this.txtProductionRecID.Location = new System.Drawing.Point(20, 80); this.txtProductionRecID.Name = "txtProductionRecID"; this.txtProductionRecID.Size = new System.Drawing.Size(100, 20); this.txtProductionRecID.ToolTip = "Production Rec ID"; // txtProducedQty this.txtProducedQty.Location = new System.Drawing.Point(130, 80); this.txtProducedQty.Name = "txtProducedQty"; this.txtProducedQty.Size = new System.Drawing.Size(100, 20); this.txtProducedQty.ToolTip = "Produced Qty"; // btnStartDowntime this.btnStartDowntime.Location = new System.Drawing.Point(20, 140); this.btnStartDowntime.Name = "btnStartDowntime"; this.btnStartDowntime.Size = new System.Drawing.Size(100, 23); this.btnStartDowntime.Text = "Start Downtime"; this.btnStartDowntime.Click += new System.EventHandler(this.btnStartDowntime_Click); // txtDowntimeCause this.txtDowntimeCause.Location = new System.Drawing.Point(130, 140); this.txtDowntimeCause.Name = "txtDowntimeCause"; this.txtDowntimeCause.Size = new System.Drawing.Size(100, 20); this.txtDowntimeCause.ToolTip = "Downtime Cause"; // btnDeclareConsumption this.btnDeclareConsumption.Location = new System.Drawing.Point(20, 170); this.btnDeclareConsumption.Name = "btnDeclareConsumption"; this.btnDeclareConsumption.Size = new System.Drawing.Size(210, 23); this.btnDeclareConsumption.Text = "Declare Consumption"; this.btnDeclareConsumption.Click += new System.EventHandler(this.btnDeclareConsumption_Click); // gridControl1 this.gridControl1.Location = new System.Drawing.Point(250, 20); this.gridControl1.MainView = this.gridView1; this.gridControl1.Size = new System.Drawing.Size(400, 200); this.gridView1.GridControl = this.gridControl1; this.gridView1.Name = "gridView1"; // OperatorForm this.ClientSize = new System.Drawing.Size(670, 240); this.Controls.Add(this.gridControl1); this.Controls.Add(this.btnDeclareConsumption); this.Controls.Add(this.txtDowntimeCause); this.Controls.Add(this.btnStartDowntime); this.Controls.Add(this.txtProducedQty); this.Controls.Add(this.txtProductionRecID); this.Controls.Add(this.btnStopProd); this.Controls.Add(this.btnStartProd); this.Controls.Add(this.txtOrderID); this.Controls.Add(this.txtMachineID); this.Text = "Operator Interface"; this.Load += new System.EventHandler(this.OperatorForm_Load); ((System.ComponentModel.ISupportInitialize)(this.txtMachineID.Properties)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtOrderID.Properties)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtProductionRecID.Properties)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtProducedQty.Properties)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtDowntimeCause.Properties)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.gridControl1)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.gridView1)).EndInit(); this.ResumeLayout(false); } } }
using DevExpress.XtraEditors; using DevExpress.XtraBars; using DevExpress.XtraGrid.Views.Grid; using MyProductionApp.Services; using System; using System.Windows.Forms; namespace MyProductionApp { public partial class TeamLeaderForm : XtraForm { public TeamLeaderForm() { InitializeComponent(); } private void TeamLeaderForm_Load(object sender, EventArgs e) { LoadData(); } private void btnStartShift_ItemClick(object sender, ItemClickEventArgs e) { string msg = ShiftService.StartShift(1); // Example shiftId=1 XtraMessageBox.Show(msg); } private void btnEndShift_ItemClick(object sender, ItemClickEventArgs e) { string msg = ShiftService.EndShift(1); XtraMessageBox.Show(msg); } private void btnRefresh_ItemClick(object sender, ItemClickEventArgs e) { LoadData(); } private void LoadData() { var recs = ShiftService.GetProductionRecordsForShift(1); // shiftId=1 gridControl1.DataSource = recs; gridView1.BestFitColumns(); } } }
TeamLeaderForm.Designer.cs (using a RibbonControl with buttons):
namespace MyProductionApp { partial class TeamLeaderForm { private DevExpress.XtraBars.Ribbon.RibbonControl ribbonControl1; private DevExpress.XtraBars.BarButtonItem btnStartShift; private DevExpress.XtraBars.BarButtonItem btnEndShift; private DevExpress.XtraBars.BarButtonItem btnRefresh; private DevExpress.XtraGrid.GridControl gridControl1; private DevExpress.XtraGrid.Views.Grid.GridView gridView1; private void InitializeComponent() { this.ribbonControl1 = new DevExpress.XtraBars.Ribbon.RibbonControl(); this.btnStartShift = new DevExpress.XtraBars.BarButtonItem(); this.btnEndShift = new DevExpress.XtraBars.BarButtonItem(); this.btnRefresh = new DevExpress.XtraBars.BarButtonItem(); this.gridControl1 = new DevExpress.XtraGrid.GridControl(); this.gridView1 = new DevExpress.XtraGrid.Views.Grid.GridView(); ((System.ComponentModel.ISupportInitialize)(this.ribbonControl1)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.gridControl1)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.gridView1)).BeginInit(); this.SuspendLayout(); // ribbonControl1 this.ribbonControl1.Items.AddRange(new DevExpress.XtraBars.BarItem[] { this.btnStartShift, this.btnEndShift, this.btnRefresh }); this.ribbonControl1.Location = new System.Drawing.Point(0, 0); this.ribbonControl1.Name = "ribbonControl1"; // Ribbon Page var ribbonPage = new DevExpress.XtraBars.Ribbon.RibbonPage("Shift Management"); var ribbonPageGroup = new DevExpress.XtraBars.Ribbon.RibbonPageGroup("Actions"); ribbonPage.Groups.Add(ribbonPageGroup); ribbonPageGroup.ItemLinks.Add(this.btnStartShift); ribbonPageGroup.ItemLinks.Add(this.btnEndShift); ribbonPageGroup.ItemLinks.Add(this.btnRefresh); this.ribbonControl1.Pages.Add(ribbonPage); this.ribbonControl1.Size = new System.Drawing.Size(800, 143); // btnStartShift this.btnStartShift.Caption = "Start Shift"; this.btnStartShift.Id = 1; this.btnStartShift.ItemClick += new DevExpress.XtraBars.ItemClickEventHandler(this.btnStartShift_ItemClick); // btnEndShift this.btnEndShift.Caption = "End Shift"; this.btnEndShift.Id = 2; this.btnEndShift.ItemClick += new DevExpress.XtraBars.ItemClickEventHandler(this.btnEndShift_ItemClick); // btnRefresh this.btnRefresh.Caption = "Refresh"; this.btnRefresh.Id = 3; this.btnRefresh.ItemClick += new DevExpress.XtraBars.ItemClickEventHandler(this.btnRefresh_ItemClick); // gridControl1 this.gridControl1.Location = new System.Drawing.Point(0, 143); this.gridControl1.MainView = this.gridView1; this.gridControl1.Size = new System.Drawing.Size(800, 357); // gridView1 this.gridView1.GridControl = this.gridControl1; // TeamLeaderForm this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 16F); this.ClientSize = new System.Drawing.Size(800, 500); this.Controls.Add(this.gridControl1); this.Controls.Add(this.ribbonControl1); this.Name = "TeamLeaderForm"; this.Text = "Team Leader Dashboard"; this.Load += new System.EventHandler(this.TeamLeaderForm_Load); ((System.ComponentModel.ISupportInitialize)(this.ribbonControl1)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.gridControl1)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.gridView1)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); } } }
using DevExpress.XtraEditors; using DevExpress.XtraCharts; using System; using System.Windows.Forms; namespace MyProductionApp { public partial class ManagerForm : XtraForm { public ManagerForm() { InitializeComponent(); } private void ManagerForm_Load(object sender, EventArgs e) { LoadKpiData(); } private void LoadKpiData() { // e.g. populate chartControl1 with data from a "ReportingService" // chartControl1.Series[0].Points.Add(new SeriesPoint("Machine1", 10)); } } }
ManagerForm.Designer.cs (with a ChartControl):
namespace MyProductionApp { partial class ManagerForm { private DevExpress.XtraCharts.ChartControl chartControl1; private void InitializeComponent() { this.chartControl1 = new DevExpress.XtraCharts.ChartControl(); ((System.ComponentModel.ISupportInitialize)(this.chartControl1)).BeginInit(); this.SuspendLayout(); // chartControl1 this.chartControl1.Dock = System.Windows.Forms.DockStyle.Fill; this.chartControl1.Legend.Name = "Default Legend"; this.chartControl1.Location = new System.Drawing.Point(0, 0); this.chartControl1.Name = "chartControl1"; this.chartControl1.Size = new System.Drawing.Size(800, 450); this.chartControl1.TabIndex = 0; // ManagerForm this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 16F); this.ClientSize = new System.Drawing.Size(800, 450); this.Controls.Add(this.chartControl1); this.Name = "ManagerForm"; this.Text = "Manager KPI Dashboard"; this.Load += new System.EventHandler(this.ManagerForm_Load); ((System.ComponentModel.ISupportInitialize)(this.chartControl1)).EndInit(); this.ResumeLayout(false); } } }
Create the Database
ProductionDB
).Set Up the C# Project
MyProductionApp
.App.config
→ root of the project.Program.cs
→ root.DbHelper.cs
→ root.Services
folder.MainForm
, OperatorForm
, TeamLeaderForm
, ManagerForm
.App.config
to match your SQL Server instance.Build & Run
Optional Enhancements
With these SQL scripts, C# WinForms classes, and DevExpress components, you have a fully working foundation for a production reporting application. Each piece is designed for fast deployment:
Simply clone, adapt, and compile—and you’ll have a functional, customizable system ready for data capture on the shop floor and real-time visibility for team leaders and managers.