feat(MMD-013): Add assets schema DDL
- 6 tables: asset_categories, assets, asset_assignments, asset_audits, asset_audit_items, asset_maintenance - Multiple enums for status tracking - RLS policies for multi-tenancy - Triggers for automatic updates - Views for common queries Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
d72145f0b7
commit
daabe5320a
497
init/15-assets-schema.sql
Normal file
497
init/15-assets-schema.sql
Normal file
@ -0,0 +1,497 @@
|
||||
-- ============================================================================
|
||||
-- ASSETS SCHEMA - MMD-013 Asset Management
|
||||
-- Mecánicas Diesel - ERP Suite
|
||||
-- ============================================================================
|
||||
-- Schema for asset/tool management with assignment tracking, audits, and maintenance
|
||||
-- ============================================================================
|
||||
|
||||
-- Create schema
|
||||
CREATE SCHEMA IF NOT EXISTS assets;
|
||||
|
||||
-- Set search path
|
||||
SET search_path TO assets, public;
|
||||
|
||||
-- ============================================================================
|
||||
-- ENUMS
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TYPE asset_status AS ENUM (
|
||||
'available',
|
||||
'assigned',
|
||||
'in_maintenance',
|
||||
'damaged',
|
||||
'retired'
|
||||
);
|
||||
|
||||
CREATE TYPE asset_location AS ENUM (
|
||||
'warehouse',
|
||||
'unit',
|
||||
'technician',
|
||||
'external'
|
||||
);
|
||||
|
||||
CREATE TYPE criticality_level AS ENUM (
|
||||
'low',
|
||||
'medium',
|
||||
'high',
|
||||
'critical'
|
||||
);
|
||||
|
||||
CREATE TYPE assignee_type AS ENUM (
|
||||
'employee',
|
||||
'unit',
|
||||
'location'
|
||||
);
|
||||
|
||||
CREATE TYPE assignment_status AS ENUM (
|
||||
'active',
|
||||
'returned',
|
||||
'overdue',
|
||||
'lost',
|
||||
'damaged'
|
||||
);
|
||||
|
||||
CREATE TYPE audit_status AS ENUM (
|
||||
'in_progress',
|
||||
'completed',
|
||||
'cancelled'
|
||||
);
|
||||
|
||||
CREATE TYPE audit_item_status AS ENUM (
|
||||
'found',
|
||||
'missing',
|
||||
'damaged',
|
||||
'extra'
|
||||
);
|
||||
|
||||
CREATE TYPE item_condition AS ENUM (
|
||||
'good',
|
||||
'fair',
|
||||
'poor'
|
||||
);
|
||||
|
||||
CREATE TYPE maintenance_type AS ENUM (
|
||||
'preventive',
|
||||
'corrective',
|
||||
'calibration'
|
||||
);
|
||||
|
||||
CREATE TYPE maintenance_status AS ENUM (
|
||||
'scheduled',
|
||||
'in_progress',
|
||||
'completed',
|
||||
'cancelled'
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- TABLE: asset_categories
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE asset_categories (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
parent_id UUID REFERENCES asset_categories(id) ON DELETE SET NULL,
|
||||
requires_service_types TEXT[], -- Service types that require this category
|
||||
sort_order INTEGER DEFAULT 0,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
|
||||
CONSTRAINT uq_asset_categories_name UNIQUE (tenant_id, name)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_asset_categories_tenant ON asset_categories(tenant_id);
|
||||
CREATE INDEX idx_asset_categories_parent ON asset_categories(parent_id);
|
||||
|
||||
-- ============================================================================
|
||||
-- TABLE: assets
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE assets (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL,
|
||||
code VARCHAR(50) NOT NULL,
|
||||
name VARCHAR(150) NOT NULL,
|
||||
description TEXT,
|
||||
category_id UUID NOT NULL REFERENCES asset_categories(id),
|
||||
serial_number VARCHAR(100),
|
||||
qr_code VARCHAR(100),
|
||||
barcode VARCHAR(100),
|
||||
manufacturer VARCHAR(100),
|
||||
model VARCHAR(100),
|
||||
purchase_date DATE,
|
||||
purchase_cost DECIMAL(12, 2),
|
||||
warranty_expiry DATE,
|
||||
status asset_status DEFAULT 'available',
|
||||
current_location asset_location DEFAULT 'warehouse',
|
||||
current_assignee_id UUID,
|
||||
current_assignee_type assignee_type,
|
||||
criticality criticality_level DEFAULT 'medium',
|
||||
requires_calibration BOOLEAN DEFAULT FALSE,
|
||||
last_calibration_date DATE,
|
||||
next_calibration_date DATE,
|
||||
photo_url TEXT,
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by UUID,
|
||||
|
||||
CONSTRAINT uq_assets_code UNIQUE (tenant_id, code),
|
||||
CONSTRAINT uq_assets_qr_code UNIQUE (tenant_id, qr_code),
|
||||
CONSTRAINT uq_assets_barcode UNIQUE (tenant_id, barcode)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_assets_tenant ON assets(tenant_id);
|
||||
CREATE INDEX idx_assets_category ON assets(category_id);
|
||||
CREATE INDEX idx_assets_status ON assets(status);
|
||||
CREATE INDEX idx_assets_location ON assets(current_location);
|
||||
CREATE INDEX idx_assets_assignee ON assets(current_assignee_id);
|
||||
CREATE INDEX idx_assets_qr ON assets(qr_code) WHERE qr_code IS NOT NULL;
|
||||
CREATE INDEX idx_assets_barcode ON assets(barcode) WHERE barcode IS NOT NULL;
|
||||
CREATE INDEX idx_assets_calibration ON assets(next_calibration_date) WHERE requires_calibration = TRUE;
|
||||
|
||||
-- ============================================================================
|
||||
-- TABLE: asset_assignments
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE asset_assignments (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL,
|
||||
asset_id UUID NOT NULL REFERENCES assets(id),
|
||||
assignee_id UUID NOT NULL,
|
||||
assignee_type assignee_type NOT NULL,
|
||||
assigned_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
assigned_by UUID NOT NULL,
|
||||
expected_return_at TIMESTAMPTZ,
|
||||
actual_return_at TIMESTAMPTZ,
|
||||
returned_to UUID,
|
||||
status assignment_status DEFAULT 'active',
|
||||
assignment_photo_url TEXT,
|
||||
return_photo_url TEXT,
|
||||
notes TEXT,
|
||||
signature_url TEXT,
|
||||
incident_id UUID, -- FK to rescue_order if applicable
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
|
||||
CONSTRAINT chk_return_dates CHECK (actual_return_at IS NULL OR actual_return_at >= assigned_at)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_asset_assignments_tenant ON asset_assignments(tenant_id);
|
||||
CREATE INDEX idx_asset_assignments_asset ON asset_assignments(asset_id);
|
||||
CREATE INDEX idx_asset_assignments_assignee ON asset_assignments(assignee_id, assignee_type);
|
||||
CREATE INDEX idx_asset_assignments_status ON asset_assignments(status);
|
||||
CREATE INDEX idx_asset_assignments_incident ON asset_assignments(incident_id) WHERE incident_id IS NOT NULL;
|
||||
CREATE INDEX idx_asset_assignments_active ON asset_assignments(asset_id) WHERE status = 'active';
|
||||
|
||||
-- ============================================================================
|
||||
-- TABLE: asset_audits
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE asset_audits (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL,
|
||||
audit_date DATE NOT NULL,
|
||||
auditor_id UUID NOT NULL,
|
||||
location_id UUID,
|
||||
unit_id UUID,
|
||||
technician_id UUID,
|
||||
status audit_status DEFAULT 'in_progress',
|
||||
total_assets INTEGER DEFAULT 0,
|
||||
found_assets INTEGER DEFAULT 0,
|
||||
missing_assets INTEGER DEFAULT 0,
|
||||
damaged_assets INTEGER DEFAULT 0,
|
||||
notes TEXT,
|
||||
started_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
completed_at TIMESTAMPTZ,
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_asset_audits_tenant ON asset_audits(tenant_id);
|
||||
CREATE INDEX idx_asset_audits_date ON asset_audits(audit_date);
|
||||
CREATE INDEX idx_asset_audits_auditor ON asset_audits(auditor_id);
|
||||
CREATE INDEX idx_asset_audits_status ON asset_audits(status);
|
||||
CREATE INDEX idx_asset_audits_unit ON asset_audits(unit_id) WHERE unit_id IS NOT NULL;
|
||||
CREATE INDEX idx_asset_audits_technician ON asset_audits(technician_id) WHERE technician_id IS NOT NULL;
|
||||
|
||||
-- ============================================================================
|
||||
-- TABLE: asset_audit_items
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE asset_audit_items (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
audit_id UUID NOT NULL REFERENCES asset_audits(id) ON DELETE CASCADE,
|
||||
asset_id UUID REFERENCES assets(id),
|
||||
status audit_item_status NOT NULL,
|
||||
condition item_condition,
|
||||
notes TEXT,
|
||||
photo_url TEXT,
|
||||
scanned_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
scanned_by UUID,
|
||||
|
||||
CONSTRAINT uq_audit_items_asset UNIQUE (audit_id, asset_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_asset_audit_items_audit ON asset_audit_items(audit_id);
|
||||
CREATE INDEX idx_asset_audit_items_asset ON asset_audit_items(asset_id);
|
||||
CREATE INDEX idx_asset_audit_items_status ON asset_audit_items(status);
|
||||
|
||||
-- ============================================================================
|
||||
-- TABLE: asset_maintenance
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE asset_maintenance (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL,
|
||||
asset_id UUID NOT NULL REFERENCES assets(id),
|
||||
maintenance_type maintenance_type NOT NULL,
|
||||
scheduled_date DATE,
|
||||
performed_date DATE,
|
||||
performed_by UUID,
|
||||
external_vendor VARCHAR(200),
|
||||
cost DECIMAL(12, 2),
|
||||
description TEXT NOT NULL,
|
||||
result TEXT,
|
||||
next_maintenance_date DATE,
|
||||
status maintenance_status DEFAULT 'scheduled',
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by UUID
|
||||
);
|
||||
|
||||
CREATE INDEX idx_asset_maintenance_tenant ON asset_maintenance(tenant_id);
|
||||
CREATE INDEX idx_asset_maintenance_asset ON asset_maintenance(asset_id);
|
||||
CREATE INDEX idx_asset_maintenance_type ON asset_maintenance(maintenance_type);
|
||||
CREATE INDEX idx_asset_maintenance_status ON asset_maintenance(status);
|
||||
CREATE INDEX idx_asset_maintenance_scheduled ON asset_maintenance(scheduled_date) WHERE status = 'scheduled';
|
||||
|
||||
-- ============================================================================
|
||||
-- FUNCTIONS
|
||||
-- ============================================================================
|
||||
|
||||
-- Function to update asset status when assigned
|
||||
CREATE OR REPLACE FUNCTION update_asset_on_assignment()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF NEW.status = 'active' THEN
|
||||
UPDATE assets
|
||||
SET
|
||||
status = 'assigned',
|
||||
current_location = CASE NEW.assignee_type
|
||||
WHEN 'employee' THEN 'technician'
|
||||
WHEN 'unit' THEN 'unit'
|
||||
ELSE 'external'
|
||||
END,
|
||||
current_assignee_id = NEW.assignee_id,
|
||||
current_assignee_type = NEW.assignee_type,
|
||||
updated_at = NOW()
|
||||
WHERE id = NEW.asset_id;
|
||||
ELSIF NEW.status IN ('returned', 'lost', 'damaged') AND OLD.status = 'active' THEN
|
||||
UPDATE assets
|
||||
SET
|
||||
status = CASE NEW.status
|
||||
WHEN 'lost' THEN 'retired'
|
||||
WHEN 'damaged' THEN 'damaged'
|
||||
ELSE 'available'
|
||||
END,
|
||||
current_location = 'warehouse',
|
||||
current_assignee_id = NULL,
|
||||
current_assignee_type = NULL,
|
||||
updated_at = NOW()
|
||||
WHERE id = NEW.asset_id;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER trg_asset_assignment_status
|
||||
AFTER INSERT OR UPDATE OF status ON asset_assignments
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_asset_on_assignment();
|
||||
|
||||
-- Function to update audit counts
|
||||
CREATE OR REPLACE FUNCTION update_audit_counts()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
UPDATE asset_audits
|
||||
SET
|
||||
total_assets = (SELECT COUNT(*) FROM asset_audit_items WHERE audit_id = COALESCE(NEW.audit_id, OLD.audit_id)),
|
||||
found_assets = (SELECT COUNT(*) FROM asset_audit_items WHERE audit_id = COALESCE(NEW.audit_id, OLD.audit_id) AND status = 'found'),
|
||||
missing_assets = (SELECT COUNT(*) FROM asset_audit_items WHERE audit_id = COALESCE(NEW.audit_id, OLD.audit_id) AND status = 'missing'),
|
||||
damaged_assets = (SELECT COUNT(*) FROM asset_audit_items WHERE audit_id = COALESCE(NEW.audit_id, OLD.audit_id) AND status = 'damaged')
|
||||
WHERE id = COALESCE(NEW.audit_id, OLD.audit_id);
|
||||
RETURN COALESCE(NEW, OLD);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER trg_audit_items_count
|
||||
AFTER INSERT OR UPDATE OR DELETE ON asset_audit_items
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_audit_counts();
|
||||
|
||||
-- Function to auto-update timestamps
|
||||
CREATE OR REPLACE FUNCTION assets_update_timestamp()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER trg_assets_updated_at
|
||||
BEFORE UPDATE ON assets
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION assets_update_timestamp();
|
||||
|
||||
CREATE TRIGGER trg_asset_maintenance_updated_at
|
||||
BEFORE UPDATE ON asset_maintenance
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION assets_update_timestamp();
|
||||
|
||||
CREATE TRIGGER trg_asset_categories_updated_at
|
||||
BEFORE UPDATE ON asset_categories
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION assets_update_timestamp();
|
||||
|
||||
-- ============================================================================
|
||||
-- VIEWS
|
||||
-- ============================================================================
|
||||
|
||||
-- View: Assets with current assignment info
|
||||
CREATE OR REPLACE VIEW vw_assets_with_assignment AS
|
||||
SELECT
|
||||
a.*,
|
||||
c.name AS category_name,
|
||||
aa.id AS active_assignment_id,
|
||||
aa.assignee_id,
|
||||
aa.assignee_type,
|
||||
aa.assigned_at,
|
||||
aa.expected_return_at,
|
||||
CASE
|
||||
WHEN aa.expected_return_at IS NOT NULL AND aa.expected_return_at < NOW()
|
||||
THEN TRUE
|
||||
ELSE FALSE
|
||||
END AS is_overdue
|
||||
FROM assets a
|
||||
LEFT JOIN asset_categories c ON a.category_id = c.id
|
||||
LEFT JOIN asset_assignments aa ON a.id = aa.asset_id AND aa.status = 'active';
|
||||
|
||||
-- View: Assets requiring calibration soon
|
||||
CREATE OR REPLACE VIEW vw_assets_calibration_due AS
|
||||
SELECT
|
||||
a.id,
|
||||
a.tenant_id,
|
||||
a.code,
|
||||
a.name,
|
||||
a.next_calibration_date,
|
||||
a.current_location,
|
||||
a.current_assignee_id,
|
||||
EXTRACT(DAY FROM a.next_calibration_date - NOW()) AS days_until_due
|
||||
FROM assets a
|
||||
WHERE a.requires_calibration = TRUE
|
||||
AND a.status != 'retired'
|
||||
AND a.next_calibration_date <= NOW() + INTERVAL '30 days'
|
||||
ORDER BY a.next_calibration_date;
|
||||
|
||||
-- View: Asset assignment history
|
||||
CREATE OR REPLACE VIEW vw_asset_assignment_history AS
|
||||
SELECT
|
||||
aa.*,
|
||||
a.code AS asset_code,
|
||||
a.name AS asset_name,
|
||||
a.category_id,
|
||||
c.name AS category_name
|
||||
FROM asset_assignments aa
|
||||
JOIN assets a ON aa.asset_id = a.id
|
||||
JOIN asset_categories c ON a.category_id = c.id
|
||||
ORDER BY aa.assigned_at DESC;
|
||||
|
||||
-- View: Audit summary
|
||||
CREATE OR REPLACE VIEW vw_audit_summary AS
|
||||
SELECT
|
||||
au.id,
|
||||
au.tenant_id,
|
||||
au.audit_date,
|
||||
au.auditor_id,
|
||||
au.status,
|
||||
au.total_assets,
|
||||
au.found_assets,
|
||||
au.missing_assets,
|
||||
au.damaged_assets,
|
||||
CASE
|
||||
WHEN au.total_assets > 0
|
||||
THEN ROUND((au.found_assets::DECIMAL / au.total_assets) * 100, 2)
|
||||
ELSE 0
|
||||
END AS accuracy_percentage,
|
||||
au.started_at,
|
||||
au.completed_at,
|
||||
EXTRACT(EPOCH FROM (COALESCE(au.completed_at, NOW()) - au.started_at)) / 60 AS duration_minutes
|
||||
FROM asset_audits au;
|
||||
|
||||
-- ============================================================================
|
||||
-- ROW LEVEL SECURITY
|
||||
-- ============================================================================
|
||||
|
||||
-- Enable RLS on all tables
|
||||
ALTER TABLE asset_categories ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE assets ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE asset_assignments ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE asset_audits ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE asset_audit_items ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE asset_maintenance ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Create policies for tenant isolation
|
||||
CREATE POLICY asset_categories_tenant_isolation ON asset_categories
|
||||
USING (tenant_id = current_setting('app.current_tenant')::UUID);
|
||||
|
||||
CREATE POLICY assets_tenant_isolation ON assets
|
||||
USING (tenant_id = current_setting('app.current_tenant')::UUID);
|
||||
|
||||
CREATE POLICY asset_assignments_tenant_isolation ON asset_assignments
|
||||
USING (tenant_id = current_setting('app.current_tenant')::UUID);
|
||||
|
||||
CREATE POLICY asset_audits_tenant_isolation ON asset_audits
|
||||
USING (tenant_id = current_setting('app.current_tenant')::UUID);
|
||||
|
||||
CREATE POLICY asset_audit_items_tenant_isolation ON asset_audit_items
|
||||
USING (audit_id IN (SELECT id FROM asset_audits WHERE tenant_id = current_setting('app.current_tenant')::UUID));
|
||||
|
||||
CREATE POLICY asset_maintenance_tenant_isolation ON asset_maintenance
|
||||
USING (tenant_id = current_setting('app.current_tenant')::UUID);
|
||||
|
||||
-- ============================================================================
|
||||
-- SEED DATA (sample categories)
|
||||
-- ============================================================================
|
||||
|
||||
-- Note: In production, seed data should be added via migration scripts
|
||||
-- These are example categories for reference:
|
||||
/*
|
||||
INSERT INTO asset_categories (tenant_id, name, description, sort_order) VALUES
|
||||
('00000000-0000-0000-0000-000000000001', 'Herramientas Manuales', 'Llaves, desarmadores, pinzas', 1),
|
||||
('00000000-0000-0000-0000-000000000001', 'Herramientas Eléctricas', 'Taladros, esmeriles, etc.', 2),
|
||||
('00000000-0000-0000-0000-000000000001', 'Equipos de Diagnóstico', 'Scanners, multímetros', 3),
|
||||
('00000000-0000-0000-0000-000000000001', 'Equipos de Rescate', 'Grúas, gatos hidráulicos', 4),
|
||||
('00000000-0000-0000-0000-000000000001', 'Equipos de Seguridad', 'EPP, señalización', 5),
|
||||
('00000000-0000-0000-0000-000000000001', 'Equipos de Medición', 'Calibradores, manómetros', 6);
|
||||
*/
|
||||
|
||||
-- ============================================================================
|
||||
-- GRANTS (adjust based on your database roles)
|
||||
-- ============================================================================
|
||||
|
||||
-- Grant usage on schema
|
||||
-- GRANT USAGE ON SCHEMA assets TO app_user;
|
||||
-- GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA assets TO app_user;
|
||||
-- GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA assets TO app_user;
|
||||
|
||||
COMMENT ON SCHEMA assets IS 'MMD-013: Asset and tool management module';
|
||||
COMMENT ON TABLE assets IS 'Master table for all trackable assets and tools';
|
||||
COMMENT ON TABLE asset_categories IS 'Hierarchical categorization of assets';
|
||||
COMMENT ON TABLE asset_assignments IS 'Track asset assignments to employees, units, or locations';
|
||||
COMMENT ON TABLE asset_audits IS 'Physical inventory audits';
|
||||
COMMENT ON TABLE asset_audit_items IS 'Individual items checked during an audit';
|
||||
COMMENT ON TABLE asset_maintenance IS 'Maintenance records including calibration';
|
||||
Loading…
Reference in New Issue
Block a user