asset
This commit is contained in:
parent
36847aaf87
commit
eb94390961
@ -47,6 +47,9 @@ class ApiEndpoints {
|
||||
static String designationById(String id) => '/masters/designations/$id';
|
||||
static const String plants = '/masters/plants';
|
||||
static String plantById(String id) => '/masters/plants/$id';
|
||||
static const String locations = '/masters/locations';
|
||||
static String locationById(String id) => '/masters/locations/$id';
|
||||
static const String locationStates = '/masters/locations/states';
|
||||
static const String uom = '/masters/uom';
|
||||
static String uomById(String id) => '/masters/uom/$id';
|
||||
static const String itemCategories = '/masters/item-categories';
|
||||
@ -148,6 +151,11 @@ class ApiEndpoints {
|
||||
static const String assetDepreciationCalculate = '/assets/depreciation/calculate';
|
||||
static const String assetAlertsExpiry = '/assets/alerts/expiry';
|
||||
static const String assetAlertsService = '/assets/alerts/service';
|
||||
static const String assetMaintenanceMy = '/assets/maintenance/my';
|
||||
static String assetMaintenanceLogs(String id) =>
|
||||
'/assets/$id/maintenance-logs';
|
||||
static String assetMaintenanceLogById(String id, String logId) =>
|
||||
'/assets/$id/maintenance-logs/$logId';
|
||||
static String assetAmc(String assetId) => '/assets/$assetId/amc';
|
||||
static String assetAmcById(String assetId, String contractId) =>
|
||||
'/assets/$assetId/amc/$contractId';
|
||||
|
||||
@ -63,6 +63,7 @@ class RouteConstants {
|
||||
static const String assetEdit = '/assets/:id/edit';
|
||||
static const String assetDetail = '/assets/:id';
|
||||
static const String assetAlerts = '/assets/alerts';
|
||||
static const String assetMaintenance = '/assets/maintenance';
|
||||
|
||||
// Master Data
|
||||
static const String masterData = '/master-data';
|
||||
|
||||
@ -357,6 +357,55 @@ class AssetRemoteDataSource {
|
||||
);
|
||||
}
|
||||
|
||||
Future<PaginatedResponse<AssetModel>> getMyMaintenanceAssets({
|
||||
bool dueOnly = false,
|
||||
int page = 1,
|
||||
int limit = 20,
|
||||
}) async {
|
||||
final response = await dio.get(
|
||||
ApiEndpoints.assetMaintenanceMy,
|
||||
queryParameters: {
|
||||
'page': page,
|
||||
'limit': limit,
|
||||
if (dueOnly) 'due_only': true,
|
||||
},
|
||||
);
|
||||
return _parsePaginated(response.data, AssetModel.fromJson);
|
||||
}
|
||||
|
||||
Future<List<AssetMaintenanceLogModel>> getMaintenanceLogs(String assetId) async {
|
||||
final response = await dio.get(ApiEndpoints.assetMaintenanceLogs(assetId));
|
||||
return _parseList(response.data, AssetMaintenanceLogModel.fromJson);
|
||||
}
|
||||
|
||||
Future<AssetMaintenanceLogModel> createMaintenanceLog(
|
||||
String assetId,
|
||||
Map<String, dynamic> data,
|
||||
) async {
|
||||
final response = await dio.post(
|
||||
ApiEndpoints.assetMaintenanceLogs(assetId),
|
||||
data: data,
|
||||
);
|
||||
return AssetMaintenanceLogModel.fromJson(
|
||||
response.data['data'] as Map<String, dynamic>,
|
||||
);
|
||||
}
|
||||
|
||||
Future<AssetMaintenanceLogModel> getMaintenanceLogById(
|
||||
String assetId,
|
||||
String logId,
|
||||
) async {
|
||||
final response =
|
||||
await dio.get(ApiEndpoints.assetMaintenanceLogById(assetId, logId));
|
||||
return AssetMaintenanceLogModel.fromJson(
|
||||
response.data['data'] as Map<String, dynamic>,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> deleteMaintenanceLog(String assetId, String logId) async {
|
||||
await dio.delete(ApiEndpoints.assetMaintenanceLogById(assetId, logId));
|
||||
}
|
||||
|
||||
Map<String, dynamic> _queryToMap(AssetListQuery query) {
|
||||
return {
|
||||
'page': query.page,
|
||||
@ -374,8 +423,11 @@ class AssetRemoteDataSource {
|
||||
if (query.itemCategoryId != null) 'item_category_id': query.itemCategoryId,
|
||||
if (query.itemSubcategoryId != null)
|
||||
'item_subcategory_id': query.itemSubcategoryId,
|
||||
if (query.plantId != null) 'plant_id': query.plantId,
|
||||
if (query.locationId != null) 'location_id': query.locationId,
|
||||
if (query.departmentId != null) 'department_id': query.departmentId,
|
||||
if (query.maintenanceInchargeUserId != null)
|
||||
'maintenance_incharge_user_id': query.maintenanceInchargeUserId,
|
||||
if (query.dueOnly == true) 'due_only': true,
|
||||
if (query.isActive != null) 'is_active': query.isActive,
|
||||
};
|
||||
}
|
||||
|
||||
@ -262,6 +262,51 @@ class AssetRepositoryImpl implements AssetRepository {
|
||||
return safeApiCall(() => dataSource.renewInsurancePolicy(assetId, policyId, data));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<PaginatedResponse<AssetModel>>> getMyMaintenanceAssets({
|
||||
bool dueOnly = false,
|
||||
int page = 1,
|
||||
int limit = 20,
|
||||
}) {
|
||||
return safeApiCall(
|
||||
() => dataSource.getMyMaintenanceAssets(
|
||||
dueOnly: dueOnly,
|
||||
page: page,
|
||||
limit: limit,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<List<AssetMaintenanceLogModel>>> getMaintenanceLogs(
|
||||
String assetId,
|
||||
) {
|
||||
return safeApiCall(() => dataSource.getMaintenanceLogs(assetId));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<AssetMaintenanceLogModel>> createMaintenanceLog(
|
||||
String assetId,
|
||||
Map<String, dynamic> data,
|
||||
) {
|
||||
return safeApiCall(() => dataSource.createMaintenanceLog(assetId, data));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<AssetMaintenanceLogModel>> getMaintenanceLogById(
|
||||
String assetId,
|
||||
String logId,
|
||||
) {
|
||||
return safeApiCall(
|
||||
() => dataSource.getMaintenanceLogById(assetId, logId),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<void>> deleteMaintenanceLog(String assetId, String logId) {
|
||||
return safeApiCall(() => dataSource.deleteMaintenanceLog(assetId, logId));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<List<EntityAttachmentModel>>> listAttachments(String assetId) {
|
||||
return safeApiCall(() => dataSource.listAttachments(assetId));
|
||||
|
||||
@ -89,6 +89,23 @@ abstract class AssetRepository {
|
||||
String policyId,
|
||||
Map<String, dynamic> data,
|
||||
);
|
||||
Future<Result<PaginatedResponse<AssetModel>>> getMyMaintenanceAssets({
|
||||
bool dueOnly = false,
|
||||
int page = 1,
|
||||
int limit = 20,
|
||||
});
|
||||
Future<Result<List<AssetMaintenanceLogModel>>> getMaintenanceLogs(
|
||||
String assetId,
|
||||
);
|
||||
Future<Result<AssetMaintenanceLogModel>> createMaintenanceLog(
|
||||
String assetId,
|
||||
Map<String, dynamic> data,
|
||||
);
|
||||
Future<Result<AssetMaintenanceLogModel>> getMaintenanceLogById(
|
||||
String assetId,
|
||||
String logId,
|
||||
);
|
||||
Future<Result<void>> deleteMaintenanceLog(String assetId, String logId);
|
||||
Future<Result<List<EntityAttachmentModel>>> listAttachments(String assetId);
|
||||
Future<Result<EntityAttachmentModel>> uploadAttachment(
|
||||
String assetId, {
|
||||
|
||||
@ -14,9 +14,8 @@ import '../../../vendors/data/repositories/vendor_repository_impl.dart';
|
||||
|
||||
class AssetFormLookups {
|
||||
const AssetFormLookups({
|
||||
this.plants = const [],
|
||||
this.locations = const [],
|
||||
this.departments = const [],
|
||||
this.warehouses = const [],
|
||||
this.vendors = const [],
|
||||
this.users = const [],
|
||||
this.purchaseOrders = const [],
|
||||
@ -24,9 +23,8 @@ class AssetFormLookups {
|
||||
this.options = const AssetDropdownOptionsModel(),
|
||||
});
|
||||
|
||||
final List<FilterOptionModel> plants;
|
||||
final List<FilterOptionModel> locations;
|
||||
final List<FilterOptionModel> departments;
|
||||
final List<FilterOptionModel> warehouses;
|
||||
final List<FilterOptionModel> vendors;
|
||||
final List<FilterOptionModel> users;
|
||||
final List<FilterOptionModel> purchaseOrders;
|
||||
@ -50,9 +48,8 @@ final assetFormLookupsProvider =
|
||||
FutureProvider.autoDispose<AssetFormLookups>((ref) async {
|
||||
final master = ref.watch(masterRemoteDataSourceProvider);
|
||||
|
||||
final plants = await _safeOptions(master.listPlants);
|
||||
final locations = await _safeOptions(master.listLocations);
|
||||
final departments = await _safeOptions(master.listDepartments);
|
||||
final warehouses = await _safeOptions(master.listWarehouses);
|
||||
final vendors = await _safeVendorOptions(ref);
|
||||
final users = await _safeUserOptions(ref);
|
||||
final purchaseOrders = await _safePurchaseOrderOptions(ref);
|
||||
@ -60,9 +57,8 @@ final assetFormLookupsProvider =
|
||||
final options = await _safeAssetOptions(ref);
|
||||
|
||||
return AssetFormLookups(
|
||||
plants: plants,
|
||||
locations: locations,
|
||||
departments: departments,
|
||||
warehouses: warehouses,
|
||||
vendors: vendors,
|
||||
users: users,
|
||||
purchaseOrders: purchaseOrders,
|
||||
|
||||
@ -118,10 +118,10 @@ class AssetsListNotifier extends AutoDisposeAsyncNotifier<AssetsListState> {
|
||||
applyQuery(current.query.copyWith(itemCategoryId: itemCategoryId, page: 1));
|
||||
}
|
||||
|
||||
void setPlantFilter(int? plantId) {
|
||||
void setLocationFilter(int? locationId) {
|
||||
final current = state.valueOrNull;
|
||||
if (current == null) return;
|
||||
applyQuery(current.query.copyWith(plantId: plantId, page: 1));
|
||||
applyQuery(current.query.copyWith(locationId: locationId, page: 1));
|
||||
}
|
||||
|
||||
void setPage(int page) {
|
||||
@ -594,3 +594,106 @@ class AssetAlertsNotifier extends AsyncNotifier<AssetAlertsState> {
|
||||
state = AsyncData(await _load(current.copyWith(expiryPage: page)));
|
||||
}
|
||||
}
|
||||
|
||||
class MyMaintenanceState {
|
||||
const MyMaintenanceState({
|
||||
this.assets = const [],
|
||||
this.dueOnly = true,
|
||||
this.page = 1,
|
||||
this.limit = 20,
|
||||
this.total = 0,
|
||||
this.totalPages = 1,
|
||||
this.isRefreshing = false,
|
||||
});
|
||||
|
||||
final List<AssetModel> assets;
|
||||
final bool dueOnly;
|
||||
final int page;
|
||||
final int limit;
|
||||
final int total;
|
||||
final int totalPages;
|
||||
final bool isRefreshing;
|
||||
|
||||
MyMaintenanceState copyWith({
|
||||
List<AssetModel>? assets,
|
||||
bool? dueOnly,
|
||||
int? page,
|
||||
int? limit,
|
||||
int? total,
|
||||
int? totalPages,
|
||||
bool? isRefreshing,
|
||||
}) {
|
||||
return MyMaintenanceState(
|
||||
assets: assets ?? this.assets,
|
||||
dueOnly: dueOnly ?? this.dueOnly,
|
||||
page: page ?? this.page,
|
||||
limit: limit ?? this.limit,
|
||||
total: total ?? this.total,
|
||||
totalPages: totalPages ?? this.totalPages,
|
||||
isRefreshing: isRefreshing ?? this.isRefreshing,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final myMaintenanceProvider =
|
||||
AsyncNotifierProvider<MyMaintenanceNotifier, MyMaintenanceState>(
|
||||
MyMaintenanceNotifier.new,
|
||||
);
|
||||
|
||||
class MyMaintenanceNotifier extends AsyncNotifier<MyMaintenanceState> {
|
||||
@override
|
||||
Future<MyMaintenanceState> build() async {
|
||||
return _load(const MyMaintenanceState());
|
||||
}
|
||||
|
||||
Future<MyMaintenanceState> _load(MyMaintenanceState filters) async {
|
||||
final repository = ref.read(assetRepositoryProvider);
|
||||
final result = await repository.getMyMaintenanceAssets(
|
||||
dueOnly: filters.dueOnly,
|
||||
page: filters.page,
|
||||
limit: filters.limit,
|
||||
);
|
||||
if (result.failure != null) throw result.failure!;
|
||||
final page = result.data!;
|
||||
return MyMaintenanceState(
|
||||
assets: page.items,
|
||||
dueOnly: filters.dueOnly,
|
||||
page: page.page,
|
||||
limit: filters.limit,
|
||||
total: page.total,
|
||||
totalPages: page.totalPages,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
final current = state.valueOrNull ?? const MyMaintenanceState();
|
||||
state = AsyncData(current.copyWith(isRefreshing: true));
|
||||
try {
|
||||
state = AsyncData(await _load(current));
|
||||
} catch (e, st) {
|
||||
state = AsyncError(e, st);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setDueOnly(bool dueOnly) async {
|
||||
final current = state.valueOrNull ?? const MyMaintenanceState();
|
||||
state = const AsyncLoading();
|
||||
state = AsyncData(
|
||||
await _load(current.copyWith(dueOnly: dueOnly, page: 1)),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> setPage(int page) async {
|
||||
final current = state.valueOrNull ?? const MyMaintenanceState();
|
||||
state = const AsyncLoading();
|
||||
state = AsyncData(await _load(current.copyWith(page: page)));
|
||||
}
|
||||
|
||||
Future<void> setPageSize(int limit) async {
|
||||
final current = state.valueOrNull ?? const MyMaintenanceState();
|
||||
state = const AsyncLoading();
|
||||
state = AsyncData(
|
||||
await _load(current.copyWith(limit: limit, page: 1)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -285,7 +285,8 @@ class _AlertCard extends StatelessWidget {
|
||||
final visual = _alertVisualStyle(alert);
|
||||
final subtitleParts = [
|
||||
if (alert.assetCode?.trim().isNotEmpty == true) alert.assetCode!.trim(),
|
||||
if (alert.plantName?.trim().isNotEmpty == true) alert.plantName!.trim(),
|
||||
if (alert.locationName?.trim().isNotEmpty == true)
|
||||
alert.locationName!.trim(),
|
||||
if (dateLabel != null) 'Due: $dateLabel',
|
||||
];
|
||||
|
||||
|
||||
@ -23,6 +23,7 @@ import '../../../../shared/widgets/app_side_panel.dart';
|
||||
import '../providers/assets_provider.dart';
|
||||
import '../providers/asset_form_lookups_provider.dart';
|
||||
import '../widgets/asset_form_panel.dart';
|
||||
import '../widgets/asset_maintenance_panel.dart';
|
||||
import '../widgets/asset_side_panels.dart';
|
||||
import '../../../../shared/widgets/app_toast.dart';
|
||||
|
||||
@ -198,6 +199,12 @@ class _OverviewTab extends ConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = Theme.of(context);
|
||||
final dateFormat = DateFormat('dd MMM yyyy');
|
||||
final users =
|
||||
ref.watch(assetFormLookupsProvider).valueOrNull?.users ?? const [];
|
||||
final maintenanceInchargeLabel = _userLabel(
|
||||
asset.maintenanceInchargeUserId,
|
||||
users,
|
||||
);
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Center(
|
||||
@ -239,7 +246,40 @@ class _OverviewTab extends ConsumerWidget {
|
||||
'Subcategory',
|
||||
asset.assetSubcategoryName ?? '—',
|
||||
),
|
||||
_AssetInfo('Plant', asset.plantName ?? '—'),
|
||||
_AssetInfo('Location', asset.locationName ?? '—'),
|
||||
_AssetInfo(
|
||||
'Commencement Date',
|
||||
asset.commencementDate != null
|
||||
? dateFormat.format(asset.commencementDate!)
|
||||
: '—',
|
||||
),
|
||||
_AssetInfo(
|
||||
'Maintenance Incharge',
|
||||
maintenanceInchargeLabel,
|
||||
),
|
||||
_AssetInfo(
|
||||
'Maintenance Frequency',
|
||||
asset.maintenanceFrequencyInDays != null
|
||||
? '${asset.maintenanceFrequencyInDays} days'
|
||||
: '—',
|
||||
),
|
||||
if (asset.maintenance != null) ...[
|
||||
_AssetInfo(
|
||||
'Maintenance Due',
|
||||
asset.maintenance!.isDue ? 'Yes' : 'No',
|
||||
),
|
||||
_AssetInfo(
|
||||
'Next Due Date',
|
||||
asset.maintenance!.nextDueDate != null
|
||||
? dateFormat
|
||||
.format(asset.maintenance!.nextDueDate!)
|
||||
: '—',
|
||||
),
|
||||
_AssetInfo(
|
||||
'Days Until Due',
|
||||
asset.maintenance!.daysUntilDue?.toString() ?? '—',
|
||||
),
|
||||
],
|
||||
_AssetInfo('Serial Number', asset.serialNumber ?? '—'),
|
||||
_AssetInfo('Brand / Model', asset.brandModel ?? '—'),
|
||||
_AssetInfo('Manufacturer', asset.manufacturer ?? '—'),
|
||||
@ -285,6 +325,36 @@ class _OverviewTab extends ConsumerWidget {
|
||||
_AssetInfo('Active', asset.isActive ? 'Yes' : 'No'),
|
||||
],
|
||||
),
|
||||
if (asset.maintenanceFrequencyInDays != null ||
|
||||
asset.maintenanceChecklistJson?.isNotEmpty == true ||
|
||||
asset.maintenance != null) ...[
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 16),
|
||||
child: Divider(height: 1),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () async {
|
||||
final saved = await openSubmitMaintenancePanel(
|
||||
context,
|
||||
ref,
|
||||
asset: asset,
|
||||
);
|
||||
if (saved == true && context.mounted) {
|
||||
showAppToastFromSnackBar(
|
||||
context,
|
||||
const SnackBar(
|
||||
content: Text('Maintenance log submitted'),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.checklist_outlined),
|
||||
label: const Text('Log Maintenance'),
|
||||
),
|
||||
),
|
||||
],
|
||||
if (asset.remarks?.trim().isNotEmpty == true) ...[
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 20),
|
||||
@ -410,6 +480,17 @@ class _AssetInfo {
|
||||
final Widget? valueWidget;
|
||||
}
|
||||
|
||||
String _userLabel(int? userId, List<FilterOptionModel> users) {
|
||||
if (userId == null || userId <= 0) return '—';
|
||||
final id = userId.toString();
|
||||
for (final user in users) {
|
||||
if (user.id == id && user.name.trim().isNotEmpty) {
|
||||
return user.name.trim();
|
||||
}
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
class _AmcTab extends ConsumerWidget {
|
||||
const _AmcTab({required this.assetId, required this.contracts});
|
||||
|
||||
|
||||
@ -69,7 +69,7 @@ class _AssetListScreenState extends ConsumerState<AssetListScreen> {
|
||||
final allCategories =
|
||||
ref.watch(itemCategoriesProvider).valueOrNull ?? [];
|
||||
final lookups = ref.watch(assetFormLookupsProvider).valueOrNull;
|
||||
final allPlants = lookups?.plants ?? const [];
|
||||
final allLocations = lookups?.locations ?? const [];
|
||||
final notifier = ref.read(assetsListProvider.notifier);
|
||||
|
||||
return Column(
|
||||
@ -139,11 +139,11 @@ class _AssetListScreenState extends ConsumerState<AssetListScreen> {
|
||||
child: _AssetsFilterBar(
|
||||
query: state.query,
|
||||
categories: allCategories,
|
||||
plants: allPlants,
|
||||
locations: allLocations,
|
||||
statuses: lookups?.statuses ?? const [],
|
||||
onSearch: notifier.setSearch,
|
||||
onCategoryChanged: notifier.setCategoryFilter,
|
||||
onPlantChanged: notifier.setPlantFilter,
|
||||
onLocationChanged: notifier.setLocationFilter,
|
||||
onStatusChanged: notifier.setStatusFilter,
|
||||
),
|
||||
),
|
||||
@ -272,21 +272,21 @@ class _AssetsFilterBar extends StatelessWidget {
|
||||
const _AssetsFilterBar({
|
||||
required this.query,
|
||||
required this.categories,
|
||||
required this.plants,
|
||||
required this.locations,
|
||||
required this.statuses,
|
||||
required this.onSearch,
|
||||
required this.onCategoryChanged,
|
||||
required this.onPlantChanged,
|
||||
required this.onLocationChanged,
|
||||
required this.onStatusChanged,
|
||||
});
|
||||
|
||||
final AssetListQuery query;
|
||||
final List<AssetCategoryModel> categories;
|
||||
final List<FilterOptionModel> plants;
|
||||
final List<FilterOptionModel> locations;
|
||||
final List<AssetDropdownOption> statuses;
|
||||
final ValueChanged<String> onSearch;
|
||||
final ValueChanged<int?> onCategoryChanged;
|
||||
final ValueChanged<int?> onPlantChanged;
|
||||
final ValueChanged<int?> onLocationChanged;
|
||||
final ValueChanged<String?> onStatusChanged;
|
||||
|
||||
@override
|
||||
@ -315,13 +315,13 @@ class _AssetsFilterBar extends StatelessWidget {
|
||||
),
|
||||
];
|
||||
|
||||
final plantOptions = <AppDropdownOption<int?>>[
|
||||
const AppDropdownOption(value: null, label: 'All Plants'),
|
||||
for (final plant in plants)
|
||||
if (int.tryParse(plant.id) != null)
|
||||
final locationOptions = <AppDropdownOption<int?>>[
|
||||
const AppDropdownOption(value: null, label: 'All Locations'),
|
||||
for (final location in locations)
|
||||
if (int.tryParse(location.id) != null)
|
||||
AppDropdownOption(
|
||||
value: int.parse(plant.id),
|
||||
label: plant.name,
|
||||
value: int.parse(location.id),
|
||||
label: location.name,
|
||||
),
|
||||
];
|
||||
|
||||
@ -347,12 +347,12 @@ class _AssetsFilterBar extends StatelessWidget {
|
||||
onChanged: onCategoryChanged,
|
||||
),
|
||||
AppSearchableDropdown<int?>(
|
||||
label: 'Plant',
|
||||
value: query.plantId,
|
||||
searchHint: 'Search plant...',
|
||||
label: 'Location',
|
||||
value: query.locationId,
|
||||
searchHint: 'Search location...',
|
||||
isDense: true,
|
||||
options: plantOptions,
|
||||
onChanged: onPlantChanged,
|
||||
options: locationOptions,
|
||||
onChanged: onLocationChanged,
|
||||
),
|
||||
AppSearchableDropdown<String?>(
|
||||
label: 'Status',
|
||||
@ -416,10 +416,10 @@ class _AssetDataTable extends StatelessWidget {
|
||||
cellBuilder: (_, asset) => Text(asset.assetCategoryName ?? '—'),
|
||||
),
|
||||
AppDataColumn(
|
||||
label: 'Plant',
|
||||
label: 'Location',
|
||||
flex: 1,
|
||||
searchText: (asset) => asset.plantName ?? '',
|
||||
cellBuilder: (_, asset) => Text(asset.plantName ?? '—'),
|
||||
searchText: (asset) => asset.locationName ?? '',
|
||||
cellBuilder: (_, asset) => Text(asset.locationName ?? '—'),
|
||||
),
|
||||
AppDataColumn(
|
||||
label: 'Warranty',
|
||||
@ -533,7 +533,7 @@ class _AssetMobileList extends StatelessWidget {
|
||||
_AssetCodeBadge(code: asset.assetCode!)
|
||||
else
|
||||
const Text('—'),
|
||||
Text('${asset.assetCategoryName ?? '—'} · ${asset.plantName ?? '—'}'),
|
||||
Text('${asset.assetCategoryName ?? '—'} · ${asset.locationName ?? '—'}'),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
||||
@ -1,30 +1,355 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../../../../core/constants/route_constants.dart';
|
||||
import '../../../../core/errors/failure.dart';
|
||||
import '../../../../core/utils/formatters.dart';
|
||||
import '../../../../core/utils/responsive_utils.dart';
|
||||
import '../../../../shared/models/asset_model.dart';
|
||||
import '../../../../shared/widgets/app_data_table.dart';
|
||||
import '../../../../shared/widgets/app_empty_state.dart';
|
||||
import '../../../../shared/widgets/app_form_toggle_field.dart';
|
||||
import '../../../../shared/widgets/app_loading_view.dart';
|
||||
import '../../../../shared/widgets/app_pagination.dart';
|
||||
import '../../../../shared/widgets/app_table_action_icon.dart';
|
||||
import '../../../../shared/widgets/app_toast.dart';
|
||||
import '../../../../shared/widgets/error_view.dart';
|
||||
import '../../../../shared/widgets/page_header.dart';
|
||||
import '../providers/assets_provider.dart';
|
||||
import '../widgets/asset_maintenance_panel.dart';
|
||||
|
||||
class AssetMaintenanceScreen extends StatelessWidget {
|
||||
class AssetMaintenanceScreen extends ConsumerWidget {
|
||||
const AssetMaintenanceScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Padding(
|
||||
padding: EdgeInsets.all(24),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
PageHeader(
|
||||
title: 'Asset Maintenance',
|
||||
subtitle: 'Track repairs, service vendors, and costs',
|
||||
),
|
||||
Expanded(
|
||||
child: EmptyStateView(
|
||||
title: 'No maintenance requests',
|
||||
description: 'Create maintenance requests and view service history.',
|
||||
icon: Icons.build_outlined,
|
||||
),
|
||||
),
|
||||
],
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final maintenanceAsync = ref.watch(myMaintenanceProvider);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: maintenanceAsync.when(
|
||||
loading: () =>
|
||||
const AppLoadingView(message: 'Loading maintenance assets...'),
|
||||
error: (e, _) => ErrorView.fromFailure(
|
||||
e is Failure ? e : Failure.unknown(message: e.toString()),
|
||||
onRetry: () => ref.invalidate(myMaintenanceProvider),
|
||||
),
|
||||
data: (state) => _MyMaintenanceBody(state: state),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MyMaintenanceBody extends ConsumerWidget {
|
||||
const _MyMaintenanceBody({required this.state});
|
||||
|
||||
final MyMaintenanceState state;
|
||||
|
||||
Future<void> _openSubmit(
|
||||
BuildContext context,
|
||||
WidgetRef ref,
|
||||
AssetModel asset,
|
||||
) async {
|
||||
final saved = await openSubmitMaintenancePanel(context, ref, asset: asset);
|
||||
if (saved == true && context.mounted) {
|
||||
showAppToastFromSnackBar(
|
||||
context,
|
||||
const SnackBar(content: Text('Maintenance log submitted')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final notifier = ref.read(myMaintenanceProvider.notifier);
|
||||
final isWide = context.isDesktop;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
PageHeader(
|
||||
title: 'My Maintenance',
|
||||
subtitle: 'Assets assigned to you for checklist-based maintenance',
|
||||
actions: [
|
||||
OutlinedButton.icon(
|
||||
onPressed: () => context.go(RouteConstants.assets),
|
||||
icon: const Icon(Icons.inventory_2_outlined),
|
||||
label: const Text('Asset Master'),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Wrap(
|
||||
spacing: 16,
|
||||
runSpacing: 8,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
AppFormToggleField(
|
||||
label: 'Due only',
|
||||
subtitle: 'Show assets with maintenance due',
|
||||
value: state.dueOnly,
|
||||
onChanged: notifier.setDueOnly,
|
||||
),
|
||||
IconButton(
|
||||
onPressed: state.isRefreshing ? null : () => notifier.refresh(),
|
||||
icon: state.isRefreshing
|
||||
? const SizedBox(
|
||||
width: 18,
|
||||
height: 18,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
)
|
||||
: const Icon(Icons.refresh),
|
||||
tooltip: 'Refresh',
|
||||
),
|
||||
Text(
|
||||
'${state.total} asset${state.total == 1 ? '' : 's'}',
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Expanded(
|
||||
child: state.assets.isEmpty
|
||||
? AppEmptyState(
|
||||
title: state.dueOnly
|
||||
? 'No due maintenance'
|
||||
: 'No maintenance assets',
|
||||
description: state.dueOnly
|
||||
? 'Nothing is due right now. Turn off “Due only” to see all assigned assets.'
|
||||
: 'Assets where you are the maintenance in-charge will appear here.',
|
||||
icon: Icons.build_circle_outlined,
|
||||
)
|
||||
: isWide
|
||||
? _MaintenanceTable(
|
||||
assets: state.assets,
|
||||
onSubmit: (asset) => _openSubmit(context, ref, asset),
|
||||
onView: (asset) =>
|
||||
context.push('${RouteConstants.assets}/${asset.id}'),
|
||||
)
|
||||
: _MaintenanceMobileList(
|
||||
assets: state.assets,
|
||||
onSubmit: (asset) => _openSubmit(context, ref, asset),
|
||||
onView: (asset) =>
|
||||
context.push('${RouteConstants.assets}/${asset.id}'),
|
||||
),
|
||||
),
|
||||
AppPagination(
|
||||
currentPage: state.page,
|
||||
totalPages: state.totalPages,
|
||||
totalItems: state.total,
|
||||
pageSize: state.limit,
|
||||
onPageChanged: notifier.setPage,
|
||||
onPageSizeChanged: notifier.setPageSize,
|
||||
itemLabel: 'assets',
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MaintenanceTable extends StatelessWidget {
|
||||
const _MaintenanceTable({
|
||||
required this.assets,
|
||||
required this.onSubmit,
|
||||
required this.onView,
|
||||
});
|
||||
|
||||
final List<AssetModel> assets;
|
||||
final void Function(AssetModel asset) onSubmit;
|
||||
final void Function(AssetModel asset) onView;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final dateFormat = DateFormat('dd MMM yyyy');
|
||||
|
||||
return AppDataTable<AssetModel>(
|
||||
wrapInCard: false,
|
||||
columns: [
|
||||
AppDataColumn(
|
||||
label: 'Asset Code',
|
||||
flex: 1,
|
||||
searchText: (asset) => asset.assetCode ?? '',
|
||||
cellBuilder: (_, asset) => Text(asset.assetCode ?? '—'),
|
||||
),
|
||||
AppDataColumn(
|
||||
label: 'Asset Name',
|
||||
flex: 2,
|
||||
searchText: (asset) => asset.assetName,
|
||||
cellBuilder: (_, asset) => Text(asset.assetName),
|
||||
),
|
||||
AppDataColumn(
|
||||
label: 'Next Due',
|
||||
flex: 1,
|
||||
searchText: (asset) => DateFormatter.displayDate(
|
||||
asset.maintenance?.nextDueDate,
|
||||
),
|
||||
cellBuilder: (_, asset) {
|
||||
final nextDue = asset.maintenance?.nextDueDate;
|
||||
return Text(
|
||||
nextDue != null ? dateFormat.format(nextDue) : '—',
|
||||
);
|
||||
},
|
||||
),
|
||||
AppDataColumn(
|
||||
label: 'Status',
|
||||
flex: 1,
|
||||
searchText: (asset) =>
|
||||
asset.maintenance?.isDue == true ? 'due' : 'ok',
|
||||
cellBuilder: (_, asset) => _DueBadge(
|
||||
isDue: asset.maintenance?.isDue == true,
|
||||
daysUntilDue: asset.maintenance?.daysUntilDue,
|
||||
),
|
||||
),
|
||||
AppDataColumn(
|
||||
label: 'Actions',
|
||||
flex: 1,
|
||||
alignment: Alignment.centerRight,
|
||||
enableSearch: false,
|
||||
cellBuilder: (_, asset) => AppTableActions(
|
||||
children: [
|
||||
AppTableActionIcon(
|
||||
tooltip: 'Submit checklist',
|
||||
icon: Icons.checklist_outlined,
|
||||
onPressed: () => onSubmit(asset),
|
||||
),
|
||||
AppTableActionIcon(
|
||||
tooltip: 'View asset',
|
||||
icon: Icons.visibility_outlined,
|
||||
onPressed: () => onView(asset),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
rows: assets,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MaintenanceMobileList extends StatelessWidget {
|
||||
const _MaintenanceMobileList({
|
||||
required this.assets,
|
||||
required this.onSubmit,
|
||||
required this.onView,
|
||||
});
|
||||
|
||||
final List<AssetModel> assets;
|
||||
final void Function(AssetModel asset) onSubmit;
|
||||
final void Function(AssetModel asset) onView;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final dateFormat = DateFormat('dd MMM yyyy');
|
||||
|
||||
return ListView.separated(
|
||||
itemCount: assets.length,
|
||||
separatorBuilder: (_, __) => const SizedBox(height: 8),
|
||||
itemBuilder: (context, index) {
|
||||
final asset = assets[index];
|
||||
final nextDue = asset.maintenance?.nextDueDate;
|
||||
final subtitleParts = [
|
||||
if (asset.assetCode?.trim().isNotEmpty == true)
|
||||
asset.assetCode!.trim(),
|
||||
if (nextDue != null) 'Next due: ${dateFormat.format(nextDue)}',
|
||||
];
|
||||
|
||||
return Material(
|
||||
color: theme.colorScheme.surfaceContainerHighest.withValues(
|
||||
alpha: 0.35,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
onTap: () => onSubmit(asset),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
asset.assetName,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: theme.textTheme.titleSmall?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
if (subtitleParts.isNotEmpty) ...[
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
subtitleParts.join(' · '),
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 8),
|
||||
_DueBadge(
|
||||
isDue: asset.maintenance?.isDue == true,
|
||||
daysUntilDue: asset.maintenance?.daysUntilDue,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
tooltip: 'View asset',
|
||||
onPressed: () => onView(asset),
|
||||
icon: const Icon(Icons.visibility_outlined),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DueBadge extends StatelessWidget {
|
||||
const _DueBadge({
|
||||
required this.isDue,
|
||||
this.daysUntilDue,
|
||||
});
|
||||
|
||||
final bool isDue;
|
||||
final int? daysUntilDue;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final color = isDue
|
||||
? const Color(0xFFDC2626)
|
||||
: const Color(0xFF16A34A);
|
||||
final label = isDue
|
||||
? (daysUntilDue != null && daysUntilDue! < 0
|
||||
? 'Overdue'
|
||||
: 'Due')
|
||||
: (daysUntilDue != null
|
||||
? 'In $daysUntilDue day${daysUntilDue == 1 ? '' : 's'}'
|
||||
: 'On track');
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||
decoration: BoxDecoration(
|
||||
color: color.withValues(alpha: 0.12),
|
||||
borderRadius: BorderRadius.circular(999),
|
||||
),
|
||||
child: Text(
|
||||
label,
|
||||
style: theme.textTheme.labelSmall?.copyWith(
|
||||
color: color,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: 0.2,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -78,12 +78,13 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
|
||||
final _depreciationRateController = TextEditingController();
|
||||
final _salvageValueController = TextEditingController();
|
||||
final _remarksController = TextEditingController();
|
||||
final _frequencyController = TextEditingController();
|
||||
int? _categoryId;
|
||||
int? _subcategoryId;
|
||||
int? _plantId;
|
||||
int? _locationId;
|
||||
int? _departmentId;
|
||||
int? _warehouseId;
|
||||
int? _assignedToUserId;
|
||||
int? _maintenanceInchargeUserId;
|
||||
int? _vendorId;
|
||||
int? _poId;
|
||||
int? _grnId;
|
||||
@ -93,7 +94,9 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
|
||||
String? _depreciationMethod;
|
||||
DateTime? _warrantyExpiry;
|
||||
DateTime? _purchaseDate;
|
||||
DateTime? _commencementDate;
|
||||
DateTime? _disposalDate;
|
||||
List<AssetMaintenanceChecklistItem> _checklistItems = [];
|
||||
bool _isActive = true;
|
||||
bool _isSubmitting = false;
|
||||
String? _populatedSignature;
|
||||
@ -130,6 +133,7 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
|
||||
_depreciationRateController.dispose();
|
||||
_salvageValueController.dispose();
|
||||
_remarksController.dispose();
|
||||
_frequencyController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@ -139,7 +143,7 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
|
||||
|
||||
String _assetSignature(AssetModel asset) =>
|
||||
'${asset.id}:${asset.assetCategoryId}:${asset.assetSubcategoryId}:'
|
||||
'${asset.plantId}:${asset.status}:${asset.assetName}';
|
||||
'${asset.locationId}:${asset.status}:${asset.assetName}';
|
||||
|
||||
int? _nullablePositiveId(int? id) {
|
||||
if (id == null || id <= 0) return null;
|
||||
@ -188,10 +192,11 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
|
||||
_nameController.text = asset.assetName;
|
||||
_categoryId = asset.assetCategoryId;
|
||||
_subcategoryId = asset.assetSubcategoryId;
|
||||
_plantId = asset.plantId;
|
||||
_locationId = asset.locationId;
|
||||
_departmentId = _nullablePositiveId(asset.departmentId);
|
||||
_warehouseId = _nullablePositiveId(asset.warehouseId);
|
||||
_assignedToUserId = _nullablePositiveId(asset.assignedToUserId);
|
||||
_maintenanceInchargeUserId =
|
||||
_nullablePositiveId(asset.maintenanceInchargeUserId);
|
||||
_vendorId = _nullablePositiveId(asset.vendorId);
|
||||
_poId = _nullablePositiveId(asset.poId);
|
||||
_grnId = _nullablePositiveId(asset.grnId);
|
||||
@ -201,7 +206,11 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
|
||||
_depreciationMethod = asset.depreciationMethod;
|
||||
_warrantyExpiry = asset.warrantyExpiryDate;
|
||||
_purchaseDate = asset.purchaseDate;
|
||||
_commencementDate = asset.commencementDate;
|
||||
_disposalDate = asset.disposalDate;
|
||||
_checklistItems = [
|
||||
...(asset.maintenanceChecklistJson ?? const []),
|
||||
];
|
||||
_isActive = asset.isActive;
|
||||
_serialController.text = asset.serialNumber ?? '';
|
||||
_partNumberController.text = asset.partNumber ?? '';
|
||||
@ -215,6 +224,8 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
|
||||
_depreciationRateController.text = asset.depreciationRate?.toString() ?? '';
|
||||
_salvageValueController.text = asset.salvageValue?.toString() ?? '';
|
||||
_remarksController.text = asset.remarks ?? '';
|
||||
_frequencyController.text =
|
||||
asset.maintenanceFrequencyInDays?.toString() ?? '';
|
||||
if (asset.purchaseCost != null) {
|
||||
_costController.text = asset.purchaseCost!.toStringAsFixed(
|
||||
asset.purchaseCost! % 1 == 0 ? 0 : 2,
|
||||
@ -261,6 +272,10 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
|
||||
final usefulLife = int.tryParse(_usefulLifeController.text.trim());
|
||||
if (usefulLife != null) payload['useful_life_years'] = usefulLife;
|
||||
|
||||
if (_commencementDate != null) {
|
||||
payload['commencement_date'] =
|
||||
DateFormat('yyyy-MM-dd').format(_commencementDate!);
|
||||
}
|
||||
if (_purchaseDate != null) {
|
||||
payload['purchase_date'] = DateFormat('yyyy-MM-dd').format(_purchaseDate!);
|
||||
}
|
||||
@ -312,15 +327,21 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
|
||||
'asset_name': _nameController.text.trim(),
|
||||
'item_category_id': _categoryId,
|
||||
'item_subcategory_id': _subcategoryId,
|
||||
'plant_id': _plantId,
|
||||
'location_id': _locationId,
|
||||
'is_active': _isActive,
|
||||
};
|
||||
|
||||
_putOptionalId(payload, 'department_id', _departmentId);
|
||||
_putOptionalId(payload, 'warehouse_id', _warehouseId);
|
||||
if (_isAssignableUser(_assignedToUserId)) {
|
||||
_putOptionalId(payload, 'assigned_to_user_id', _assignedToUserId);
|
||||
}
|
||||
if (_isAssignableUser(_maintenanceInchargeUserId)) {
|
||||
_putOptionalId(
|
||||
payload,
|
||||
'maintenance_incharge_user_id',
|
||||
_maintenanceInchargeUserId,
|
||||
);
|
||||
}
|
||||
_putOptionalId(payload, 'vendor_id', _vendorId);
|
||||
_putOptionalId(payload, 'po_id', _poId);
|
||||
_putOptionalId(payload, 'grn_id', _grnId);
|
||||
@ -338,6 +359,10 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
|
||||
if (_purchaseDate != null) {
|
||||
payload['purchase_date'] = DateFormat('yyyy-MM-dd').format(_purchaseDate!);
|
||||
}
|
||||
if (_commencementDate != null) {
|
||||
payload['commencement_date'] =
|
||||
DateFormat('yyyy-MM-dd').format(_commencementDate!);
|
||||
}
|
||||
if (_warrantyExpiry != null) {
|
||||
payload['warranty_expiry_date'] =
|
||||
DateFormat('yyyy-MM-dd').format(_warrantyExpiry!);
|
||||
@ -353,6 +378,31 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
|
||||
final usefulLife = int.tryParse(_usefulLifeController.text.trim());
|
||||
if (usefulLife != null) payload['useful_life_years'] = usefulLife;
|
||||
|
||||
final frequency = int.tryParse(_frequencyController.text.trim());
|
||||
if (frequency != null) payload['maintenance_frequency_in_days'] = frequency;
|
||||
|
||||
final checklistPayload = _checklistItems
|
||||
.map((item) => item.toJson())
|
||||
.where((item) {
|
||||
final key = (item['key'] as String?)?.trim() ?? '';
|
||||
final label = (item['label'] as String?)?.trim() ?? '';
|
||||
return key.isNotEmpty || label.isNotEmpty;
|
||||
})
|
||||
.map((item) {
|
||||
final key = (item['key'] as String?)?.trim() ?? '';
|
||||
final label = (item['label'] as String?)?.trim() ?? '';
|
||||
return {
|
||||
'key': key.isNotEmpty ? key : label,
|
||||
'label': label.isNotEmpty ? label : key,
|
||||
'required': item['required'] == true,
|
||||
};
|
||||
})
|
||||
.where((item) => (item['key'] as String).isNotEmpty)
|
||||
.toList();
|
||||
if (checklistPayload.isNotEmpty) {
|
||||
payload['maintenance_checklist_json'] = checklistPayload;
|
||||
}
|
||||
|
||||
if (_depreciationMethod != null) {
|
||||
payload['depreciation_method'] = _depreciationMethod;
|
||||
}
|
||||
@ -467,7 +517,6 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final categoriesAsync = ref.watch(itemCategoriesFormProvider);
|
||||
final plantsAsync = ref.watch(assetPlantsProvider);
|
||||
|
||||
if (widget.isEditing) {
|
||||
ref.listen(assetFormProvider(widget.assetId), (prev, next) {
|
||||
@ -513,15 +562,14 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
|
||||
e is Failure ? e : Failure.unknown(message: e.toString()),
|
||||
onRetry: () => ref.invalidate(assetFormProvider(widget.assetId)),
|
||||
),
|
||||
data: (_) => _buildForm(categoriesAsync, plantsAsync),
|
||||
data: (_) => _buildForm(categoriesAsync),
|
||||
)
|
||||
: _buildForm(categoriesAsync, plantsAsync),
|
||||
: _buildForm(categoriesAsync),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildForm(
|
||||
AsyncValue<List<AssetCategoryModel>> categoriesAsync,
|
||||
AsyncValue<List<FilterOptionModel>> plantsAsync,
|
||||
) {
|
||||
final subcategoriesAsync = ref.watch(itemSubcategoriesProvider(_categoryId));
|
||||
final lookupsAsync = ref.watch(assetFormLookupsProvider);
|
||||
@ -589,10 +637,10 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
plantsAsync.when(
|
||||
lookupsAsync.when(
|
||||
loading: () => const LinearProgressIndicator(),
|
||||
error: (_, __) => const Text('Failed to load plants'),
|
||||
data: (plants) => _plantDropdown(plants),
|
||||
error: (_, __) => const Text('Failed to load locations'),
|
||||
data: (lookups) => _locationDropdown(lookups.locations),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -681,29 +729,21 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
|
||||
masterId: 'departments',
|
||||
onChanged: (v) => setState(() => _departmentId = v),
|
||||
),
|
||||
right: _optionalLookupDropdown(
|
||||
label: 'Warehouse',
|
||||
value: _warehouseId,
|
||||
options: lookups.warehouses,
|
||||
masterId: 'warehouses',
|
||||
onChanged: (v) => setState(() => _warehouseId = v),
|
||||
right: AppTextField(
|
||||
isDense: true,
|
||||
controller: _locationDetailController,
|
||||
label: 'Location Detail',
|
||||
validator: (v) {
|
||||
if (v == null || v.trim().isEmpty) return null;
|
||||
return Validators.minLength(
|
||||
v.trim(),
|
||||
2,
|
||||
fieldName: 'Location Detail',
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
AppTextField(
|
||||
isDense: true,
|
||||
controller: _locationDetailController,
|
||||
label: 'Location Detail',
|
||||
validator: (v) {
|
||||
if (v == null || v.trim().isEmpty) return null;
|
||||
return Validators.minLength(
|
||||
v.trim(),
|
||||
2,
|
||||
fieldName: 'Location Detail',
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_optionalLookupDropdown(
|
||||
label: 'Assigned To',
|
||||
value: _assignedToUserId,
|
||||
@ -713,6 +753,33 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
|
||||
),
|
||||
],
|
||||
),
|
||||
SidePanelSection(
|
||||
title: 'MAINTENANCE',
|
||||
children: [
|
||||
SidePanelFormRow(
|
||||
left: _optionalLookupDropdown(
|
||||
label: 'Maintenance Incharge',
|
||||
value: _maintenanceInchargeUserId,
|
||||
options: lookups.users,
|
||||
onChanged: (v) =>
|
||||
setState(() => _maintenanceInchargeUserId = v),
|
||||
emptyHint: 'Unassigned',
|
||||
),
|
||||
right: AppTextField(
|
||||
isDense: true,
|
||||
controller: _frequencyController,
|
||||
label: 'Frequency (Days)',
|
||||
keyboardType: TextInputType.number,
|
||||
validator: (v) => Validators.optionalPositiveInt(
|
||||
v,
|
||||
fieldName: 'Frequency',
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildChecklistEditor(),
|
||||
],
|
||||
),
|
||||
SidePanelSection(
|
||||
title: 'PROCUREMENT',
|
||||
children: [
|
||||
@ -779,11 +846,23 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
|
||||
_pickDate((d) => _purchaseDate = d, _purchaseDate),
|
||||
),
|
||||
right: _AssetFormDateField(
|
||||
label: 'Commencement Date',
|
||||
value: _commencementDate,
|
||||
onPick: () => _pickDate(
|
||||
(d) => _commencementDate = d,
|
||||
_commencementDate,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
SidePanelFormRow(
|
||||
left: _AssetFormDateField(
|
||||
label: 'Warranty Expiry Date',
|
||||
value: _warrantyExpiry,
|
||||
onPick: () =>
|
||||
_pickDate((d) => _warrantyExpiry = d, _warrantyExpiry),
|
||||
),
|
||||
right: const SizedBox.shrink(),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
SidePanelFormRow(
|
||||
@ -979,6 +1058,76 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildChecklistEditor() {
|
||||
final theme = Theme.of(context);
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Checklist Template',
|
||||
style: theme.textTheme.labelLarge?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
TextButton.icon(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_checklistItems = [
|
||||
..._checklistItems,
|
||||
const AssetMaintenanceChecklistItem(
|
||||
key: '',
|
||||
label: '',
|
||||
),
|
||||
];
|
||||
});
|
||||
},
|
||||
icon: const Icon(Icons.add, size: 18),
|
||||
label: const Text('Add Item'),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (_checklistItems.isEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
child: Text(
|
||||
'No checklist items. Add items for maintenance checks.',
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
for (var i = 0; i < _checklistItems.length; i++) ...[
|
||||
if (i > 0) const SizedBox(height: 8),
|
||||
_ChecklistItemRow(
|
||||
key: ValueKey('checklist-row-$i'),
|
||||
item: _checklistItems[i],
|
||||
onChanged: (updated) {
|
||||
setState(() {
|
||||
_checklistItems = [
|
||||
for (var j = 0; j < _checklistItems.length; j++)
|
||||
if (j == i) updated else _checklistItems[j],
|
||||
];
|
||||
});
|
||||
},
|
||||
onRemove: () {
|
||||
setState(() {
|
||||
_checklistItems = [
|
||||
for (var j = 0; j < _checklistItems.length; j++)
|
||||
if (j != i) _checklistItems[j],
|
||||
];
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _optionalLookupDropdown({
|
||||
required String label,
|
||||
required int? value,
|
||||
@ -1034,7 +1183,6 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
|
||||
options: dropdownOptions,
|
||||
refreshLookups: () {
|
||||
ref.invalidate(assetFormLookupsProvider);
|
||||
ref.invalidate(assetPlantsProvider);
|
||||
},
|
||||
parseCreatedId: int.tryParse,
|
||||
onChanged: onChanged,
|
||||
@ -1128,33 +1276,125 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _plantDropdown(List<FilterOptionModel> plants) {
|
||||
final plantIds = plants
|
||||
.map((p) => int.tryParse(p.id))
|
||||
Widget _locationDropdown(List<FilterOptionModel> locations) {
|
||||
final locationIds = locations
|
||||
.map((location) => int.tryParse(location.id))
|
||||
.whereType<int>()
|
||||
.toList();
|
||||
return MasterQuickAddDropdown<int>(
|
||||
masterId: 'plants',
|
||||
label: 'Plant *',
|
||||
value: _dropdownValue(_plantId, plantIds),
|
||||
searchHint: 'Search plant...',
|
||||
return AppSearchableDropdown<int>(
|
||||
label: 'Location *',
|
||||
value: _dropdownValue(_locationId, locationIds),
|
||||
searchHint: 'Search plant or warehouse...',
|
||||
isDense: true,
|
||||
options: plants
|
||||
options: locations
|
||||
.map(
|
||||
(p) => AppDropdownOption(
|
||||
value: int.tryParse(p.id) ?? 0,
|
||||
label: p.name,
|
||||
(location) => AppDropdownOption(
|
||||
value: int.tryParse(location.id) ?? 0,
|
||||
label: location.name,
|
||||
),
|
||||
)
|
||||
.where((option) => option.value != 0)
|
||||
.toList(),
|
||||
refreshLookups: () {
|
||||
ref.invalidate(assetPlantsProvider);
|
||||
ref.invalidate(assetFormLookupsProvider);
|
||||
},
|
||||
parseCreatedId: int.tryParse,
|
||||
onChanged: (v) => setState(() => _plantId = v),
|
||||
validator: (v) => v == null ? 'Plant is required' : null,
|
||||
onChanged: (v) => setState(() => _locationId = v),
|
||||
validator: (v) => v == null ? 'Location is required' : null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ChecklistItemRow extends StatefulWidget {
|
||||
const _ChecklistItemRow({
|
||||
super.key,
|
||||
required this.item,
|
||||
required this.onChanged,
|
||||
required this.onRemove,
|
||||
});
|
||||
|
||||
final AssetMaintenanceChecklistItem item;
|
||||
final ValueChanged<AssetMaintenanceChecklistItem> onChanged;
|
||||
final VoidCallback onRemove;
|
||||
|
||||
@override
|
||||
State<_ChecklistItemRow> createState() => _ChecklistItemRowState();
|
||||
}
|
||||
|
||||
class _ChecklistItemRowState extends State<_ChecklistItemRow> {
|
||||
late final TextEditingController _keyController;
|
||||
late final TextEditingController _labelController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_keyController = TextEditingController(text: widget.item.key);
|
||||
_labelController = TextEditingController(text: widget.item.label);
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant _ChecklistItemRow oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.item.key != widget.item.key &&
|
||||
_keyController.text != widget.item.key) {
|
||||
_keyController.text = widget.item.key;
|
||||
}
|
||||
if (oldWidget.item.label != widget.item.label &&
|
||||
_labelController.text != widget.item.label) {
|
||||
_labelController.text = widget.item.label;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_keyController.dispose();
|
||||
_labelController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _emit({String? key, String? label, bool? required}) {
|
||||
widget.onChanged(
|
||||
AssetMaintenanceChecklistItem(
|
||||
key: key ?? _keyController.text,
|
||||
label: label ?? _labelController.text,
|
||||
required: required ?? widget.item.required,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
SidePanelFormRow(
|
||||
left: AppTextField(
|
||||
isDense: true,
|
||||
controller: _keyController,
|
||||
label: 'Key',
|
||||
onChanged: (value) => _emit(key: value),
|
||||
),
|
||||
right: AppTextField(
|
||||
isDense: true,
|
||||
controller: _labelController,
|
||||
label: 'Label',
|
||||
onChanged: (value) => _emit(label: value),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: AppFormToggleField(
|
||||
label: 'Required',
|
||||
value: widget.item.required,
|
||||
onChanged: (value) => _emit(required: value),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
tooltip: 'Remove item',
|
||||
onPressed: widget.onRemove,
|
||||
icon: const Icon(Icons.delete_outline, size: 20),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1340,11 +1580,6 @@ class _AssetFormDateFieldState extends State<_AssetFormDateField> {
|
||||
}
|
||||
}
|
||||
|
||||
final assetPlantsProvider = FutureProvider<List<FilterOptionModel>>((ref) async {
|
||||
final dataSource = ref.watch(masterRemoteDataSourceProvider);
|
||||
return dataSource.listPlants();
|
||||
});
|
||||
|
||||
final itemSubcategoriesProvider =
|
||||
FutureProvider.family<List<FilterOptionModel>, int?>((ref, categoryId) async {
|
||||
if (categoryId == null) return [];
|
||||
|
||||
@ -0,0 +1,475 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../../../../core/utils/formatters.dart';
|
||||
import '../../../../shared/models/asset_model.dart';
|
||||
import '../../../../shared/widgets/app_button.dart';
|
||||
import '../../../../shared/widgets/app_date_popup.dart';
|
||||
import '../../../../shared/widgets/app_dropdown.dart';
|
||||
import '../../../../shared/widgets/app_empty_state.dart';
|
||||
import '../../../../shared/widgets/app_side_panel.dart';
|
||||
import '../../../../shared/widgets/app_text_field.dart';
|
||||
import '../../data/repositories/asset_repository_impl.dart';
|
||||
import '../providers/assets_provider.dart';
|
||||
|
||||
List<AssetMaintenanceChecklistItem> checklistForAsset(AssetModel asset) {
|
||||
final fromSummary = asset.maintenance?.checklist;
|
||||
if (fromSummary != null && fromSummary.isNotEmpty) {
|
||||
return fromSummary;
|
||||
}
|
||||
return asset.maintenanceChecklistJson ?? const [];
|
||||
}
|
||||
|
||||
Future<bool?> openSubmitMaintenancePanel(
|
||||
BuildContext context,
|
||||
WidgetRef ref, {
|
||||
required AssetModel asset,
|
||||
}) {
|
||||
return showSidePanel<bool>(
|
||||
context,
|
||||
SubmitMaintenanceLogPanel(asset: asset),
|
||||
width: 600,
|
||||
);
|
||||
}
|
||||
|
||||
class SubmitMaintenanceLogPanel extends ConsumerStatefulWidget {
|
||||
const SubmitMaintenanceLogPanel({super.key, required this.asset});
|
||||
|
||||
final AssetModel asset;
|
||||
|
||||
@override
|
||||
ConsumerState<SubmitMaintenanceLogPanel> createState() =>
|
||||
_SubmitMaintenanceLogPanelState();
|
||||
}
|
||||
|
||||
class _ChecklistRowState {
|
||||
_ChecklistRowState({
|
||||
required this.keyName,
|
||||
required this.label,
|
||||
});
|
||||
|
||||
final String keyName;
|
||||
final String label;
|
||||
String status = 'OK';
|
||||
final TextEditingController remarksController = TextEditingController();
|
||||
|
||||
void dispose() => remarksController.dispose();
|
||||
}
|
||||
|
||||
class _SubmitMaintenanceLogPanelState
|
||||
extends ConsumerState<SubmitMaintenanceLogPanel> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final _remarksController = TextEditingController();
|
||||
DateTime _performedDate = DateTime.now();
|
||||
late final List<_ChecklistRowState> _rows;
|
||||
bool _isSubmitting = false;
|
||||
bool _showLogs = false;
|
||||
List<AssetMaintenanceLogModel>? _logs;
|
||||
bool _logsLoading = false;
|
||||
String? _logsError;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final checklist = checklistForAsset(widget.asset);
|
||||
_rows = checklist
|
||||
.map(
|
||||
(item) => _ChecklistRowState(
|
||||
keyName: item.key.isNotEmpty ? item.key : item.label,
|
||||
label: item.label.isNotEmpty ? item.label : item.key,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_remarksController.dispose();
|
||||
for (final row in _rows) {
|
||||
row.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _pickPerformedDate() async {
|
||||
final picked = await showAppDatePopup(
|
||||
context: context,
|
||||
initialDate: _performedDate,
|
||||
firstDate: DateTime(2000),
|
||||
lastDate: DateTime(2100),
|
||||
helpText: 'Performed date',
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() => _performedDate = picked);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadLogs() async {
|
||||
setState(() {
|
||||
_showLogs = true;
|
||||
_logsLoading = true;
|
||||
_logsError = null;
|
||||
});
|
||||
final result = await ref
|
||||
.read(assetRepositoryProvider)
|
||||
.getMaintenanceLogs(widget.asset.id);
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_logsLoading = false;
|
||||
if (result.failure != null) {
|
||||
_logsError = result.failure!.message;
|
||||
_logs = null;
|
||||
} else {
|
||||
_logs = result.data ?? [];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _save() async {
|
||||
if (!_formKey.currentState!.validate()) return;
|
||||
if (_rows.isEmpty) {
|
||||
showSidePanelSnackBar(context, 'No checklist items configured for this asset');
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() => _isSubmitting = true);
|
||||
try {
|
||||
final payload = <String, dynamic>{
|
||||
'performed_date': DateFormatter.toApiDate(_performedDate),
|
||||
'checklist_json': _rows
|
||||
.map(
|
||||
(row) => {
|
||||
'key': row.keyName,
|
||||
'status': row.status,
|
||||
if (row.remarksController.text.trim().isNotEmpty)
|
||||
'remarks': row.remarksController.text.trim(),
|
||||
},
|
||||
)
|
||||
.toList(),
|
||||
if (_remarksController.text.trim().isNotEmpty)
|
||||
'remarks': _remarksController.text.trim(),
|
||||
};
|
||||
|
||||
final result = await ref
|
||||
.read(assetRepositoryProvider)
|
||||
.createMaintenanceLog(widget.asset.id, payload);
|
||||
if (result.failure != null) throw result.failure!;
|
||||
|
||||
ref.invalidate(myMaintenanceProvider);
|
||||
ref.invalidate(assetDetailProvider(widget.asset.id));
|
||||
|
||||
if (mounted) Navigator.of(context, rootNavigator: true).pop(true);
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
showSidePanelSnackBar(context, e.toString());
|
||||
}
|
||||
} finally {
|
||||
if (mounted) setState(() => _isSubmitting = false);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final assetLabel = widget.asset.assetCode?.trim().isNotEmpty == true
|
||||
? '${widget.asset.assetName} (${widget.asset.assetCode})'
|
||||
: widget.asset.assetName;
|
||||
|
||||
return SidePanelScaffold(
|
||||
title: 'Submit Maintenance',
|
||||
footer: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
OutlinedButton(
|
||||
onPressed: _isSubmitting
|
||||
? null
|
||||
: () => Navigator.of(context, rootNavigator: true).pop(),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
AppButton(
|
||||
label: 'Submit Log',
|
||||
expand: false,
|
||||
icon: Icons.check,
|
||||
isLoading: _isSubmitting,
|
||||
onPressed: _isSubmitting ? null : _save,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Text(
|
||||
assetLabel,
|
||||
style: theme.textTheme.titleSmall?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'Record checklist results for this maintenance visit.',
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
SidePanelSection(
|
||||
title: 'Visit',
|
||||
children: [
|
||||
_DateField(
|
||||
label: 'Performed Date *',
|
||||
value: _performedDate,
|
||||
onPick: _pickPerformedDate,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
AppTextField(
|
||||
controller: _remarksController,
|
||||
label: 'Remarks',
|
||||
maxLines: 3,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
SidePanelSection(
|
||||
title: 'Checklist',
|
||||
children: [
|
||||
if (_rows.isEmpty)
|
||||
const AppEmptyState(
|
||||
title: 'No checklist items',
|
||||
description:
|
||||
'Configure a maintenance checklist on the asset first.',
|
||||
icon: Icons.checklist_outlined,
|
||||
)
|
||||
else
|
||||
..._rows.asMap().entries.map((entry) {
|
||||
final index = entry.key;
|
||||
final row = entry.value;
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: index == _rows.length - 1 ? 0 : 12,
|
||||
),
|
||||
child: _ChecklistItemCard(
|
||||
row: row,
|
||||
onStatusChanged: (status) {
|
||||
if (status == null) return;
|
||||
setState(() => row.status = status);
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'Recent Logs',
|
||||
style: theme.textTheme.titleSmall?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
TextButton.icon(
|
||||
onPressed: _logsLoading
|
||||
? null
|
||||
: () {
|
||||
if (_showLogs && _logs != null) {
|
||||
setState(() => _showLogs = !_showLogs);
|
||||
} else {
|
||||
_loadLogs();
|
||||
}
|
||||
},
|
||||
icon: Icon(
|
||||
_showLogs ? Icons.expand_less : Icons.history,
|
||||
size: 18,
|
||||
),
|
||||
label: Text(_showLogs ? 'Hide' : 'View'),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (_showLogs) ...[
|
||||
const SizedBox(height: 8),
|
||||
if (_logsLoading)
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 24),
|
||||
child: Center(child: CircularProgressIndicator()),
|
||||
)
|
||||
else if (_logsError != null)
|
||||
Text(
|
||||
_logsError!,
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.colorScheme.error,
|
||||
),
|
||||
)
|
||||
else if (_logs == null || _logs!.isEmpty)
|
||||
const AppEmptyState(
|
||||
title: 'No logs yet',
|
||||
description: 'Submitted maintenance visits will appear here.',
|
||||
icon: Icons.history_toggle_off_outlined,
|
||||
)
|
||||
else
|
||||
..._logs!.take(5).map(
|
||||
(log) => Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: _MaintenanceLogTile(log: log),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DateField extends StatelessWidget {
|
||||
const _DateField({
|
||||
required this.label,
|
||||
required this.value,
|
||||
required this.onPick,
|
||||
});
|
||||
|
||||
final String label;
|
||||
final DateTime? value;
|
||||
final VoidCallback onPick;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextFormField(
|
||||
readOnly: true,
|
||||
onTap: onPick,
|
||||
decoration: InputDecoration(
|
||||
labelText: label,
|
||||
hintText: 'Select date',
|
||||
suffixIcon: const Icon(Icons.calendar_today_outlined, size: 20),
|
||||
),
|
||||
controller: TextEditingController(
|
||||
text: value != null ? DateFormatter.displayDate(value) : '',
|
||||
),
|
||||
validator: (_) => value == null ? 'Required' : null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ChecklistItemCard extends StatelessWidget {
|
||||
const _ChecklistItemCard({
|
||||
required this.row,
|
||||
required this.onStatusChanged,
|
||||
});
|
||||
|
||||
final _ChecklistRowState row;
|
||||
final ValueChanged<String?> onStatusChanged;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: theme.dividerColor),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Text(
|
||||
row.label,
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
AppDropdown<String>(
|
||||
label: 'Status',
|
||||
isDense: true,
|
||||
value: row.status,
|
||||
options: const [
|
||||
AppDropdownOption(value: 'OK', label: 'OK'),
|
||||
AppDropdownOption(value: 'NOT_OK', label: 'Not OK'),
|
||||
AppDropdownOption(value: 'NA', label: 'N/A'),
|
||||
],
|
||||
onChanged: onStatusChanged,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
AppTextField(
|
||||
controller: row.remarksController,
|
||||
label: 'Item remarks',
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MaintenanceLogTile extends StatelessWidget {
|
||||
const _MaintenanceLogTile({required this.log});
|
||||
|
||||
final AssetMaintenanceLogModel log;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final dateFormat = DateFormat('dd MMM yyyy');
|
||||
final performed = log.performedDate != null
|
||||
? dateFormat.format(log.performedDate!)
|
||||
: '—';
|
||||
final nextDue =
|
||||
log.nextDueDate != null ? dateFormat.format(log.nextDueDate!) : null;
|
||||
final subtitleParts = [
|
||||
'Performed: $performed',
|
||||
if (nextDue != null) 'Next due: $nextDue',
|
||||
if (log.createdByName?.trim().isNotEmpty == true) log.createdByName!.trim(),
|
||||
];
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.surfaceContainerHighest.withValues(alpha: 0.35),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
subtitleParts.join(' · '),
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
if (log.remarks?.trim().isNotEmpty == true) ...[
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
log.remarks!,
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
if (log.checklistJson.isNotEmpty) ...[
|
||||
const SizedBox(height: 6),
|
||||
Wrap(
|
||||
spacing: 6,
|
||||
runSpacing: 4,
|
||||
children: log.checklistJson.map((item) {
|
||||
final key = item['key']?.toString() ?? 'Item';
|
||||
final status = item['status']?.toString() ?? '—';
|
||||
return Chip(
|
||||
visualDensity: VisualDensity.compact,
|
||||
label: Text(
|
||||
'$key: $status',
|
||||
style: theme.textTheme.labelSmall,
|
||||
),
|
||||
padding: EdgeInsets.zero,
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1242,10 +1242,9 @@ class _TransferAssetPanelState extends ConsumerState<TransferAssetPanel> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final _reasonController = TextEditingController();
|
||||
DateTime _transferDate = DateTime.now();
|
||||
int? _toPlantId;
|
||||
int? _toLocationId;
|
||||
int? _toDepartmentId;
|
||||
int? _toUserId;
|
||||
int? _toWarehouseId;
|
||||
bool _isSubmitting = false;
|
||||
|
||||
@override
|
||||
@ -1271,10 +1270,9 @@ class _TransferAssetPanelState extends ConsumerState<TransferAssetPanel> {
|
||||
if (!_formKey.currentState!.validate()) return;
|
||||
|
||||
final hasDestination =
|
||||
_toPlantId != null ||
|
||||
_toLocationId != null ||
|
||||
_toDepartmentId != null ||
|
||||
_toUserId != null ||
|
||||
_toWarehouseId != null;
|
||||
_toUserId != null;
|
||||
if (!hasDestination) {
|
||||
showSidePanelSnackBar(
|
||||
context,
|
||||
@ -1287,10 +1285,9 @@ class _TransferAssetPanelState extends ConsumerState<TransferAssetPanel> {
|
||||
try {
|
||||
await ref.read(assetDetailProvider(widget.assetId).notifier).transferAsset({
|
||||
'transfer_date': DateFormatter.toApiDate(_transferDate),
|
||||
if (_toPlantId != null) 'to_plant_id': _toPlantId,
|
||||
if (_toLocationId != null) 'to_location_id': _toLocationId,
|
||||
if (_toDepartmentId != null) 'to_department_id': _toDepartmentId,
|
||||
if (_toUserId != null) 'to_user_id': _toUserId,
|
||||
if (_toWarehouseId != null) 'to_warehouse_id': _toWarehouseId,
|
||||
'reason': _reasonController.text.trim(),
|
||||
});
|
||||
if (mounted) {
|
||||
@ -1331,12 +1328,11 @@ class _TransferAssetPanelState extends ConsumerState<TransferAssetPanel> {
|
||||
onPick: _pickDate,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
MasterQuickAddDropdown<int>(
|
||||
masterId: 'plants',
|
||||
label: 'To Plant',
|
||||
value: _toPlantId,
|
||||
searchHint: 'Search plant...',
|
||||
options: lookups.plants
|
||||
AppSearchableDropdown<int>(
|
||||
label: 'To Location',
|
||||
value: _toLocationId,
|
||||
searchHint: 'Search plant or warehouse...',
|
||||
options: lookups.locations
|
||||
.map((option) {
|
||||
final id = int.tryParse(option.id);
|
||||
if (id == null) return null;
|
||||
@ -1344,10 +1340,7 @@ class _TransferAssetPanelState extends ConsumerState<TransferAssetPanel> {
|
||||
})
|
||||
.whereType<AppDropdownOption<int>>()
|
||||
.toList(),
|
||||
refreshLookups: () =>
|
||||
ref.invalidate(assetFormLookupsProvider),
|
||||
parseCreatedId: int.tryParse,
|
||||
onChanged: (v) => setState(() => _toPlantId = v),
|
||||
onChanged: (v) => setState(() => _toLocationId = v),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
MasterQuickAddDropdown<int>(
|
||||
@ -1384,25 +1377,6 @@ class _TransferAssetPanelState extends ConsumerState<TransferAssetPanel> {
|
||||
onChanged: (v) => setState(() => _toUserId = v),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
MasterQuickAddDropdown<int>(
|
||||
masterId: 'warehouses',
|
||||
label: 'To Warehouse',
|
||||
value: _toWarehouseId,
|
||||
searchHint: 'Search warehouse...',
|
||||
options: lookups.warehouses
|
||||
.map((option) {
|
||||
final id = int.tryParse(option.id);
|
||||
if (id == null) return null;
|
||||
return AppDropdownOption(value: id, label: option.name);
|
||||
})
|
||||
.whereType<AppDropdownOption<int>>()
|
||||
.toList(),
|
||||
refreshLookups: () =>
|
||||
ref.invalidate(assetFormLookupsProvider),
|
||||
parseCreatedId: int.tryParse,
|
||||
onChanged: (v) => setState(() => _toWarehouseId = v),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
AppTextField(
|
||||
controller: _reasonController,
|
||||
label: 'Reason *',
|
||||
@ -1476,9 +1450,8 @@ class _TransferHistoryEntry extends StatelessWidget {
|
||||
Expanded(
|
||||
child: _TransferLocationBlock(
|
||||
label: 'From',
|
||||
plant: item.fromPlantName,
|
||||
location: item.fromLocationName,
|
||||
department: item.fromDepartmentName,
|
||||
warehouse: item.fromWarehouseName,
|
||||
user: item.fromUserName,
|
||||
),
|
||||
),
|
||||
@ -1493,9 +1466,8 @@ class _TransferHistoryEntry extends StatelessWidget {
|
||||
Expanded(
|
||||
child: _TransferLocationBlock(
|
||||
label: 'To',
|
||||
plant: item.toPlantName,
|
||||
location: item.toLocationName,
|
||||
department: item.toDepartmentName,
|
||||
warehouse: item.toWarehouseName,
|
||||
user: item.toUserName,
|
||||
),
|
||||
),
|
||||
@ -1535,16 +1507,14 @@ class _TransferHistoryEntry extends StatelessWidget {
|
||||
class _TransferLocationBlock extends StatelessWidget {
|
||||
const _TransferLocationBlock({
|
||||
required this.label,
|
||||
this.plant,
|
||||
this.location,
|
||||
this.department,
|
||||
this.warehouse,
|
||||
this.user,
|
||||
});
|
||||
|
||||
final String label;
|
||||
final String? plant;
|
||||
final String? location;
|
||||
final String? department;
|
||||
final String? warehouse;
|
||||
final String? user;
|
||||
|
||||
@override
|
||||
@ -1564,17 +1534,13 @@ class _TransferLocationBlock extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_TransferLocationRow(
|
||||
icon: Icons.factory_outlined,
|
||||
value: plant,
|
||||
icon: Icons.place_outlined,
|
||||
value: location,
|
||||
),
|
||||
_TransferLocationRow(
|
||||
icon: Icons.apartment_outlined,
|
||||
value: department,
|
||||
),
|
||||
_TransferLocationRow(
|
||||
icon: Icons.warehouse_outlined,
|
||||
value: warehouse,
|
||||
),
|
||||
_TransferLocationRow(
|
||||
icon: Icons.person_outline,
|
||||
value: user,
|
||||
|
||||
@ -23,7 +23,12 @@ final grnLookupsProvider = FutureProvider.autoDispose<GrnLookups>((ref) async {
|
||||
final master = ref.watch(masterRemoteDataSourceProvider);
|
||||
final poRepo = ref.watch(purchaseOrderRepositoryProvider);
|
||||
|
||||
final warehouses = await _safeOptions(master.listWarehouses);
|
||||
final warehouses = await _safeOptions(() async {
|
||||
final locations = await master.listLocations();
|
||||
return locations
|
||||
.where((location) => location.slug?.toLowerCase() == 'warehouse')
|
||||
.toList();
|
||||
});
|
||||
final users = await _safeUserOptions(ref);
|
||||
|
||||
final receivablePos = <PurchaseOrderModel>[];
|
||||
|
||||
@ -112,8 +112,8 @@ class _GrnFormScreenState extends ConsumerState<GrnFormScreen> {
|
||||
_lines
|
||||
..clear()
|
||||
..addAll(draftsFromPurchaseOrder(po));
|
||||
if (_warehouseId == null && po.warehouseId != null) {
|
||||
_warehouseId = po.warehouseId;
|
||||
if (_warehouseId == null && po.shippingId != null) {
|
||||
_warehouseId = po.shippingId;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -453,12 +453,13 @@ class _GrnFormScreenState extends ConsumerState<GrnFormScreen> {
|
||||
value: existing?.poNumber ?? '—',
|
||||
),
|
||||
MasterQuickAddDropdown<int>(
|
||||
masterId: 'warehouses',
|
||||
masterId: 'locations',
|
||||
label: 'Warehouse *',
|
||||
value: _dropdownValue(_warehouseId, warehouseIds),
|
||||
hint: 'Select warehouse',
|
||||
searchHint: 'Search warehouse...',
|
||||
options: _intOptions(lookups.warehouses),
|
||||
initialValues: const {'type': 'warehouse'},
|
||||
refreshLookups: () =>
|
||||
ref.invalidate(grnLookupsProvider),
|
||||
parseCreatedId: int.tryParse,
|
||||
|
||||
@ -278,6 +278,12 @@ const masterDefinitions = <MasterDefinition>[
|
||||
type: MasterFieldType.number,
|
||||
required: true,
|
||||
),
|
||||
MasterFieldDef(
|
||||
key: 'tags',
|
||||
label: 'Tags',
|
||||
multiline: true,
|
||||
showInList: true,
|
||||
),
|
||||
MasterFieldDef(key: 'description', label: 'Description', multiline: true),
|
||||
MasterFieldDef(key: 'specification', label: 'Specification', multiline: true),
|
||||
_activeField,
|
||||
@ -341,47 +347,54 @@ const masterDefinitions = <MasterDefinition>[
|
||||
],
|
||||
),
|
||||
MasterDefinition(
|
||||
id: 'plants',
|
||||
title: 'Plants',
|
||||
subtitle: 'Manufacturing plants and units',
|
||||
id: 'locations',
|
||||
title: 'Locations',
|
||||
subtitle: 'Plants and warehouses',
|
||||
category: 'Organization',
|
||||
routeKey: 'plants',
|
||||
apiPath: '/masters/plants',
|
||||
module: 'plants',
|
||||
icon: Icons.factory_outlined,
|
||||
routeKey: 'locations',
|
||||
apiPath: '/masters/locations',
|
||||
module: 'locations',
|
||||
icon: Icons.place_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',
|
||||
key: 'type',
|
||||
label: 'Type',
|
||||
type: MasterFieldType.dropdown,
|
||||
required: true,
|
||||
showInList: true,
|
||||
optionsMasterKey: 'plants',
|
||||
staticOptions: const ['plant', 'warehouse'],
|
||||
),
|
||||
MasterFieldDef(key: 'code', label: 'Code', required: true, showInList: true),
|
||||
MasterFieldDef(key: 'name', label: 'Name', required: true, showInList: true),
|
||||
MasterFieldDef(
|
||||
key: 'parent_id',
|
||||
label: 'Parent Plant',
|
||||
type: MasterFieldType.dropdown,
|
||||
showInList: true,
|
||||
optionsMasterKey: 'locations',
|
||||
optionsQueryParams: const {'type': 'plant'},
|
||||
visibleWhenFieldKey: 'type',
|
||||
visibleWhenValue: 'warehouse',
|
||||
required: true,
|
||||
),
|
||||
MasterFieldDef(key: 'gstin', label: 'GSTIN', showInList: true),
|
||||
MasterFieldDef(key: 'city', label: 'City', showInList: true),
|
||||
MasterFieldDef(
|
||||
key: 'state',
|
||||
label: 'State',
|
||||
type: MasterFieldType.dropdown,
|
||||
optionsMasterKey: 'location_states',
|
||||
showInList: true,
|
||||
),
|
||||
MasterFieldDef(key: 'address', label: 'Address', multiline: true),
|
||||
MasterFieldDef(key: 'pincode', label: 'Pincode'),
|
||||
MasterFieldDef(key: 'phone', label: 'Phone'),
|
||||
MasterFieldDef(
|
||||
key: 'location',
|
||||
label: 'Location Detail',
|
||||
visibleWhenFieldKey: 'type',
|
||||
visibleWhenValue: 'warehouse',
|
||||
),
|
||||
MasterFieldDef(key: 'location', label: 'Location'),
|
||||
_activeField,
|
||||
],
|
||||
),
|
||||
@ -521,6 +534,18 @@ String masterCellValue(Map<String, dynamic> row, MasterFieldDef field) {
|
||||
final value = row[field.key];
|
||||
if (value == null || value == '') return '—';
|
||||
|
||||
if (field.key == 'tags') {
|
||||
if (value is List) {
|
||||
final tags = value
|
||||
.map((e) => e.toString().trim())
|
||||
.where((e) => e.isNotEmpty)
|
||||
.toList();
|
||||
return tags.isEmpty ? '—' : tags.join(', ');
|
||||
}
|
||||
final text = value.toString().trim();
|
||||
return text.isEmpty ? '—' : text;
|
||||
}
|
||||
|
||||
if (field.type == MasterFieldType.boolean) {
|
||||
return value == true ? 'Yes' : 'No';
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import '../../../../core/constants/app_constants.dart';
|
||||
import '../../../../core/utils/table_search.dart';
|
||||
import '../../../../shared/models/export_file_result.dart';
|
||||
import '../../../assets/data/repositories/asset_repository_impl.dart';
|
||||
import '../../../masters/data/datasources/master_remote_data_source.dart';
|
||||
import '../../data/repositories/master_repository_impl.dart';
|
||||
import '../../domain/entities/master_definition.dart';
|
||||
|
||||
@ -259,6 +260,13 @@ class MasterFormNotifier extends FamilyAsyncNotifier<MasterFormState, MasterForm
|
||||
.getById(_definition, arg.recordId!);
|
||||
if (result.failure != null) throw result.failure!;
|
||||
values = Map<String, dynamic>.from(result.data ?? const {});
|
||||
final tags = values['tags'];
|
||||
if (tags is List) {
|
||||
values['tags'] = tags
|
||||
.map((e) => e.toString().trim())
|
||||
.where((e) => e.isNotEmpty)
|
||||
.join(', ');
|
||||
}
|
||||
} else {
|
||||
for (final field in _definition.formFields) {
|
||||
if (field.type == MasterFieldType.boolean) {
|
||||
@ -342,6 +350,23 @@ class MasterFormNotifier extends FamilyAsyncNotifier<MasterFormState, MasterForm
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key == 'location_states') {
|
||||
try {
|
||||
final states = await ref
|
||||
.read(masterRemoteDataSourceProvider)
|
||||
.listLocationStates();
|
||||
options[lookupKey] = states
|
||||
.map(
|
||||
(option) => <String, dynamic>{
|
||||
'id': option.id,
|
||||
'name': option.name,
|
||||
},
|
||||
)
|
||||
.toList();
|
||||
} catch (_) {}
|
||||
continue;
|
||||
}
|
||||
|
||||
final def = masterDefinitionById(key);
|
||||
if (def == null) continue;
|
||||
final queryParameters = masterFieldOptionsQuery(
|
||||
@ -481,10 +506,17 @@ class MasterFormNotifier extends FamilyAsyncNotifier<MasterFormState, MasterForm
|
||||
final value = current.values[field.key];
|
||||
if (value == null || value == '') continue;
|
||||
|
||||
if (field.key == 'tags') {
|
||||
final tags = _parseTags(value);
|
||||
if (tags.isNotEmpty) payload['tags'] = tags;
|
||||
continue;
|
||||
}
|
||||
|
||||
payload[field.key] = switch (field.type) {
|
||||
MasterFieldType.number => num.tryParse(value.toString()) ?? value,
|
||||
MasterFieldType.dropdown => field.staticOptions != null ||
|
||||
field.optionsMasterKey == 'asset_depreciation_methods'
|
||||
field.optionsMasterKey == 'asset_depreciation_methods' ||
|
||||
field.optionsMasterKey == 'location_states'
|
||||
? value.toString()
|
||||
: int.tryParse(value.toString()) ?? value,
|
||||
MasterFieldType.boolean => value == true,
|
||||
@ -494,6 +526,26 @@ class MasterFormNotifier extends FamilyAsyncNotifier<MasterFormState, MasterForm
|
||||
return payload;
|
||||
}
|
||||
|
||||
List<String> _parseTags(dynamic value) {
|
||||
if (value is List) {
|
||||
return value
|
||||
.map((e) => e.toString().trim())
|
||||
.where((e) => e.isNotEmpty)
|
||||
.take(50)
|
||||
.map((e) => e.length > 50 ? e.substring(0, 50) : e)
|
||||
.toSet()
|
||||
.toList();
|
||||
}
|
||||
final parts = value
|
||||
.toString()
|
||||
.split(RegExp(r'[,;\n]+'))
|
||||
.map((e) => e.trim())
|
||||
.where((e) => e.isNotEmpty)
|
||||
.take(50)
|
||||
.map((e) => e.length > 50 ? e.substring(0, 50) : e);
|
||||
return parts.toSet().toList();
|
||||
}
|
||||
|
||||
/// Returns the created/updated record id on success, otherwise null.
|
||||
Future<String?> submit() async {
|
||||
final current = state.valueOrNull ?? const MasterFormState();
|
||||
|
||||
@ -99,6 +99,9 @@ class _MasterFormPanelState extends ConsumerState<MasterFormPanel> {
|
||||
labelText: _fieldLabel(field),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
alignLabelWithHint: true,
|
||||
hintText: field.key == 'tags'
|
||||
? 'Comma-separated, e.g. critical, imported, rm'
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -224,6 +224,7 @@ String masterQuickAddNoun(String masterId) {
|
||||
'item_subcategories' => 'subcategory',
|
||||
'items' => 'item',
|
||||
'hsn_codes' => 'HSN code',
|
||||
'locations' => 'location',
|
||||
'plants' => 'plant',
|
||||
'warehouses' => 'warehouse',
|
||||
'designations' => 'designation',
|
||||
|
||||
@ -23,6 +23,77 @@ class MasterRemoteDataSource {
|
||||
Future<List<FilterOptionModel>> listPlants() =>
|
||||
_listOptions(ApiEndpoints.plants);
|
||||
|
||||
/// Plants and warehouses from the unified locations master.
|
||||
/// Labels include type, e.g. "Chennai Plant (Plant)".
|
||||
Future<List<FilterOptionModel>> listLocations() async {
|
||||
final result = await listLocationsWithState();
|
||||
return result.options;
|
||||
}
|
||||
|
||||
/// Indian states/UTs for location.state (same values as vendor source_of_supply).
|
||||
Future<List<FilterOptionModel>> listLocationStates() async {
|
||||
final response = await dio.get(ApiEndpoints.locationStates);
|
||||
final data = response.data;
|
||||
final list = data is Map ? data['data'] : null;
|
||||
if (list is! List) return const [];
|
||||
|
||||
return list
|
||||
.map((item) {
|
||||
if (item is String) {
|
||||
final value = item.trim();
|
||||
if (value.isEmpty) return null;
|
||||
return FilterOptionModel(id: value, name: value);
|
||||
}
|
||||
if (item is Map) {
|
||||
final map = Map<String, dynamic>.from(item);
|
||||
final value =
|
||||
(map['value'] ?? map['id'] ?? map['code'] ?? '').toString().trim();
|
||||
final label =
|
||||
(map['label'] ?? map['name'] ?? value).toString().trim();
|
||||
if (value.isEmpty) return null;
|
||||
return FilterOptionModel(
|
||||
id: value,
|
||||
name: label.isEmpty ? value : label,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.whereType<FilterOptionModel>()
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// Locations plus each row's `state` (for PO GST intra/inter preview).
|
||||
Future<({List<FilterOptionModel> options, Map<String, String?> stateById})>
|
||||
listLocationsWithState() async {
|
||||
final rows = await _listAllMaps(ApiEndpoints.locations);
|
||||
final options = <FilterOptionModel>[];
|
||||
final stateById = <String, String?>{};
|
||||
|
||||
for (final item in rows) {
|
||||
if (!isActiveOptionRow(item)) continue;
|
||||
final id = item['id']?.toString() ?? '';
|
||||
final name = _optionLabel(item);
|
||||
if (id.isEmpty || name.isEmpty) continue;
|
||||
|
||||
final type = item['type']?.toString().trim();
|
||||
final typeLabel = (type == null || type.isEmpty)
|
||||
? null
|
||||
: '${type[0].toUpperCase()}${type.substring(1)}';
|
||||
final state = item['state']?.toString().trim();
|
||||
stateById[id] = (state == null || state.isEmpty) ? null : state;
|
||||
|
||||
options.add(
|
||||
FilterOptionModel(
|
||||
id: id,
|
||||
name: typeLabel == null ? name : '$name ($typeLabel)',
|
||||
slug: type,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return (options: options, stateById: stateById);
|
||||
}
|
||||
|
||||
Future<List<FilterOptionModel>> listDesignations() =>
|
||||
_listOptions(ApiEndpoints.designations);
|
||||
|
||||
|
||||
@ -232,7 +232,8 @@ class PurchaseOrderRemoteDataSource {
|
||||
if (query.search != null && query.search!.isNotEmpty) 'search': query.search,
|
||||
if (query.status != null) 'status': query.status,
|
||||
if (query.vendorId != null) 'vendor_id': query.vendorId,
|
||||
if (query.plantId != null) 'plant_id': query.plantId,
|
||||
if (query.billingId != null) 'billing_id': query.billingId,
|
||||
if (query.shippingId != null) 'shipping_id': query.shippingId,
|
||||
if (query.dateFrom != null) 'date_from': query.dateFrom,
|
||||
if (query.dateTo != null) 'date_to': query.dateTo,
|
||||
};
|
||||
|
||||
@ -9,8 +9,9 @@ import '../../../vendors/domain/repositories/vendor_repository.dart';
|
||||
class PurchaseOrderLookups {
|
||||
const PurchaseOrderLookups({
|
||||
this.vendors = const [],
|
||||
this.plants = const [],
|
||||
this.warehouses = const [],
|
||||
this.locations = const [],
|
||||
this.vendorSourceOfSupplyById = const {},
|
||||
this.locationStateById = const {},
|
||||
this.paymentTerms = const [],
|
||||
this.deliveryTerms = const [],
|
||||
this.items = const [],
|
||||
@ -25,8 +26,12 @@ class PurchaseOrderLookups {
|
||||
});
|
||||
|
||||
final List<FilterOptionModel> vendors;
|
||||
final List<FilterOptionModel> plants;
|
||||
final List<FilterOptionModel> warehouses;
|
||||
/// Billing / shipping options (plants + warehouses from locations master).
|
||||
final List<FilterOptionModel> locations;
|
||||
/// Vendor id → `source_of_supply` (state code/name) for GST split preview.
|
||||
final Map<String, String?> vendorSourceOfSupplyById;
|
||||
/// Location id → `state` for GST split preview.
|
||||
final Map<String, String?> locationStateById;
|
||||
final List<FilterOptionModel> paymentTerms;
|
||||
final List<FilterOptionModel> deliveryTerms;
|
||||
final List<FilterOptionModel> items;
|
||||
@ -50,31 +55,31 @@ final purchaseOrderLookupsProvider =
|
||||
final master = ref.watch(masterRemoteDataSourceProvider);
|
||||
final vendorRepo = ref.watch(vendorRepositoryProvider);
|
||||
|
||||
final vendors = await _safeOptions(() => _fetchActiveVendors(vendorRepo));
|
||||
final vendorsWithSos = await _safeVendorsWithSos(vendorRepo);
|
||||
final locationsWithState = await _safeLocationsWithState(master);
|
||||
|
||||
final itemsWithDefaults = await _safeItemsWithDefaults(master.listItemsWithHsn);
|
||||
final gstWithPct = await _safeGstRatesWithPct(master.listGstRatesWithPct);
|
||||
final hsnWithGst = await _safeHsnWithGst(master.listHsnCodesWithGstRate);
|
||||
|
||||
final results = await Future.wait([
|
||||
_safeOptions(master.listPlants),
|
||||
_safeOptions(master.listWarehouses),
|
||||
_safeOptions(master.listPaymentTerms),
|
||||
_safeOptions(master.listDeliveryTerms),
|
||||
_safeOptions(master.listUom),
|
||||
]);
|
||||
|
||||
return PurchaseOrderLookups(
|
||||
vendors: vendors,
|
||||
plants: results[0],
|
||||
warehouses: results[1],
|
||||
paymentTerms: results[2],
|
||||
deliveryTerms: results[3],
|
||||
vendors: vendorsWithSos.options,
|
||||
locations: locationsWithState.options,
|
||||
vendorSourceOfSupplyById: vendorsWithSos.sourceOfSupplyById,
|
||||
locationStateById: locationsWithState.stateById,
|
||||
paymentTerms: results[0],
|
||||
deliveryTerms: results[1],
|
||||
items: itemsWithDefaults.options,
|
||||
itemHsnById: itemsWithDefaults.hsnByItemId,
|
||||
itemUomById: itemsWithDefaults.uomByItemId,
|
||||
itemGstRateById: itemsWithDefaults.gstRateByItemId,
|
||||
uom: results[4],
|
||||
uom: results[2],
|
||||
gstRates: gstWithPct.options,
|
||||
gstRatePctById: gstWithPct.pctById,
|
||||
hsnCodes: hsnWithGst.options,
|
||||
@ -92,6 +97,52 @@ Future<List<FilterOptionModel>> _safeOptions(
|
||||
}
|
||||
}
|
||||
|
||||
Future<
|
||||
({
|
||||
List<FilterOptionModel> options,
|
||||
Map<String, String?> sourceOfSupplyById,
|
||||
})> _safeVendorsWithSos(VendorRepository vendorRepo) async {
|
||||
try {
|
||||
final result = await vendorRepo.listVendorOptions();
|
||||
if (result.failure != null) {
|
||||
throw result.failure!;
|
||||
}
|
||||
|
||||
final options = <FilterOptionModel>[];
|
||||
final sourceOfSupplyById = <String, String?>{};
|
||||
for (final vendor in result.data ?? const []) {
|
||||
if (!isActiveVendorOption(
|
||||
isActive: vendor.isActive,
|
||||
status: vendor.status,
|
||||
)) {
|
||||
continue;
|
||||
}
|
||||
options.add(FilterOptionModel(id: vendor.id, name: vendor.vendorName));
|
||||
final sos = vendor.sourceOfSupply?.trim();
|
||||
sourceOfSupplyById[vendor.id] =
|
||||
(sos == null || sos.isEmpty) ? null : sos;
|
||||
}
|
||||
return (options: options, sourceOfSupplyById: sourceOfSupplyById);
|
||||
} catch (_) {
|
||||
return (
|
||||
options: <FilterOptionModel>[],
|
||||
sourceOfSupplyById: <String, String?>{},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<
|
||||
({
|
||||
List<FilterOptionModel> options,
|
||||
Map<String, String?> stateById,
|
||||
})> _safeLocationsWithState(MasterRemoteDataSource master) async {
|
||||
try {
|
||||
return await master.listLocationsWithState();
|
||||
} catch (_) {
|
||||
return (options: <FilterOptionModel>[], stateById: <String, String?>{});
|
||||
}
|
||||
}
|
||||
|
||||
Future<
|
||||
({
|
||||
List<FilterOptionModel> options,
|
||||
@ -146,24 +197,3 @@ Future<({List<FilterOptionModel> options, Map<String, int?> gstRateByHsnId})>
|
||||
return (options: <FilterOptionModel>[], gstRateByHsnId: <String, int?>{});
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<FilterOptionModel>> _fetchActiveVendors(
|
||||
VendorRepository vendorRepo,
|
||||
) async {
|
||||
final result = await vendorRepo.listVendorOptions();
|
||||
if (result.failure != null) {
|
||||
throw result.failure!;
|
||||
}
|
||||
|
||||
return (result.data ?? const [])
|
||||
.where(
|
||||
(vendor) => isActiveVendorOption(
|
||||
isActive: vendor.isActive,
|
||||
status: vendor.status,
|
||||
),
|
||||
)
|
||||
.map(
|
||||
(vendor) => FilterOptionModel(id: vendor.id, name: vendor.vendorName),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@ -421,7 +421,8 @@ class _DetailHeader extends StatelessWidget {
|
||||
final subtitleParts = [
|
||||
vendorTypeLabel(order.vendorType),
|
||||
if (order.vendorName?.trim().isNotEmpty == true) order.vendorName!.trim(),
|
||||
if (order.plantName?.trim().isNotEmpty == true) order.plantName!.trim(),
|
||||
if (order.billingName?.trim().isNotEmpty == true)
|
||||
order.billingName!.trim(),
|
||||
];
|
||||
|
||||
final actions = Wrap(
|
||||
@ -737,12 +738,12 @@ class _OrderDetailsCard extends StatelessWidget {
|
||||
value: vendorTypeLabel(order.vendorType),
|
||||
),
|
||||
_DetailField(
|
||||
label: 'Plant',
|
||||
value: _displayOrDash(order.plantName),
|
||||
label: 'Billing',
|
||||
value: _displayOrDash(order.billingName),
|
||||
),
|
||||
_DetailField(
|
||||
label: 'Warehouse',
|
||||
value: _displayOrDash(order.warehouseName),
|
||||
label: 'Shipping',
|
||||
value: _displayOrDash(order.shippingName),
|
||||
),
|
||||
_DetailField(label: 'Payment Term', value: paymentTerm),
|
||||
_DetailField(label: 'Delivery Term', value: deliveryTerm),
|
||||
@ -1150,10 +1151,47 @@ class _AmountSummaryCard extends StatelessWidget {
|
||||
|
||||
final PurchaseOrderModel order;
|
||||
|
||||
List<Widget> _gstSummaryRows() {
|
||||
final igst = order.igst ?? 0;
|
||||
final cgst = order.cgst ?? 0;
|
||||
final sgst = order.sgst ?? 0;
|
||||
final hasSplit =
|
||||
order.cgst != null || order.sgst != null || order.igst != null;
|
||||
if (!hasSplit) return const [];
|
||||
|
||||
if (igst > 0) {
|
||||
return [
|
||||
_SummaryRow(
|
||||
label: 'IGST',
|
||||
value: CurrencyFormatter.format(igst),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
_SummaryRow(
|
||||
label: 'CGST',
|
||||
value: CurrencyFormatter.format(cgst),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_SummaryRow(
|
||||
label: 'SGST',
|
||||
value: CurrencyFormatter.format(sgst),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final discount = order.discountAmount ?? 0;
|
||||
final freight = order.freightCharges ?? 0;
|
||||
final other = order.otherCharges ?? 0;
|
||||
final subTotal = order.taxableAmount ?? 0;
|
||||
final taxableAmount = (subTotal + freight + other - discount)
|
||||
.clamp(0.0, double.infinity);
|
||||
final primaryTint = theme.colorScheme.primary.withValues(alpha: 0.1);
|
||||
|
||||
return _SectionCard(
|
||||
@ -1162,32 +1200,38 @@ class _AmountSummaryCard extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_SummaryRow(
|
||||
label: 'Taxable Amount',
|
||||
value: CurrencyFormatter.format(order.taxableAmount),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_SummaryRow(
|
||||
label: 'Tax (GST)',
|
||||
value: CurrencyFormatter.format(order.taxAmount),
|
||||
label: 'Sub Total',
|
||||
value: CurrencyFormatter.format(subTotal),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_SummaryRow(
|
||||
label: 'Freight Charges',
|
||||
value: CurrencyFormatter.format(order.freightCharges ?? 0),
|
||||
value: CurrencyFormatter.format(freight),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_SummaryRow(
|
||||
label: 'Other Charges',
|
||||
value: CurrencyFormatter.format(order.otherCharges ?? 0),
|
||||
value: CurrencyFormatter.format(other),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_SummaryRow(
|
||||
label: 'Discount',
|
||||
label: 'Discount Amount',
|
||||
value: discount > 0
|
||||
? '-${CurrencyFormatter.format(discount)}'
|
||||
: CurrencyFormatter.format(0),
|
||||
valueColor: discount > 0 ? AppColors.error : null,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_SummaryRow(
|
||||
label: 'Taxable Amount',
|
||||
value: CurrencyFormatter.format(taxableAmount),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
..._gstSummaryRows(),
|
||||
_SummaryRow(
|
||||
label: 'Tax Total',
|
||||
value: CurrencyFormatter.format(order.taxAmount),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||||
|
||||
@ -51,8 +51,8 @@ class _PurchaseOrderFormScreenState extends ConsumerState<PurchaseOrderFormScree
|
||||
DateTime? _poDate;
|
||||
DateTime? _expectedDeliveryDate;
|
||||
int? _vendorId;
|
||||
int? _plantId;
|
||||
int? _warehouseId;
|
||||
int? _billingId;
|
||||
int? _shippingId;
|
||||
int? _paymentTermId;
|
||||
int? _deliveryTermId;
|
||||
final List<PoLineItemDraft> _lines = [];
|
||||
@ -98,8 +98,8 @@ class _PurchaseOrderFormScreenState extends ConsumerState<PurchaseOrderFormScree
|
||||
_poDate = order.poDate ?? DateTime.now();
|
||||
_expectedDeliveryDate = order.expectedDeliveryDate;
|
||||
_vendorId = order.vendorId;
|
||||
_plantId = order.plantId;
|
||||
_warehouseId = order.warehouseId;
|
||||
_billingId = order.billingId;
|
||||
_shippingId = order.shippingId;
|
||||
_paymentTermId = order.paymentTermId;
|
||||
_deliveryTermId = order.deliveryTermId;
|
||||
_discountController.text =
|
||||
@ -183,12 +183,30 @@ class _PurchaseOrderFormScreenState extends ConsumerState<PurchaseOrderFormScree
|
||||
);
|
||||
}
|
||||
|
||||
/// Mirrors BE: compare billing location state vs vendor source_of_supply.
|
||||
/// Missing either side → intra-state (CGST/SGST).
|
||||
bool _isInterStateGst(PurchaseOrderLookups lookups) {
|
||||
final billingState = lookups.locationStateById[_billingId?.toString()]
|
||||
?.trim()
|
||||
.toLowerCase();
|
||||
final vendorSos = lookups.vendorSourceOfSupplyById[_vendorId?.toString()]
|
||||
?.trim()
|
||||
.toLowerCase();
|
||||
if (billingState == null ||
|
||||
billingState.isEmpty ||
|
||||
vendorSos == null ||
|
||||
vendorSos.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
return billingState != vendorSos;
|
||||
}
|
||||
|
||||
Map<String, dynamic> _buildPayload() {
|
||||
return {
|
||||
'po_date': DateFormatter.toApiDate(_poDate ?? DateTime.now()),
|
||||
'vendor_id': _vendorId,
|
||||
'plant_id': _plantId,
|
||||
if (_warehouseId != null) 'warehouse_id': _warehouseId,
|
||||
'billing_id': _billingId,
|
||||
'shipping_id': _shippingId,
|
||||
if (_paymentTermId != null) 'payment_term_id': _paymentTermId,
|
||||
if (_deliveryTermId != null) 'delivery_term_id': _deliveryTermId,
|
||||
if (_expectedDeliveryDate != null)
|
||||
@ -238,7 +256,7 @@ class _PurchaseOrderFormScreenState extends ConsumerState<PurchaseOrderFormScree
|
||||
return;
|
||||
}
|
||||
|
||||
if (_vendorId == null || _plantId == null) {
|
||||
if (_vendorId == null || _billingId == null || _shippingId == null) {
|
||||
showAppToastFromSnackBar(context,
|
||||
const SnackBar(content: Text('Please complete all required fields')),
|
||||
);
|
||||
@ -352,8 +370,8 @@ class _PurchaseOrderFormScreenState extends ConsumerState<PurchaseOrderFormScree
|
||||
|
||||
final vendorIds =
|
||||
lookups.vendors.map((e) => _parseId(e.id)).whereType<int>();
|
||||
final plantIds =
|
||||
lookups.plants.map((e) => _parseId(e.id)).whereType<int>();
|
||||
final locationIds =
|
||||
lookups.locations.map((e) => _parseId(e.id)).whereType<int>();
|
||||
final totals = _computeTotals(lookups.gstRatePctById);
|
||||
final theme = Theme.of(context);
|
||||
|
||||
@ -373,105 +391,94 @@ class _PurchaseOrderFormScreenState extends ConsumerState<PurchaseOrderFormScree
|
||||
const SizedBox(height: 16),
|
||||
_SectionCard(
|
||||
title: 'ORDER DETAILS',
|
||||
child: Column(
|
||||
children: [
|
||||
FormRowFour(
|
||||
children: [
|
||||
_DateField(
|
||||
label: 'PO Date *',
|
||||
value: _poDate,
|
||||
onTap: () => _pickDate(
|
||||
current: _poDate,
|
||||
onPicked: (d) => setState(() => _poDate = d),
|
||||
child: QuickAddInlineHost(
|
||||
child: ResponsiveFormGrid(
|
||||
smallColumns: 1,
|
||||
mediumColumns: 2,
|
||||
largeColumns: 4,
|
||||
mediumBreakpoint: 640,
|
||||
largeBreakpoint: 1100,
|
||||
children: [
|
||||
_DateField(
|
||||
label: 'PO Date *',
|
||||
value: _poDate,
|
||||
onTap: () => _pickDate(
|
||||
current: _poDate,
|
||||
onPicked: (d) => setState(() => _poDate = d),
|
||||
),
|
||||
),
|
||||
AppSearchableDropdown<int>(
|
||||
label: 'Vendor *',
|
||||
value: _dropdownValue(_vendorId, vendorIds),
|
||||
hint: 'Select vendor',
|
||||
searchHint: 'Search vendor...',
|
||||
options: _intOptions(lookups.vendors),
|
||||
onChanged: (v) => setState(() => _vendorId = v),
|
||||
validator: (v) =>
|
||||
v == null ? 'Vendor is required' : null,
|
||||
),
|
||||
AppSearchableDropdown<int>(
|
||||
label: 'Billing *',
|
||||
value: _dropdownValue(_billingId, locationIds),
|
||||
hint: 'Select billing location',
|
||||
searchHint: 'Search plant or warehouse...',
|
||||
options: _intOptions(lookups.locations),
|
||||
onChanged: (v) =>
|
||||
setState(() => _billingId = v),
|
||||
validator: (v) =>
|
||||
v == null ? 'Billing is required' : null,
|
||||
),
|
||||
AppSearchableDropdown<int>(
|
||||
label: 'Shipping *',
|
||||
value: _dropdownValue(_shippingId, locationIds),
|
||||
hint: 'Select shipping location',
|
||||
searchHint: 'Search plant or warehouse...',
|
||||
options: _intOptions(lookups.locations),
|
||||
onChanged: (v) =>
|
||||
setState(() => _shippingId = v),
|
||||
validator: (v) =>
|
||||
v == null ? 'Shipping is required' : null,
|
||||
),
|
||||
MasterQuickAddDropdown<int?>(
|
||||
masterId: 'payment_terms',
|
||||
label: 'Payment Term',
|
||||
value: _paymentTermId,
|
||||
hint: 'Select payment term',
|
||||
searchHint: 'Search payment term...',
|
||||
options:
|
||||
_nullableIntOptions(lookups.paymentTerms),
|
||||
refreshLookups: () =>
|
||||
ref.invalidate(purchaseOrderLookupsProvider),
|
||||
parseCreatedId: int.tryParse,
|
||||
onChanged: (v) =>
|
||||
setState(() => _paymentTermId = v),
|
||||
),
|
||||
MasterQuickAddDropdown<int?>(
|
||||
masterId: 'delivery_terms',
|
||||
label: 'Delivery Term',
|
||||
value: _deliveryTermId,
|
||||
hint: 'Select delivery term',
|
||||
searchHint: 'Search delivery term...',
|
||||
options:
|
||||
_nullableIntOptions(lookups.deliveryTerms),
|
||||
refreshLookups: () =>
|
||||
ref.invalidate(purchaseOrderLookupsProvider),
|
||||
parseCreatedId: int.tryParse,
|
||||
onChanged: (v) =>
|
||||
setState(() => _deliveryTermId = v),
|
||||
),
|
||||
_DateField(
|
||||
label: 'Expected Delivery',
|
||||
value: _expectedDeliveryDate,
|
||||
onTap: () => _pickDate(
|
||||
current: _expectedDeliveryDate,
|
||||
onPicked: (d) => setState(
|
||||
() => _expectedDeliveryDate = d,
|
||||
),
|
||||
),
|
||||
AppSearchableDropdown<int>(
|
||||
label: 'Vendor *',
|
||||
value: _dropdownValue(_vendorId, vendorIds),
|
||||
hint: 'Select vendor',
|
||||
searchHint: 'Search vendor...',
|
||||
options: _intOptions(lookups.vendors),
|
||||
onChanged: (v) => setState(() => _vendorId = v),
|
||||
validator: (v) =>
|
||||
v == null ? 'Vendor is required' : null,
|
||||
),
|
||||
MasterQuickAddDropdown<int>(
|
||||
masterId: 'plants',
|
||||
label: 'Plant *',
|
||||
value: _dropdownValue(_plantId, plantIds),
|
||||
hint: 'Select plant',
|
||||
searchHint: 'Search plant...',
|
||||
options: _intOptions(lookups.plants),
|
||||
refreshLookups: () =>
|
||||
ref.invalidate(purchaseOrderLookupsProvider),
|
||||
parseCreatedId: int.tryParse,
|
||||
onChanged: (v) => setState(() => _plantId = v),
|
||||
validator: (v) =>
|
||||
v == null ? 'Plant is required' : null,
|
||||
),
|
||||
MasterQuickAddDropdown<int?>(
|
||||
masterId: 'warehouses',
|
||||
label: 'Warehouse',
|
||||
value: _warehouseId,
|
||||
hint: 'Select warehouse',
|
||||
searchHint: 'Search warehouse...',
|
||||
options: _nullableIntOptions(lookups.warehouses),
|
||||
refreshLookups: () =>
|
||||
ref.invalidate(purchaseOrderLookupsProvider),
|
||||
parseCreatedId: int.tryParse,
|
||||
onChanged: (v) =>
|
||||
setState(() => _warehouseId = v),
|
||||
),
|
||||
],
|
||||
),
|
||||
FormRowFour(
|
||||
children: [
|
||||
MasterQuickAddDropdown<int?>(
|
||||
masterId: 'payment_terms',
|
||||
label: 'Payment Term',
|
||||
value: _paymentTermId,
|
||||
hint: 'Select payment term',
|
||||
searchHint: 'Search payment term...',
|
||||
options:
|
||||
_nullableIntOptions(lookups.paymentTerms),
|
||||
refreshLookups: () =>
|
||||
ref.invalidate(purchaseOrderLookupsProvider),
|
||||
parseCreatedId: int.tryParse,
|
||||
onChanged: (v) =>
|
||||
setState(() => _paymentTermId = v),
|
||||
),
|
||||
MasterQuickAddDropdown<int?>(
|
||||
masterId: 'delivery_terms',
|
||||
label: 'Delivery Term',
|
||||
value: _deliveryTermId,
|
||||
hint: 'Select delivery term',
|
||||
searchHint: 'Search delivery term...',
|
||||
options:
|
||||
_nullableIntOptions(lookups.deliveryTerms),
|
||||
refreshLookups: () =>
|
||||
ref.invalidate(purchaseOrderLookupsProvider),
|
||||
parseCreatedId: int.tryParse,
|
||||
onChanged: (v) =>
|
||||
setState(() => _deliveryTermId = v),
|
||||
),
|
||||
],
|
||||
),
|
||||
FormRow(
|
||||
columnCount: 4,
|
||||
children: [
|
||||
_DateField(
|
||||
label: 'Expected Delivery',
|
||||
value: _expectedDeliveryDate,
|
||||
onTap: () => _pickDate(
|
||||
current: _expectedDeliveryDate,
|
||||
onPicked: (d) => setState(
|
||||
() => _expectedDeliveryDate = d,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
@ -518,6 +525,7 @@ class _PurchaseOrderFormScreenState extends ConsumerState<PurchaseOrderFormScree
|
||||
otherChargesController: _otherChargesController,
|
||||
discountController: _discountController,
|
||||
isEditing: widget.isEditing,
|
||||
isInterState: _isInterStateGst(lookups),
|
||||
);
|
||||
|
||||
if (stack) {
|
||||
@ -764,6 +772,7 @@ class _AmountSummaryCard extends StatelessWidget {
|
||||
required this.otherChargesController,
|
||||
required this.discountController,
|
||||
required this.isEditing,
|
||||
required this.isInterState,
|
||||
});
|
||||
|
||||
final PoOrderTotals totals;
|
||||
@ -771,6 +780,7 @@ class _AmountSummaryCard extends StatelessWidget {
|
||||
final TextEditingController otherChargesController;
|
||||
final TextEditingController discountController;
|
||||
final bool isEditing;
|
||||
final bool isInterState;
|
||||
|
||||
String? _validateDiscount(String? value) {
|
||||
final text = value?.trim() ?? '';
|
||||
@ -784,6 +794,34 @@ class _AmountSummaryCard extends StatelessWidget {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Widget> _gstSplitRows() {
|
||||
final tax = totals.taxAmount;
|
||||
if (isInterState) {
|
||||
return [
|
||||
_SummaryReadOnlyRow(
|
||||
label: 'IGST',
|
||||
value: CurrencyFormatter.format(tax),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
];
|
||||
}
|
||||
|
||||
final cgst = double.parse((tax / 2).toStringAsFixed(4));
|
||||
final sgst = double.parse((tax - cgst).toStringAsFixed(4));
|
||||
return [
|
||||
_SummaryReadOnlyRow(
|
||||
label: 'CGST',
|
||||
value: CurrencyFormatter.format(cgst),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
_SummaryReadOnlyRow(
|
||||
label: 'SGST',
|
||||
value: CurrencyFormatter.format(sgst),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
@ -800,13 +838,8 @@ class _AmountSummaryCard extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_SummaryReadOnlyRow(
|
||||
label: 'Taxable Amount',
|
||||
value: CurrencyFormatter.format(totals.taxableAmount),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
_SummaryReadOnlyRow(
|
||||
label: 'Tax (GST)',
|
||||
value: CurrencyFormatter.format(totals.taxAmount),
|
||||
label: 'Sub Total',
|
||||
value: CurrencyFormatter.format(totals.subTotal),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
_SummaryInputRow(
|
||||
@ -826,6 +859,17 @@ class _AmountSummaryCard extends StatelessWidget {
|
||||
validator: _validateDiscount,
|
||||
autovalidateMode: AutovalidateMode.always,
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
_SummaryReadOnlyRow(
|
||||
label: 'Taxable Amount',
|
||||
value: CurrencyFormatter.format(totals.taxableAmount),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
..._gstSplitRows(),
|
||||
_SummaryReadOnlyRow(
|
||||
label: 'Tax Total',
|
||||
value: CurrencyFormatter.format(totals.taxAmount),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||||
@ -856,7 +900,9 @@ class _AmountSummaryCard extends StatelessWidget {
|
||||
Text(
|
||||
isEditing
|
||||
? 'Editing charges or discount recalculates the grand total immediately — matches what will print on the PDF.'
|
||||
: 'Taxable amount and tax are calculated automatically from line items. Grand total updates as you edit freight, other charges or discount.',
|
||||
: isInterState
|
||||
? 'IGST is previewed (vendor state differs from billing location); final values are computed on save.'
|
||||
: 'CGST/SGST are previewed (vendor state matches billing location); final values are computed on save.',
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
height: 1.4,
|
||||
|
||||
@ -57,49 +57,60 @@ class PoLineCalculation {
|
||||
/// Order-level totals derived from line calculations + header charges.
|
||||
class PoOrderTotals {
|
||||
const PoOrderTotals({
|
||||
required this.subTotal,
|
||||
required this.taxableAmount,
|
||||
required this.taxAmount,
|
||||
required this.grandTotal,
|
||||
required this.maxDiscountAmount,
|
||||
});
|
||||
|
||||
/// Sum of line amounts (before freight / other / discount).
|
||||
final double subTotal;
|
||||
|
||||
/// Sub Total + Freight + Other − Discount.
|
||||
final double taxableAmount;
|
||||
final double taxAmount;
|
||||
final double grandTotal;
|
||||
|
||||
/// Taxable + Tax + Freight + Other (discount cannot exceed this).
|
||||
/// Sub Total + Tax + Freight + Other (discount cannot exceed this).
|
||||
final double maxDiscountAmount;
|
||||
|
||||
static const zero = PoOrderTotals(
|
||||
subTotal: 0,
|
||||
taxableAmount: 0,
|
||||
taxAmount: 0,
|
||||
grandTotal: 0,
|
||||
maxDiscountAmount: 0,
|
||||
);
|
||||
|
||||
/// 3.5 Taxable = sum of Line Amounts
|
||||
/// 3.6 Tax = sum of GST Amounts
|
||||
/// 3.7 Grand Total = Taxable + Tax + Freight + Other − Discount
|
||||
/// Sub Total = sum of line amounts
|
||||
/// Taxable = Sub Total + Freight + Other − Discount
|
||||
/// Tax = sum of line GST amounts
|
||||
/// Grand Total = Taxable + Tax
|
||||
factory PoOrderTotals.compute({
|
||||
required Iterable<PoLineCalculation> lines,
|
||||
required double freight,
|
||||
required double otherCharges,
|
||||
required double discountAmount,
|
||||
}) {
|
||||
var taxable = 0.0;
|
||||
var subTotal = 0.0;
|
||||
var tax = 0.0;
|
||||
for (final line in lines) {
|
||||
taxable += line.lineAmount;
|
||||
subTotal += line.lineAmount;
|
||||
tax += line.gstAmount;
|
||||
}
|
||||
final maxDiscount = taxable + tax + freight + otherCharges;
|
||||
final clampedDiscount =
|
||||
discountAmount < 0 ? 0.0 : discountAmount;
|
||||
final grandTotalRaw = maxDiscount - clampedDiscount;
|
||||
final freightSafe = freight < 0 ? 0.0 : freight;
|
||||
final otherSafe = otherCharges < 0 ? 0.0 : otherCharges;
|
||||
final clampedDiscount = discountAmount < 0 ? 0.0 : discountAmount;
|
||||
final taxableRaw = subTotal + freightSafe + otherSafe - clampedDiscount;
|
||||
final taxable = taxableRaw < 0 ? 0.0 : taxableRaw;
|
||||
final maxDiscount = subTotal + tax + freightSafe + otherSafe;
|
||||
final grandTotal = taxable + tax;
|
||||
return PoOrderTotals(
|
||||
subTotal: subTotal,
|
||||
taxableAmount: taxable,
|
||||
taxAmount: tax,
|
||||
grandTotal: grandTotalRaw < 0 ? 0 : grandTotalRaw,
|
||||
grandTotal: grandTotal < 0 ? 0 : grandTotal,
|
||||
maxDiscountAmount: maxDiscount < 0 ? 0 : maxDiscount,
|
||||
);
|
||||
}
|
||||
@ -521,120 +532,92 @@ class _LineItemCardState extends ConsumerState<_LineItemCard> {
|
||||
].whereType<AppDropdownOption<int?>>().toList();
|
||||
|
||||
const spacing = 8.0;
|
||||
const minRowWidth = 1040.0;
|
||||
|
||||
Widget flex({required int flex, required Widget child}) {
|
||||
return Expanded(flex: flex, child: child);
|
||||
}
|
||||
|
||||
final fields = <Widget>[
|
||||
flex(
|
||||
flex: 3,
|
||||
child: MasterQuickAddDropdown<int>(
|
||||
key: ValueKey('$lineKey-item'),
|
||||
masterId: 'items',
|
||||
label: 'Item *',
|
||||
value: line.itemId,
|
||||
hint: 'Select item',
|
||||
searchHint: 'Search item name or code...',
|
||||
options: itemOptions,
|
||||
refreshLookups: () async {
|
||||
ref.invalidate(purchaseOrderLookupsProvider);
|
||||
await ref.read(purchaseOrderLookupsProvider.future);
|
||||
},
|
||||
parseCreatedId: int.tryParse,
|
||||
onChanged: _onItemChanged,
|
||||
validator: (v) => v == null ? 'Item is required' : null,
|
||||
),
|
||||
),
|
||||
flex(
|
||||
flex: 1,
|
||||
child: AppTextField(
|
||||
key: ValueKey('$lineKey-qty'),
|
||||
controller: line.qtyController,
|
||||
label: 'Qty *',
|
||||
hint: '0',
|
||||
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
||||
validator: (v) {
|
||||
if (v == null || v.trim().isEmpty) return 'Required';
|
||||
final qty = double.tryParse(v);
|
||||
if (qty == null || qty <= 0) return 'Invalid';
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
flex(
|
||||
flex: 2,
|
||||
child: MasterQuickAddDropdown<int>(
|
||||
key: ValueKey('$lineKey-uom'),
|
||||
masterId: 'uom',
|
||||
label: 'UOM *',
|
||||
value: line.uomId,
|
||||
hint: 'Select UOM',
|
||||
searchHint: 'Search UOM...',
|
||||
options: uomOptions,
|
||||
refreshLookups: () async {
|
||||
ref.invalidate(purchaseOrderLookupsProvider);
|
||||
await ref.read(purchaseOrderLookupsProvider.future);
|
||||
},
|
||||
parseCreatedId: int.tryParse,
|
||||
onChanged: (v) => _updateLine(() => line.uomId = v),
|
||||
validator: (v) => v == null ? 'Required' : null,
|
||||
),
|
||||
),
|
||||
flex(
|
||||
flex: 1,
|
||||
child: AppTextField(
|
||||
key: ValueKey('$lineKey-rate'),
|
||||
controller: line.rateController,
|
||||
label: 'Rate *',
|
||||
hint: '0.00',
|
||||
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
||||
validator: (v) {
|
||||
if (v == null || v.trim().isEmpty) return 'Required';
|
||||
final rate = double.tryParse(v);
|
||||
if (rate == null || rate < 0) return 'Invalid';
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
flex(
|
||||
flex: 1,
|
||||
child: AppTextField(
|
||||
key: ValueKey('$lineKey-discount'),
|
||||
controller: line.discountController,
|
||||
label: 'Disc %',
|
||||
hint: '0',
|
||||
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
||||
),
|
||||
),
|
||||
flex(
|
||||
flex: 2,
|
||||
child: MasterQuickAddDropdown<int?>(
|
||||
key: ValueKey('$lineKey-gst'),
|
||||
masterId: 'gst_rates',
|
||||
label: 'GST %',
|
||||
value: line.gstRateId,
|
||||
hint: 'Select',
|
||||
searchHint: 'Search GST %...',
|
||||
options: gstOptions,
|
||||
refreshLookups: () async {
|
||||
ref.invalidate(purchaseOrderLookupsProvider);
|
||||
await ref.read(purchaseOrderLookupsProvider.future);
|
||||
},
|
||||
parseCreatedId: int.tryParse,
|
||||
onChanged: (v) => _updateLine(() => line.gstRateId = v),
|
||||
),
|
||||
),
|
||||
flex(
|
||||
flex: 2,
|
||||
child: _AmountWithRemove(
|
||||
amount: CurrencyFormatter.format(calc.lineAmount),
|
||||
backgroundColor: amountBg,
|
||||
onRemove: widget.onRemove,
|
||||
),
|
||||
),
|
||||
];
|
||||
final itemField = MasterQuickAddDropdown<int>(
|
||||
key: ValueKey('$lineKey-item'),
|
||||
masterId: 'items',
|
||||
label: 'Item *',
|
||||
value: line.itemId,
|
||||
hint: 'Select item',
|
||||
searchHint: 'Search item name or code...',
|
||||
options: itemOptions,
|
||||
refreshLookups: () async {
|
||||
ref.invalidate(purchaseOrderLookupsProvider);
|
||||
await ref.read(purchaseOrderLookupsProvider.future);
|
||||
},
|
||||
parseCreatedId: int.tryParse,
|
||||
onChanged: _onItemChanged,
|
||||
validator: (v) => v == null ? 'Item is required' : null,
|
||||
);
|
||||
final qtyField = AppTextField(
|
||||
key: ValueKey('$lineKey-qty'),
|
||||
controller: line.qtyController,
|
||||
label: 'Qty *',
|
||||
hint: '0',
|
||||
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
||||
validator: (v) {
|
||||
if (v == null || v.trim().isEmpty) return 'Required';
|
||||
final qty = double.tryParse(v);
|
||||
if (qty == null || qty <= 0) return 'Invalid';
|
||||
return null;
|
||||
},
|
||||
);
|
||||
final uomField = MasterQuickAddDropdown<int>(
|
||||
key: ValueKey('$lineKey-uom'),
|
||||
masterId: 'uom',
|
||||
label: 'UOM *',
|
||||
value: line.uomId,
|
||||
hint: 'Select UOM',
|
||||
searchHint: 'Search UOM...',
|
||||
options: uomOptions,
|
||||
refreshLookups: () async {
|
||||
ref.invalidate(purchaseOrderLookupsProvider);
|
||||
await ref.read(purchaseOrderLookupsProvider.future);
|
||||
},
|
||||
parseCreatedId: int.tryParse,
|
||||
onChanged: (v) => _updateLine(() => line.uomId = v),
|
||||
validator: (v) => v == null ? 'Required' : null,
|
||||
);
|
||||
final rateField = AppTextField(
|
||||
key: ValueKey('$lineKey-rate'),
|
||||
controller: line.rateController,
|
||||
label: 'Rate *',
|
||||
hint: '0.00',
|
||||
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
||||
validator: (v) {
|
||||
if (v == null || v.trim().isEmpty) return 'Required';
|
||||
final rate = double.tryParse(v);
|
||||
if (rate == null || rate < 0) return 'Invalid';
|
||||
return null;
|
||||
},
|
||||
);
|
||||
final discField = AppTextField(
|
||||
key: ValueKey('$lineKey-discount'),
|
||||
controller: line.discountController,
|
||||
label: 'Disc %',
|
||||
hint: '0',
|
||||
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
||||
);
|
||||
final gstField = MasterQuickAddDropdown<int?>(
|
||||
key: ValueKey('$lineKey-gst'),
|
||||
masterId: 'gst_rates',
|
||||
label: 'GST %',
|
||||
value: line.gstRateId,
|
||||
hint: 'Select',
|
||||
searchHint: 'Search GST %...',
|
||||
options: gstOptions,
|
||||
refreshLookups: () async {
|
||||
ref.invalidate(purchaseOrderLookupsProvider);
|
||||
await ref.read(purchaseOrderLookupsProvider.future);
|
||||
},
|
||||
parseCreatedId: int.tryParse,
|
||||
onChanged: (v) => _updateLine(() => line.gstRateId = v),
|
||||
);
|
||||
final amountField = _AmountWithRemove(
|
||||
amount: CurrencyFormatter.format(calc.lineAmount),
|
||||
backgroundColor: amountBg,
|
||||
onRemove: widget.onRemove,
|
||||
);
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
@ -645,29 +628,48 @@ class _LineItemCardState extends ConsumerState<_LineItemCard> {
|
||||
child: QuickAddInlineHost(
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final rowWidth = constraints.maxWidth < minRowWidth
|
||||
? minRowWidth
|
||||
: constraints.maxWidth;
|
||||
final row = SizedBox(
|
||||
width: rowWidth,
|
||||
child: Row(
|
||||
final width = constraints.maxWidth;
|
||||
|
||||
// Wide: single flex row
|
||||
if (width >= 1100) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
for (var i = 0; i < fields.length; i++) ...[
|
||||
if (i > 0) const SizedBox(width: spacing),
|
||||
fields[i],
|
||||
],
|
||||
Expanded(flex: 3, child: itemField),
|
||||
const SizedBox(width: spacing),
|
||||
Expanded(flex: 1, child: qtyField),
|
||||
const SizedBox(width: spacing),
|
||||
Expanded(flex: 2, child: uomField),
|
||||
const SizedBox(width: spacing),
|
||||
Expanded(flex: 1, child: rateField),
|
||||
const SizedBox(width: spacing),
|
||||
Expanded(flex: 1, child: discField),
|
||||
const SizedBox(width: spacing),
|
||||
Expanded(flex: 2, child: gstField),
|
||||
const SizedBox(width: spacing),
|
||||
Expanded(flex: 2, child: amountField),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (constraints.maxWidth < minRowWidth) {
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: row,
|
||||
);
|
||||
}
|
||||
return row;
|
||||
|
||||
// Medium / narrow: wrapping grid (2–3 columns)
|
||||
return ResponsiveFormGrid(
|
||||
spacing: spacing,
|
||||
smallColumns: 1,
|
||||
mediumColumns: 2,
|
||||
largeColumns: 3,
|
||||
mediumBreakpoint: 520,
|
||||
largeBreakpoint: 800,
|
||||
children: [
|
||||
itemField,
|
||||
qtyField,
|
||||
uomField,
|
||||
rateField,
|
||||
discField,
|
||||
gstField,
|
||||
amountField,
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
@ -66,7 +66,11 @@ class AddUserFormNotifier extends FamilyAsyncNotifier<AddUserFormState, String?>
|
||||
if (rolesResult.failure != null) throw rolesResult.failure!;
|
||||
|
||||
final departments = await masterRemote.listDepartments();
|
||||
final plants = await masterRemote.listPlants();
|
||||
final plants = await masterRemote.listLocations().then(
|
||||
(locations) => locations
|
||||
.where((location) => location.slug?.toLowerCase() == 'plant')
|
||||
.toList(),
|
||||
);
|
||||
final designations = await masterRemote.listDesignations();
|
||||
|
||||
final usersResult = await userRepository.listUserOptions();
|
||||
|
||||
@ -187,6 +187,7 @@ class _AddUserPanelState extends ConsumerState<AddUserPanel> {
|
||||
String? hint,
|
||||
bool required = false,
|
||||
String? masterId,
|
||||
Map<String, dynamic>? initialValues,
|
||||
}) {
|
||||
final fieldLabel = required ? '$label *' : label;
|
||||
final fieldHint = hint ?? 'Select ${label.toLowerCase()}';
|
||||
@ -215,6 +216,7 @@ class _AddUserPanelState extends ConsumerState<AddUserPanel> {
|
||||
searchHint: 'Search $label...',
|
||||
enabled: fieldEnabled,
|
||||
options: _toOptions(options),
|
||||
initialValues: initialValues,
|
||||
refreshLookups: () =>
|
||||
ref.invalidate(addUserFormProvider(widget.userId)),
|
||||
parseCreatedId: (id) => id,
|
||||
@ -357,7 +359,8 @@ class _AddUserPanelState extends ConsumerState<AddUserPanel> {
|
||||
label: 'Plant / Unit',
|
||||
value: _selectedPlantId,
|
||||
options: formState.plants,
|
||||
masterId: 'plants',
|
||||
masterId: 'locations',
|
||||
initialValues: const {'type': 'plant'},
|
||||
onChanged: (v) => setState(() => _selectedPlantId = v),
|
||||
),
|
||||
right: _buildDropdown(
|
||||
|
||||
@ -106,8 +106,8 @@ class ReportsRemoteDataSource {
|
||||
if (query.itemSubcategoryId != null &&
|
||||
query.itemSubcategoryId!.isNotEmpty)
|
||||
'item_subcategory_id': query.itemSubcategoryId,
|
||||
if (query.plantId != null && query.plantId!.isNotEmpty)
|
||||
'plant_id': query.plantId,
|
||||
if (query.locationId != null && query.locationId!.isNotEmpty)
|
||||
'location_id': query.locationId,
|
||||
if (query.departmentId != null && query.departmentId!.isNotEmpty)
|
||||
'department_id': query.departmentId,
|
||||
if (query.isActive != null) 'is_active': query.isActive,
|
||||
|
||||
@ -22,12 +22,17 @@ class ReportFilterOption {
|
||||
.toString()
|
||||
.trim();
|
||||
final code = map['code']?.toString().trim();
|
||||
final display = (code != null &&
|
||||
final type = map['type']?.toString().trim();
|
||||
var display = (code != null &&
|
||||
code.isNotEmpty &&
|
||||
label.isNotEmpty &&
|
||||
code != label)
|
||||
? '$label ($code)'
|
||||
: (label.isEmpty ? value : label);
|
||||
if (type != null && type.isNotEmpty) {
|
||||
final typeLabel = '${type[0].toUpperCase()}${type.substring(1)}';
|
||||
display = '$display ($typeLabel)';
|
||||
}
|
||||
final parentId = (map['item_category_id'] ?? map['parent_id'])
|
||||
?.toString()
|
||||
.trim();
|
||||
@ -45,7 +50,7 @@ class ReportFilterOption {
|
||||
|
||||
class DepreciationReportFilters {
|
||||
const DepreciationReportFilters({
|
||||
this.plants = const [],
|
||||
this.locations = const [],
|
||||
this.categories = const [],
|
||||
this.subcategories = const [],
|
||||
this.departments = const [],
|
||||
@ -53,7 +58,7 @@ class DepreciationReportFilters {
|
||||
this.depreciationMethods = const [],
|
||||
});
|
||||
|
||||
final List<ReportFilterOption> plants;
|
||||
final List<ReportFilterOption> locations;
|
||||
final List<ReportFilterOption> categories;
|
||||
final List<ReportFilterOption> subcategories;
|
||||
final List<ReportFilterOption> departments;
|
||||
@ -79,7 +84,7 @@ class DepreciationReportFilters {
|
||||
}
|
||||
|
||||
return DepreciationReportFilters(
|
||||
plants: parse(['plants', 'plant_options', 'plant']),
|
||||
locations: parse(['locations', 'location_options', 'location']),
|
||||
categories: parse([
|
||||
'item_categories',
|
||||
'categories',
|
||||
@ -155,7 +160,7 @@ class DepreciationReportRow {
|
||||
this.assetName,
|
||||
this.categoryName,
|
||||
this.subcategoryName,
|
||||
this.plantName,
|
||||
this.locationName,
|
||||
this.departmentName,
|
||||
this.status,
|
||||
this.purchaseDate,
|
||||
@ -175,7 +180,7 @@ class DepreciationReportRow {
|
||||
final String? assetName;
|
||||
final String? categoryName;
|
||||
final String? subcategoryName;
|
||||
final String? plantName;
|
||||
final String? locationName;
|
||||
final String? departmentName;
|
||||
final String? status;
|
||||
final DateTime? purchaseDate;
|
||||
@ -262,8 +267,9 @@ class DepreciationReportRow {
|
||||
]) ??
|
||||
readNestedName('item_subcategory') ??
|
||||
readNestedName('subcategory'),
|
||||
plantName:
|
||||
readString(['plant_name', 'plantName']) ?? readNestedName('plant'),
|
||||
locationName:
|
||||
readString(['location_name', 'locationName']) ??
|
||||
readNestedName('location'),
|
||||
departmentName: readString(['department_name', 'departmentName']) ??
|
||||
readNestedName('department'),
|
||||
status: readString(['status']),
|
||||
@ -315,7 +321,7 @@ class DepreciationReportQuery {
|
||||
this.page = 1,
|
||||
this.limit = 20,
|
||||
this.search,
|
||||
this.plantId,
|
||||
this.locationId,
|
||||
this.itemCategoryId,
|
||||
this.itemSubcategoryId,
|
||||
this.departmentId,
|
||||
@ -330,7 +336,7 @@ class DepreciationReportQuery {
|
||||
final int page;
|
||||
final int limit;
|
||||
final String? search;
|
||||
final String? plantId;
|
||||
final String? locationId;
|
||||
final String? itemCategoryId;
|
||||
final String? itemSubcategoryId;
|
||||
final String? departmentId;
|
||||
@ -343,7 +349,7 @@ class DepreciationReportQuery {
|
||||
|
||||
bool get hasActiveFilter =>
|
||||
(search?.isNotEmpty ?? false) ||
|
||||
(plantId?.isNotEmpty ?? false) ||
|
||||
(locationId?.isNotEmpty ?? false) ||
|
||||
(itemCategoryId?.isNotEmpty ?? false) ||
|
||||
(itemSubcategoryId?.isNotEmpty ?? false) ||
|
||||
(departmentId?.isNotEmpty ?? false) ||
|
||||
@ -358,7 +364,7 @@ class DepreciationReportQuery {
|
||||
int? page,
|
||||
int? limit,
|
||||
String? search,
|
||||
String? plantId,
|
||||
String? locationId,
|
||||
String? itemCategoryId,
|
||||
String? itemSubcategoryId,
|
||||
String? departmentId,
|
||||
@ -369,7 +375,7 @@ class DepreciationReportQuery {
|
||||
DateTime? purchaseDateFrom,
|
||||
DateTime? purchaseDateTo,
|
||||
bool clearSearch = false,
|
||||
bool clearPlantId = false,
|
||||
bool clearLocationId = false,
|
||||
bool clearItemCategoryId = false,
|
||||
bool clearItemSubcategoryId = false,
|
||||
bool clearDepartmentId = false,
|
||||
@ -384,7 +390,7 @@ class DepreciationReportQuery {
|
||||
page: page ?? this.page,
|
||||
limit: limit ?? this.limit,
|
||||
search: clearSearch ? null : search ?? this.search,
|
||||
plantId: clearPlantId ? null : plantId ?? this.plantId,
|
||||
locationId: clearLocationId ? null : locationId ?? this.locationId,
|
||||
itemCategoryId: clearItemCategoryId
|
||||
? null
|
||||
: itemCategoryId ?? this.itemCategoryId,
|
||||
|
||||
@ -145,14 +145,14 @@ class DepreciationReportNotifier
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> setPlantId(String? value) async {
|
||||
Future<void> setLocationId(String? value) async {
|
||||
final current = state.valueOrNull?.query ??
|
||||
const DepreciationReportQuery(limit: AppConstants.defaultPageSize);
|
||||
await applyQuery(
|
||||
current.copyWith(
|
||||
page: 1,
|
||||
plantId: value,
|
||||
clearPlantId: value == null || value.isEmpty,
|
||||
locationId: value,
|
||||
clearLocationId: value == null || value.isEmpty,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -174,7 +174,7 @@ class _DepreciationReportScreenState
|
||||
filters: state.filters,
|
||||
query: state.query,
|
||||
onSearch: notifier.setSearch,
|
||||
onPlantChanged: notifier.setPlantId,
|
||||
onLocationChanged: notifier.setLocationId,
|
||||
onCategoryChanged: notifier.setItemCategoryId,
|
||||
onSubcategoryChanged: notifier.setItemSubcategoryId,
|
||||
onDepartmentChanged: notifier.setDepartmentId,
|
||||
@ -324,7 +324,7 @@ class _FiltersBar extends StatefulWidget {
|
||||
required this.filters,
|
||||
required this.query,
|
||||
required this.onSearch,
|
||||
required this.onPlantChanged,
|
||||
required this.onLocationChanged,
|
||||
required this.onCategoryChanged,
|
||||
required this.onSubcategoryChanged,
|
||||
required this.onDepartmentChanged,
|
||||
@ -342,7 +342,7 @@ class _FiltersBar extends StatefulWidget {
|
||||
final DepreciationReportFilters filters;
|
||||
final DepreciationReportQuery query;
|
||||
final ValueChanged<String> onSearch;
|
||||
final ValueChanged<String?> onPlantChanged;
|
||||
final ValueChanged<String?> onLocationChanged;
|
||||
final ValueChanged<String?> onCategoryChanged;
|
||||
final ValueChanged<String?> onSubcategoryChanged;
|
||||
final ValueChanged<String?> onDepartmentChanged;
|
||||
@ -425,11 +425,11 @@ class _FiltersBarState extends State<_FiltersBar> {
|
||||
);
|
||||
}
|
||||
|
||||
final plant = dropdown(
|
||||
label: 'Plant',
|
||||
value: query.plantId,
|
||||
options: filters.plants,
|
||||
onChanged: widget.onPlantChanged,
|
||||
final location = dropdown(
|
||||
label: 'Location',
|
||||
value: query.locationId,
|
||||
options: filters.locations,
|
||||
onChanged: widget.onLocationChanged,
|
||||
);
|
||||
final category = dropdown(
|
||||
label: 'Category',
|
||||
@ -549,7 +549,7 @@ class _FiltersBarState extends State<_FiltersBar> {
|
||||
: null;
|
||||
|
||||
return AppResponsiveFilterGrid(
|
||||
fields: [searchField, plant, category, asOfField],
|
||||
fields: [searchField, location, category, asOfField],
|
||||
footer: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: actions,
|
||||
@ -589,10 +589,10 @@ class _ReportTable extends StatelessWidget {
|
||||
cellBuilder: (_, row) => AppTableCell.text(row.categoryName),
|
||||
),
|
||||
AppDataColumn(
|
||||
label: 'Plant',
|
||||
label: 'Location',
|
||||
flex: 2,
|
||||
searchText: (row) => row.plantName ?? '',
|
||||
cellBuilder: (_, row) => AppTableCell.text(row.plantName),
|
||||
searchText: (row) => row.locationName ?? '',
|
||||
cellBuilder: (_, row) => AppTableCell.text(row.locationName),
|
||||
),
|
||||
AppDataColumn(
|
||||
label: 'Purchase Date',
|
||||
|
||||
@ -45,10 +45,21 @@ Object? _readItemCategoryName(Map<dynamic, dynamic> json, String key) {
|
||||
_readNestedName(json, 'asset_category');
|
||||
}
|
||||
|
||||
Object? _readPlantName(Map<dynamic, dynamic> json, String key) {
|
||||
final flat = json['plant_name'];
|
||||
Object? _readLocationName(Map<dynamic, dynamic> json, String key) {
|
||||
final flat = json['location_name'];
|
||||
if (flat is String && flat.isNotEmpty) return flat;
|
||||
return _readNestedName(json, 'plant');
|
||||
final nested = json['location'];
|
||||
if (nested is Map) {
|
||||
final name = nested['name']?.toString().trim();
|
||||
if (name == null || name.isEmpty) return null;
|
||||
final type = nested['type']?.toString().trim();
|
||||
if (type != null && type.isNotEmpty) {
|
||||
final typeLabel = type[0].toUpperCase() + type.substring(1);
|
||||
return '$name ($typeLabel)';
|
||||
}
|
||||
return name;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Object? _readItemCategoryId(Map<dynamic, dynamic> json, String key) {
|
||||
@ -61,10 +72,10 @@ Object? _readItemCategoryId(Map<dynamic, dynamic> json, String key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object? _readPlantId(Map<dynamic, dynamic> json, String key) {
|
||||
final flat = json['plant_id'];
|
||||
Object? _readLocationId(Map<dynamic, dynamic> json, String key) {
|
||||
final flat = json['location_id'];
|
||||
if (flat != null) return flat;
|
||||
final nested = json['plant'];
|
||||
final nested = json['location'];
|
||||
if (nested is Map) return nested['id'];
|
||||
return null;
|
||||
}
|
||||
@ -94,12 +105,6 @@ Object? _readDepartmentName(Map<dynamic, dynamic> json, String key) {
|
||||
return _readNestedName(json, 'department');
|
||||
}
|
||||
|
||||
Object? _readWarehouseName(Map<dynamic, dynamic> json, String key) {
|
||||
final flat = json['warehouse_name'];
|
||||
if (flat is String && flat.isNotEmpty) return flat;
|
||||
return _readNestedName(json, 'warehouse');
|
||||
}
|
||||
|
||||
Object? _readVendorNameFromNested(Map<dynamic, dynamic> json, String key) {
|
||||
final flat = json['vendor_name'];
|
||||
if (flat is String && flat.isNotEmpty) return flat;
|
||||
@ -153,9 +158,14 @@ class AssetModel with _$AssetModel {
|
||||
int? assetSubcategoryId,
|
||||
@JsonKey(name: 'item_subcategory_name', readValue: _readItemSubcategoryName)
|
||||
String? assetSubcategoryName,
|
||||
@JsonKey(name: 'plant_id', readValue: _readPlantId, fromJson: _intFromJsonNullable)
|
||||
int? plantId,
|
||||
@JsonKey(name: 'plant_name', readValue: _readPlantName) String? plantName,
|
||||
@JsonKey(
|
||||
name: 'location_id',
|
||||
readValue: _readLocationId,
|
||||
fromJson: _intFromJsonNullable,
|
||||
)
|
||||
int? locationId,
|
||||
@JsonKey(name: 'location_name', readValue: _readLocationName)
|
||||
String? locationName,
|
||||
@JsonKey(name: 'brand_model') String? brandModel,
|
||||
String? manufacturer,
|
||||
@JsonKey(name: 'serial_number') String? serialNumber,
|
||||
@ -163,11 +173,21 @@ class AssetModel with _$AssetModel {
|
||||
@JsonKey(name: 'department_id', fromJson: _intFromJsonNullable) int? departmentId,
|
||||
@JsonKey(name: 'department_name', readValue: _readDepartmentName)
|
||||
String? departmentName,
|
||||
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable) int? warehouseId,
|
||||
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName) String? warehouseName,
|
||||
@JsonKey(name: 'location_detail') String? locationDetail,
|
||||
@JsonKey(name: 'assigned_to_user_id', fromJson: _intFromJsonNullable)
|
||||
int? assignedToUserId,
|
||||
@JsonKey(name: 'maintenance_incharge_user_id', fromJson: _intFromJsonNullable)
|
||||
int? maintenanceInchargeUserId,
|
||||
@JsonKey(name: 'maintenance_frequency_in_days', fromJson: _intFromJsonNullable)
|
||||
int? maintenanceFrequencyInDays,
|
||||
@JsonKey(
|
||||
name: 'maintenance_checklist_json',
|
||||
fromJson: _checklistFromJson,
|
||||
toJson: _checklistToJson,
|
||||
)
|
||||
List<AssetMaintenanceChecklistItem>? maintenanceChecklistJson,
|
||||
@JsonKey(name: 'commencement_date', fromJson: _dateFromJsonNullable)
|
||||
DateTime? commencementDate,
|
||||
@JsonKey(name: 'vendor_id', fromJson: _intFromJsonNullable) int? vendorId,
|
||||
@JsonKey(name: 'vendor_name', readValue: _readVendorNameFromNested) String? vendorName,
|
||||
@JsonKey(name: 'po_id', fromJson: _intFromJsonNullable) int? poId,
|
||||
@ -192,11 +212,161 @@ class AssetModel with _$AssetModel {
|
||||
@JsonKey(name: 'is_active') @Default(true) bool isActive,
|
||||
@JsonKey(name: 'created_at', fromJson: _dateFromJsonNullable) DateTime? createdAt,
|
||||
@JsonKey(name: 'updated_at', fromJson: _dateFromJsonNullable) DateTime? updatedAt,
|
||||
@JsonKey(
|
||||
name: 'maintenance',
|
||||
fromJson: AssetMaintenanceSummary.fromJsonNullable,
|
||||
toJson: AssetMaintenanceSummary.toJsonNullable,
|
||||
)
|
||||
AssetMaintenanceSummary? maintenance,
|
||||
}) = _AssetModel;
|
||||
|
||||
factory AssetModel.fromJson(Map<String, dynamic> json) => _$AssetModelFromJson(json);
|
||||
}
|
||||
|
||||
class AssetMaintenanceChecklistItem {
|
||||
const AssetMaintenanceChecklistItem({
|
||||
required this.key,
|
||||
required this.label,
|
||||
this.required = false,
|
||||
});
|
||||
|
||||
final String key;
|
||||
final String label;
|
||||
final bool required;
|
||||
|
||||
factory AssetMaintenanceChecklistItem.fromJson(Map<String, dynamic> json) {
|
||||
return AssetMaintenanceChecklistItem(
|
||||
key: json['key']?.toString() ?? '',
|
||||
label: json['label']?.toString() ?? '',
|
||||
required: json['required'] == true,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'key': key,
|
||||
'label': label,
|
||||
'required': required,
|
||||
};
|
||||
}
|
||||
|
||||
class AssetMaintenanceSummary {
|
||||
const AssetMaintenanceSummary({
|
||||
this.isDue = false,
|
||||
this.nextDueDate,
|
||||
this.lastMaintenanceDate,
|
||||
this.daysUntilDue,
|
||||
this.checklist = const [],
|
||||
});
|
||||
|
||||
final bool isDue;
|
||||
final DateTime? nextDueDate;
|
||||
final DateTime? lastMaintenanceDate;
|
||||
final int? daysUntilDue;
|
||||
final List<AssetMaintenanceChecklistItem> checklist;
|
||||
|
||||
static AssetMaintenanceSummary? fromJsonNullable(Object? value) {
|
||||
if (value is! Map) return null;
|
||||
return AssetMaintenanceSummary.fromJson(Map<String, dynamic>.from(value));
|
||||
}
|
||||
|
||||
static Object? toJsonNullable(AssetMaintenanceSummary? value) {
|
||||
if (value == null) return null;
|
||||
return value.toJson();
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'is_due': isDue,
|
||||
if (nextDueDate != null)
|
||||
'next_due_date': nextDueDate!.toIso8601String(),
|
||||
if (lastMaintenanceDate != null)
|
||||
'last_maintenance_date': lastMaintenanceDate!.toIso8601String(),
|
||||
if (daysUntilDue != null) 'days_until_due': daysUntilDue,
|
||||
'checklist': checklist.map((item) => item.toJson()).toList(),
|
||||
};
|
||||
|
||||
factory AssetMaintenanceSummary.fromJson(Map<String, dynamic> json) {
|
||||
DateTime? parseDate(dynamic value) {
|
||||
if (value == null) return null;
|
||||
if (value is DateTime) return value;
|
||||
return DateTime.tryParse(value.toString());
|
||||
}
|
||||
|
||||
final rawChecklist = json['checklist'] ?? json['checklist_json'];
|
||||
final checklist = <AssetMaintenanceChecklistItem>[];
|
||||
if (rawChecklist is List) {
|
||||
for (final item in rawChecklist) {
|
||||
if (item is Map) {
|
||||
checklist.add(
|
||||
AssetMaintenanceChecklistItem.fromJson(
|
||||
Map<String, dynamic>.from(item),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return AssetMaintenanceSummary(
|
||||
isDue: json['is_due'] == true,
|
||||
nextDueDate: parseDate(json['next_due_date']),
|
||||
lastMaintenanceDate: parseDate(json['last_maintenance_date']),
|
||||
daysUntilDue: _intFromJsonNullable(json['days_until_due']),
|
||||
checklist: checklist,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AssetMaintenanceLogModel {
|
||||
const AssetMaintenanceLogModel({
|
||||
required this.id,
|
||||
this.performedDate,
|
||||
this.nextDueDate,
|
||||
this.remarks,
|
||||
this.checklistJson = const [],
|
||||
this.createdAt,
|
||||
this.createdByName,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final DateTime? performedDate;
|
||||
final DateTime? nextDueDate;
|
||||
final String? remarks;
|
||||
final List<Map<String, dynamic>> checklistJson;
|
||||
final DateTime? createdAt;
|
||||
final String? createdByName;
|
||||
|
||||
factory AssetMaintenanceLogModel.fromJson(Map<String, dynamic> json) {
|
||||
DateTime? parseDate(dynamic value) {
|
||||
if (value == null) return null;
|
||||
if (value is DateTime) return value;
|
||||
return DateTime.tryParse(value.toString());
|
||||
}
|
||||
|
||||
final rawChecklist = json['checklist_json'] ?? json['checklist'];
|
||||
final checklist = <Map<String, dynamic>>[];
|
||||
if (rawChecklist is List) {
|
||||
for (final item in rawChecklist) {
|
||||
if (item is Map) {
|
||||
checklist.add(Map<String, dynamic>.from(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return AssetMaintenanceLogModel(
|
||||
id: _idFromJson(json['id']),
|
||||
performedDate: parseDate(json['performed_date']),
|
||||
nextDueDate: parseDate(json['next_due_date']),
|
||||
remarks: json['remarks']?.toString(),
|
||||
checklistJson: checklist,
|
||||
createdAt: parseDate(json['created_at']),
|
||||
createdByName: json['created_by_name']?.toString() ??
|
||||
(json['created_by'] is Map
|
||||
? (json['created_by']['name'] ?? json['created_by']['full_name'])
|
||||
?.toString()
|
||||
: null),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String assetConditionLabel(String? value) {
|
||||
if (value == null || value.trim().isEmpty) return '—';
|
||||
return value.replaceAll('_', ' ');
|
||||
@ -402,6 +572,35 @@ class InsurancePolicyModel with _$InsurancePolicyModel {
|
||||
_$InsurancePolicyModelFromJson(json);
|
||||
}
|
||||
|
||||
List<AssetMaintenanceChecklistItem>? _checklistFromJson(Object? value) {
|
||||
if (value is! List) return null;
|
||||
return value
|
||||
.whereType<Map>()
|
||||
.map(
|
||||
(item) => AssetMaintenanceChecklistItem.fromJson(
|
||||
Map<String, dynamic>.from(item),
|
||||
),
|
||||
)
|
||||
.where((item) => item.key.isNotEmpty)
|
||||
.toList();
|
||||
}
|
||||
|
||||
Object? _checklistToJson(List<AssetMaintenanceChecklistItem>? value) {
|
||||
if (value == null) return null;
|
||||
return value.map((item) => item.toJson()).toList();
|
||||
}
|
||||
|
||||
Object? _readAlertLocationName(Map<dynamic, dynamic> json, String key) {
|
||||
final flat = json['location_name'];
|
||||
if (flat is String && flat.isNotEmpty) return flat;
|
||||
final nested = json['location'];
|
||||
if (nested is Map) {
|
||||
final name = nested['name'];
|
||||
if (name is String && name.isNotEmpty) return name;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class AssetAlertModel with _$AssetAlertModel {
|
||||
const factory AssetAlertModel({
|
||||
@ -416,7 +615,8 @@ class AssetAlertModel with _$AssetAlertModel {
|
||||
@JsonKey(name: 'due_date', fromJson: _dateFromJsonNullable) DateTime? dueDate,
|
||||
@JsonKey(name: 'days_remaining', fromJson: _intFromJsonNullable) int? daysRemaining,
|
||||
String? status,
|
||||
@JsonKey(name: 'plant_name') String? plantName,
|
||||
@JsonKey(name: 'location_name', readValue: _readAlertLocationName)
|
||||
String? locationName,
|
||||
}) = _AssetAlertModel;
|
||||
|
||||
factory AssetAlertModel.fromJson(Map<String, dynamic> json) =>
|
||||
@ -428,12 +628,10 @@ class AssetTransferHistoryModel {
|
||||
required this.id,
|
||||
this.transferDate,
|
||||
this.reason,
|
||||
this.fromPlantName,
|
||||
this.toPlantName,
|
||||
this.fromLocationName,
|
||||
this.toLocationName,
|
||||
this.fromDepartmentName,
|
||||
this.toDepartmentName,
|
||||
this.fromWarehouseName,
|
||||
this.toWarehouseName,
|
||||
this.fromUserName,
|
||||
this.toUserName,
|
||||
this.createdAt,
|
||||
@ -443,12 +641,10 @@ class AssetTransferHistoryModel {
|
||||
final String id;
|
||||
final DateTime? transferDate;
|
||||
final String? reason;
|
||||
final String? fromPlantName;
|
||||
final String? toPlantName;
|
||||
final String? fromLocationName;
|
||||
final String? toLocationName;
|
||||
final String? fromDepartmentName;
|
||||
final String? toDepartmentName;
|
||||
final String? fromWarehouseName;
|
||||
final String? toWarehouseName;
|
||||
final String? fromUserName;
|
||||
final String? toUserName;
|
||||
final DateTime? createdAt;
|
||||
@ -466,7 +662,15 @@ class AssetTransferHistoryModel {
|
||||
if (nested is Map) {
|
||||
for (final field in ['name', 'full_name']) {
|
||||
final value = nested[field];
|
||||
if (value is String && value.trim().isNotEmpty) return value.trim();
|
||||
if (value is String && value.trim().isNotEmpty) {
|
||||
final name = value.trim();
|
||||
final type = nested['type']?.toString().trim();
|
||||
if (type != null && type.isNotEmpty) {
|
||||
final typeLabel = type[0].toUpperCase() + type.substring(1);
|
||||
return '$name ($typeLabel)';
|
||||
}
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@ -482,14 +686,13 @@ class AssetTransferHistoryModel {
|
||||
id: _idFromJson(json['id']),
|
||||
transferDate: parseDate(json['transfer_date']),
|
||||
reason: readName('reason'),
|
||||
fromPlantName: readName('from_plant_name') ?? readNestedName('from_plant'),
|
||||
toPlantName: readName('to_plant_name') ?? readNestedName('to_plant'),
|
||||
fromLocationName:
|
||||
readName('from_location_name') ?? readNestedName('from_location'),
|
||||
toLocationName:
|
||||
readName('to_location_name') ?? readNestedName('to_location'),
|
||||
fromDepartmentName:
|
||||
readName('from_department_name') ?? readNestedName('from_department'),
|
||||
toDepartmentName: readName('to_department_name') ?? readNestedName('to_department'),
|
||||
fromWarehouseName:
|
||||
readName('from_warehouse_name') ?? readNestedName('from_warehouse'),
|
||||
toWarehouseName: readName('to_warehouse_name') ?? readNestedName('to_warehouse'),
|
||||
fromUserName: readName('from_user_name') ?? readNestedName('from_user'),
|
||||
toUserName: readName('to_user_name') ?? readNestedName('to_user'),
|
||||
createdAt: parseDate(json['created_at']),
|
||||
@ -557,8 +760,10 @@ class AssetListQuery {
|
||||
this.condition,
|
||||
this.itemCategoryId,
|
||||
this.itemSubcategoryId,
|
||||
this.plantId,
|
||||
this.locationId,
|
||||
this.departmentId,
|
||||
this.maintenanceInchargeUserId,
|
||||
this.dueOnly,
|
||||
this.isActive,
|
||||
});
|
||||
|
||||
@ -569,8 +774,10 @@ class AssetListQuery {
|
||||
final String? condition;
|
||||
final int? itemCategoryId;
|
||||
final int? itemSubcategoryId;
|
||||
final int? plantId;
|
||||
final int? locationId;
|
||||
final int? departmentId;
|
||||
final int? maintenanceInchargeUserId;
|
||||
final bool? dueOnly;
|
||||
final bool? isActive;
|
||||
|
||||
AssetListQuery copyWith({
|
||||
@ -581,8 +788,10 @@ class AssetListQuery {
|
||||
Object? condition = _unset,
|
||||
Object? itemCategoryId = _unset,
|
||||
Object? itemSubcategoryId = _unset,
|
||||
Object? plantId = _unset,
|
||||
Object? locationId = _unset,
|
||||
Object? departmentId = _unset,
|
||||
Object? maintenanceInchargeUserId = _unset,
|
||||
Object? dueOnly = _unset,
|
||||
Object? isActive = _unset,
|
||||
}) {
|
||||
return AssetListQuery(
|
||||
@ -598,10 +807,15 @@ class AssetListQuery {
|
||||
itemSubcategoryId: identical(itemSubcategoryId, _unset)
|
||||
? this.itemSubcategoryId
|
||||
: itemSubcategoryId as int?,
|
||||
plantId: identical(plantId, _unset) ? this.plantId : plantId as int?,
|
||||
locationId:
|
||||
identical(locationId, _unset) ? this.locationId : locationId as int?,
|
||||
departmentId: identical(departmentId, _unset)
|
||||
? this.departmentId
|
||||
: departmentId as int?,
|
||||
maintenanceInchargeUserId: identical(maintenanceInchargeUserId, _unset)
|
||||
? this.maintenanceInchargeUserId
|
||||
: maintenanceInchargeUserId as int?,
|
||||
dueOnly: identical(dueOnly, _unset) ? this.dueOnly : dueOnly as bool?,
|
||||
isActive:
|
||||
identical(isActive, _unset) ? this.isActive : isActive as bool?,
|
||||
);
|
||||
|
||||
@ -438,13 +438,13 @@ mixin _$AssetModel {
|
||||
@JsonKey(name: 'item_subcategory_name', readValue: _readItemSubcategoryName)
|
||||
String? get assetSubcategoryName => throw _privateConstructorUsedError;
|
||||
@JsonKey(
|
||||
name: 'plant_id',
|
||||
readValue: _readPlantId,
|
||||
name: 'location_id',
|
||||
readValue: _readLocationId,
|
||||
fromJson: _intFromJsonNullable,
|
||||
)
|
||||
int? get plantId => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'plant_name', readValue: _readPlantName)
|
||||
String? get plantName => throw _privateConstructorUsedError;
|
||||
int? get locationId => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'location_name', readValue: _readLocationName)
|
||||
String? get locationName => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'brand_model')
|
||||
String? get brandModel => throw _privateConstructorUsedError;
|
||||
String? get manufacturer => throw _privateConstructorUsedError;
|
||||
@ -456,14 +456,26 @@ mixin _$AssetModel {
|
||||
int? get departmentId => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'department_name', readValue: _readDepartmentName)
|
||||
String? get departmentName => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable)
|
||||
int? get warehouseId => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName)
|
||||
String? get warehouseName => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'location_detail')
|
||||
String? get locationDetail => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'assigned_to_user_id', fromJson: _intFromJsonNullable)
|
||||
int? get assignedToUserId => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'maintenance_incharge_user_id', fromJson: _intFromJsonNullable)
|
||||
int? get maintenanceInchargeUserId => throw _privateConstructorUsedError;
|
||||
@JsonKey(
|
||||
name: 'maintenance_frequency_in_days',
|
||||
fromJson: _intFromJsonNullable,
|
||||
)
|
||||
int? get maintenanceFrequencyInDays => throw _privateConstructorUsedError;
|
||||
@JsonKey(
|
||||
name: 'maintenance_checklist_json',
|
||||
fromJson: _checklistFromJson,
|
||||
toJson: _checklistToJson,
|
||||
)
|
||||
List<AssetMaintenanceChecklistItem>? get maintenanceChecklistJson =>
|
||||
throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'commencement_date', fromJson: _dateFromJsonNullable)
|
||||
DateTime? get commencementDate => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'vendor_id', fromJson: _intFromJsonNullable)
|
||||
int? get vendorId => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'vendor_name', readValue: _readVendorNameFromNested)
|
||||
@ -505,6 +517,13 @@ mixin _$AssetModel {
|
||||
DateTime? get createdAt => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'updated_at', fromJson: _dateFromJsonNullable)
|
||||
DateTime? get updatedAt => throw _privateConstructorUsedError;
|
||||
@JsonKey(
|
||||
name: 'maintenance',
|
||||
fromJson: AssetMaintenanceSummary.fromJsonNullable,
|
||||
toJson: AssetMaintenanceSummary.toJsonNullable,
|
||||
)
|
||||
AssetMaintenanceSummary? get maintenance =>
|
||||
throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this AssetModel to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@ -544,12 +563,13 @@ abstract class $AssetModelCopyWith<$Res> {
|
||||
@JsonKey(name: 'item_subcategory_name', readValue: _readItemSubcategoryName)
|
||||
String? assetSubcategoryName,
|
||||
@JsonKey(
|
||||
name: 'plant_id',
|
||||
readValue: _readPlantId,
|
||||
name: 'location_id',
|
||||
readValue: _readLocationId,
|
||||
fromJson: _intFromJsonNullable,
|
||||
)
|
||||
int? plantId,
|
||||
@JsonKey(name: 'plant_name', readValue: _readPlantName) String? plantName,
|
||||
int? locationId,
|
||||
@JsonKey(name: 'location_name', readValue: _readLocationName)
|
||||
String? locationName,
|
||||
@JsonKey(name: 'brand_model') String? brandModel,
|
||||
String? manufacturer,
|
||||
@JsonKey(name: 'serial_number') String? serialNumber,
|
||||
@ -558,13 +578,27 @@ abstract class $AssetModelCopyWith<$Res> {
|
||||
int? departmentId,
|
||||
@JsonKey(name: 'department_name', readValue: _readDepartmentName)
|
||||
String? departmentName,
|
||||
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable)
|
||||
int? warehouseId,
|
||||
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName)
|
||||
String? warehouseName,
|
||||
@JsonKey(name: 'location_detail') String? locationDetail,
|
||||
@JsonKey(name: 'assigned_to_user_id', fromJson: _intFromJsonNullable)
|
||||
int? assignedToUserId,
|
||||
@JsonKey(
|
||||
name: 'maintenance_incharge_user_id',
|
||||
fromJson: _intFromJsonNullable,
|
||||
)
|
||||
int? maintenanceInchargeUserId,
|
||||
@JsonKey(
|
||||
name: 'maintenance_frequency_in_days',
|
||||
fromJson: _intFromJsonNullable,
|
||||
)
|
||||
int? maintenanceFrequencyInDays,
|
||||
@JsonKey(
|
||||
name: 'maintenance_checklist_json',
|
||||
fromJson: _checklistFromJson,
|
||||
toJson: _checklistToJson,
|
||||
)
|
||||
List<AssetMaintenanceChecklistItem>? maintenanceChecklistJson,
|
||||
@JsonKey(name: 'commencement_date', fromJson: _dateFromJsonNullable)
|
||||
DateTime? commencementDate,
|
||||
@JsonKey(name: 'vendor_id', fromJson: _intFromJsonNullable) int? vendorId,
|
||||
@JsonKey(name: 'vendor_name', readValue: _readVendorNameFromNested)
|
||||
String? vendorName,
|
||||
@ -599,6 +633,12 @@ abstract class $AssetModelCopyWith<$Res> {
|
||||
DateTime? createdAt,
|
||||
@JsonKey(name: 'updated_at', fromJson: _dateFromJsonNullable)
|
||||
DateTime? updatedAt,
|
||||
@JsonKey(
|
||||
name: 'maintenance',
|
||||
fromJson: AssetMaintenanceSummary.fromJsonNullable,
|
||||
toJson: AssetMaintenanceSummary.toJsonNullable,
|
||||
)
|
||||
AssetMaintenanceSummary? maintenance,
|
||||
});
|
||||
}
|
||||
|
||||
@ -624,18 +664,20 @@ class _$AssetModelCopyWithImpl<$Res, $Val extends AssetModel>
|
||||
Object? assetCategoryName = freezed,
|
||||
Object? assetSubcategoryId = freezed,
|
||||
Object? assetSubcategoryName = freezed,
|
||||
Object? plantId = freezed,
|
||||
Object? plantName = freezed,
|
||||
Object? locationId = freezed,
|
||||
Object? locationName = freezed,
|
||||
Object? brandModel = freezed,
|
||||
Object? manufacturer = freezed,
|
||||
Object? serialNumber = freezed,
|
||||
Object? partNumber = freezed,
|
||||
Object? departmentId = freezed,
|
||||
Object? departmentName = freezed,
|
||||
Object? warehouseId = freezed,
|
||||
Object? warehouseName = freezed,
|
||||
Object? locationDetail = freezed,
|
||||
Object? assignedToUserId = freezed,
|
||||
Object? maintenanceInchargeUserId = freezed,
|
||||
Object? maintenanceFrequencyInDays = freezed,
|
||||
Object? maintenanceChecklistJson = freezed,
|
||||
Object? commencementDate = freezed,
|
||||
Object? vendorId = freezed,
|
||||
Object? vendorName = freezed,
|
||||
Object? poId = freezed,
|
||||
@ -658,6 +700,7 @@ class _$AssetModelCopyWithImpl<$Res, $Val extends AssetModel>
|
||||
Object? isActive = null,
|
||||
Object? createdAt = freezed,
|
||||
Object? updatedAt = freezed,
|
||||
Object? maintenance = freezed,
|
||||
}) {
|
||||
return _then(
|
||||
_value.copyWith(
|
||||
@ -689,13 +732,13 @@ class _$AssetModelCopyWithImpl<$Res, $Val extends AssetModel>
|
||||
? _value.assetSubcategoryName
|
||||
: assetSubcategoryName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
plantId: freezed == plantId
|
||||
? _value.plantId
|
||||
: plantId // ignore: cast_nullable_to_non_nullable
|
||||
locationId: freezed == locationId
|
||||
? _value.locationId
|
||||
: locationId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
plantName: freezed == plantName
|
||||
? _value.plantName
|
||||
: plantName // ignore: cast_nullable_to_non_nullable
|
||||
locationName: freezed == locationName
|
||||
? _value.locationName
|
||||
: locationName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
brandModel: freezed == brandModel
|
||||
? _value.brandModel
|
||||
@ -721,14 +764,6 @@ class _$AssetModelCopyWithImpl<$Res, $Val extends AssetModel>
|
||||
? _value.departmentName
|
||||
: departmentName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
warehouseId: freezed == warehouseId
|
||||
? _value.warehouseId
|
||||
: warehouseId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
warehouseName: freezed == warehouseName
|
||||
? _value.warehouseName
|
||||
: warehouseName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
locationDetail: freezed == locationDetail
|
||||
? _value.locationDetail
|
||||
: locationDetail // ignore: cast_nullable_to_non_nullable
|
||||
@ -737,6 +772,22 @@ class _$AssetModelCopyWithImpl<$Res, $Val extends AssetModel>
|
||||
? _value.assignedToUserId
|
||||
: assignedToUserId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
maintenanceInchargeUserId: freezed == maintenanceInchargeUserId
|
||||
? _value.maintenanceInchargeUserId
|
||||
: maintenanceInchargeUserId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
maintenanceFrequencyInDays: freezed == maintenanceFrequencyInDays
|
||||
? _value.maintenanceFrequencyInDays
|
||||
: maintenanceFrequencyInDays // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
maintenanceChecklistJson: freezed == maintenanceChecklistJson
|
||||
? _value.maintenanceChecklistJson
|
||||
: maintenanceChecklistJson // ignore: cast_nullable_to_non_nullable
|
||||
as List<AssetMaintenanceChecklistItem>?,
|
||||
commencementDate: freezed == commencementDate
|
||||
? _value.commencementDate
|
||||
: commencementDate // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
vendorId: freezed == vendorId
|
||||
? _value.vendorId
|
||||
: vendorId // ignore: cast_nullable_to_non_nullable
|
||||
@ -825,6 +876,10 @@ class _$AssetModelCopyWithImpl<$Res, $Val extends AssetModel>
|
||||
? _value.updatedAt
|
||||
: updatedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
maintenance: freezed == maintenance
|
||||
? _value.maintenance
|
||||
: maintenance // ignore: cast_nullable_to_non_nullable
|
||||
as AssetMaintenanceSummary?,
|
||||
)
|
||||
as $Val,
|
||||
);
|
||||
@ -861,12 +916,13 @@ abstract class _$$AssetModelImplCopyWith<$Res>
|
||||
@JsonKey(name: 'item_subcategory_name', readValue: _readItemSubcategoryName)
|
||||
String? assetSubcategoryName,
|
||||
@JsonKey(
|
||||
name: 'plant_id',
|
||||
readValue: _readPlantId,
|
||||
name: 'location_id',
|
||||
readValue: _readLocationId,
|
||||
fromJson: _intFromJsonNullable,
|
||||
)
|
||||
int? plantId,
|
||||
@JsonKey(name: 'plant_name', readValue: _readPlantName) String? plantName,
|
||||
int? locationId,
|
||||
@JsonKey(name: 'location_name', readValue: _readLocationName)
|
||||
String? locationName,
|
||||
@JsonKey(name: 'brand_model') String? brandModel,
|
||||
String? manufacturer,
|
||||
@JsonKey(name: 'serial_number') String? serialNumber,
|
||||
@ -875,13 +931,27 @@ abstract class _$$AssetModelImplCopyWith<$Res>
|
||||
int? departmentId,
|
||||
@JsonKey(name: 'department_name', readValue: _readDepartmentName)
|
||||
String? departmentName,
|
||||
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable)
|
||||
int? warehouseId,
|
||||
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName)
|
||||
String? warehouseName,
|
||||
@JsonKey(name: 'location_detail') String? locationDetail,
|
||||
@JsonKey(name: 'assigned_to_user_id', fromJson: _intFromJsonNullable)
|
||||
int? assignedToUserId,
|
||||
@JsonKey(
|
||||
name: 'maintenance_incharge_user_id',
|
||||
fromJson: _intFromJsonNullable,
|
||||
)
|
||||
int? maintenanceInchargeUserId,
|
||||
@JsonKey(
|
||||
name: 'maintenance_frequency_in_days',
|
||||
fromJson: _intFromJsonNullable,
|
||||
)
|
||||
int? maintenanceFrequencyInDays,
|
||||
@JsonKey(
|
||||
name: 'maintenance_checklist_json',
|
||||
fromJson: _checklistFromJson,
|
||||
toJson: _checklistToJson,
|
||||
)
|
||||
List<AssetMaintenanceChecklistItem>? maintenanceChecklistJson,
|
||||
@JsonKey(name: 'commencement_date', fromJson: _dateFromJsonNullable)
|
||||
DateTime? commencementDate,
|
||||
@JsonKey(name: 'vendor_id', fromJson: _intFromJsonNullable) int? vendorId,
|
||||
@JsonKey(name: 'vendor_name', readValue: _readVendorNameFromNested)
|
||||
String? vendorName,
|
||||
@ -916,6 +986,12 @@ abstract class _$$AssetModelImplCopyWith<$Res>
|
||||
DateTime? createdAt,
|
||||
@JsonKey(name: 'updated_at', fromJson: _dateFromJsonNullable)
|
||||
DateTime? updatedAt,
|
||||
@JsonKey(
|
||||
name: 'maintenance',
|
||||
fromJson: AssetMaintenanceSummary.fromJsonNullable,
|
||||
toJson: AssetMaintenanceSummary.toJsonNullable,
|
||||
)
|
||||
AssetMaintenanceSummary? maintenance,
|
||||
});
|
||||
}
|
||||
|
||||
@ -940,18 +1016,20 @@ class __$$AssetModelImplCopyWithImpl<$Res>
|
||||
Object? assetCategoryName = freezed,
|
||||
Object? assetSubcategoryId = freezed,
|
||||
Object? assetSubcategoryName = freezed,
|
||||
Object? plantId = freezed,
|
||||
Object? plantName = freezed,
|
||||
Object? locationId = freezed,
|
||||
Object? locationName = freezed,
|
||||
Object? brandModel = freezed,
|
||||
Object? manufacturer = freezed,
|
||||
Object? serialNumber = freezed,
|
||||
Object? partNumber = freezed,
|
||||
Object? departmentId = freezed,
|
||||
Object? departmentName = freezed,
|
||||
Object? warehouseId = freezed,
|
||||
Object? warehouseName = freezed,
|
||||
Object? locationDetail = freezed,
|
||||
Object? assignedToUserId = freezed,
|
||||
Object? maintenanceInchargeUserId = freezed,
|
||||
Object? maintenanceFrequencyInDays = freezed,
|
||||
Object? maintenanceChecklistJson = freezed,
|
||||
Object? commencementDate = freezed,
|
||||
Object? vendorId = freezed,
|
||||
Object? vendorName = freezed,
|
||||
Object? poId = freezed,
|
||||
@ -974,6 +1052,7 @@ class __$$AssetModelImplCopyWithImpl<$Res>
|
||||
Object? isActive = null,
|
||||
Object? createdAt = freezed,
|
||||
Object? updatedAt = freezed,
|
||||
Object? maintenance = freezed,
|
||||
}) {
|
||||
return _then(
|
||||
_$AssetModelImpl(
|
||||
@ -1005,13 +1084,13 @@ class __$$AssetModelImplCopyWithImpl<$Res>
|
||||
? _value.assetSubcategoryName
|
||||
: assetSubcategoryName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
plantId: freezed == plantId
|
||||
? _value.plantId
|
||||
: plantId // ignore: cast_nullable_to_non_nullable
|
||||
locationId: freezed == locationId
|
||||
? _value.locationId
|
||||
: locationId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
plantName: freezed == plantName
|
||||
? _value.plantName
|
||||
: plantName // ignore: cast_nullable_to_non_nullable
|
||||
locationName: freezed == locationName
|
||||
? _value.locationName
|
||||
: locationName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
brandModel: freezed == brandModel
|
||||
? _value.brandModel
|
||||
@ -1037,14 +1116,6 @@ class __$$AssetModelImplCopyWithImpl<$Res>
|
||||
? _value.departmentName
|
||||
: departmentName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
warehouseId: freezed == warehouseId
|
||||
? _value.warehouseId
|
||||
: warehouseId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
warehouseName: freezed == warehouseName
|
||||
? _value.warehouseName
|
||||
: warehouseName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
locationDetail: freezed == locationDetail
|
||||
? _value.locationDetail
|
||||
: locationDetail // ignore: cast_nullable_to_non_nullable
|
||||
@ -1053,6 +1124,22 @@ class __$$AssetModelImplCopyWithImpl<$Res>
|
||||
? _value.assignedToUserId
|
||||
: assignedToUserId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
maintenanceInchargeUserId: freezed == maintenanceInchargeUserId
|
||||
? _value.maintenanceInchargeUserId
|
||||
: maintenanceInchargeUserId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
maintenanceFrequencyInDays: freezed == maintenanceFrequencyInDays
|
||||
? _value.maintenanceFrequencyInDays
|
||||
: maintenanceFrequencyInDays // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
maintenanceChecklistJson: freezed == maintenanceChecklistJson
|
||||
? _value._maintenanceChecklistJson
|
||||
: maintenanceChecklistJson // ignore: cast_nullable_to_non_nullable
|
||||
as List<AssetMaintenanceChecklistItem>?,
|
||||
commencementDate: freezed == commencementDate
|
||||
? _value.commencementDate
|
||||
: commencementDate // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
vendorId: freezed == vendorId
|
||||
? _value.vendorId
|
||||
: vendorId // ignore: cast_nullable_to_non_nullable
|
||||
@ -1141,6 +1228,10 @@ class __$$AssetModelImplCopyWithImpl<$Res>
|
||||
? _value.updatedAt
|
||||
: updatedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
maintenance: freezed == maintenance
|
||||
? _value.maintenance
|
||||
: maintenance // ignore: cast_nullable_to_non_nullable
|
||||
as AssetMaintenanceSummary?,
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -1170,12 +1261,13 @@ class _$AssetModelImpl implements _AssetModel {
|
||||
@JsonKey(name: 'item_subcategory_name', readValue: _readItemSubcategoryName)
|
||||
this.assetSubcategoryName,
|
||||
@JsonKey(
|
||||
name: 'plant_id',
|
||||
readValue: _readPlantId,
|
||||
name: 'location_id',
|
||||
readValue: _readLocationId,
|
||||
fromJson: _intFromJsonNullable,
|
||||
)
|
||||
this.plantId,
|
||||
@JsonKey(name: 'plant_name', readValue: _readPlantName) this.plantName,
|
||||
this.locationId,
|
||||
@JsonKey(name: 'location_name', readValue: _readLocationName)
|
||||
this.locationName,
|
||||
@JsonKey(name: 'brand_model') this.brandModel,
|
||||
this.manufacturer,
|
||||
@JsonKey(name: 'serial_number') this.serialNumber,
|
||||
@ -1184,13 +1276,27 @@ class _$AssetModelImpl implements _AssetModel {
|
||||
this.departmentId,
|
||||
@JsonKey(name: 'department_name', readValue: _readDepartmentName)
|
||||
this.departmentName,
|
||||
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable)
|
||||
this.warehouseId,
|
||||
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName)
|
||||
this.warehouseName,
|
||||
@JsonKey(name: 'location_detail') this.locationDetail,
|
||||
@JsonKey(name: 'assigned_to_user_id', fromJson: _intFromJsonNullable)
|
||||
this.assignedToUserId,
|
||||
@JsonKey(
|
||||
name: 'maintenance_incharge_user_id',
|
||||
fromJson: _intFromJsonNullable,
|
||||
)
|
||||
this.maintenanceInchargeUserId,
|
||||
@JsonKey(
|
||||
name: 'maintenance_frequency_in_days',
|
||||
fromJson: _intFromJsonNullable,
|
||||
)
|
||||
this.maintenanceFrequencyInDays,
|
||||
@JsonKey(
|
||||
name: 'maintenance_checklist_json',
|
||||
fromJson: _checklistFromJson,
|
||||
toJson: _checklistToJson,
|
||||
)
|
||||
final List<AssetMaintenanceChecklistItem>? maintenanceChecklistJson,
|
||||
@JsonKey(name: 'commencement_date', fromJson: _dateFromJsonNullable)
|
||||
this.commencementDate,
|
||||
@JsonKey(name: 'vendor_id', fromJson: _intFromJsonNullable) this.vendorId,
|
||||
@JsonKey(name: 'vendor_name', readValue: _readVendorNameFromNested)
|
||||
this.vendorName,
|
||||
@ -1225,7 +1331,13 @@ class _$AssetModelImpl implements _AssetModel {
|
||||
this.createdAt,
|
||||
@JsonKey(name: 'updated_at', fromJson: _dateFromJsonNullable)
|
||||
this.updatedAt,
|
||||
});
|
||||
@JsonKey(
|
||||
name: 'maintenance',
|
||||
fromJson: AssetMaintenanceSummary.fromJsonNullable,
|
||||
toJson: AssetMaintenanceSummary.toJsonNullable,
|
||||
)
|
||||
this.maintenance,
|
||||
}) : _maintenanceChecklistJson = maintenanceChecklistJson;
|
||||
|
||||
factory _$AssetModelImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$AssetModelImplFromJson(json);
|
||||
@ -1261,14 +1373,14 @@ class _$AssetModelImpl implements _AssetModel {
|
||||
final String? assetSubcategoryName;
|
||||
@override
|
||||
@JsonKey(
|
||||
name: 'plant_id',
|
||||
readValue: _readPlantId,
|
||||
name: 'location_id',
|
||||
readValue: _readLocationId,
|
||||
fromJson: _intFromJsonNullable,
|
||||
)
|
||||
final int? plantId;
|
||||
final int? locationId;
|
||||
@override
|
||||
@JsonKey(name: 'plant_name', readValue: _readPlantName)
|
||||
final String? plantName;
|
||||
@JsonKey(name: 'location_name', readValue: _readLocationName)
|
||||
final String? locationName;
|
||||
@override
|
||||
@JsonKey(name: 'brand_model')
|
||||
final String? brandModel;
|
||||
@ -1287,18 +1399,40 @@ class _$AssetModelImpl implements _AssetModel {
|
||||
@JsonKey(name: 'department_name', readValue: _readDepartmentName)
|
||||
final String? departmentName;
|
||||
@override
|
||||
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable)
|
||||
final int? warehouseId;
|
||||
@override
|
||||
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName)
|
||||
final String? warehouseName;
|
||||
@override
|
||||
@JsonKey(name: 'location_detail')
|
||||
final String? locationDetail;
|
||||
@override
|
||||
@JsonKey(name: 'assigned_to_user_id', fromJson: _intFromJsonNullable)
|
||||
final int? assignedToUserId;
|
||||
@override
|
||||
@JsonKey(name: 'maintenance_incharge_user_id', fromJson: _intFromJsonNullable)
|
||||
final int? maintenanceInchargeUserId;
|
||||
@override
|
||||
@JsonKey(
|
||||
name: 'maintenance_frequency_in_days',
|
||||
fromJson: _intFromJsonNullable,
|
||||
)
|
||||
final int? maintenanceFrequencyInDays;
|
||||
final List<AssetMaintenanceChecklistItem>? _maintenanceChecklistJson;
|
||||
@override
|
||||
@JsonKey(
|
||||
name: 'maintenance_checklist_json',
|
||||
fromJson: _checklistFromJson,
|
||||
toJson: _checklistToJson,
|
||||
)
|
||||
List<AssetMaintenanceChecklistItem>? get maintenanceChecklistJson {
|
||||
final value = _maintenanceChecklistJson;
|
||||
if (value == null) return null;
|
||||
if (_maintenanceChecklistJson is EqualUnmodifiableListView)
|
||||
return _maintenanceChecklistJson;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(value);
|
||||
}
|
||||
|
||||
@override
|
||||
@JsonKey(name: 'commencement_date', fromJson: _dateFromJsonNullable)
|
||||
final DateTime? commencementDate;
|
||||
@override
|
||||
@JsonKey(name: 'vendor_id', fromJson: _intFromJsonNullable)
|
||||
final int? vendorId;
|
||||
@override
|
||||
@ -1361,10 +1495,17 @@ class _$AssetModelImpl implements _AssetModel {
|
||||
@override
|
||||
@JsonKey(name: 'updated_at', fromJson: _dateFromJsonNullable)
|
||||
final DateTime? updatedAt;
|
||||
@override
|
||||
@JsonKey(
|
||||
name: 'maintenance',
|
||||
fromJson: AssetMaintenanceSummary.fromJsonNullable,
|
||||
toJson: AssetMaintenanceSummary.toJsonNullable,
|
||||
)
|
||||
final AssetMaintenanceSummary? maintenance;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AssetModel(id: $id, assetName: $assetName, assetCode: $assetCode, assetCategoryId: $assetCategoryId, assetCategoryName: $assetCategoryName, assetSubcategoryId: $assetSubcategoryId, assetSubcategoryName: $assetSubcategoryName, plantId: $plantId, plantName: $plantName, brandModel: $brandModel, manufacturer: $manufacturer, serialNumber: $serialNumber, partNumber: $partNumber, departmentId: $departmentId, departmentName: $departmentName, warehouseId: $warehouseId, warehouseName: $warehouseName, locationDetail: $locationDetail, assignedToUserId: $assignedToUserId, vendorId: $vendorId, vendorName: $vendorName, poId: $poId, grnId: $grnId, grnItemId: $grnItemId, purchaseDate: $purchaseDate, purchaseCost: $purchaseCost, usefulLifeYears: $usefulLifeYears, depreciationMethod: $depreciationMethod, depreciationRate: $depreciationRate, salvageValue: $salvageValue, warrantyExpiryDate: $warrantyExpiryDate, condition: $condition, status: $status, qrCodeValue: $qrCodeValue, disposalDate: $disposalDate, disposalReason: $disposalReason, disposalValue: $disposalValue, remarks: $remarks, isActive: $isActive, createdAt: $createdAt, updatedAt: $updatedAt)';
|
||||
return 'AssetModel(id: $id, assetName: $assetName, assetCode: $assetCode, assetCategoryId: $assetCategoryId, assetCategoryName: $assetCategoryName, assetSubcategoryId: $assetSubcategoryId, assetSubcategoryName: $assetSubcategoryName, locationId: $locationId, locationName: $locationName, brandModel: $brandModel, manufacturer: $manufacturer, serialNumber: $serialNumber, partNumber: $partNumber, departmentId: $departmentId, departmentName: $departmentName, locationDetail: $locationDetail, assignedToUserId: $assignedToUserId, maintenanceInchargeUserId: $maintenanceInchargeUserId, maintenanceFrequencyInDays: $maintenanceFrequencyInDays, maintenanceChecklistJson: $maintenanceChecklistJson, commencementDate: $commencementDate, vendorId: $vendorId, vendorName: $vendorName, poId: $poId, grnId: $grnId, grnItemId: $grnItemId, purchaseDate: $purchaseDate, purchaseCost: $purchaseCost, usefulLifeYears: $usefulLifeYears, depreciationMethod: $depreciationMethod, depreciationRate: $depreciationRate, salvageValue: $salvageValue, warrantyExpiryDate: $warrantyExpiryDate, condition: $condition, status: $status, qrCodeValue: $qrCodeValue, disposalDate: $disposalDate, disposalReason: $disposalReason, disposalValue: $disposalValue, remarks: $remarks, isActive: $isActive, createdAt: $createdAt, updatedAt: $updatedAt, maintenance: $maintenance)';
|
||||
}
|
||||
|
||||
@override
|
||||
@ -1385,9 +1526,10 @@ class _$AssetModelImpl implements _AssetModel {
|
||||
other.assetSubcategoryId == assetSubcategoryId) &&
|
||||
(identical(other.assetSubcategoryName, assetSubcategoryName) ||
|
||||
other.assetSubcategoryName == assetSubcategoryName) &&
|
||||
(identical(other.plantId, plantId) || other.plantId == plantId) &&
|
||||
(identical(other.plantName, plantName) ||
|
||||
other.plantName == plantName) &&
|
||||
(identical(other.locationId, locationId) ||
|
||||
other.locationId == locationId) &&
|
||||
(identical(other.locationName, locationName) ||
|
||||
other.locationName == locationName) &&
|
||||
(identical(other.brandModel, brandModel) ||
|
||||
other.brandModel == brandModel) &&
|
||||
(identical(other.manufacturer, manufacturer) ||
|
||||
@ -1400,14 +1542,27 @@ class _$AssetModelImpl implements _AssetModel {
|
||||
other.departmentId == departmentId) &&
|
||||
(identical(other.departmentName, departmentName) ||
|
||||
other.departmentName == departmentName) &&
|
||||
(identical(other.warehouseId, warehouseId) ||
|
||||
other.warehouseId == warehouseId) &&
|
||||
(identical(other.warehouseName, warehouseName) ||
|
||||
other.warehouseName == warehouseName) &&
|
||||
(identical(other.locationDetail, locationDetail) ||
|
||||
other.locationDetail == locationDetail) &&
|
||||
(identical(other.assignedToUserId, assignedToUserId) ||
|
||||
other.assignedToUserId == assignedToUserId) &&
|
||||
(identical(
|
||||
other.maintenanceInchargeUserId,
|
||||
maintenanceInchargeUserId,
|
||||
) ||
|
||||
other.maintenanceInchargeUserId == maintenanceInchargeUserId) &&
|
||||
(identical(
|
||||
other.maintenanceFrequencyInDays,
|
||||
maintenanceFrequencyInDays,
|
||||
) ||
|
||||
other.maintenanceFrequencyInDays ==
|
||||
maintenanceFrequencyInDays) &&
|
||||
const DeepCollectionEquality().equals(
|
||||
other._maintenanceChecklistJson,
|
||||
_maintenanceChecklistJson,
|
||||
) &&
|
||||
(identical(other.commencementDate, commencementDate) ||
|
||||
other.commencementDate == commencementDate) &&
|
||||
(identical(other.vendorId, vendorId) ||
|
||||
other.vendorId == vendorId) &&
|
||||
(identical(other.vendorName, vendorName) ||
|
||||
@ -1447,7 +1602,9 @@ class _$AssetModelImpl implements _AssetModel {
|
||||
(identical(other.createdAt, createdAt) ||
|
||||
other.createdAt == createdAt) &&
|
||||
(identical(other.updatedAt, updatedAt) ||
|
||||
other.updatedAt == updatedAt));
|
||||
other.updatedAt == updatedAt) &&
|
||||
(identical(other.maintenance, maintenance) ||
|
||||
other.maintenance == maintenance));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@ -1461,18 +1618,20 @@ class _$AssetModelImpl implements _AssetModel {
|
||||
assetCategoryName,
|
||||
assetSubcategoryId,
|
||||
assetSubcategoryName,
|
||||
plantId,
|
||||
plantName,
|
||||
locationId,
|
||||
locationName,
|
||||
brandModel,
|
||||
manufacturer,
|
||||
serialNumber,
|
||||
partNumber,
|
||||
departmentId,
|
||||
departmentName,
|
||||
warehouseId,
|
||||
warehouseName,
|
||||
locationDetail,
|
||||
assignedToUserId,
|
||||
maintenanceInchargeUserId,
|
||||
maintenanceFrequencyInDays,
|
||||
const DeepCollectionEquality().hash(_maintenanceChecklistJson),
|
||||
commencementDate,
|
||||
vendorId,
|
||||
vendorName,
|
||||
poId,
|
||||
@ -1495,6 +1654,7 @@ class _$AssetModelImpl implements _AssetModel {
|
||||
isActive,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
maintenance,
|
||||
]);
|
||||
|
||||
/// Create a copy of AssetModel
|
||||
@ -1533,13 +1693,13 @@ abstract class _AssetModel implements AssetModel {
|
||||
@JsonKey(name: 'item_subcategory_name', readValue: _readItemSubcategoryName)
|
||||
final String? assetSubcategoryName,
|
||||
@JsonKey(
|
||||
name: 'plant_id',
|
||||
readValue: _readPlantId,
|
||||
name: 'location_id',
|
||||
readValue: _readLocationId,
|
||||
fromJson: _intFromJsonNullable,
|
||||
)
|
||||
final int? plantId,
|
||||
@JsonKey(name: 'plant_name', readValue: _readPlantName)
|
||||
final String? plantName,
|
||||
final int? locationId,
|
||||
@JsonKey(name: 'location_name', readValue: _readLocationName)
|
||||
final String? locationName,
|
||||
@JsonKey(name: 'brand_model') final String? brandModel,
|
||||
final String? manufacturer,
|
||||
@JsonKey(name: 'serial_number') final String? serialNumber,
|
||||
@ -1548,13 +1708,27 @@ abstract class _AssetModel implements AssetModel {
|
||||
final int? departmentId,
|
||||
@JsonKey(name: 'department_name', readValue: _readDepartmentName)
|
||||
final String? departmentName,
|
||||
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable)
|
||||
final int? warehouseId,
|
||||
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName)
|
||||
final String? warehouseName,
|
||||
@JsonKey(name: 'location_detail') final String? locationDetail,
|
||||
@JsonKey(name: 'assigned_to_user_id', fromJson: _intFromJsonNullable)
|
||||
final int? assignedToUserId,
|
||||
@JsonKey(
|
||||
name: 'maintenance_incharge_user_id',
|
||||
fromJson: _intFromJsonNullable,
|
||||
)
|
||||
final int? maintenanceInchargeUserId,
|
||||
@JsonKey(
|
||||
name: 'maintenance_frequency_in_days',
|
||||
fromJson: _intFromJsonNullable,
|
||||
)
|
||||
final int? maintenanceFrequencyInDays,
|
||||
@JsonKey(
|
||||
name: 'maintenance_checklist_json',
|
||||
fromJson: _checklistFromJson,
|
||||
toJson: _checklistToJson,
|
||||
)
|
||||
final List<AssetMaintenanceChecklistItem>? maintenanceChecklistJson,
|
||||
@JsonKey(name: 'commencement_date', fromJson: _dateFromJsonNullable)
|
||||
final DateTime? commencementDate,
|
||||
@JsonKey(name: 'vendor_id', fromJson: _intFromJsonNullable)
|
||||
final int? vendorId,
|
||||
@JsonKey(name: 'vendor_name', readValue: _readVendorNameFromNested)
|
||||
@ -1590,6 +1764,12 @@ abstract class _AssetModel implements AssetModel {
|
||||
final DateTime? createdAt,
|
||||
@JsonKey(name: 'updated_at', fromJson: _dateFromJsonNullable)
|
||||
final DateTime? updatedAt,
|
||||
@JsonKey(
|
||||
name: 'maintenance',
|
||||
fromJson: AssetMaintenanceSummary.fromJsonNullable,
|
||||
toJson: AssetMaintenanceSummary.toJsonNullable,
|
||||
)
|
||||
final AssetMaintenanceSummary? maintenance,
|
||||
}) = _$AssetModelImpl;
|
||||
|
||||
factory _AssetModel.fromJson(Map<String, dynamic> json) =
|
||||
@ -1626,14 +1806,14 @@ abstract class _AssetModel implements AssetModel {
|
||||
String? get assetSubcategoryName;
|
||||
@override
|
||||
@JsonKey(
|
||||
name: 'plant_id',
|
||||
readValue: _readPlantId,
|
||||
name: 'location_id',
|
||||
readValue: _readLocationId,
|
||||
fromJson: _intFromJsonNullable,
|
||||
)
|
||||
int? get plantId;
|
||||
int? get locationId;
|
||||
@override
|
||||
@JsonKey(name: 'plant_name', readValue: _readPlantName)
|
||||
String? get plantName;
|
||||
@JsonKey(name: 'location_name', readValue: _readLocationName)
|
||||
String? get locationName;
|
||||
@override
|
||||
@JsonKey(name: 'brand_model')
|
||||
String? get brandModel;
|
||||
@ -1652,18 +1832,31 @@ abstract class _AssetModel implements AssetModel {
|
||||
@JsonKey(name: 'department_name', readValue: _readDepartmentName)
|
||||
String? get departmentName;
|
||||
@override
|
||||
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable)
|
||||
int? get warehouseId;
|
||||
@override
|
||||
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName)
|
||||
String? get warehouseName;
|
||||
@override
|
||||
@JsonKey(name: 'location_detail')
|
||||
String? get locationDetail;
|
||||
@override
|
||||
@JsonKey(name: 'assigned_to_user_id', fromJson: _intFromJsonNullable)
|
||||
int? get assignedToUserId;
|
||||
@override
|
||||
@JsonKey(name: 'maintenance_incharge_user_id', fromJson: _intFromJsonNullable)
|
||||
int? get maintenanceInchargeUserId;
|
||||
@override
|
||||
@JsonKey(
|
||||
name: 'maintenance_frequency_in_days',
|
||||
fromJson: _intFromJsonNullable,
|
||||
)
|
||||
int? get maintenanceFrequencyInDays;
|
||||
@override
|
||||
@JsonKey(
|
||||
name: 'maintenance_checklist_json',
|
||||
fromJson: _checklistFromJson,
|
||||
toJson: _checklistToJson,
|
||||
)
|
||||
List<AssetMaintenanceChecklistItem>? get maintenanceChecklistJson;
|
||||
@override
|
||||
@JsonKey(name: 'commencement_date', fromJson: _dateFromJsonNullable)
|
||||
DateTime? get commencementDate;
|
||||
@override
|
||||
@JsonKey(name: 'vendor_id', fromJson: _intFromJsonNullable)
|
||||
int? get vendorId;
|
||||
@override
|
||||
@ -1726,6 +1919,13 @@ abstract class _AssetModel implements AssetModel {
|
||||
@override
|
||||
@JsonKey(name: 'updated_at', fromJson: _dateFromJsonNullable)
|
||||
DateTime? get updatedAt;
|
||||
@override
|
||||
@JsonKey(
|
||||
name: 'maintenance',
|
||||
fromJson: AssetMaintenanceSummary.fromJsonNullable,
|
||||
toJson: AssetMaintenanceSummary.toJsonNullable,
|
||||
)
|
||||
AssetMaintenanceSummary? get maintenance;
|
||||
|
||||
/// Create a copy of AssetModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@ -3913,8 +4113,8 @@ mixin _$AssetAlertModel {
|
||||
@JsonKey(name: 'days_remaining', fromJson: _intFromJsonNullable)
|
||||
int? get daysRemaining => throw _privateConstructorUsedError;
|
||||
String? get status => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'plant_name')
|
||||
String? get plantName => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'location_name', readValue: _readAlertLocationName)
|
||||
String? get locationName => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this AssetAlertModel to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@ -3948,7 +4148,8 @@ abstract class $AssetAlertModelCopyWith<$Res> {
|
||||
@JsonKey(name: 'days_remaining', fromJson: _intFromJsonNullable)
|
||||
int? daysRemaining,
|
||||
String? status,
|
||||
@JsonKey(name: 'plant_name') String? plantName,
|
||||
@JsonKey(name: 'location_name', readValue: _readAlertLocationName)
|
||||
String? locationName,
|
||||
});
|
||||
}
|
||||
|
||||
@ -3978,7 +4179,7 @@ class _$AssetAlertModelCopyWithImpl<$Res, $Val extends AssetAlertModel>
|
||||
Object? dueDate = freezed,
|
||||
Object? daysRemaining = freezed,
|
||||
Object? status = freezed,
|
||||
Object? plantName = freezed,
|
||||
Object? locationName = freezed,
|
||||
}) {
|
||||
return _then(
|
||||
_value.copyWith(
|
||||
@ -4026,9 +4227,9 @@ class _$AssetAlertModelCopyWithImpl<$Res, $Val extends AssetAlertModel>
|
||||
? _value.status
|
||||
: status // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
plantName: freezed == plantName
|
||||
? _value.plantName
|
||||
: plantName // ignore: cast_nullable_to_non_nullable
|
||||
locationName: freezed == locationName
|
||||
? _value.locationName
|
||||
: locationName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
)
|
||||
as $Val,
|
||||
@ -4060,7 +4261,8 @@ abstract class _$$AssetAlertModelImplCopyWith<$Res>
|
||||
@JsonKey(name: 'days_remaining', fromJson: _intFromJsonNullable)
|
||||
int? daysRemaining,
|
||||
String? status,
|
||||
@JsonKey(name: 'plant_name') String? plantName,
|
||||
@JsonKey(name: 'location_name', readValue: _readAlertLocationName)
|
||||
String? locationName,
|
||||
});
|
||||
}
|
||||
|
||||
@ -4089,7 +4291,7 @@ class __$$AssetAlertModelImplCopyWithImpl<$Res>
|
||||
Object? dueDate = freezed,
|
||||
Object? daysRemaining = freezed,
|
||||
Object? status = freezed,
|
||||
Object? plantName = freezed,
|
||||
Object? locationName = freezed,
|
||||
}) {
|
||||
return _then(
|
||||
_$AssetAlertModelImpl(
|
||||
@ -4137,9 +4339,9 @@ class __$$AssetAlertModelImplCopyWithImpl<$Res>
|
||||
? _value.status
|
||||
: status // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
plantName: freezed == plantName
|
||||
? _value.plantName
|
||||
: plantName // ignore: cast_nullable_to_non_nullable
|
||||
locationName: freezed == locationName
|
||||
? _value.locationName
|
||||
: locationName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
),
|
||||
);
|
||||
@ -4163,7 +4365,8 @@ class _$AssetAlertModelImpl implements _AssetAlertModel {
|
||||
@JsonKey(name: 'days_remaining', fromJson: _intFromJsonNullable)
|
||||
this.daysRemaining,
|
||||
this.status,
|
||||
@JsonKey(name: 'plant_name') this.plantName,
|
||||
@JsonKey(name: 'location_name', readValue: _readAlertLocationName)
|
||||
this.locationName,
|
||||
});
|
||||
|
||||
factory _$AssetAlertModelImpl.fromJson(Map<String, dynamic> json) =>
|
||||
@ -4199,12 +4402,12 @@ class _$AssetAlertModelImpl implements _AssetAlertModel {
|
||||
@override
|
||||
final String? status;
|
||||
@override
|
||||
@JsonKey(name: 'plant_name')
|
||||
final String? plantName;
|
||||
@JsonKey(name: 'location_name', readValue: _readAlertLocationName)
|
||||
final String? locationName;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AssetAlertModel(id: $id, assetId: $assetId, assetName: $assetName, assetCode: $assetCode, type: $type, title: $title, message: $message, expiryDate: $expiryDate, dueDate: $dueDate, daysRemaining: $daysRemaining, status: $status, plantName: $plantName)';
|
||||
return 'AssetAlertModel(id: $id, assetId: $assetId, assetName: $assetName, assetCode: $assetCode, type: $type, title: $title, message: $message, expiryDate: $expiryDate, dueDate: $dueDate, daysRemaining: $daysRemaining, status: $status, locationName: $locationName)';
|
||||
}
|
||||
|
||||
@override
|
||||
@ -4227,8 +4430,8 @@ class _$AssetAlertModelImpl implements _AssetAlertModel {
|
||||
(identical(other.daysRemaining, daysRemaining) ||
|
||||
other.daysRemaining == daysRemaining) &&
|
||||
(identical(other.status, status) || other.status == status) &&
|
||||
(identical(other.plantName, plantName) ||
|
||||
other.plantName == plantName));
|
||||
(identical(other.locationName, locationName) ||
|
||||
other.locationName == locationName));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@ -4246,7 +4449,7 @@ class _$AssetAlertModelImpl implements _AssetAlertModel {
|
||||
dueDate,
|
||||
daysRemaining,
|
||||
status,
|
||||
plantName,
|
||||
locationName,
|
||||
);
|
||||
|
||||
/// Create a copy of AssetAlertModel
|
||||
@ -4282,7 +4485,8 @@ abstract class _AssetAlertModel implements AssetAlertModel {
|
||||
@JsonKey(name: 'days_remaining', fromJson: _intFromJsonNullable)
|
||||
final int? daysRemaining,
|
||||
final String? status,
|
||||
@JsonKey(name: 'plant_name') final String? plantName,
|
||||
@JsonKey(name: 'location_name', readValue: _readAlertLocationName)
|
||||
final String? locationName,
|
||||
}) = _$AssetAlertModelImpl;
|
||||
|
||||
factory _AssetAlertModel.fromJson(Map<String, dynamic> json) =
|
||||
@ -4318,8 +4522,8 @@ abstract class _AssetAlertModel implements AssetAlertModel {
|
||||
@override
|
||||
String? get status;
|
||||
@override
|
||||
@JsonKey(name: 'plant_name')
|
||||
String? get plantName;
|
||||
@JsonKey(name: 'location_name', readValue: _readAlertLocationName)
|
||||
String? get locationName;
|
||||
|
||||
/// Create a copy of AssetAlertModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
|
||||
@ -57,18 +57,26 @@ _$AssetModelImpl _$$AssetModelImplFromJson(Map<String, dynamic> json) =>
|
||||
),
|
||||
assetSubcategoryName:
|
||||
_readItemSubcategoryName(json, 'item_subcategory_name') as String?,
|
||||
plantId: _intFromJsonNullable(_readPlantId(json, 'plant_id')),
|
||||
plantName: _readPlantName(json, 'plant_name') as String?,
|
||||
locationId: _intFromJsonNullable(_readLocationId(json, 'location_id')),
|
||||
locationName: _readLocationName(json, 'location_name') as String?,
|
||||
brandModel: json['brand_model'] as String?,
|
||||
manufacturer: json['manufacturer'] as String?,
|
||||
serialNumber: json['serial_number'] as String?,
|
||||
partNumber: json['part_number'] as String?,
|
||||
departmentId: _intFromJsonNullable(json['department_id']),
|
||||
departmentName: _readDepartmentName(json, 'department_name') as String?,
|
||||
warehouseId: _intFromJsonNullable(json['warehouse_id']),
|
||||
warehouseName: _readWarehouseName(json, 'warehouse_name') as String?,
|
||||
locationDetail: json['location_detail'] as String?,
|
||||
assignedToUserId: _intFromJsonNullable(json['assigned_to_user_id']),
|
||||
maintenanceInchargeUserId: _intFromJsonNullable(
|
||||
json['maintenance_incharge_user_id'],
|
||||
),
|
||||
maintenanceFrequencyInDays: _intFromJsonNullable(
|
||||
json['maintenance_frequency_in_days'],
|
||||
),
|
||||
maintenanceChecklistJson: _checklistFromJson(
|
||||
json['maintenance_checklist_json'],
|
||||
),
|
||||
commencementDate: _dateFromJsonNullable(json['commencement_date']),
|
||||
vendorId: _intFromJsonNullable(json['vendor_id']),
|
||||
vendorName: _readVendorNameFromNested(json, 'vendor_name') as String?,
|
||||
poId: _intFromJsonNullable(json['po_id']),
|
||||
@ -91,52 +99,61 @@ _$AssetModelImpl _$$AssetModelImplFromJson(Map<String, dynamic> json) =>
|
||||
isActive: json['is_active'] as bool? ?? true,
|
||||
createdAt: _dateFromJsonNullable(json['created_at']),
|
||||
updatedAt: _dateFromJsonNullable(json['updated_at']),
|
||||
maintenance: AssetMaintenanceSummary.fromJsonNullable(
|
||||
json['maintenance'],
|
||||
),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$AssetModelImplToJson(_$AssetModelImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'asset_name': instance.assetName,
|
||||
'asset_code': instance.assetCode,
|
||||
'item_category_id': instance.assetCategoryId,
|
||||
'item_category_name': instance.assetCategoryName,
|
||||
'item_subcategory_id': instance.assetSubcategoryId,
|
||||
'item_subcategory_name': instance.assetSubcategoryName,
|
||||
'plant_id': instance.plantId,
|
||||
'plant_name': instance.plantName,
|
||||
'brand_model': instance.brandModel,
|
||||
'manufacturer': instance.manufacturer,
|
||||
'serial_number': instance.serialNumber,
|
||||
'part_number': instance.partNumber,
|
||||
'department_id': instance.departmentId,
|
||||
'department_name': instance.departmentName,
|
||||
'warehouse_id': instance.warehouseId,
|
||||
'warehouse_name': instance.warehouseName,
|
||||
'location_detail': instance.locationDetail,
|
||||
'assigned_to_user_id': instance.assignedToUserId,
|
||||
'vendor_id': instance.vendorId,
|
||||
'vendor_name': instance.vendorName,
|
||||
'po_id': instance.poId,
|
||||
'grn_id': instance.grnId,
|
||||
'grn_item_id': instance.grnItemId,
|
||||
'purchase_date': instance.purchaseDate?.toIso8601String(),
|
||||
'purchase_cost': instance.purchaseCost,
|
||||
'useful_life_years': instance.usefulLifeYears,
|
||||
'depreciation_method': instance.depreciationMethod,
|
||||
'depreciation_rate': instance.depreciationRate,
|
||||
'salvage_value': instance.salvageValue,
|
||||
'warranty_expiry_date': instance.warrantyExpiryDate?.toIso8601String(),
|
||||
'condition': instance.condition,
|
||||
'status': instance.status,
|
||||
'qr_code_value': instance.qrCodeValue,
|
||||
'disposal_date': instance.disposalDate?.toIso8601String(),
|
||||
'disposal_reason': instance.disposalReason,
|
||||
'disposal_value': instance.disposalValue,
|
||||
'remarks': instance.remarks,
|
||||
'is_active': instance.isActive,
|
||||
'created_at': instance.createdAt?.toIso8601String(),
|
||||
'updated_at': instance.updatedAt?.toIso8601String(),
|
||||
};
|
||||
Map<String, dynamic> _$$AssetModelImplToJson(
|
||||
_$AssetModelImpl instance,
|
||||
) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'asset_name': instance.assetName,
|
||||
'asset_code': instance.assetCode,
|
||||
'item_category_id': instance.assetCategoryId,
|
||||
'item_category_name': instance.assetCategoryName,
|
||||
'item_subcategory_id': instance.assetSubcategoryId,
|
||||
'item_subcategory_name': instance.assetSubcategoryName,
|
||||
'location_id': instance.locationId,
|
||||
'location_name': instance.locationName,
|
||||
'brand_model': instance.brandModel,
|
||||
'manufacturer': instance.manufacturer,
|
||||
'serial_number': instance.serialNumber,
|
||||
'part_number': instance.partNumber,
|
||||
'department_id': instance.departmentId,
|
||||
'department_name': instance.departmentName,
|
||||
'location_detail': instance.locationDetail,
|
||||
'assigned_to_user_id': instance.assignedToUserId,
|
||||
'maintenance_incharge_user_id': instance.maintenanceInchargeUserId,
|
||||
'maintenance_frequency_in_days': instance.maintenanceFrequencyInDays,
|
||||
'maintenance_checklist_json': _checklistToJson(
|
||||
instance.maintenanceChecklistJson,
|
||||
),
|
||||
'commencement_date': instance.commencementDate?.toIso8601String(),
|
||||
'vendor_id': instance.vendorId,
|
||||
'vendor_name': instance.vendorName,
|
||||
'po_id': instance.poId,
|
||||
'grn_id': instance.grnId,
|
||||
'grn_item_id': instance.grnItemId,
|
||||
'purchase_date': instance.purchaseDate?.toIso8601String(),
|
||||
'purchase_cost': instance.purchaseCost,
|
||||
'useful_life_years': instance.usefulLifeYears,
|
||||
'depreciation_method': instance.depreciationMethod,
|
||||
'depreciation_rate': instance.depreciationRate,
|
||||
'salvage_value': instance.salvageValue,
|
||||
'warranty_expiry_date': instance.warrantyExpiryDate?.toIso8601String(),
|
||||
'condition': instance.condition,
|
||||
'status': instance.status,
|
||||
'qr_code_value': instance.qrCodeValue,
|
||||
'disposal_date': instance.disposalDate?.toIso8601String(),
|
||||
'disposal_reason': instance.disposalReason,
|
||||
'disposal_value': instance.disposalValue,
|
||||
'remarks': instance.remarks,
|
||||
'is_active': instance.isActive,
|
||||
'created_at': instance.createdAt?.toIso8601String(),
|
||||
'updated_at': instance.updatedAt?.toIso8601String(),
|
||||
'maintenance': AssetMaintenanceSummary.toJsonNullable(instance.maintenance),
|
||||
};
|
||||
|
||||
_$AmcContractModelImpl _$$AmcContractModelImplFromJson(
|
||||
Map<String, dynamic> json,
|
||||
@ -314,7 +331,7 @@ _$AssetAlertModelImpl _$$AssetAlertModelImplFromJson(
|
||||
dueDate: _dateFromJsonNullable(json['due_date']),
|
||||
daysRemaining: _intFromJsonNullable(json['days_remaining']),
|
||||
status: json['status'] as String?,
|
||||
plantName: json['plant_name'] as String?,
|
||||
locationName: _readAlertLocationName(json, 'location_name') as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$AssetAlertModelImplToJson(
|
||||
@ -331,5 +348,5 @@ Map<String, dynamic> _$$AssetAlertModelImplToJson(
|
||||
'due_date': instance.dueDate?.toIso8601String(),
|
||||
'days_remaining': instance.daysRemaining,
|
||||
'status': instance.status,
|
||||
'plant_name': instance.plantName,
|
||||
'location_name': instance.locationName,
|
||||
};
|
||||
|
||||
@ -49,11 +49,32 @@ Object? _readVendorType(Map<dynamic, dynamic> json, String key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object? _readPlantName(Map<dynamic, dynamic> json, String key) =>
|
||||
_readNestedName(json, 'plant_name', 'plant');
|
||||
Object? _readLocationDisplayName(
|
||||
Map<dynamic, dynamic> json,
|
||||
String flatKey,
|
||||
String nestedKey,
|
||||
) {
|
||||
final flat = json[flatKey];
|
||||
if (flat is String && flat.isNotEmpty) return flat;
|
||||
final nested = json[nestedKey];
|
||||
if (nested is Map) {
|
||||
final name = nested['name']?.toString().trim();
|
||||
if (name == null || name.isEmpty) return null;
|
||||
final type = nested['type']?.toString().trim();
|
||||
if (type != null && type.isNotEmpty) {
|
||||
final typeLabel = type[0].toUpperCase() + type.substring(1);
|
||||
return '$name ($typeLabel)';
|
||||
}
|
||||
return name;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Object? _readWarehouseName(Map<dynamic, dynamic> json, String key) =>
|
||||
_readNestedName(json, 'warehouse_name', 'warehouse');
|
||||
Object? _readBillingName(Map<dynamic, dynamic> json, String key) =>
|
||||
_readLocationDisplayName(json, 'billing_name', 'billing');
|
||||
|
||||
Object? _readShippingName(Map<dynamic, dynamic> json, String key) =>
|
||||
_readLocationDisplayName(json, 'shipping_name', 'shipping');
|
||||
|
||||
Object? _readItemName(Map<dynamic, dynamic> json, String key) {
|
||||
final flat = json['item_name'];
|
||||
@ -154,10 +175,11 @@ class PurchaseOrderModel with _$PurchaseOrderModel {
|
||||
@JsonKey(name: 'vendor_id', fromJson: _intFromJsonNullable) int? vendorId,
|
||||
@JsonKey(name: 'vendor_name', readValue: _readVendorName) String? vendorName,
|
||||
@JsonKey(name: 'vendor_type', readValue: _readVendorType) String? vendorType,
|
||||
@JsonKey(name: 'plant_id', fromJson: _intFromJsonNullable) int? plantId,
|
||||
@JsonKey(name: 'plant_name', readValue: _readPlantName) String? plantName,
|
||||
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable) int? warehouseId,
|
||||
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName) String? warehouseName,
|
||||
@JsonKey(name: 'billing_id', fromJson: _intFromJsonNullable) int? billingId,
|
||||
@JsonKey(name: 'billing_name', readValue: _readBillingName) String? billingName,
|
||||
@JsonKey(name: 'shipping_id', fromJson: _intFromJsonNullable) int? shippingId,
|
||||
@JsonKey(name: 'shipping_name', readValue: _readShippingName)
|
||||
String? shippingName,
|
||||
@JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable) int? paymentTermId,
|
||||
@JsonKey(name: 'delivery_term_id', fromJson: _intFromJsonNullable) int? deliveryTermId,
|
||||
@JsonKey(name: 'expected_delivery_date', fromJson: _dateFromJsonNullable)
|
||||
@ -172,6 +194,9 @@ class PurchaseOrderModel with _$PurchaseOrderModel {
|
||||
double? taxableAmount,
|
||||
@JsonKey(name: 'tax_total', readValue: _readTaxTotal)
|
||||
double? taxAmount,
|
||||
@JsonKey(name: 'cgst', fromJson: _doubleFromJsonNullable) double? cgst,
|
||||
@JsonKey(name: 'sgst', fromJson: _doubleFromJsonNullable) double? sgst,
|
||||
@JsonKey(name: 'igst', fromJson: _doubleFromJsonNullable) double? igst,
|
||||
@JsonKey(name: 'grand_total', readValue: _readGrandTotal)
|
||||
double? totalAmount,
|
||||
@JsonKey(name: 'terms_and_conditions') String? termsAndConditions,
|
||||
@ -265,7 +290,8 @@ class PurchaseOrderListQuery with _$PurchaseOrderListQuery {
|
||||
String? search,
|
||||
String? status,
|
||||
int? vendorId,
|
||||
int? plantId,
|
||||
int? billingId,
|
||||
int? shippingId,
|
||||
String? dateFrom,
|
||||
String? dateTo,
|
||||
}) = _PurchaseOrderListQuery;
|
||||
|
||||
@ -34,14 +34,14 @@ mixin _$PurchaseOrderModel {
|
||||
String? get vendorName => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'vendor_type', readValue: _readVendorType)
|
||||
String? get vendorType => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'plant_id', fromJson: _intFromJsonNullable)
|
||||
int? get plantId => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'plant_name', readValue: _readPlantName)
|
||||
String? get plantName => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable)
|
||||
int? get warehouseId => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName)
|
||||
String? get warehouseName => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'billing_id', fromJson: _intFromJsonNullable)
|
||||
int? get billingId => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'billing_name', readValue: _readBillingName)
|
||||
String? get billingName => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'shipping_id', fromJson: _intFromJsonNullable)
|
||||
int? get shippingId => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'shipping_name', readValue: _readShippingName)
|
||||
String? get shippingName => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable)
|
||||
int? get paymentTermId => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'delivery_term_id', fromJson: _intFromJsonNullable)
|
||||
@ -58,6 +58,12 @@ mixin _$PurchaseOrderModel {
|
||||
double? get taxableAmount => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'tax_total', readValue: _readTaxTotal)
|
||||
double? get taxAmount => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'cgst', fromJson: _doubleFromJsonNullable)
|
||||
double? get cgst => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'sgst', fromJson: _doubleFromJsonNullable)
|
||||
double? get sgst => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'igst', fromJson: _doubleFromJsonNullable)
|
||||
double? get igst => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'grand_total', readValue: _readGrandTotal)
|
||||
double? get totalAmount => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'terms_and_conditions')
|
||||
@ -98,12 +104,13 @@ abstract class $PurchaseOrderModelCopyWith<$Res> {
|
||||
String? vendorName,
|
||||
@JsonKey(name: 'vendor_type', readValue: _readVendorType)
|
||||
String? vendorType,
|
||||
@JsonKey(name: 'plant_id', fromJson: _intFromJsonNullable) int? plantId,
|
||||
@JsonKey(name: 'plant_name', readValue: _readPlantName) String? plantName,
|
||||
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable)
|
||||
int? warehouseId,
|
||||
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName)
|
||||
String? warehouseName,
|
||||
@JsonKey(name: 'billing_id', fromJson: _intFromJsonNullable) int? billingId,
|
||||
@JsonKey(name: 'billing_name', readValue: _readBillingName)
|
||||
String? billingName,
|
||||
@JsonKey(name: 'shipping_id', fromJson: _intFromJsonNullable)
|
||||
int? shippingId,
|
||||
@JsonKey(name: 'shipping_name', readValue: _readShippingName)
|
||||
String? shippingName,
|
||||
@JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable)
|
||||
int? paymentTermId,
|
||||
@JsonKey(name: 'delivery_term_id', fromJson: _intFromJsonNullable)
|
||||
@ -118,6 +125,9 @@ abstract class $PurchaseOrderModelCopyWith<$Res> {
|
||||
double? otherCharges,
|
||||
@JsonKey(name: 'sub_total', readValue: _readSubTotal) double? taxableAmount,
|
||||
@JsonKey(name: 'tax_total', readValue: _readTaxTotal) double? taxAmount,
|
||||
@JsonKey(name: 'cgst', fromJson: _doubleFromJsonNullable) double? cgst,
|
||||
@JsonKey(name: 'sgst', fromJson: _doubleFromJsonNullable) double? sgst,
|
||||
@JsonKey(name: 'igst', fromJson: _doubleFromJsonNullable) double? igst,
|
||||
@JsonKey(name: 'grand_total', readValue: _readGrandTotal)
|
||||
double? totalAmount,
|
||||
@JsonKey(name: 'terms_and_conditions') String? termsAndConditions,
|
||||
@ -154,10 +164,10 @@ class _$PurchaseOrderModelCopyWithImpl<$Res, $Val extends PurchaseOrderModel>
|
||||
Object? vendorId = freezed,
|
||||
Object? vendorName = freezed,
|
||||
Object? vendorType = freezed,
|
||||
Object? plantId = freezed,
|
||||
Object? plantName = freezed,
|
||||
Object? warehouseId = freezed,
|
||||
Object? warehouseName = freezed,
|
||||
Object? billingId = freezed,
|
||||
Object? billingName = freezed,
|
||||
Object? shippingId = freezed,
|
||||
Object? shippingName = freezed,
|
||||
Object? paymentTermId = freezed,
|
||||
Object? deliveryTermId = freezed,
|
||||
Object? expectedDeliveryDate = freezed,
|
||||
@ -166,6 +176,9 @@ class _$PurchaseOrderModelCopyWithImpl<$Res, $Val extends PurchaseOrderModel>
|
||||
Object? otherCharges = freezed,
|
||||
Object? taxableAmount = freezed,
|
||||
Object? taxAmount = freezed,
|
||||
Object? cgst = freezed,
|
||||
Object? sgst = freezed,
|
||||
Object? igst = freezed,
|
||||
Object? totalAmount = freezed,
|
||||
Object? termsAndConditions = freezed,
|
||||
Object? remarks = freezed,
|
||||
@ -204,21 +217,21 @@ class _$PurchaseOrderModelCopyWithImpl<$Res, $Val extends PurchaseOrderModel>
|
||||
? _value.vendorType
|
||||
: vendorType // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
plantId: freezed == plantId
|
||||
? _value.plantId
|
||||
: plantId // ignore: cast_nullable_to_non_nullable
|
||||
billingId: freezed == billingId
|
||||
? _value.billingId
|
||||
: billingId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
plantName: freezed == plantName
|
||||
? _value.plantName
|
||||
: plantName // ignore: cast_nullable_to_non_nullable
|
||||
billingName: freezed == billingName
|
||||
? _value.billingName
|
||||
: billingName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
warehouseId: freezed == warehouseId
|
||||
? _value.warehouseId
|
||||
: warehouseId // ignore: cast_nullable_to_non_nullable
|
||||
shippingId: freezed == shippingId
|
||||
? _value.shippingId
|
||||
: shippingId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
warehouseName: freezed == warehouseName
|
||||
? _value.warehouseName
|
||||
: warehouseName // ignore: cast_nullable_to_non_nullable
|
||||
shippingName: freezed == shippingName
|
||||
? _value.shippingName
|
||||
: shippingName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
paymentTermId: freezed == paymentTermId
|
||||
? _value.paymentTermId
|
||||
@ -252,6 +265,18 @@ class _$PurchaseOrderModelCopyWithImpl<$Res, $Val extends PurchaseOrderModel>
|
||||
? _value.taxAmount
|
||||
: taxAmount // ignore: cast_nullable_to_non_nullable
|
||||
as double?,
|
||||
cgst: freezed == cgst
|
||||
? _value.cgst
|
||||
: cgst // ignore: cast_nullable_to_non_nullable
|
||||
as double?,
|
||||
sgst: freezed == sgst
|
||||
? _value.sgst
|
||||
: sgst // ignore: cast_nullable_to_non_nullable
|
||||
as double?,
|
||||
igst: freezed == igst
|
||||
? _value.igst
|
||||
: igst // ignore: cast_nullable_to_non_nullable
|
||||
as double?,
|
||||
totalAmount: freezed == totalAmount
|
||||
? _value.totalAmount
|
||||
: totalAmount // ignore: cast_nullable_to_non_nullable
|
||||
@ -305,12 +330,13 @@ abstract class _$$PurchaseOrderModelImplCopyWith<$Res>
|
||||
String? vendorName,
|
||||
@JsonKey(name: 'vendor_type', readValue: _readVendorType)
|
||||
String? vendorType,
|
||||
@JsonKey(name: 'plant_id', fromJson: _intFromJsonNullable) int? plantId,
|
||||
@JsonKey(name: 'plant_name', readValue: _readPlantName) String? plantName,
|
||||
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable)
|
||||
int? warehouseId,
|
||||
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName)
|
||||
String? warehouseName,
|
||||
@JsonKey(name: 'billing_id', fromJson: _intFromJsonNullable) int? billingId,
|
||||
@JsonKey(name: 'billing_name', readValue: _readBillingName)
|
||||
String? billingName,
|
||||
@JsonKey(name: 'shipping_id', fromJson: _intFromJsonNullable)
|
||||
int? shippingId,
|
||||
@JsonKey(name: 'shipping_name', readValue: _readShippingName)
|
||||
String? shippingName,
|
||||
@JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable)
|
||||
int? paymentTermId,
|
||||
@JsonKey(name: 'delivery_term_id', fromJson: _intFromJsonNullable)
|
||||
@ -325,6 +351,9 @@ abstract class _$$PurchaseOrderModelImplCopyWith<$Res>
|
||||
double? otherCharges,
|
||||
@JsonKey(name: 'sub_total', readValue: _readSubTotal) double? taxableAmount,
|
||||
@JsonKey(name: 'tax_total', readValue: _readTaxTotal) double? taxAmount,
|
||||
@JsonKey(name: 'cgst', fromJson: _doubleFromJsonNullable) double? cgst,
|
||||
@JsonKey(name: 'sgst', fromJson: _doubleFromJsonNullable) double? sgst,
|
||||
@JsonKey(name: 'igst', fromJson: _doubleFromJsonNullable) double? igst,
|
||||
@JsonKey(name: 'grand_total', readValue: _readGrandTotal)
|
||||
double? totalAmount,
|
||||
@JsonKey(name: 'terms_and_conditions') String? termsAndConditions,
|
||||
@ -360,10 +389,10 @@ class __$$PurchaseOrderModelImplCopyWithImpl<$Res>
|
||||
Object? vendorId = freezed,
|
||||
Object? vendorName = freezed,
|
||||
Object? vendorType = freezed,
|
||||
Object? plantId = freezed,
|
||||
Object? plantName = freezed,
|
||||
Object? warehouseId = freezed,
|
||||
Object? warehouseName = freezed,
|
||||
Object? billingId = freezed,
|
||||
Object? billingName = freezed,
|
||||
Object? shippingId = freezed,
|
||||
Object? shippingName = freezed,
|
||||
Object? paymentTermId = freezed,
|
||||
Object? deliveryTermId = freezed,
|
||||
Object? expectedDeliveryDate = freezed,
|
||||
@ -372,6 +401,9 @@ class __$$PurchaseOrderModelImplCopyWithImpl<$Res>
|
||||
Object? otherCharges = freezed,
|
||||
Object? taxableAmount = freezed,
|
||||
Object? taxAmount = freezed,
|
||||
Object? cgst = freezed,
|
||||
Object? sgst = freezed,
|
||||
Object? igst = freezed,
|
||||
Object? totalAmount = freezed,
|
||||
Object? termsAndConditions = freezed,
|
||||
Object? remarks = freezed,
|
||||
@ -410,21 +442,21 @@ class __$$PurchaseOrderModelImplCopyWithImpl<$Res>
|
||||
? _value.vendorType
|
||||
: vendorType // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
plantId: freezed == plantId
|
||||
? _value.plantId
|
||||
: plantId // ignore: cast_nullable_to_non_nullable
|
||||
billingId: freezed == billingId
|
||||
? _value.billingId
|
||||
: billingId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
plantName: freezed == plantName
|
||||
? _value.plantName
|
||||
: plantName // ignore: cast_nullable_to_non_nullable
|
||||
billingName: freezed == billingName
|
||||
? _value.billingName
|
||||
: billingName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
warehouseId: freezed == warehouseId
|
||||
? _value.warehouseId
|
||||
: warehouseId // ignore: cast_nullable_to_non_nullable
|
||||
shippingId: freezed == shippingId
|
||||
? _value.shippingId
|
||||
: shippingId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
warehouseName: freezed == warehouseName
|
||||
? _value.warehouseName
|
||||
: warehouseName // ignore: cast_nullable_to_non_nullable
|
||||
shippingName: freezed == shippingName
|
||||
? _value.shippingName
|
||||
: shippingName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
paymentTermId: freezed == paymentTermId
|
||||
? _value.paymentTermId
|
||||
@ -458,6 +490,18 @@ class __$$PurchaseOrderModelImplCopyWithImpl<$Res>
|
||||
? _value.taxAmount
|
||||
: taxAmount // ignore: cast_nullable_to_non_nullable
|
||||
as double?,
|
||||
cgst: freezed == cgst
|
||||
? _value.cgst
|
||||
: cgst // ignore: cast_nullable_to_non_nullable
|
||||
as double?,
|
||||
sgst: freezed == sgst
|
||||
? _value.sgst
|
||||
: sgst // ignore: cast_nullable_to_non_nullable
|
||||
as double?,
|
||||
igst: freezed == igst
|
||||
? _value.igst
|
||||
: igst // ignore: cast_nullable_to_non_nullable
|
||||
as double?,
|
||||
totalAmount: freezed == totalAmount
|
||||
? _value.totalAmount
|
||||
: totalAmount // ignore: cast_nullable_to_non_nullable
|
||||
@ -502,12 +546,13 @@ class _$PurchaseOrderModelImpl extends _PurchaseOrderModel {
|
||||
@JsonKey(name: 'vendor_id', fromJson: _intFromJsonNullable) this.vendorId,
|
||||
@JsonKey(name: 'vendor_name', readValue: _readVendorName) this.vendorName,
|
||||
@JsonKey(name: 'vendor_type', readValue: _readVendorType) this.vendorType,
|
||||
@JsonKey(name: 'plant_id', fromJson: _intFromJsonNullable) this.plantId,
|
||||
@JsonKey(name: 'plant_name', readValue: _readPlantName) this.plantName,
|
||||
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable)
|
||||
this.warehouseId,
|
||||
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName)
|
||||
this.warehouseName,
|
||||
@JsonKey(name: 'billing_id', fromJson: _intFromJsonNullable) this.billingId,
|
||||
@JsonKey(name: 'billing_name', readValue: _readBillingName)
|
||||
this.billingName,
|
||||
@JsonKey(name: 'shipping_id', fromJson: _intFromJsonNullable)
|
||||
this.shippingId,
|
||||
@JsonKey(name: 'shipping_name', readValue: _readShippingName)
|
||||
this.shippingName,
|
||||
@JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable)
|
||||
this.paymentTermId,
|
||||
@JsonKey(name: 'delivery_term_id', fromJson: _intFromJsonNullable)
|
||||
@ -522,6 +567,9 @@ class _$PurchaseOrderModelImpl extends _PurchaseOrderModel {
|
||||
this.otherCharges,
|
||||
@JsonKey(name: 'sub_total', readValue: _readSubTotal) this.taxableAmount,
|
||||
@JsonKey(name: 'tax_total', readValue: _readTaxTotal) this.taxAmount,
|
||||
@JsonKey(name: 'cgst', fromJson: _doubleFromJsonNullable) this.cgst,
|
||||
@JsonKey(name: 'sgst', fromJson: _doubleFromJsonNullable) this.sgst,
|
||||
@JsonKey(name: 'igst', fromJson: _doubleFromJsonNullable) this.igst,
|
||||
@JsonKey(name: 'grand_total', readValue: _readGrandTotal) this.totalAmount,
|
||||
@JsonKey(name: 'terms_and_conditions') this.termsAndConditions,
|
||||
this.remarks,
|
||||
@ -560,17 +608,17 @@ class _$PurchaseOrderModelImpl extends _PurchaseOrderModel {
|
||||
@JsonKey(name: 'vendor_type', readValue: _readVendorType)
|
||||
final String? vendorType;
|
||||
@override
|
||||
@JsonKey(name: 'plant_id', fromJson: _intFromJsonNullable)
|
||||
final int? plantId;
|
||||
@JsonKey(name: 'billing_id', fromJson: _intFromJsonNullable)
|
||||
final int? billingId;
|
||||
@override
|
||||
@JsonKey(name: 'plant_name', readValue: _readPlantName)
|
||||
final String? plantName;
|
||||
@JsonKey(name: 'billing_name', readValue: _readBillingName)
|
||||
final String? billingName;
|
||||
@override
|
||||
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable)
|
||||
final int? warehouseId;
|
||||
@JsonKey(name: 'shipping_id', fromJson: _intFromJsonNullable)
|
||||
final int? shippingId;
|
||||
@override
|
||||
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName)
|
||||
final String? warehouseName;
|
||||
@JsonKey(name: 'shipping_name', readValue: _readShippingName)
|
||||
final String? shippingName;
|
||||
@override
|
||||
@JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable)
|
||||
final int? paymentTermId;
|
||||
@ -596,6 +644,15 @@ class _$PurchaseOrderModelImpl extends _PurchaseOrderModel {
|
||||
@JsonKey(name: 'tax_total', readValue: _readTaxTotal)
|
||||
final double? taxAmount;
|
||||
@override
|
||||
@JsonKey(name: 'cgst', fromJson: _doubleFromJsonNullable)
|
||||
final double? cgst;
|
||||
@override
|
||||
@JsonKey(name: 'sgst', fromJson: _doubleFromJsonNullable)
|
||||
final double? sgst;
|
||||
@override
|
||||
@JsonKey(name: 'igst', fromJson: _doubleFromJsonNullable)
|
||||
final double? igst;
|
||||
@override
|
||||
@JsonKey(name: 'grand_total', readValue: _readGrandTotal)
|
||||
final double? totalAmount;
|
||||
@override
|
||||
@ -623,7 +680,7 @@ class _$PurchaseOrderModelImpl extends _PurchaseOrderModel {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'PurchaseOrderModel(id: $id, poNo: $poNo, poDate: $poDate, status: $status, vendorId: $vendorId, vendorName: $vendorName, vendorType: $vendorType, plantId: $plantId, plantName: $plantName, warehouseId: $warehouseId, warehouseName: $warehouseName, paymentTermId: $paymentTermId, deliveryTermId: $deliveryTermId, expectedDeliveryDate: $expectedDeliveryDate, discountAmount: $discountAmount, freightCharges: $freightCharges, otherCharges: $otherCharges, taxableAmount: $taxableAmount, taxAmount: $taxAmount, totalAmount: $totalAmount, termsAndConditions: $termsAndConditions, remarks: $remarks, revisionNo: $revisionNo, createdAt: $createdAt, updatedAt: $updatedAt, items: $items)';
|
||||
return 'PurchaseOrderModel(id: $id, poNo: $poNo, poDate: $poDate, status: $status, vendorId: $vendorId, vendorName: $vendorName, vendorType: $vendorType, billingId: $billingId, billingName: $billingName, shippingId: $shippingId, shippingName: $shippingName, paymentTermId: $paymentTermId, deliveryTermId: $deliveryTermId, expectedDeliveryDate: $expectedDeliveryDate, discountAmount: $discountAmount, freightCharges: $freightCharges, otherCharges: $otherCharges, taxableAmount: $taxableAmount, taxAmount: $taxAmount, cgst: $cgst, sgst: $sgst, igst: $igst, totalAmount: $totalAmount, termsAndConditions: $termsAndConditions, remarks: $remarks, revisionNo: $revisionNo, createdAt: $createdAt, updatedAt: $updatedAt, items: $items)';
|
||||
}
|
||||
|
||||
@override
|
||||
@ -641,13 +698,14 @@ class _$PurchaseOrderModelImpl extends _PurchaseOrderModel {
|
||||
other.vendorName == vendorName) &&
|
||||
(identical(other.vendorType, vendorType) ||
|
||||
other.vendorType == vendorType) &&
|
||||
(identical(other.plantId, plantId) || other.plantId == plantId) &&
|
||||
(identical(other.plantName, plantName) ||
|
||||
other.plantName == plantName) &&
|
||||
(identical(other.warehouseId, warehouseId) ||
|
||||
other.warehouseId == warehouseId) &&
|
||||
(identical(other.warehouseName, warehouseName) ||
|
||||
other.warehouseName == warehouseName) &&
|
||||
(identical(other.billingId, billingId) ||
|
||||
other.billingId == billingId) &&
|
||||
(identical(other.billingName, billingName) ||
|
||||
other.billingName == billingName) &&
|
||||
(identical(other.shippingId, shippingId) ||
|
||||
other.shippingId == shippingId) &&
|
||||
(identical(other.shippingName, shippingName) ||
|
||||
other.shippingName == shippingName) &&
|
||||
(identical(other.paymentTermId, paymentTermId) ||
|
||||
other.paymentTermId == paymentTermId) &&
|
||||
(identical(other.deliveryTermId, deliveryTermId) ||
|
||||
@ -664,6 +722,9 @@ class _$PurchaseOrderModelImpl extends _PurchaseOrderModel {
|
||||
other.taxableAmount == taxableAmount) &&
|
||||
(identical(other.taxAmount, taxAmount) ||
|
||||
other.taxAmount == taxAmount) &&
|
||||
(identical(other.cgst, cgst) || other.cgst == cgst) &&
|
||||
(identical(other.sgst, sgst) || other.sgst == sgst) &&
|
||||
(identical(other.igst, igst) || other.igst == igst) &&
|
||||
(identical(other.totalAmount, totalAmount) ||
|
||||
other.totalAmount == totalAmount) &&
|
||||
(identical(other.termsAndConditions, termsAndConditions) ||
|
||||
@ -689,10 +750,10 @@ class _$PurchaseOrderModelImpl extends _PurchaseOrderModel {
|
||||
vendorId,
|
||||
vendorName,
|
||||
vendorType,
|
||||
plantId,
|
||||
plantName,
|
||||
warehouseId,
|
||||
warehouseName,
|
||||
billingId,
|
||||
billingName,
|
||||
shippingId,
|
||||
shippingName,
|
||||
paymentTermId,
|
||||
deliveryTermId,
|
||||
expectedDeliveryDate,
|
||||
@ -701,6 +762,9 @@ class _$PurchaseOrderModelImpl extends _PurchaseOrderModel {
|
||||
otherCharges,
|
||||
taxableAmount,
|
||||
taxAmount,
|
||||
cgst,
|
||||
sgst,
|
||||
igst,
|
||||
totalAmount,
|
||||
termsAndConditions,
|
||||
remarks,
|
||||
@ -740,14 +804,14 @@ abstract class _PurchaseOrderModel extends PurchaseOrderModel {
|
||||
final String? vendorName,
|
||||
@JsonKey(name: 'vendor_type', readValue: _readVendorType)
|
||||
final String? vendorType,
|
||||
@JsonKey(name: 'plant_id', fromJson: _intFromJsonNullable)
|
||||
final int? plantId,
|
||||
@JsonKey(name: 'plant_name', readValue: _readPlantName)
|
||||
final String? plantName,
|
||||
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable)
|
||||
final int? warehouseId,
|
||||
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName)
|
||||
final String? warehouseName,
|
||||
@JsonKey(name: 'billing_id', fromJson: _intFromJsonNullable)
|
||||
final int? billingId,
|
||||
@JsonKey(name: 'billing_name', readValue: _readBillingName)
|
||||
final String? billingName,
|
||||
@JsonKey(name: 'shipping_id', fromJson: _intFromJsonNullable)
|
||||
final int? shippingId,
|
||||
@JsonKey(name: 'shipping_name', readValue: _readShippingName)
|
||||
final String? shippingName,
|
||||
@JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable)
|
||||
final int? paymentTermId,
|
||||
@JsonKey(name: 'delivery_term_id', fromJson: _intFromJsonNullable)
|
||||
@ -764,6 +828,12 @@ abstract class _PurchaseOrderModel extends PurchaseOrderModel {
|
||||
final double? taxableAmount,
|
||||
@JsonKey(name: 'tax_total', readValue: _readTaxTotal)
|
||||
final double? taxAmount,
|
||||
@JsonKey(name: 'cgst', fromJson: _doubleFromJsonNullable)
|
||||
final double? cgst,
|
||||
@JsonKey(name: 'sgst', fromJson: _doubleFromJsonNullable)
|
||||
final double? sgst,
|
||||
@JsonKey(name: 'igst', fromJson: _doubleFromJsonNullable)
|
||||
final double? igst,
|
||||
@JsonKey(name: 'grand_total', readValue: _readGrandTotal)
|
||||
final double? totalAmount,
|
||||
@JsonKey(name: 'terms_and_conditions') final String? termsAndConditions,
|
||||
@ -802,17 +872,17 @@ abstract class _PurchaseOrderModel extends PurchaseOrderModel {
|
||||
@JsonKey(name: 'vendor_type', readValue: _readVendorType)
|
||||
String? get vendorType;
|
||||
@override
|
||||
@JsonKey(name: 'plant_id', fromJson: _intFromJsonNullable)
|
||||
int? get plantId;
|
||||
@JsonKey(name: 'billing_id', fromJson: _intFromJsonNullable)
|
||||
int? get billingId;
|
||||
@override
|
||||
@JsonKey(name: 'plant_name', readValue: _readPlantName)
|
||||
String? get plantName;
|
||||
@JsonKey(name: 'billing_name', readValue: _readBillingName)
|
||||
String? get billingName;
|
||||
@override
|
||||
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable)
|
||||
int? get warehouseId;
|
||||
@JsonKey(name: 'shipping_id', fromJson: _intFromJsonNullable)
|
||||
int? get shippingId;
|
||||
@override
|
||||
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName)
|
||||
String? get warehouseName;
|
||||
@JsonKey(name: 'shipping_name', readValue: _readShippingName)
|
||||
String? get shippingName;
|
||||
@override
|
||||
@JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable)
|
||||
int? get paymentTermId;
|
||||
@ -838,6 +908,15 @@ abstract class _PurchaseOrderModel extends PurchaseOrderModel {
|
||||
@JsonKey(name: 'tax_total', readValue: _readTaxTotal)
|
||||
double? get taxAmount;
|
||||
@override
|
||||
@JsonKey(name: 'cgst', fromJson: _doubleFromJsonNullable)
|
||||
double? get cgst;
|
||||
@override
|
||||
@JsonKey(name: 'sgst', fromJson: _doubleFromJsonNullable)
|
||||
double? get sgst;
|
||||
@override
|
||||
@JsonKey(name: 'igst', fromJson: _doubleFromJsonNullable)
|
||||
double? get igst;
|
||||
@override
|
||||
@JsonKey(name: 'grand_total', readValue: _readGrandTotal)
|
||||
double? get totalAmount;
|
||||
@override
|
||||
@ -1642,7 +1721,8 @@ mixin _$PurchaseOrderListQuery {
|
||||
String? get search => throw _privateConstructorUsedError;
|
||||
String? get status => throw _privateConstructorUsedError;
|
||||
int? get vendorId => throw _privateConstructorUsedError;
|
||||
int? get plantId => throw _privateConstructorUsedError;
|
||||
int? get billingId => throw _privateConstructorUsedError;
|
||||
int? get shippingId => throw _privateConstructorUsedError;
|
||||
String? get dateFrom => throw _privateConstructorUsedError;
|
||||
String? get dateTo => throw _privateConstructorUsedError;
|
||||
|
||||
@ -1666,7 +1746,8 @@ abstract class $PurchaseOrderListQueryCopyWith<$Res> {
|
||||
String? search,
|
||||
String? status,
|
||||
int? vendorId,
|
||||
int? plantId,
|
||||
int? billingId,
|
||||
int? shippingId,
|
||||
String? dateFrom,
|
||||
String? dateTo,
|
||||
});
|
||||
@ -1695,7 +1776,8 @@ class _$PurchaseOrderListQueryCopyWithImpl<
|
||||
Object? search = freezed,
|
||||
Object? status = freezed,
|
||||
Object? vendorId = freezed,
|
||||
Object? plantId = freezed,
|
||||
Object? billingId = freezed,
|
||||
Object? shippingId = freezed,
|
||||
Object? dateFrom = freezed,
|
||||
Object? dateTo = freezed,
|
||||
}) {
|
||||
@ -1721,9 +1803,13 @@ class _$PurchaseOrderListQueryCopyWithImpl<
|
||||
? _value.vendorId
|
||||
: vendorId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
plantId: freezed == plantId
|
||||
? _value.plantId
|
||||
: plantId // ignore: cast_nullable_to_non_nullable
|
||||
billingId: freezed == billingId
|
||||
? _value.billingId
|
||||
: billingId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
shippingId: freezed == shippingId
|
||||
? _value.shippingId
|
||||
: shippingId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
dateFrom: freezed == dateFrom
|
||||
? _value.dateFrom
|
||||
@ -1754,7 +1840,8 @@ abstract class _$$PurchaseOrderListQueryImplCopyWith<$Res>
|
||||
String? search,
|
||||
String? status,
|
||||
int? vendorId,
|
||||
int? plantId,
|
||||
int? billingId,
|
||||
int? shippingId,
|
||||
String? dateFrom,
|
||||
String? dateTo,
|
||||
});
|
||||
@ -1780,7 +1867,8 @@ class __$$PurchaseOrderListQueryImplCopyWithImpl<$Res>
|
||||
Object? search = freezed,
|
||||
Object? status = freezed,
|
||||
Object? vendorId = freezed,
|
||||
Object? plantId = freezed,
|
||||
Object? billingId = freezed,
|
||||
Object? shippingId = freezed,
|
||||
Object? dateFrom = freezed,
|
||||
Object? dateTo = freezed,
|
||||
}) {
|
||||
@ -1806,9 +1894,13 @@ class __$$PurchaseOrderListQueryImplCopyWithImpl<$Res>
|
||||
? _value.vendorId
|
||||
: vendorId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
plantId: freezed == plantId
|
||||
? _value.plantId
|
||||
: plantId // ignore: cast_nullable_to_non_nullable
|
||||
billingId: freezed == billingId
|
||||
? _value.billingId
|
||||
: billingId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
shippingId: freezed == shippingId
|
||||
? _value.shippingId
|
||||
: shippingId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
dateFrom: freezed == dateFrom
|
||||
? _value.dateFrom
|
||||
@ -1832,7 +1924,8 @@ class _$PurchaseOrderListQueryImpl implements _PurchaseOrderListQuery {
|
||||
this.search,
|
||||
this.status,
|
||||
this.vendorId,
|
||||
this.plantId,
|
||||
this.billingId,
|
||||
this.shippingId,
|
||||
this.dateFrom,
|
||||
this.dateTo,
|
||||
});
|
||||
@ -1850,7 +1943,9 @@ class _$PurchaseOrderListQueryImpl implements _PurchaseOrderListQuery {
|
||||
@override
|
||||
final int? vendorId;
|
||||
@override
|
||||
final int? plantId;
|
||||
final int? billingId;
|
||||
@override
|
||||
final int? shippingId;
|
||||
@override
|
||||
final String? dateFrom;
|
||||
@override
|
||||
@ -1858,7 +1953,7 @@ class _$PurchaseOrderListQueryImpl implements _PurchaseOrderListQuery {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'PurchaseOrderListQuery(page: $page, limit: $limit, search: $search, status: $status, vendorId: $vendorId, plantId: $plantId, dateFrom: $dateFrom, dateTo: $dateTo)';
|
||||
return 'PurchaseOrderListQuery(page: $page, limit: $limit, search: $search, status: $status, vendorId: $vendorId, billingId: $billingId, shippingId: $shippingId, dateFrom: $dateFrom, dateTo: $dateTo)';
|
||||
}
|
||||
|
||||
@override
|
||||
@ -1872,7 +1967,10 @@ class _$PurchaseOrderListQueryImpl implements _PurchaseOrderListQuery {
|
||||
(identical(other.status, status) || other.status == status) &&
|
||||
(identical(other.vendorId, vendorId) ||
|
||||
other.vendorId == vendorId) &&
|
||||
(identical(other.plantId, plantId) || other.plantId == plantId) &&
|
||||
(identical(other.billingId, billingId) ||
|
||||
other.billingId == billingId) &&
|
||||
(identical(other.shippingId, shippingId) ||
|
||||
other.shippingId == shippingId) &&
|
||||
(identical(other.dateFrom, dateFrom) ||
|
||||
other.dateFrom == dateFrom) &&
|
||||
(identical(other.dateTo, dateTo) || other.dateTo == dateTo));
|
||||
@ -1886,7 +1984,8 @@ class _$PurchaseOrderListQueryImpl implements _PurchaseOrderListQuery {
|
||||
search,
|
||||
status,
|
||||
vendorId,
|
||||
plantId,
|
||||
billingId,
|
||||
shippingId,
|
||||
dateFrom,
|
||||
dateTo,
|
||||
);
|
||||
@ -1911,7 +2010,8 @@ abstract class _PurchaseOrderListQuery implements PurchaseOrderListQuery {
|
||||
final String? search,
|
||||
final String? status,
|
||||
final int? vendorId,
|
||||
final int? plantId,
|
||||
final int? billingId,
|
||||
final int? shippingId,
|
||||
final String? dateFrom,
|
||||
final String? dateTo,
|
||||
}) = _$PurchaseOrderListQueryImpl;
|
||||
@ -1927,7 +2027,9 @@ abstract class _PurchaseOrderListQuery implements PurchaseOrderListQuery {
|
||||
@override
|
||||
int? get vendorId;
|
||||
@override
|
||||
int? get plantId;
|
||||
int? get billingId;
|
||||
@override
|
||||
int? get shippingId;
|
||||
@override
|
||||
String? get dateFrom;
|
||||
@override
|
||||
|
||||
@ -16,10 +16,10 @@ _$PurchaseOrderModelImpl _$$PurchaseOrderModelImplFromJson(
|
||||
vendorId: _intFromJsonNullable(json['vendor_id']),
|
||||
vendorName: _readVendorName(json, 'vendor_name') as String?,
|
||||
vendorType: _readVendorType(json, 'vendor_type') as String?,
|
||||
plantId: _intFromJsonNullable(json['plant_id']),
|
||||
plantName: _readPlantName(json, 'plant_name') as String?,
|
||||
warehouseId: _intFromJsonNullable(json['warehouse_id']),
|
||||
warehouseName: _readWarehouseName(json, 'warehouse_name') as String?,
|
||||
billingId: _intFromJsonNullable(json['billing_id']),
|
||||
billingName: _readBillingName(json, 'billing_name') as String?,
|
||||
shippingId: _intFromJsonNullable(json['shipping_id']),
|
||||
shippingName: _readShippingName(json, 'shipping_name') as String?,
|
||||
paymentTermId: _intFromJsonNullable(json['payment_term_id']),
|
||||
deliveryTermId: _intFromJsonNullable(json['delivery_term_id']),
|
||||
expectedDeliveryDate: _dateFromJsonNullable(json['expected_delivery_date']),
|
||||
@ -28,6 +28,9 @@ _$PurchaseOrderModelImpl _$$PurchaseOrderModelImplFromJson(
|
||||
otherCharges: _doubleFromJsonNullable(json['other_charges']),
|
||||
taxableAmount: (_readSubTotal(json, 'sub_total') as num?)?.toDouble(),
|
||||
taxAmount: (_readTaxTotal(json, 'tax_total') as num?)?.toDouble(),
|
||||
cgst: _doubleFromJsonNullable(json['cgst']),
|
||||
sgst: _doubleFromJsonNullable(json['sgst']),
|
||||
igst: _doubleFromJsonNullable(json['igst']),
|
||||
totalAmount: (_readGrandTotal(json, 'grand_total') as num?)?.toDouble(),
|
||||
termsAndConditions: json['terms_and_conditions'] as String?,
|
||||
remarks: json['remarks'] as String?,
|
||||
@ -53,10 +56,10 @@ Map<String, dynamic> _$$PurchaseOrderModelImplToJson(
|
||||
'vendor_id': instance.vendorId,
|
||||
'vendor_name': instance.vendorName,
|
||||
'vendor_type': instance.vendorType,
|
||||
'plant_id': instance.plantId,
|
||||
'plant_name': instance.plantName,
|
||||
'warehouse_id': instance.warehouseId,
|
||||
'warehouse_name': instance.warehouseName,
|
||||
'billing_id': instance.billingId,
|
||||
'billing_name': instance.billingName,
|
||||
'shipping_id': instance.shippingId,
|
||||
'shipping_name': instance.shippingName,
|
||||
'payment_term_id': instance.paymentTermId,
|
||||
'delivery_term_id': instance.deliveryTermId,
|
||||
'expected_delivery_date': instance.expectedDeliveryDate?.toIso8601String(),
|
||||
@ -65,6 +68,9 @@ Map<String, dynamic> _$$PurchaseOrderModelImplToJson(
|
||||
'other_charges': instance.otherCharges,
|
||||
'sub_total': instance.taxableAmount,
|
||||
'tax_total': instance.taxAmount,
|
||||
'cgst': instance.cgst,
|
||||
'sgst': instance.sgst,
|
||||
'igst': instance.igst,
|
||||
'grand_total': instance.totalAmount,
|
||||
'terms_and_conditions': instance.termsAndConditions,
|
||||
'remarks': instance.remarks,
|
||||
|
||||
@ -8,6 +8,7 @@ import '../../modules/dashboard/presentation/screens/dashboard_screen.dart';
|
||||
import '../../modules/assets/presentation/screens/asset_alerts_screen.dart';
|
||||
import '../../modules/assets/presentation/screens/asset_detail_screen.dart';
|
||||
import '../../modules/assets/presentation/screens/asset_list_screen.dart';
|
||||
import '../../modules/assets/presentation/screens/asset_maintenance_screen.dart';
|
||||
import '../../modules/auth/presentation/screens/change_password_screen.dart';
|
||||
import '../../modules/auth/presentation/screens/forgot_password_screen.dart';
|
||||
import '../../modules/auth/presentation/screens/login_screen.dart';
|
||||
@ -298,6 +299,10 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
path: 'alerts',
|
||||
builder: (context, state) => const AssetAlertsScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: 'maintenance',
|
||||
builder: (context, state) => const AssetMaintenanceScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: ':id',
|
||||
builder: (context, state) =>
|
||||
|
||||
@ -60,6 +60,12 @@ const List<MenuItem> appMenuItems = [
|
||||
route: RouteConstants.assetAlerts,
|
||||
module: 'assets',
|
||||
),
|
||||
MenuItem(
|
||||
label: 'My Maintenance',
|
||||
icon: Icons.build_outlined,
|
||||
route: RouteConstants.assetMaintenance,
|
||||
module: 'assets',
|
||||
),
|
||||
],
|
||||
),
|
||||
MenuItem(
|
||||
|
||||
@ -41,8 +41,11 @@ class AppResponsiveFilterBar extends StatelessWidget {
|
||||
final hasTrailing = trailing != null;
|
||||
|
||||
if (maxWidth >= _rowBreakpoint) {
|
||||
final alignWithLabeledFilters = filters.isNotEmpty;
|
||||
return Row(
|
||||
crossAxisAlignment: crossAxisAlignment,
|
||||
crossAxisAlignment: alignWithLabeledFilters
|
||||
? crossAxisAlignment
|
||||
: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(flex: searchFlex, child: search),
|
||||
for (final filter in filters) ...[
|
||||
@ -52,7 +55,7 @@ class AppResponsiveFilterBar extends StatelessWidget {
|
||||
if (hasTrailing) ...[
|
||||
SizedBox(width: spacing),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
padding: EdgeInsets.only(top: alignWithLabeledFilters ? 8 : 0),
|
||||
child: trailing!,
|
||||
),
|
||||
],
|
||||
@ -61,6 +64,7 @@ class AppResponsiveFilterBar extends StatelessWidget {
|
||||
}
|
||||
|
||||
if (maxWidth >= _stackBreakpoint) {
|
||||
final alignWithLabeledFilters = filters.isNotEmpty;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
@ -69,6 +73,7 @@ class AppResponsiveFilterBar extends StatelessWidget {
|
||||
Wrap(
|
||||
spacing: spacing,
|
||||
runSpacing: runSpacing,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
for (final filter in filters)
|
||||
SizedBox(
|
||||
@ -87,7 +92,9 @@ class AppResponsiveFilterBar extends StatelessWidget {
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
padding: EdgeInsets.only(
|
||||
top: alignWithLabeledFilters ? 8 : 0,
|
||||
),
|
||||
child: trailing!,
|
||||
),
|
||||
),
|
||||
|
||||
@ -34,6 +34,7 @@ class AppSearchExportBar extends StatelessWidget {
|
||||
hintText: searchHint,
|
||||
prefixIcon: const Icon(Icons.search, size: 20),
|
||||
isDense: true,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
|
||||
),
|
||||
onChanged: onSearch,
|
||||
);
|
||||
@ -41,16 +42,19 @@ class AppSearchExportBar extends StatelessWidget {
|
||||
return AppResponsiveFilterBar(
|
||||
search: searchField,
|
||||
trailing: showExport
|
||||
? OutlinedButton.icon(
|
||||
onPressed: isExporting ? null : onExport,
|
||||
icon: isExporting
|
||||
? const SizedBox(
|
||||
width: 18,
|
||||
height: 18,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
)
|
||||
: const Icon(Icons.download_outlined, size: 18),
|
||||
label: Text(isExporting ? 'Exporting...' : 'Export'),
|
||||
? SizedBox(
|
||||
height: 40,
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: isExporting ? null : onExport,
|
||||
icon: isExporting
|
||||
? const SizedBox(
|
||||
width: 18,
|
||||
height: 18,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
)
|
||||
: const Icon(Icons.download_outlined, size: 18),
|
||||
label: Text(isExporting ? 'Exporting...' : 'Export'),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user