Upload markdown files and organize them in a tree structure
⚠️ Warning: This action cannot be undone!
controllers/unitController.php (312 lines)
unit
public/authentication.php throughout all operations
$_SESSION['erp_lang']
YoutubeLink model
conditions field (0=active, 1=deleted)
unit
CREATE TABLE unit (
unitId INT PRIMARY KEY AUTO_INCREMENT,
unitName VARCHAR(256) NOT NULL,
unitSymbol VARCHAR(50),
unitDescription TEXT,
unitDate DATE NOT NULL,
conditions TINYINT NOT NULL DEFAULT 0, -- 0=active, 1=deleted
userId INT NOT NULL
);
productunit - Product-Unit Relationships
CREATE TABLE productunit (
productunitid INT PRIMARY KEY AUTO_INCREMENT,
productid INT NOT NULL,
unitid INT NOT NULL,
unitsellprice DECIMAL(10,2) NOT NULL,
unitbuyprice DECIMAL(10,2) NOT NULL,
unitquantity DECIMAL(10,2) NOT NULL, -- conversion factor
conditions INT NOT NULL DEFAULT 0,
isMainUnit TINYINT NOT NULL DEFAULT 0
);
add() function)
-- Insert new unit (via UnitMySqlDAO.insert)
INSERT INTO unit (
unitName, unitSymbol, unitDescription, unitDate, conditions, userId
) VALUES (?, ?, ?, ?, 0, ?)
show() function)
-- Get all units (active and deleted)
SELECT * FROM unit
-- Get units by status
SELECT * FROM unit WHERE conditions = ?
-- Get paginated units
SELECT * FROM unit WHERE conditions = ? LIMIT ?, ?
update() function)
-- Update existing unit
UPDATE unit SET
unitName = ?, unitSymbol = ?, unitDescription = ?,
unitDate = ?, conditions = ?, userId = ?
WHERE unitId = ?
-- Soft delete (tempdelete function)
UPDATE unit SET conditions = 1 WHERE unitId = ?
-- Restore deleted unit (returndelete function)
UPDATE unit SET conditions = 0 WHERE unitId = ?
-- Check product dependencies before deletion
SELECT * FROM productunit WHERE unitid = ?
-- Permanent delete (not implemented for safety)
-- DELETE FROM unit WHERE unitId = ?
-- Load single unit
SELECT * FROM unit WHERE unitId = ?
-- Search by name
SELECT * FROM unit WHERE unitName = ?
-- Search by symbol
SELECT * FROM unit WHERE unitSymbol = ?
-- Get units by user
SELECT * FROM unit WHERE userId = ?
-- Get units by date
SELECT * FROM unit WHERE unitDate = ?
unitController.php
Method: GET
Purpose: Display add unit form
Template: unitview/add.html
SQL: None (form display only)
unitController.php?do=add
Method: POST
Purpose: Create new unit
SQL Operations:
INSERT INTO unit (unitName, unitSymbol, unitDescription, unitDate, conditions, userId)
VALUES (?, ?, ?, ?, 0, ?)
Business Logic: Creates new unit with user tracking and current date
unitController.php?do=show
Method: GET
Purpose: List all units with management interface
Template: unitview/show.html
SQL Operations:
SELECT * FROM unit
Business Logic: Shows all units with bulk operation controls
unitController.php?do=executeOperation
Method: POST
Purpose: Bulk operations on selected units
SQL Operations:
-- For bulk soft delete (operation=1):
UPDATE unit SET conditions = 1 WHERE unitId = ?
-- For bulk restore (operation=2):
UPDATE unit SET conditions = 0 WHERE unitId = ?
-- Dependency check for each unit:
SELECT * FROM productunit WHERE unitid = ?
Business Logic:
unitController.php?do=returndelete&id={unitId}
Method: GET
Purpose: Restore soft-deleted unit
SQL Operations:
UPDATE unit SET conditions = 0 WHERE unitId = ?
Business Logic: Restores deleted unit to active status
unitController.php?do=tempdelete&id={unitId}
Method: GET
Purpose: Soft delete unit with dependency checking
SQL Operations:
-- Check dependencies first:
SELECT * FROM productunit WHERE unitid = ?
-- If no dependencies, soft delete:
UPDATE unit SET conditions = 1 WHERE unitId = ?
Business Logic:
unitController.php?do=editprint&id={unitId}
Method: GET
Purpose: Print-friendly edit view
Template: unitview/editprint.html
SQL Operations:
SELECT * FROM unit WHERE unitId = ?
Business Logic: Same as edit but with print styling
unitController.php?do=edit&id={unitId}
Method: GET
Purpose: Display edit form for unit
Template: unitview/edit.html
SQL Operations:
SELECT * FROM unit WHERE unitId = ?
Business Logic: Loads unit data for editing
unitController.php?do=update
Method: POST
Purpose: Update existing unit
SQL Operations:
UPDATE unit SET
unitName = ?, unitSymbol = ?, unitDescription = ?,
unitDate = ?, conditions = ?, userId = ?
WHERE unitId = ?
Business Logic: Updates unit with new data and current user
unitController.php?do=sucess
Method: GET
Purpose: Display success confirmation page
Template: succes.html
SQL: None (status page only)
unitController.php?do=error
Method: GET
Purpose: Display error notification page
Template: error.html
SQL: None (status page only)
/api/v1/units
GET /api/v1/units
Query Parameters:
page: Page number (default: 1)
limit: Items per page (default: 20, max: 100)
search: Search in unit name/symbol
conditions: Filter by status (0=active, 1=deleted)
user_id: Filter by creator
date_from: Filter by creation date (YYYY-MM-DD)
date_to: Filter by creation date (YYYY-MM-DD)
{
"success": true,
"data": [
{
"unitId": 1,
"unitName": "kilogram",
"unitSymbol": "kg",
"unitDescription": "Weight measurement unit",
"unitDate": "2024-01-15",
"conditions": 0,
"userId": 1,
"created_by": "Admin User",
"is_assigned_to_products": true,
"product_count": 25
}
],
"pagination": {
"current_page": 1,
"total_pages": 5,
"total_items": 87,
"per_page": 20
}
}
GET /api/v1/units/{id}
Response:
{
"success": true,
"data": {
"unitId": 1,
"unitName": "kilogram",
"unitSymbol": "kg",
"unitDescription": "Weight measurement unit for products",
"unitDate": "2024-01-15",
"conditions": 0,
"userId": 1,
"created_by": "Admin User",
"assigned_products": [
{
"productid": 5,
"productName": "Rice Bag",
"isMainUnit": 1
},
{
"productid": 12,
"productName": "Flour",
"isMainUnit": 0
}
],
"usage_statistics": {
"total_products": 25,
"main_unit_products": 15,
"conversion_products": 10
}
}
}
POST /api/v1/units
Request Body:
{
"unitName": "meter",
"unitSymbol": "m",
"unitDescription": "Length measurement unit"
}
Response:
{
"success": true,
"message": "Unit created successfully",
"data": {
"unitId": 15,
"unitName": "meter",
"unitSymbol": "m",
"unitDate": "2024-01-20",
"conditions": 0,
"userId": 2
}
}
PUT /api/v1/units/{id}
Request Body:
{
"unitName": "meter",
"unitSymbol": "m",
"unitDescription": "Updated length measurement unit",
"conditions": 0
}
Response:
{
"success": true,
"message": "Unit updated successfully",
"data": {
"unitId": 15,
"unitName": "meter",
"updated_at": "2024-01-20T11:30:00Z",
"updated_by": 2
}
}
DELETE /api/v1/units/{id}
Response Success:
{
"success": true,
"message": "Unit deleted successfully"
}
Response Error (Has Dependencies):
{
"success": false,
"error": {
"code": "UNIT_HAS_DEPENDENCIES",
"message": "Cannot delete unit as it is assigned to products",
"details": {
"assigned_products": 25,
"example_products": ["Rice Bag", "Flour", "Sugar"]
}
}
}
POST /api/v1/units/{id}/restore
Response:
{
"success": true,
"message": "Unit restored successfully"
}
POST /api/v1/units/bulk
Request Body:
{
"operation": "delete", // or "restore"
"unit_ids": [1, 3, 5, 7]
}
Response:
{
"success": true,
"message": "Bulk operation completed",
"results": [
{
"unitId": 1,
"unitName": "kilogram",
"status": "success",
"message": "Unit deleted successfully"
},
{
"unitId": 3,
"unitName": "liter",
"status": "error",
"message": "Cannot delete unit - assigned to 15 products"
}
],
"summary": {
"total": 4,
"successful": 3,
"failed": 1
}
}
GET /api/v1/units/search
Query Parameters:
q: Search query
fields: Comma-separated fields to search (name,symbol,description)
limit: Maximum results (default: 50)
{
"success": true,
"data": [
{
"unitId": 1,
"unitName": "kilogram",
"unitSymbol": "kg",
"conditions": 0,
"product_count": 25
}
]
}
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "The given data was invalid",
"details": {
"unitName": ["Unit name is required"],
"unitSymbol": ["Symbol must be unique"]
}
}
}
// Unit creation validation
$rules = [
'unitName039; => 039;required|string|max:256039;,
'unitSymbol039; => 039;nullable|string|max:50|unique:unit,unitSymbol039;,
'unitDescription039; => 039;nullable|string|max:1000039;
];
// Business rule validations
">if ($unitExists = Unit::where('unitName039;, $unitName)->exists()) {
throw new ValidationException('Unit name already exists039;);
}
// Dependency check before deletion
$productCount = ProductUnit::where('unitid039;, $unitId)->count();
">if ($productCount > 0) {
throw ">new BusinessException('Cannot delete unit - assigned to 039; . $productCount . 039; products039;);
}
-- Base query (from UnitMySqlDAO.queryAll)
SELECT * FROM unit ORDER BY unitId ASC
LIMIT ? OFFSET ?
-- With filters:
-- Status filter: WHERE conditions = ?
-- Search filter: WHERE (unitName LIKE "%?%" OR unitSymbol LIKE "%?%")
-- User filter: WHERE userId = ?
-- Date range: WHERE unitDate BETWEEN ? AND ?
BEGIN TRANSACTION;
-- Insert new unit
INSERT INTO unit (unitName, unitSymbol, unitDescription, unitDate, conditions, userId)
VALUES (?, ?, ?, ?, 0, ?);
SET @unit_id = LAST_INSERT_ID();
COMMIT;
-- Update unit data
UPDATE unit SET
unitName = ?, unitSymbol = ?, unitDescription = ?,
unitDate = ?, conditions = ?, userId = ?
WHERE unitId = ?;
-- Check dependencies first
SELECT COUNT(*) FROM productunit WHERE unitid = ?;
-- If no dependencies, soft delete
UPDATE unit SET conditions = 1 WHERE unitId = ?;
-- Main unit data
SELECT * FROM unit WHERE unitId = ?;
-- Get assigned products
SELECT pu.*, p.productName
FROM productunit pu
JOIN product p ON pu.productid = p.productId
WHERE pu.unitid = ? AND pu.conditions = 0;
-- Usage statistics
SELECT
COUNT(*) as total_products,
SUM(CASE WHEN isMainUnit = 1 THEN 1 ELSE 0 END) as main_unit_count
FROM productunit
WHERE unitid = ? AND conditions = 0;
-- Essential indexes for units
CREATE INDEX idx_unit_name ON unit(unitName);
CREATE INDEX idx_unit_symbol ON unit(unitSymbol);
CREATE INDEX idx_unit_conditions ON unit(conditions);
CREATE INDEX idx_unit_userid ON unit(userId);
CREATE INDEX idx_unit_date ON unit(unitDate);
-- Product-unit relationship indexes
CREATE INDEX idx_productunit_unitid ON productunit(unitid);
CREATE INDEX idx_productunit_productid ON productunit(productid);
CREATE INDEX idx_productunit_conditions ON productunit(conditions);
curl -X POST http://localhost/api/v1/units \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-jwt-token" \
-d '{
"unitName": "gram",
"unitSymbol": "g",
"unitDescription": "Small weight measurement unit"
}'
curl -X GET "http://localhost/api/v1/units/search?q=weight&limit=10" \
-H "Authorization: Bearer your-jwt-token"
curl -X POST http://localhost/api/v1/units/bulk \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-jwt-token" \
-d '{
"operation": "delete",
"unit_ids": [5, 7, 9]
}'
class UnitAPI {
constructor(baseURL, token) {
this.baseURL = baseURL;
this.token = token;
}
async getUnits(filters = {}) {
const params = new URLSearchParams(filters);
const response = ">await fetch(${this.baseURL}/api/v1/units?${params}, {
headers: { 'Authorization039;: Bearer ${this.token} }
});
return await response.json();
}
async createUnit(unitData) {
const response = ">await fetch(${this.baseURL}/api/v1/units, {
method: 'POST039;,
headers: {
'Content-Type039;: 039;application/json039;,
'Authorization039;: Bearer ${this.token}
},
body: JSON.stringify(unitData)
});
return await response.json();
}
async updateUnit(unitId, updateData) {
const response = ">await fetch(${this.baseURL}/api/v1/units/${unitId}, {
method: 'PUT039;,
headers: {
'Content-Type039;: 039;application/json039;,
'Authorization039;: Bearer ${this.token}
},
body: JSON.stringify(updateData)
});
return await response.json();
}
async deleteUnit(unitId) {
const response = ">await fetch(${this.baseURL}/api/v1/units/${unitId}, {
method: 'DELETE039;,
headers: { 'Authorization039;: Bearer ${this.token} }
});
return await response.json();
}
async bulkOperation(operation, unitIds) {
const response = ">await fetch(${this.baseURL}/api/v1/units/bulk, {
method: 'POST039;,
headers: {
'Content-Type039;: 039;application/json039;,
'Authorization039;: Bearer ${this.token}
},
body: JSON.stringify({ operation, unit_ids: unitIds })
});
return await response.json();
}
}
// Usage
">const unitAPI = new UnitAPI('http://localhost039;, 039;your-token039;);
">const units = await unitAPI.getUnits({ conditions: 0, limit: 50 });
/api/v1/, /api/v2/
// Unit caching strategy
Cache::remember("unit_{">$unitId}", 7200, ">function() ">use ($unitId) {
return Unit::with(['assignedProducts039;])->find($unitId);
});
// Optimized unit listing
$units = Unit::select('unitId039;, 039;unitName039;, 039;unitSymbol039;, 039;conditions039;)
->with(['assignedProducts039; => ">function($query) {
$query->select('unitid039;, 039;productid039;);
}])
->where('conditions039;, 0)
->orderBy('unitName039;)
->paginate(20);
">public function test_can_create_unit_with_valid_data()
{
$unitData = [
'unitName039; => 039;Test Unit039;,
'unitSymbol039; => 039;TU039;,
'unitDescription039; => 039;A test measurement unit039;
];
$response = ">$this->postJson('/api/v1/units039;, $unitData);
$response->assertStatus(201)
->assertJson([
'success039; => true,
'data039; => [
'unitName039; => 039;Test Unit039;
]
]);
$this->assertDatabaseHas('unit039;, [039;unitName039; => 039;Test Unit039;]);
}
">public function test_cannot_delete_unit_with_product_dependencies()
{
$unit = Unit::factory()->create();
ProductUnit::factory()->create(['unitid039; => $unit->unitId]);
$response = ">$this->deleteJson("/api/v1/units/{$unit->unitId}");
$response->assertStatus(422)
->assertJson([
'success039; => false,
'error039; => [
'code039; => 039;UNIT_HAS_DEPENDENCIES039;
]
]);
}
">public function test_bulk_delete_with_mixed_results()
{
$unit1 = Unit::factory()->create();
$unit2 = Unit::factory()->create();
ProductUnit::factory()->create(['unitid039; => $unit2->unitId]);
$response = $this->postJson('/api/v1/units/bulk039;, [
'operation039; => 039;delete039;,
'unit_ids039; => [">$unit1->unitId, $unit2->unitId]
]);
$response->assertStatus(200)
->assertJsonPath('results.0.status039;, 039;success039;)
->assertJsonPath('results.1.status039;, 039;error039;)
->assertJsonPath('summary.successful039;, 1)
->assertJsonPath('summary.failed039;, 1);
}
---