AppML Mini Project — Building a Complete Inventory Management System
In this tutorial, you will learn about AppML Mini Project. We cover key concepts, practical examples, and best practices to help you master this topic.
This project guides you through building a complete inventory management system using AppML. You will create models, views, forms, events, filters, and custom components, applying everything you have learned.
What You'll Learn
You will build a production-ready inventory management application with product management, stock tracking, supplier management, order processing, and reporting dashboards.
Why It Matters
Building a complete application ties together all the AppML concepts you have learned. You will see how models, views, events, and filters work together in a real-world scenario that mirrors actual business requirements.
Real-World Use
DodaZIP's internal inventory system uses the same architecture you will build here. The system tracks hardware inventory for the Durga Antivirus Pro development team, managing hundreds of assets across multiple locations.
flowchart LR
A[Inventory System] --> B[Product Management]
A --> C[Stock Tracking]
A --> D[Supplier Management]
A --> E[Order Processing]
A --> F[Dashboard]
B --> G[CRUD Views]
C --> H[Movement Logs]
D --> I[Supplier Records]
E --> J[Purchase Orders]
F --> K[Reports & Charts]
style A fill:#1e293b,color:#fff
Step 1: Database Setup
Create a SQLite database for the inventory system.
CREATE TABLE categories (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
description TEXT
);
CREATE TABLE suppliers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
contact_person TEXT,
email TEXT,
phone TEXT,
address TEXT
);
CREATE TABLE products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sku TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
description TEXT,
category_id INTEGER REFERENCES categories(id),
supplier_id INTEGER REFERENCES suppliers(id),
unit_price DECIMAL(10,2) NOT NULL,
quantity INTEGER DEFAULT 0,
reorder_point INTEGER DEFAULT 10,
reorder_quantity INTEGER DEFAULT 50,
location TEXT,
active BOOLEAN DEFAULT 1
);
CREATE TABLE stock_movements (
id INTEGER PRIMARY KEY AUTOINCREMENT,
product_id INTEGER REFERENCES products(id),
type TEXT CHECK(type IN ('in', 'out')),
quantity INTEGER NOT NULL,
reference TEXT,
notes TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
Expected output: A database file with four tables for the inventory system.
Step 2: AppML Model
Create the main AppML model file.
<!-- inventory.xml -->
<appml>
<datasource type="sqlite">
<connection file="./data/inventory.db"/>
<table name="categories">
<field name="id" type="integer" key="true"/>
<field name="name" type="string" required="true"/>
<field name="description" type="text"/>
</table>
<table name="suppliers">
<field name="id" type="integer" key="true"/>
<field name="name" type="string" required="true"/>
<field name="contact_person" type="string"/>
<field name="email" type="string"/>
<field name="phone" type="string"/>
<field name="address" type="text"/>
</table>
<table name="products">
<field name="id" type="integer" key="true"/>
<field name="sku" type="string" required="true" unique="true"/>
<field name="name" type="string" required="true"/>
<field name="description" type="text"/>
<field name="category_id" type="integer">
<relationship table="categories" field="id"/>
</field>
<field name="supplier_id" type="integer">
<relationship table="suppliers" field="id"/>
</field>
<field name="unit_price" type="decimal" required="true"/>
<field name="quantity" type="integer"/>
<field name="reorder_point" type="integer"/>
<field name="reorder_quantity" type="integer"/>
<field name="location" type="string"/>
<field name="active" type="boolean"/>
</table>
<table name="stock_movements">
<field name="id" type="integer" key="true"/>
<field name="product_id" type="integer">
<relationship table="products" field="id"/>
</field>
<field name="type" type="string" required="true"/>
<field name="quantity" type="integer" required="true"/>
<field name="reference" type="string"/>
<field name="notes" type="text"/>
<field name="created_at" type="datetime"/>
</table>
</datasource>
</appml>
Expected output: AppML generates CRUD interfaces for all four tables with proper relationships and field types.
Step 3: Product List View with Filters
Configure a comprehensive product list view.
<view type="list" table="products" page_size="25">
<filter field="active" type="boolean" label="Active Products"/>
<filter field="category_id" type="dropdown" label="Category"
options="categories" value_field="id" display_field="name"/>
<filter field="supplier_id" type="dropdown" label="Supplier"
options="suppliers" value_field="id" display_field="name"/>
<filter type="range" field="quantity" label="Stock Level"
min="0" max="10000"/>
<search fields="sku,name,location" placeholder="Search products..."/>
<column field="sku" header="SKU" sortable="true"/>
<column field="name" header="Product Name" sortable="true"/>
<column field="category_id" header="Category" display="categories.name"/>
<column field="unit_price" header="Price" format="currency" sortable="true"/>
<column field="quantity" header="In Stock" sortable="true"/>
<column field="location" header="Location"/>
<column field="active" header="Active" format="boolean"/>
</view>
Expected output: A product listing with category and supplier dropdown filters, stock level range, and search. Columns are sortable with formatted data.
Step 4: Stock Movement Event Handler
Create an event handler that updates product quantity when a stock movement is recorded.
// events/stock-movements.js
module.exports = {
afterSave: function(data, context) {
const db = context.database;
const sign = data.type === 'in' ? 1 : -1;
db.query(
'UPDATE products SET quantity = quantity + (? * ?) WHERE id = ?',
[sign, data.quantity, data.product_id]
);
const product = db.query(
'SELECT name, quantity, reorder_point FROM products WHERE id = ?',
[data.product_id]
)[0];
if (product && product.quantity <= product.reorder_point) {
context.notification.send({
to: 'inventory@example.com',
subject: 'Low stock alert',
body: `${product.name} (ID: ${data.product_id}) has ${product.quantity} units remaining. Reorder point is ${product.reorder_point}.`
});
}
return true;
}
};
Expected output: Recording a stock movement automatically updates the product quantity. If stock drops below the reorder point, an email alert is sent.
Register the handler in the model:
<table name="stock_movements">
<event type="after-save" handler="events/stock-movements.js"/>
</table>
Step 5: Dashboard with Charts
Create a dashboard view with summary statistics and charts.
<view type="dashboard" table="products">
<widget type="stat" label="Total Products" query="SELECT COUNT(*) FROM products"/>
<widget type="stat" label="Low Stock Items"
query="SELECT COUNT(*) FROM products WHERE quantity <= reorder_point"/>
<widget type="stat" label="Total Value"
query="SELECT SUM(unit_price * quantity) FROM products" format="currency"/>
<widget type="chart" label="Stock by Category" chart_type="bar"
query="SELECT c.name, SUM(p.quantity) as total FROM products p JOIN categories c ON p.category_id = c.id GROUP BY c.name"/>
<widget type="table" label="Recent Movements" table="stock_movements" limit="10">
<column field="product_id" header="Product" display="products.name"/>
<column field="type" header="Type"/>
<column field="quantity" header="Qty"/>
<column field="created_at" header="Date" format="datetime"/>
</widget>
</view>
Expected output: A dashboard showing total products, low stock count, total inventory value, a bar chart of stock by category, and a table of recent movements.
Step 6: Deployment
Deploy the inventory management system to a production server.
# Copy files to server
scp -r appml/* user@server:/var/www/inventory/
# Configure the web server (Nginx example)
server {
listen 80;
server_name inventory.example.com;
root /var/www/inventory/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
Expected output: The inventory system is live at inventory.example.com with all features working.
Common Mistakes
Skipping the database schema design phase: A poorly designed schema creates problems throughout the application. Spend time normalizing your tables before writing the model.
Not testing the event handler with edge cases: Test stock movements with zero quantity, negative quantities (returns), and concurrent updates.
Forgetting to set up the notification system: The low-stock alert requires a configured email server. Test email delivery separately.
Deploying without security configuration: Set up authentication, HTTPS, and database backup before going live with real data.
Not adding indexes on frequently queried columns: The products table will be queried by SKU, category, and supplier. Add database indexes for performance.
Practice Questions
- What does the stock movement event handler do?
It updates the product quantity based on the movement type and sends a low-stock alert if the quantity falls below the reorder point.
- How does the stock value stat widget calculate its value?
It sums the product of unit_price and quantity for all products.
- What is the purpose of the reorder_point field?
It defines the minimum stock level that triggers a low-stock notification.
- How do you add a new product category?
Through the generated CRUD interface for the categories table, or by inserting directly into the database.
- What security measures should you take before deploying?
Enable authentication, configure HTTPS, set up database backups, and restrict access to the admin interface.
Challenge
Extend the inventory system with a purchase order module. Create a purchase_orders table that generates purchase orders when stock falls below the reorder point. Add a form that lets managers approve purchase orders and automatically create inbound stock movements when approved.
Frequently Asked Questions
Mini Project
You just built it. The inventory management system is your mini project. Add one additional feature of your choice: a reporting module with PDF export, a supplier portal for self-service ordering, or a mobile-responsive interface for warehouse staff.
What's Next
Congratulations on completing the AppML tutorial series. Review the topics you covered by returning to the AppML overview page, or explore other framework topics like React, Vue.js, or Angular.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro