Building an Inventory Management System in C (C Language Series #183)
Welcome to another installment in our C Language Series! In this post, we're diving into a practical application of C programming: developing a simple Inventory Management System. This project will help solidify your understanding of fundamental C concepts, including data structures, functions, and crucial file handling for data persistence.
An inventory management system is at the heart of any business that deals with products. It tracks items, quantities, and other essential details, ensuring smooth operations and informed decision-making. While modern systems are often built with databases and sophisticated user interfaces, building a basic one in C provides invaluable insight into how data is organized, processed, and stored at a lower level.
By the end of this guide, you'll have a functional command-line inventory system that can add, view, update, delete products, and save/load its data from a file.
Core Components of an Inventory System
At its most basic, an inventory system needs to manage information about individual products. Each product typically has several attributes. Let's define what kind of information we'll track for each item:
- Product ID: A unique identifier for each product.
- Product Name: A descriptive name for the item.
- Quantity: The number of units currently in stock.
- Price: The cost per unit.
Representing Product Data with C Structures
In C, the perfect way to group these related pieces of information into a single unit is using a `struct`. Let's define our `Product` structure:
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // Required for string manipulation
// Define the maximum number of products our system can handle
#define MAX_PRODUCTS 100
// Define the maximum length for product name
#define MAX_NAME_LENGTH 50
// Structure to represent a single product
typedef struct Product {
int id; // Unique identifier for the product
char name[MAX_NAME_LENGTH]; // Name of the product
int quantity; // Quantity in stock
float price; // Price per unit
} Product;
// Global array to store products and a counter for current products
Product inventory[MAX_PRODUCTS];
int currentProductCount = 0;
We've also introduced `MAX_PRODUCTS` and `MAX_NAME_LENGTH` for array sizing and string buffer management. `currentProductCount` will keep track of how many products are currently in our inventory.
Key Operations for Inventory Management
A functional inventory system requires several operations. We'll implement functions for each of these:
- Add Product: To introduce new items into the inventory.
- View Products: To display all items currently in stock.
- Update Product: To modify details of an existing product.
- Delete Product: To remove an item from the inventory.
- Search Product: To find a specific product by its ID.
- Save Inventory: To store the current state of the inventory to a file.
- Load Inventory: To retrieve previously saved inventory data from a file.
Implementing Core Functions
1. Add Product
This function will prompt the user for product details and add a new `Product` struct to our `inventory` array. We'll assign a simple auto-incrementing ID for new products.
void addProduct() {
if (currentProductCount >= MAX_PRODUCTS) {
printf("Inventory is full. Cannot add more products.\n");
return;
}
Product newProduct;
// Simple auto-incrementing ID
newProduct.id = currentProductCount == 0 ? 1 : inventory[currentProductCount - 1].id + 1;
printf("\nEnter Product Name: ");
// Use fgets for safer string input to prevent buffer overflows
// Trim newline character if present
if (fgets(newProduct.name, MAX_NAME_LENGTH, stdin) != NULL) {
newProduct.name[strcspn(newProduct.name, "\n")] = 0;
}
printf("Enter Quantity: ");
scanf("%d", &newProduct.quantity);
printf("Enter Price: ");
scanf("%f", &newProduct.price);
// Consume the leftover newline character after scanf
while (getchar() != '\n');
inventory[currentProductCount] = newProduct;
currentProductCount++;
printf("Product added successfully! ID: %d\n", newProduct.id);
}
2. View All Products
This function iterates through the `inventory` array and prints the details of each product.
void viewProducts() {
if (currentProductCount == 0) {
printf("\nInventory is empty.\n");
return;
}
printf("\n--- Current Inventory ---\n");
printf("%-5s %-30s %-10s %-10s\n", "ID", "Name", "Quantity", "Price");
printf("------------------------------------------------------------\n");
for (int i = 0; i < currentProductCount; i++) {
printf("%-5d %-30s %-10d %-10.2f\n",
inventory[i].id,
inventory[i].name,
inventory[i].quantity,
inventory[i].price);
}
printf("------------------------------------------------------------\n");
}
3. Find Product by ID (Helper Function)
Before we can update or delete, we need a way to find a product. This helper function will return the index of a product in the array given its ID.
int findProductIndex(int id) {
for (int i = 0; i < currentProductCount; i++) {
if (inventory[i].id == id) {
return i; // Product found at this index
}
}
return -1; // Product not found
}
4. Update Product
This function will ask for a product ID, find it, and then allow the user to modify its details.
void updateProduct() {
int idToUpdate;
printf("\nEnter Product ID to update: ");
scanf("%d", &idToUpdate);
while (getchar() != '\n'); // Consume newline
int index = findProductIndex(idToUpdate);
if (index != -1) {
printf("Product found (ID: %d, Name: %s). Enter new details:\n",
inventory[index].id, inventory[index].name);
printf("Enter New Product Name (current: %s): ", inventory[index].name);
if (fgets(inventory[index].name, MAX_NAME_LENGTH, stdin) != NULL) {
inventory[index].name[strcspn(inventory[index].name, "\n")] = 0;
}
printf("Enter New Quantity (current: %d): ", inventory[index].quantity);
scanf("%d", &inventory[index].quantity);
printf("Enter New Price (current: %.2f): ", inventory[index].price);
scanf("%f", &inventory[index].price);
while (getchar() != '\n'); // Consume newline
printf("Product updated successfully!\n");
} else {
printf("Product with ID %d not found.\n", idToUpdate);
}
}
5. Delete Product
To delete a product, we find its index and then shift all subsequent elements in the array one position back to fill the gap.
void deleteProduct() {
int idToDelete;
printf("\nEnter Product ID to delete: ");
scanf("%d", &idToDelete);
while (getchar() != '\n'); // Consume newline
int index = findProductIndex(idToDelete);
if (index != -1) {
// Shift elements to the left to overwrite the deleted product
for (int i = index; i < currentProductCount - 1; i++) {
inventory[i] = inventory[i + 1];
}
currentProductCount--;
printf("Product with ID %d deleted successfully!\n", idToDelete);
} else {
printf("Product with ID %d not found.\n", idToDelete);
}
}
Persistence with File Handling
Without file handling, our inventory data would disappear every time the program closes. To make our system useful, we need to save and load data from a file. We'll use a simple text file format, where each line represents a product.
File Handling Functions
1. Save Inventory to File
This function opens a file in write mode (`"w"`), iterates through the `inventory` array, and writes each product's details to the file, typically one product per line.
void saveInventory(const char* filename) {
FILE *file = fopen(filename, "w");
if (file == NULL) {
perror("Error opening file for writing");
return;
}
for (int i = 0; i < currentProductCount; i++) {
fprintf(file, "%d,%s,%d,%.2f\n",
inventory[i].id,
inventory[i].name,
inventory[i].quantity,
inventory[i].price);
}
fclose(file);
printf("Inventory saved to %s successfully.\n", filename);
}
2. Load Inventory from File
This function opens a file in read mode (`"r"`), reads product data line by line, and populates our `inventory` array.
void loadInventory(const char* filename) {
FILE *file = fopen(filename, "r");
if (file == NULL) {
perror("Error opening file for reading");
currentProductCount = 0; // Ensure inventory is empty if file doesn't exist or error
return;
}
currentProductCount = 0; // Clear existing inventory before loading
int maxId = 0; // To keep track of the highest ID loaded for future additions
while (currentProductCount < MAX_PRODUCTS &&
fscanf(file, "%d,%49[^,],%d,%f\n",
&inventory[currentProductCount].id,
inventory[currentProductCount].name,
&inventory[currentProductCount].quantity,
&inventory[currentProductCount].price) == 4) {
// Update maxId to ensure new products get unique IDs greater than existing ones
if (inventory[currentProductCount].id > maxId) {
maxId = inventory[currentProductCount].id;
}
currentProductCount++;
}
// If you wanted to continue ID generation from the highest loaded ID,
// you would need to adjust the addProduct function or store this maxId globally.
// For this example, we assume addProduct re-calculates based on currentProductCount,
// which might lead to ID collisions if loaded IDs are not sequential.
// A more robust system would manage a global 'next_available_id'.
fclose(file);
printf("Inventory loaded from %s. Total products: %d\n", filename, currentProductCount);
}
Note on IDs: In the `loadInventory` function, we track `maxId`. For a truly robust system, you'd modify `addProduct` to use a global `nextProductId` counter initialized from `maxId + 1` after loading, rather than simply `inventory[currentProductCount - 1].id + 1` or `currentProductCount + 1`, to ensure IDs remain unique even across multiple program runs. For this example, we'll keep `addProduct` simple.
Putting It All Together: The Main Program Loop
Finally, we'll create a `main` function that presents a menu to the user, allowing them to choose which operation to perform. We'll load the inventory at the start and save it before exiting.
int main() {
const char* inventoryFilename = "inventory.csv"; // Or .txt
loadInventory(inventoryFilename); // Load inventory at startup
int choice;
do {
printf("\n--- Inventory Management System ---\n");
printf("1. Add Product\n");
printf("2. View Products\n");
printf("3. Update Product\n");
printf("4. Delete Product\n");
printf("5. Save Inventory\n");
printf("6. Load Inventory\n");
printf("0. Exit\n");
printf("Enter your choice: ");
scanf("%d", &choice);
while (getchar() != '\n'); // Consume leftover newline
switch (choice) {
case 1:
addProduct();
break;
case 2:
viewProducts();
break;
case 3:
updateProduct();
break;
case 4:
deleteProduct();
break;
case 5:
saveInventory(inventoryFilename);
break;
case 6:
loadInventory(inventoryFilename);
break;
case 0:
printf("Exiting program. Saving inventory...\n");
saveInventory(inventoryFilename); // Save before exit
break;
default:
printf("Invalid choice. Please try again.\n");
}
} while (choice != 0);
return 0;
}
Limitations and Potential Enhancements
This basic system provides a solid foundation, but it has several limitations and areas for improvement:
- Fixed Array Size: The `MAX_PRODUCTS` limit restricts scalability. Dynamic memory allocation (e.g., using linked lists or reallocating arrays) would allow for an unlimited number of products.
- Error Handling: Input validation (e.g., ensuring quantity is positive, ID is an integer) and more robust file error handling could be added.
- Unique ID Generation: The current ID generation (`currentProductCount == 0 ? 1 : inventory[currentProductCount - 1].id + 1;`) is simplistic and can lead to issues if products are deleted or loaded from a file out of order. A dedicated ID counter, perhaps stored in the file or managed carefully, would be better.
- Search Functionality: While `findProductIndex` exists, a dedicated user-facing search function based on name or ID would be useful.
- User Interface: This is a command-line interface. A graphical user interface (GUI) would require external libraries (like GTK or SDL, though less common for pure data entry) or a different programming paradigm.
- Reporting: Features like showing low-stock items, total inventory value, etc., would add significant value.
- Sorting: Displaying products sorted by ID, name, or quantity could improve usability.
Conclusion
Congratulations! You've successfully built a foundational Inventory Management System using C. This project demonstrates how to effectively use structures to define complex data types, write functions to perform specific operations, and implement file I/O for data persistence.
This exercise reinforces critical programming skills that are transferable to many other C applications. Feel free to experiment with the code, add more features, and explore ways to make it more robust and user-friendly. Happy coding!