495 lines
15 KiB
Dart
495 lines
15 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
enum MasterFieldType { text, number, boolean, dropdown }
|
|
|
|
const brandTypes = ['OWN', 'OEM', 'THIRD_PARTY'];
|
|
|
|
class MasterFieldDef {
|
|
const MasterFieldDef({
|
|
required this.key,
|
|
required this.label,
|
|
this.type = MasterFieldType.text,
|
|
this.required = false,
|
|
this.showInList = false,
|
|
this.optionsMasterKey,
|
|
this.staticOptions,
|
|
this.multiline = false,
|
|
});
|
|
|
|
final String key;
|
|
final String label;
|
|
final MasterFieldType type;
|
|
final bool required;
|
|
final bool showInList;
|
|
/// Master key used to populate dropdown options (e.g. `plants` for plant_id).
|
|
final String? optionsMasterKey;
|
|
/// Fixed dropdown choices (e.g. brand type) — no API lookup.
|
|
final List<String>? staticOptions;
|
|
final bool multiline;
|
|
}
|
|
|
|
class MasterDefinition {
|
|
const MasterDefinition({
|
|
required this.id,
|
|
required this.title,
|
|
required this.subtitle,
|
|
required this.category,
|
|
required this.routeKey,
|
|
required this.apiPath,
|
|
required this.module,
|
|
required this.icon,
|
|
required this.fields,
|
|
});
|
|
|
|
final String id;
|
|
final String title;
|
|
final String subtitle;
|
|
final String category;
|
|
final String routeKey;
|
|
final String apiPath;
|
|
final String module;
|
|
final IconData icon;
|
|
final List<MasterFieldDef> fields;
|
|
|
|
List<MasterFieldDef> get listFields =>
|
|
fields.where((field) => field.showInList).toList();
|
|
|
|
List<MasterFieldDef> get formFields => fields;
|
|
|
|
String listRoute(String base) => '$base/$routeKey';
|
|
String addRoute(String base) => '$base/$routeKey/add';
|
|
String editRoute(String base, String id) => '$base/$routeKey/$id/edit';
|
|
}
|
|
|
|
const _activeField = MasterFieldDef(
|
|
key: 'is_active',
|
|
label: 'Active',
|
|
type: MasterFieldType.boolean,
|
|
);
|
|
|
|
MasterDefinition? masterDefinitionById(String id) {
|
|
for (final def in masterDefinitions) {
|
|
if (def.id == id) return def;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
MasterDefinition? masterDefinitionByRouteKey(String routeKey) {
|
|
for (final def in masterDefinitions) {
|
|
if (def.routeKey == routeKey) return def;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
const masterDefinitions = <MasterDefinition>[
|
|
MasterDefinition(
|
|
id: 'uom',
|
|
title: 'UOM',
|
|
subtitle: 'Units of measure',
|
|
category: 'Inventory & Items',
|
|
routeKey: 'uom',
|
|
apiPath: '/masters/uom',
|
|
module: 'uom',
|
|
icon: Icons.straighten_outlined,
|
|
fields: [
|
|
MasterFieldDef(key: 'code', label: 'Code', required: true, showInList: true),
|
|
MasterFieldDef(key: 'name', label: 'Name', required: true, showInList: true),
|
|
_activeField,
|
|
],
|
|
),
|
|
MasterDefinition(
|
|
id: 'item_categories',
|
|
title: 'Item Categories',
|
|
subtitle: 'Top-level item grouping',
|
|
category: 'Inventory & Items',
|
|
routeKey: 'item-categories',
|
|
apiPath: '/masters/item-categories',
|
|
module: 'item_categories',
|
|
icon: Icons.category_outlined,
|
|
fields: [
|
|
MasterFieldDef(key: 'code', label: 'Code', required: true, showInList: true),
|
|
MasterFieldDef(key: 'name', label: 'Name', required: true, showInList: true),
|
|
_activeField,
|
|
],
|
|
),
|
|
MasterDefinition(
|
|
id: 'item_subcategories',
|
|
title: 'Item Sub Categories',
|
|
subtitle: 'Sub-grouping under item categories',
|
|
category: 'Inventory & Items',
|
|
routeKey: 'item-subcategories',
|
|
apiPath: '/masters/item-subcategories',
|
|
module: 'item_subcategories',
|
|
icon: Icons.subdirectory_arrow_right,
|
|
fields: [
|
|
MasterFieldDef(
|
|
key: 'item_category_id',
|
|
label: 'Item Category',
|
|
type: MasterFieldType.dropdown,
|
|
required: true,
|
|
showInList: true,
|
|
optionsMasterKey: 'item_categories',
|
|
),
|
|
MasterFieldDef(key: 'code', label: 'Code', required: true, showInList: true),
|
|
MasterFieldDef(key: 'name', label: 'Name', required: true, showInList: true),
|
|
_activeField,
|
|
],
|
|
),
|
|
MasterDefinition(
|
|
id: 'items',
|
|
title: 'Items',
|
|
subtitle: 'Material and product master',
|
|
category: 'Inventory & Items',
|
|
routeKey: 'items',
|
|
apiPath: '/masters/items',
|
|
module: 'items',
|
|
icon: Icons.inventory_outlined,
|
|
fields: [
|
|
MasterFieldDef(key: 'item_code', label: 'Item Code', required: true, showInList: true),
|
|
MasterFieldDef(key: 'item_name', label: 'Item Name', required: true, showInList: true),
|
|
MasterFieldDef(
|
|
key: 'item_category_id',
|
|
label: 'Category',
|
|
type: MasterFieldType.dropdown,
|
|
required: true,
|
|
optionsMasterKey: 'item_categories',
|
|
),
|
|
MasterFieldDef(
|
|
key: 'item_subcategory_id',
|
|
label: 'Sub Category',
|
|
type: MasterFieldType.dropdown,
|
|
optionsMasterKey: 'item_subcategories',
|
|
),
|
|
MasterFieldDef(
|
|
key: 'uom_id',
|
|
label: 'UOM',
|
|
type: MasterFieldType.dropdown,
|
|
required: true,
|
|
optionsMasterKey: 'uom',
|
|
),
|
|
MasterFieldDef(
|
|
key: 'gst_rate_id',
|
|
label: 'GST Rate',
|
|
type: MasterFieldType.dropdown,
|
|
optionsMasterKey: 'gst_rates',
|
|
),
|
|
MasterFieldDef(
|
|
key: 'brand_id',
|
|
label: 'Brand',
|
|
type: MasterFieldType.dropdown,
|
|
optionsMasterKey: 'brands',
|
|
),
|
|
MasterFieldDef(
|
|
key: 'is_asset_item',
|
|
label: 'Asset Item',
|
|
type: MasterFieldType.boolean,
|
|
required: true,
|
|
),
|
|
MasterFieldDef(
|
|
key: 'min_order_qty',
|
|
label: 'Min Order Qty',
|
|
type: MasterFieldType.number,
|
|
required: true,
|
|
),
|
|
MasterFieldDef(
|
|
key: 'reorder_level',
|
|
label: 'Reorder Level',
|
|
type: MasterFieldType.number,
|
|
required: true,
|
|
),
|
|
MasterFieldDef(key: 'description', label: 'Description', multiline: true),
|
|
MasterFieldDef(key: 'specification', label: 'Specification', multiline: true),
|
|
_activeField,
|
|
],
|
|
),
|
|
MasterDefinition(
|
|
id: 'brands',
|
|
title: 'Brands',
|
|
subtitle: 'Own and third-party brands',
|
|
category: 'Inventory & Items',
|
|
routeKey: 'brands',
|
|
apiPath: '/masters/brands',
|
|
module: 'brands',
|
|
icon: Icons.loyalty_outlined,
|
|
fields: [
|
|
MasterFieldDef(key: 'code', label: 'Code', required: true, showInList: true),
|
|
MasterFieldDef(key: 'name', label: 'Name', required: true, showInList: true),
|
|
MasterFieldDef(
|
|
key: 'brand_type',
|
|
label: 'Brand Type',
|
|
type: MasterFieldType.dropdown,
|
|
required: true,
|
|
showInList: true,
|
|
staticOptions: brandTypes,
|
|
),
|
|
MasterFieldDef(key: 'contact_person', label: 'Contact Person'),
|
|
MasterFieldDef(key: 'phone', label: 'Phone'),
|
|
MasterFieldDef(key: 'email', label: 'Email'),
|
|
_activeField,
|
|
],
|
|
),
|
|
MasterDefinition(
|
|
id: 'document_series',
|
|
title: 'Document Series',
|
|
subtitle: 'Numbering series for documents',
|
|
category: 'Finance & Terms',
|
|
routeKey: 'document-series',
|
|
apiPath: '/masters/document-series',
|
|
module: 'document_series',
|
|
icon: Icons.numbers_outlined,
|
|
fields: [
|
|
MasterFieldDef(key: 'code', label: 'Code', required: true, showInList: true),
|
|
MasterFieldDef(key: 'prefix', label: 'Prefix', required: true, showInList: true),
|
|
MasterFieldDef(
|
|
key: 'current_number',
|
|
label: 'Current Number',
|
|
type: MasterFieldType.number,
|
|
required: true,
|
|
showInList: true,
|
|
),
|
|
MasterFieldDef(
|
|
key: 'padding',
|
|
label: 'Padding',
|
|
type: MasterFieldType.number,
|
|
required: true,
|
|
),
|
|
MasterFieldDef(key: 'description', label: 'Description', multiline: true),
|
|
_activeField,
|
|
],
|
|
),
|
|
MasterDefinition(
|
|
id: 'plants',
|
|
title: 'Plants',
|
|
subtitle: 'Manufacturing plants and units',
|
|
category: 'Organization',
|
|
routeKey: 'plants',
|
|
apiPath: '/masters/plants',
|
|
module: 'plants',
|
|
icon: Icons.factory_outlined,
|
|
fields: [
|
|
MasterFieldDef(key: 'code', label: 'Code', required: true, showInList: true),
|
|
MasterFieldDef(key: 'name', label: 'Name', required: true, showInList: true),
|
|
MasterFieldDef(key: 'gstin', label: 'GSTIN', showInList: true),
|
|
MasterFieldDef(key: 'city', label: 'City', showInList: true),
|
|
MasterFieldDef(key: 'state', label: 'State'),
|
|
MasterFieldDef(key: 'address', label: 'Address', multiline: true),
|
|
MasterFieldDef(key: 'pincode', label: 'Pincode'),
|
|
MasterFieldDef(key: 'phone', label: 'Phone'),
|
|
_activeField,
|
|
],
|
|
),
|
|
MasterDefinition(
|
|
id: 'warehouses',
|
|
title: 'Warehouse',
|
|
subtitle: 'Storage locations by plant',
|
|
category: 'Organization',
|
|
routeKey: 'warehouses',
|
|
apiPath: '/masters/warehouses',
|
|
module: 'warehouses',
|
|
icon: Icons.warehouse_outlined,
|
|
fields: [
|
|
MasterFieldDef(key: 'code', label: 'Code', required: true, showInList: true),
|
|
MasterFieldDef(key: 'name', label: 'Name', required: true, showInList: true),
|
|
MasterFieldDef(
|
|
key: 'plant_id',
|
|
label: 'Plant',
|
|
type: MasterFieldType.dropdown,
|
|
required: true,
|
|
showInList: true,
|
|
optionsMasterKey: 'plants',
|
|
),
|
|
MasterFieldDef(key: 'location', label: 'Location'),
|
|
_activeField,
|
|
],
|
|
),
|
|
MasterDefinition(
|
|
id: 'designations',
|
|
title: 'Designations',
|
|
subtitle: 'Employee designations',
|
|
category: 'Organization',
|
|
routeKey: 'designations',
|
|
apiPath: '/masters/designations',
|
|
module: 'designations',
|
|
icon: Icons.badge_outlined,
|
|
fields: [
|
|
MasterFieldDef(key: 'name', label: 'Name', required: true, showInList: true),
|
|
_activeField,
|
|
],
|
|
),
|
|
MasterDefinition(
|
|
id: 'departments',
|
|
title: 'Departments',
|
|
subtitle: 'Organisation departments',
|
|
category: 'Organization',
|
|
routeKey: 'departments',
|
|
apiPath: '/masters/departments',
|
|
module: 'departments',
|
|
icon: Icons.apartment_outlined,
|
|
fields: [
|
|
MasterFieldDef(key: 'name', label: 'Name', required: true, showInList: true),
|
|
_activeField,
|
|
],
|
|
),
|
|
MasterDefinition(
|
|
id: 'asset_categories',
|
|
title: 'Asset Categories',
|
|
subtitle: 'Fixed asset classification',
|
|
category: 'Assets',
|
|
routeKey: 'asset-categories',
|
|
apiPath: '/masters/asset-categories',
|
|
module: 'asset_categories',
|
|
icon: Icons.precision_manufacturing_outlined,
|
|
fields: [
|
|
MasterFieldDef(key: 'code', label: 'Code', required: true, showInList: true),
|
|
MasterFieldDef(key: 'name', label: 'Name', required: true, showInList: true),
|
|
MasterFieldDef(key: 'code_prefix', label: 'Code Prefix', required: true, showInList: true),
|
|
MasterFieldDef(
|
|
key: 'default_useful_life_years',
|
|
label: 'Useful Life (Years)',
|
|
type: MasterFieldType.number,
|
|
required: true,
|
|
),
|
|
MasterFieldDef(
|
|
key: 'default_depreciation_method',
|
|
label: 'Depreciation Method',
|
|
type: MasterFieldType.dropdown,
|
|
required: true,
|
|
showInList: true,
|
|
optionsMasterKey: 'asset_depreciation_methods',
|
|
),
|
|
_activeField,
|
|
],
|
|
),
|
|
MasterDefinition(
|
|
id: 'asset_subcategories',
|
|
title: 'Asset Subcategories',
|
|
subtitle: 'Sub-classification under asset categories',
|
|
category: 'Assets',
|
|
routeKey: 'asset-subcategories',
|
|
apiPath: '/masters/asset-subcategories',
|
|
module: 'asset_subcategories',
|
|
icon: Icons.category_outlined,
|
|
fields: [
|
|
MasterFieldDef(
|
|
key: 'asset_category_id',
|
|
label: 'Asset Category',
|
|
type: MasterFieldType.dropdown,
|
|
required: true,
|
|
showInList: true,
|
|
optionsMasterKey: 'asset_categories',
|
|
),
|
|
MasterFieldDef(key: 'code', label: 'Code', required: true, showInList: true),
|
|
MasterFieldDef(key: 'name', label: 'Name', required: true, showInList: true),
|
|
_activeField,
|
|
],
|
|
),
|
|
MasterDefinition(
|
|
id: 'delivery_terms',
|
|
title: 'Delivery Terms',
|
|
subtitle: 'Delivery incoterms and terms',
|
|
category: 'Finance & Terms',
|
|
routeKey: 'delivery-terms',
|
|
apiPath: '/masters/delivery-terms',
|
|
module: 'delivery_terms',
|
|
icon: Icons.local_shipping_outlined,
|
|
fields: [
|
|
MasterFieldDef(key: 'code', label: 'Code', required: true, showInList: true),
|
|
MasterFieldDef(key: 'name', label: 'Name', required: true, showInList: true),
|
|
MasterFieldDef(key: 'description', label: 'Description', multiline: true),
|
|
_activeField,
|
|
],
|
|
),
|
|
MasterDefinition(
|
|
id: 'payment_terms',
|
|
title: 'Payment Terms',
|
|
subtitle: 'Vendor payment terms',
|
|
category: 'Finance & Terms',
|
|
routeKey: 'payment-terms',
|
|
apiPath: '/masters/payment-terms',
|
|
module: 'payment_terms',
|
|
icon: Icons.payments_outlined,
|
|
fields: [
|
|
MasterFieldDef(key: 'code', label: 'Code', required: true, showInList: true),
|
|
MasterFieldDef(key: 'name', label: 'Name', required: true, showInList: true),
|
|
MasterFieldDef(
|
|
key: 'credit_days',
|
|
label: 'Credit Days',
|
|
type: MasterFieldType.number,
|
|
required: true,
|
|
showInList: true,
|
|
),
|
|
MasterFieldDef(key: 'description', label: 'Description', multiline: true),
|
|
_activeField,
|
|
],
|
|
),
|
|
MasterDefinition(
|
|
id: 'gst_rates',
|
|
title: 'GST Rates',
|
|
subtitle: 'Tax rate master',
|
|
category: 'Finance & Terms',
|
|
routeKey: 'gst-rates',
|
|
apiPath: '/masters/gst-rates',
|
|
module: 'gst_rates',
|
|
icon: Icons.percent_outlined,
|
|
fields: [
|
|
MasterFieldDef(
|
|
key: 'rate_pct',
|
|
label: 'Rate (%)',
|
|
type: MasterFieldType.number,
|
|
required: true,
|
|
showInList: true,
|
|
),
|
|
MasterFieldDef(key: 'description', label: 'Description', showInList: true),
|
|
_activeField,
|
|
],
|
|
),
|
|
];
|
|
|
|
List<String> get masterCategories =>
|
|
masterDefinitions.map((def) => def.category).toSet().toList();
|
|
|
|
String masterRecordLabel(Map<String, dynamic> row) {
|
|
final ratePct = row['rate_pct'];
|
|
if (ratePct != null) {
|
|
final rate = ratePct is num
|
|
? ratePct.toDouble()
|
|
: double.tryParse(ratePct.toString());
|
|
if (rate != null) {
|
|
return rate % 1 == 0 ? '${rate.toInt()}%' : '$rate%';
|
|
}
|
|
}
|
|
|
|
for (final key in ['name', 'item_name', 'code', 'item_code', 'description']) {
|
|
final value = row[key];
|
|
if (value != null && value.toString().trim().isNotEmpty) {
|
|
return value.toString();
|
|
}
|
|
}
|
|
return row['id']?.toString() ?? 'Record';
|
|
}
|
|
|
|
String masterCellValue(Map<String, dynamic> row, MasterFieldDef field) {
|
|
final value = row[field.key];
|
|
if (value == null || value == '') return '—';
|
|
|
|
if (field.type == MasterFieldType.boolean) {
|
|
return value == true ? 'Yes' : 'No';
|
|
}
|
|
|
|
if (field.type == MasterFieldType.dropdown) {
|
|
final label = row['${field.key}_label'];
|
|
if (label != null && label.toString().isNotEmpty) return label.toString();
|
|
if (field.staticOptions != null) {
|
|
return value.toString().replaceAll('_', ' ');
|
|
}
|
|
}
|
|
|
|
return value.toString();
|
|
}
|
|
|
|
String masterStatusValue(Map<String, dynamic> row) {
|
|
final active = row['is_active'];
|
|
if (active == false) return 'inactive';
|
|
return 'active';
|
|
}
|