Inventory Management Smart Contract

What it does:
Tracks inventory levels, manages stock in/out transactions, and provides real-time, auditable records of goods across warehouses or stores.

Why it matters:
Reduces human error, prevents stock discrepancies, increases supply chain efficiency, and enables transparent auditing for stakeholders.

How it works:

  • Warehouses, suppliers, and stores are registered on-chain

  • Each product or SKU is recorded with quantity, location, and metadata

  • Stock additions, removals, or transfers are logged immutably

  • Alerts can be triggered for low stock or overstock situations

  • Smart contracts maintain historical records for auditing and reporting

  • Integrates with Supply Chain Traceability, logistics, and automated reordering systems

  • Public dashboards allow visibility into inventory levels and movement

      // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/access/Ownable.sol";

/**
 * @title InventoryManagement
 * @author Nam
 * @notice Manages inventory on-chain with stock tracking and auditing
 */
contract InventoryManagement is Ownable {

    struct Product {
        string name;
        string metadataURI;
        uint256 totalQuantity;
        bool exists;
    }

    struct StockEvent {
        uint256 timestamp;
        address operator;
        int256 quantityChange;
        string note;
    }

    mapping(uint256 => Product) public products; // productId => Product
    mapping(uint256 => StockEvent[]) public productEvents; // productId => events
    mapping(address => bool) public operators;

    uint256 public productCount;

    // -------------------- EVENTS --------------------

    event ProductRegistered(uint256 indexed productId, string name, uint256 quantity);
    event StockUpdated(uint256 indexed productId, int256 quantityChange, string note, address operator);
    event OperatorApproved(address operator);

    // -------------------- OPERATOR MANAGEMENT --------------------

    function approveOperator(address _operator) external onlyOwner {
        operators[_operator] = true;
        emit OperatorApproved(_operator);
    }

    function revokeOperator(address _operator) external onlyOwner {
        operators[_operator] = false;
    }

    modifier onlyOperator() {
        require(operators[msg.sender], "Not an authorized operator");
        _;
    }

    // -------------------- PRODUCT MANAGEMENT --------------------

    function registerProduct(string calldata _name, string calldata _metadataURI, uint256 _initialQuantity) external onlyOperator {
        productCount += 1;
        products[productCount] = Product({
            name: _name,
            metadataURI: _metadataURI,
            totalQuantity: _initialQuantity,
            exists: true
        });

        productEvents[productCount].push(StockEvent({
            timestamp: block.timestamp,
            operator: msg.sender,
            quantityChange: int256(_initialQuantity),
            note: "Initial stock"
        }));

        emit ProductRegistered(productCount, _name, _initialQuantity);
    }

    // -------------------- STOCK MANAGEMENT --------------------

    function updateStock(uint256 _productId, int256 _quantityChange, string calldata _note) external onlyOperator {
        require(products[_productId].exists, "Product does not exist");

        int256 newQuantity = int256(products[_productId].totalQuantity) + _quantityChange;
        require(newQuantity >= 0, "Stock cannot be negative");

        products[_productId].totalQuantity = uint256(newQuantity);

        productEvents[_productId].push(StockEvent({
            timestamp: block.timestamp,
            operator: msg.sender,
            quantityChange: _quantityChange,
            note: _note
        }));

        emit StockUpdated(_productId, _quantityChange, _note, msg.sender);
    }

    // -------------------- VIEW FUNCTIONS --------------------

    function getProductEvents(uint256 _productId) external view returns (StockEvent[] memory) {
        return productEvents[_productId];
    }

    function getProductQuantity(uint256 _productId) external view returns (uint256) {
        require(products[_productId].exists, "Product does not exist");
        return products[_productId].totalQuantity;
    }
}