This commit is contained in:
Surendiran 2026-07-17 09:54:15 +05:30
parent 36847aaf87
commit eb94390961
42 changed files with 3095 additions and 905 deletions

View File

@ -47,6 +47,9 @@ class ApiEndpoints {
static String designationById(String id) => '/masters/designations/$id'; static String designationById(String id) => '/masters/designations/$id';
static const String plants = '/masters/plants'; static const String plants = '/masters/plants';
static String plantById(String id) => '/masters/plants/$id'; 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 const String uom = '/masters/uom';
static String uomById(String id) => '/masters/uom/$id'; static String uomById(String id) => '/masters/uom/$id';
static const String itemCategories = '/masters/item-categories'; static const String itemCategories = '/masters/item-categories';
@ -148,6 +151,11 @@ class ApiEndpoints {
static const String assetDepreciationCalculate = '/assets/depreciation/calculate'; static const String assetDepreciationCalculate = '/assets/depreciation/calculate';
static const String assetAlertsExpiry = '/assets/alerts/expiry'; static const String assetAlertsExpiry = '/assets/alerts/expiry';
static const String assetAlertsService = '/assets/alerts/service'; 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 assetAmc(String assetId) => '/assets/$assetId/amc';
static String assetAmcById(String assetId, String contractId) => static String assetAmcById(String assetId, String contractId) =>
'/assets/$assetId/amc/$contractId'; '/assets/$assetId/amc/$contractId';

View File

@ -63,6 +63,7 @@ class RouteConstants {
static const String assetEdit = '/assets/:id/edit'; static const String assetEdit = '/assets/:id/edit';
static const String assetDetail = '/assets/:id'; static const String assetDetail = '/assets/:id';
static const String assetAlerts = '/assets/alerts'; static const String assetAlerts = '/assets/alerts';
static const String assetMaintenance = '/assets/maintenance';
// Master Data // Master Data
static const String masterData = '/master-data'; static const String masterData = '/master-data';

View File

@ -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) { Map<String, dynamic> _queryToMap(AssetListQuery query) {
return { return {
'page': query.page, 'page': query.page,
@ -374,8 +423,11 @@ class AssetRemoteDataSource {
if (query.itemCategoryId != null) 'item_category_id': query.itemCategoryId, if (query.itemCategoryId != null) 'item_category_id': query.itemCategoryId,
if (query.itemSubcategoryId != null) if (query.itemSubcategoryId != null)
'item_subcategory_id': query.itemSubcategoryId, '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.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, if (query.isActive != null) 'is_active': query.isActive,
}; };
} }

View File

@ -262,6 +262,51 @@ class AssetRepositoryImpl implements AssetRepository {
return safeApiCall(() => dataSource.renewInsurancePolicy(assetId, policyId, data)); 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 @override
Future<Result<List<EntityAttachmentModel>>> listAttachments(String assetId) { Future<Result<List<EntityAttachmentModel>>> listAttachments(String assetId) {
return safeApiCall(() => dataSource.listAttachments(assetId)); return safeApiCall(() => dataSource.listAttachments(assetId));

View File

@ -89,6 +89,23 @@ abstract class AssetRepository {
String policyId, String policyId,
Map<String, dynamic> data, 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<List<EntityAttachmentModel>>> listAttachments(String assetId);
Future<Result<EntityAttachmentModel>> uploadAttachment( Future<Result<EntityAttachmentModel>> uploadAttachment(
String assetId, { String assetId, {

View File

@ -14,9 +14,8 @@ import '../../../vendors/data/repositories/vendor_repository_impl.dart';
class AssetFormLookups { class AssetFormLookups {
const AssetFormLookups({ const AssetFormLookups({
this.plants = const [], this.locations = const [],
this.departments = const [], this.departments = const [],
this.warehouses = const [],
this.vendors = const [], this.vendors = const [],
this.users = const [], this.users = const [],
this.purchaseOrders = const [], this.purchaseOrders = const [],
@ -24,9 +23,8 @@ class AssetFormLookups {
this.options = const AssetDropdownOptionsModel(), this.options = const AssetDropdownOptionsModel(),
}); });
final List<FilterOptionModel> plants; final List<FilterOptionModel> locations;
final List<FilterOptionModel> departments; final List<FilterOptionModel> departments;
final List<FilterOptionModel> warehouses;
final List<FilterOptionModel> vendors; final List<FilterOptionModel> vendors;
final List<FilterOptionModel> users; final List<FilterOptionModel> users;
final List<FilterOptionModel> purchaseOrders; final List<FilterOptionModel> purchaseOrders;
@ -50,9 +48,8 @@ final assetFormLookupsProvider =
FutureProvider.autoDispose<AssetFormLookups>((ref) async { FutureProvider.autoDispose<AssetFormLookups>((ref) async {
final master = ref.watch(masterRemoteDataSourceProvider); final master = ref.watch(masterRemoteDataSourceProvider);
final plants = await _safeOptions(master.listPlants); final locations = await _safeOptions(master.listLocations);
final departments = await _safeOptions(master.listDepartments); final departments = await _safeOptions(master.listDepartments);
final warehouses = await _safeOptions(master.listWarehouses);
final vendors = await _safeVendorOptions(ref); final vendors = await _safeVendorOptions(ref);
final users = await _safeUserOptions(ref); final users = await _safeUserOptions(ref);
final purchaseOrders = await _safePurchaseOrderOptions(ref); final purchaseOrders = await _safePurchaseOrderOptions(ref);
@ -60,9 +57,8 @@ final assetFormLookupsProvider =
final options = await _safeAssetOptions(ref); final options = await _safeAssetOptions(ref);
return AssetFormLookups( return AssetFormLookups(
plants: plants, locations: locations,
departments: departments, departments: departments,
warehouses: warehouses,
vendors: vendors, vendors: vendors,
users: users, users: users,
purchaseOrders: purchaseOrders, purchaseOrders: purchaseOrders,

View File

@ -118,10 +118,10 @@ class AssetsListNotifier extends AutoDisposeAsyncNotifier<AssetsListState> {
applyQuery(current.query.copyWith(itemCategoryId: itemCategoryId, page: 1)); applyQuery(current.query.copyWith(itemCategoryId: itemCategoryId, page: 1));
} }
void setPlantFilter(int? plantId) { void setLocationFilter(int? locationId) {
final current = state.valueOrNull; final current = state.valueOrNull;
if (current == null) return; if (current == null) return;
applyQuery(current.query.copyWith(plantId: plantId, page: 1)); applyQuery(current.query.copyWith(locationId: locationId, page: 1));
} }
void setPage(int page) { void setPage(int page) {
@ -594,3 +594,106 @@ class AssetAlertsNotifier extends AsyncNotifier<AssetAlertsState> {
state = AsyncData(await _load(current.copyWith(expiryPage: page))); 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)),
);
}
}

View File

@ -285,7 +285,8 @@ class _AlertCard extends StatelessWidget {
final visual = _alertVisualStyle(alert); final visual = _alertVisualStyle(alert);
final subtitleParts = [ final subtitleParts = [
if (alert.assetCode?.trim().isNotEmpty == true) alert.assetCode!.trim(), 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', if (dateLabel != null) 'Due: $dateLabel',
]; ];

View File

@ -23,6 +23,7 @@ import '../../../../shared/widgets/app_side_panel.dart';
import '../providers/assets_provider.dart'; import '../providers/assets_provider.dart';
import '../providers/asset_form_lookups_provider.dart'; import '../providers/asset_form_lookups_provider.dart';
import '../widgets/asset_form_panel.dart'; import '../widgets/asset_form_panel.dart';
import '../widgets/asset_maintenance_panel.dart';
import '../widgets/asset_side_panels.dart'; import '../widgets/asset_side_panels.dart';
import '../../../../shared/widgets/app_toast.dart'; import '../../../../shared/widgets/app_toast.dart';
@ -198,6 +199,12 @@ class _OverviewTab extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context); final theme = Theme.of(context);
final dateFormat = DateFormat('dd MMM yyyy'); final dateFormat = DateFormat('dd MMM yyyy');
final users =
ref.watch(assetFormLookupsProvider).valueOrNull?.users ?? const [];
final maintenanceInchargeLabel = _userLabel(
asset.maintenanceInchargeUserId,
users,
);
return SingleChildScrollView( return SingleChildScrollView(
child: Center( child: Center(
@ -239,7 +246,40 @@ class _OverviewTab extends ConsumerWidget {
'Subcategory', 'Subcategory',
asset.assetSubcategoryName ?? '', 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('Serial Number', asset.serialNumber ?? ''),
_AssetInfo('Brand / Model', asset.brandModel ?? ''), _AssetInfo('Brand / Model', asset.brandModel ?? ''),
_AssetInfo('Manufacturer', asset.manufacturer ?? ''), _AssetInfo('Manufacturer', asset.manufacturer ?? ''),
@ -285,6 +325,36 @@ class _OverviewTab extends ConsumerWidget {
_AssetInfo('Active', asset.isActive ? 'Yes' : 'No'), _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) ...[ if (asset.remarks?.trim().isNotEmpty == true) ...[
const Padding( const Padding(
padding: EdgeInsets.symmetric(vertical: 20), padding: EdgeInsets.symmetric(vertical: 20),
@ -410,6 +480,17 @@ class _AssetInfo {
final Widget? valueWidget; 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 { class _AmcTab extends ConsumerWidget {
const _AmcTab({required this.assetId, required this.contracts}); const _AmcTab({required this.assetId, required this.contracts});

View File

@ -69,7 +69,7 @@ class _AssetListScreenState extends ConsumerState<AssetListScreen> {
final allCategories = final allCategories =
ref.watch(itemCategoriesProvider).valueOrNull ?? []; ref.watch(itemCategoriesProvider).valueOrNull ?? [];
final lookups = ref.watch(assetFormLookupsProvider).valueOrNull; final lookups = ref.watch(assetFormLookupsProvider).valueOrNull;
final allPlants = lookups?.plants ?? const []; final allLocations = lookups?.locations ?? const [];
final notifier = ref.read(assetsListProvider.notifier); final notifier = ref.read(assetsListProvider.notifier);
return Column( return Column(
@ -139,11 +139,11 @@ class _AssetListScreenState extends ConsumerState<AssetListScreen> {
child: _AssetsFilterBar( child: _AssetsFilterBar(
query: state.query, query: state.query,
categories: allCategories, categories: allCategories,
plants: allPlants, locations: allLocations,
statuses: lookups?.statuses ?? const [], statuses: lookups?.statuses ?? const [],
onSearch: notifier.setSearch, onSearch: notifier.setSearch,
onCategoryChanged: notifier.setCategoryFilter, onCategoryChanged: notifier.setCategoryFilter,
onPlantChanged: notifier.setPlantFilter, onLocationChanged: notifier.setLocationFilter,
onStatusChanged: notifier.setStatusFilter, onStatusChanged: notifier.setStatusFilter,
), ),
), ),
@ -272,21 +272,21 @@ class _AssetsFilterBar extends StatelessWidget {
const _AssetsFilterBar({ const _AssetsFilterBar({
required this.query, required this.query,
required this.categories, required this.categories,
required this.plants, required this.locations,
required this.statuses, required this.statuses,
required this.onSearch, required this.onSearch,
required this.onCategoryChanged, required this.onCategoryChanged,
required this.onPlantChanged, required this.onLocationChanged,
required this.onStatusChanged, required this.onStatusChanged,
}); });
final AssetListQuery query; final AssetListQuery query;
final List<AssetCategoryModel> categories; final List<AssetCategoryModel> categories;
final List<FilterOptionModel> plants; final List<FilterOptionModel> locations;
final List<AssetDropdownOption> statuses; final List<AssetDropdownOption> statuses;
final ValueChanged<String> onSearch; final ValueChanged<String> onSearch;
final ValueChanged<int?> onCategoryChanged; final ValueChanged<int?> onCategoryChanged;
final ValueChanged<int?> onPlantChanged; final ValueChanged<int?> onLocationChanged;
final ValueChanged<String?> onStatusChanged; final ValueChanged<String?> onStatusChanged;
@override @override
@ -315,13 +315,13 @@ class _AssetsFilterBar extends StatelessWidget {
), ),
]; ];
final plantOptions = <AppDropdownOption<int?>>[ final locationOptions = <AppDropdownOption<int?>>[
const AppDropdownOption(value: null, label: 'All Plants'), const AppDropdownOption(value: null, label: 'All Locations'),
for (final plant in plants) for (final location in locations)
if (int.tryParse(plant.id) != null) if (int.tryParse(location.id) != null)
AppDropdownOption( AppDropdownOption(
value: int.parse(plant.id), value: int.parse(location.id),
label: plant.name, label: location.name,
), ),
]; ];
@ -347,12 +347,12 @@ class _AssetsFilterBar extends StatelessWidget {
onChanged: onCategoryChanged, onChanged: onCategoryChanged,
), ),
AppSearchableDropdown<int?>( AppSearchableDropdown<int?>(
label: 'Plant', label: 'Location',
value: query.plantId, value: query.locationId,
searchHint: 'Search plant...', searchHint: 'Search location...',
isDense: true, isDense: true,
options: plantOptions, options: locationOptions,
onChanged: onPlantChanged, onChanged: onLocationChanged,
), ),
AppSearchableDropdown<String?>( AppSearchableDropdown<String?>(
label: 'Status', label: 'Status',
@ -416,10 +416,10 @@ class _AssetDataTable extends StatelessWidget {
cellBuilder: (_, asset) => Text(asset.assetCategoryName ?? ''), cellBuilder: (_, asset) => Text(asset.assetCategoryName ?? ''),
), ),
AppDataColumn( AppDataColumn(
label: 'Plant', label: 'Location',
flex: 1, flex: 1,
searchText: (asset) => asset.plantName ?? '', searchText: (asset) => asset.locationName ?? '',
cellBuilder: (_, asset) => Text(asset.plantName ?? ''), cellBuilder: (_, asset) => Text(asset.locationName ?? ''),
), ),
AppDataColumn( AppDataColumn(
label: 'Warranty', label: 'Warranty',
@ -533,7 +533,7 @@ class _AssetMobileList extends StatelessWidget {
_AssetCodeBadge(code: asset.assetCode!) _AssetCodeBadge(code: asset.assetCode!)
else else
const Text(''), const Text(''),
Text('${asset.assetCategoryName ?? ''} · ${asset.plantName ?? ''}'), Text('${asset.assetCategoryName ?? ''} · ${asset.locationName ?? ''}'),
const SizedBox(height: 8), const SizedBox(height: 8),
Row( Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,

View File

@ -1,30 +1,355 @@
import 'package:flutter/material.dart'; 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/error_view.dart';
import '../../../../shared/widgets/page_header.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}); const AssetMaintenanceScreen({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context, WidgetRef ref) {
return const Padding( final maintenanceAsync = ref.watch(myMaintenanceProvider);
padding: EdgeInsets.all(24),
child: Column( return Padding(
crossAxisAlignment: CrossAxisAlignment.start, padding: const EdgeInsets.all(24),
children: [ child: maintenanceAsync.when(
PageHeader( loading: () =>
title: 'Asset Maintenance', const AppLoadingView(message: 'Loading maintenance assets...'),
subtitle: 'Track repairs, service vendors, and costs', error: (e, _) => ErrorView.fromFailure(
), e is Failure ? e : Failure.unknown(message: e.toString()),
Expanded( onRetry: () => ref.invalidate(myMaintenanceProvider),
child: EmptyStateView( ),
title: 'No maintenance requests', data: (state) => _MyMaintenanceBody(state: state),
description: 'Create maintenance requests and view service history.', ),
icon: Icons.build_outlined, );
), }
), }
],
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,
),
), ),
); );
} }

View File

@ -78,12 +78,13 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
final _depreciationRateController = TextEditingController(); final _depreciationRateController = TextEditingController();
final _salvageValueController = TextEditingController(); final _salvageValueController = TextEditingController();
final _remarksController = TextEditingController(); final _remarksController = TextEditingController();
final _frequencyController = TextEditingController();
int? _categoryId; int? _categoryId;
int? _subcategoryId; int? _subcategoryId;
int? _plantId; int? _locationId;
int? _departmentId; int? _departmentId;
int? _warehouseId;
int? _assignedToUserId; int? _assignedToUserId;
int? _maintenanceInchargeUserId;
int? _vendorId; int? _vendorId;
int? _poId; int? _poId;
int? _grnId; int? _grnId;
@ -93,7 +94,9 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
String? _depreciationMethod; String? _depreciationMethod;
DateTime? _warrantyExpiry; DateTime? _warrantyExpiry;
DateTime? _purchaseDate; DateTime? _purchaseDate;
DateTime? _commencementDate;
DateTime? _disposalDate; DateTime? _disposalDate;
List<AssetMaintenanceChecklistItem> _checklistItems = [];
bool _isActive = true; bool _isActive = true;
bool _isSubmitting = false; bool _isSubmitting = false;
String? _populatedSignature; String? _populatedSignature;
@ -130,6 +133,7 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
_depreciationRateController.dispose(); _depreciationRateController.dispose();
_salvageValueController.dispose(); _salvageValueController.dispose();
_remarksController.dispose(); _remarksController.dispose();
_frequencyController.dispose();
super.dispose(); super.dispose();
} }
@ -139,7 +143,7 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
String _assetSignature(AssetModel asset) => String _assetSignature(AssetModel asset) =>
'${asset.id}:${asset.assetCategoryId}:${asset.assetSubcategoryId}:' '${asset.id}:${asset.assetCategoryId}:${asset.assetSubcategoryId}:'
'${asset.plantId}:${asset.status}:${asset.assetName}'; '${asset.locationId}:${asset.status}:${asset.assetName}';
int? _nullablePositiveId(int? id) { int? _nullablePositiveId(int? id) {
if (id == null || id <= 0) return null; if (id == null || id <= 0) return null;
@ -188,10 +192,11 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
_nameController.text = asset.assetName; _nameController.text = asset.assetName;
_categoryId = asset.assetCategoryId; _categoryId = asset.assetCategoryId;
_subcategoryId = asset.assetSubcategoryId; _subcategoryId = asset.assetSubcategoryId;
_plantId = asset.plantId; _locationId = asset.locationId;
_departmentId = _nullablePositiveId(asset.departmentId); _departmentId = _nullablePositiveId(asset.departmentId);
_warehouseId = _nullablePositiveId(asset.warehouseId);
_assignedToUserId = _nullablePositiveId(asset.assignedToUserId); _assignedToUserId = _nullablePositiveId(asset.assignedToUserId);
_maintenanceInchargeUserId =
_nullablePositiveId(asset.maintenanceInchargeUserId);
_vendorId = _nullablePositiveId(asset.vendorId); _vendorId = _nullablePositiveId(asset.vendorId);
_poId = _nullablePositiveId(asset.poId); _poId = _nullablePositiveId(asset.poId);
_grnId = _nullablePositiveId(asset.grnId); _grnId = _nullablePositiveId(asset.grnId);
@ -201,7 +206,11 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
_depreciationMethod = asset.depreciationMethod; _depreciationMethod = asset.depreciationMethod;
_warrantyExpiry = asset.warrantyExpiryDate; _warrantyExpiry = asset.warrantyExpiryDate;
_purchaseDate = asset.purchaseDate; _purchaseDate = asset.purchaseDate;
_commencementDate = asset.commencementDate;
_disposalDate = asset.disposalDate; _disposalDate = asset.disposalDate;
_checklistItems = [
...(asset.maintenanceChecklistJson ?? const []),
];
_isActive = asset.isActive; _isActive = asset.isActive;
_serialController.text = asset.serialNumber ?? ''; _serialController.text = asset.serialNumber ?? '';
_partNumberController.text = asset.partNumber ?? ''; _partNumberController.text = asset.partNumber ?? '';
@ -215,6 +224,8 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
_depreciationRateController.text = asset.depreciationRate?.toString() ?? ''; _depreciationRateController.text = asset.depreciationRate?.toString() ?? '';
_salvageValueController.text = asset.salvageValue?.toString() ?? ''; _salvageValueController.text = asset.salvageValue?.toString() ?? '';
_remarksController.text = asset.remarks ?? ''; _remarksController.text = asset.remarks ?? '';
_frequencyController.text =
asset.maintenanceFrequencyInDays?.toString() ?? '';
if (asset.purchaseCost != null) { if (asset.purchaseCost != null) {
_costController.text = asset.purchaseCost!.toStringAsFixed( _costController.text = asset.purchaseCost!.toStringAsFixed(
asset.purchaseCost! % 1 == 0 ? 0 : 2, asset.purchaseCost! % 1 == 0 ? 0 : 2,
@ -261,6 +272,10 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
final usefulLife = int.tryParse(_usefulLifeController.text.trim()); final usefulLife = int.tryParse(_usefulLifeController.text.trim());
if (usefulLife != null) payload['useful_life_years'] = usefulLife; if (usefulLife != null) payload['useful_life_years'] = usefulLife;
if (_commencementDate != null) {
payload['commencement_date'] =
DateFormat('yyyy-MM-dd').format(_commencementDate!);
}
if (_purchaseDate != null) { if (_purchaseDate != null) {
payload['purchase_date'] = DateFormat('yyyy-MM-dd').format(_purchaseDate!); payload['purchase_date'] = DateFormat('yyyy-MM-dd').format(_purchaseDate!);
} }
@ -312,15 +327,21 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
'asset_name': _nameController.text.trim(), 'asset_name': _nameController.text.trim(),
'item_category_id': _categoryId, 'item_category_id': _categoryId,
'item_subcategory_id': _subcategoryId, 'item_subcategory_id': _subcategoryId,
'plant_id': _plantId, 'location_id': _locationId,
'is_active': _isActive, 'is_active': _isActive,
}; };
_putOptionalId(payload, 'department_id', _departmentId); _putOptionalId(payload, 'department_id', _departmentId);
_putOptionalId(payload, 'warehouse_id', _warehouseId);
if (_isAssignableUser(_assignedToUserId)) { if (_isAssignableUser(_assignedToUserId)) {
_putOptionalId(payload, 'assigned_to_user_id', _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, 'vendor_id', _vendorId);
_putOptionalId(payload, 'po_id', _poId); _putOptionalId(payload, 'po_id', _poId);
_putOptionalId(payload, 'grn_id', _grnId); _putOptionalId(payload, 'grn_id', _grnId);
@ -338,6 +359,10 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
if (_purchaseDate != null) { if (_purchaseDate != null) {
payload['purchase_date'] = DateFormat('yyyy-MM-dd').format(_purchaseDate!); payload['purchase_date'] = DateFormat('yyyy-MM-dd').format(_purchaseDate!);
} }
if (_commencementDate != null) {
payload['commencement_date'] =
DateFormat('yyyy-MM-dd').format(_commencementDate!);
}
if (_warrantyExpiry != null) { if (_warrantyExpiry != null) {
payload['warranty_expiry_date'] = payload['warranty_expiry_date'] =
DateFormat('yyyy-MM-dd').format(_warrantyExpiry!); DateFormat('yyyy-MM-dd').format(_warrantyExpiry!);
@ -353,6 +378,31 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
final usefulLife = int.tryParse(_usefulLifeController.text.trim()); final usefulLife = int.tryParse(_usefulLifeController.text.trim());
if (usefulLife != null) payload['useful_life_years'] = usefulLife; 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) { if (_depreciationMethod != null) {
payload['depreciation_method'] = _depreciationMethod; payload['depreciation_method'] = _depreciationMethod;
} }
@ -467,7 +517,6 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final categoriesAsync = ref.watch(itemCategoriesFormProvider); final categoriesAsync = ref.watch(itemCategoriesFormProvider);
final plantsAsync = ref.watch(assetPlantsProvider);
if (widget.isEditing) { if (widget.isEditing) {
ref.listen(assetFormProvider(widget.assetId), (prev, next) { 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()), e is Failure ? e : Failure.unknown(message: e.toString()),
onRetry: () => ref.invalidate(assetFormProvider(widget.assetId)), onRetry: () => ref.invalidate(assetFormProvider(widget.assetId)),
), ),
data: (_) => _buildForm(categoriesAsync, plantsAsync), data: (_) => _buildForm(categoriesAsync),
) )
: _buildForm(categoriesAsync, plantsAsync), : _buildForm(categoriesAsync),
); );
} }
Widget _buildForm( Widget _buildForm(
AsyncValue<List<AssetCategoryModel>> categoriesAsync, AsyncValue<List<AssetCategoryModel>> categoriesAsync,
AsyncValue<List<FilterOptionModel>> plantsAsync,
) { ) {
final subcategoriesAsync = ref.watch(itemSubcategoriesProvider(_categoryId)); final subcategoriesAsync = ref.watch(itemSubcategoriesProvider(_categoryId));
final lookupsAsync = ref.watch(assetFormLookupsProvider); final lookupsAsync = ref.watch(assetFormLookupsProvider);
@ -589,10 +637,10 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
), ),
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
plantsAsync.when( lookupsAsync.when(
loading: () => const LinearProgressIndicator(), loading: () => const LinearProgressIndicator(),
error: (_, __) => const Text('Failed to load plants'), error: (_, __) => const Text('Failed to load locations'),
data: (plants) => _plantDropdown(plants), data: (lookups) => _locationDropdown(lookups.locations),
), ),
], ],
), ),
@ -681,29 +729,21 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
masterId: 'departments', masterId: 'departments',
onChanged: (v) => setState(() => _departmentId = v), onChanged: (v) => setState(() => _departmentId = v),
), ),
right: _optionalLookupDropdown( right: AppTextField(
label: 'Warehouse', isDense: true,
value: _warehouseId, controller: _locationDetailController,
options: lookups.warehouses, label: 'Location Detail',
masterId: 'warehouses', validator: (v) {
onChanged: (v) => setState(() => _warehouseId = v), if (v == null || v.trim().isEmpty) return null;
return Validators.minLength(
v.trim(),
2,
fieldName: 'Location Detail',
);
},
), ),
), ),
const SizedBox(height: 12), 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( _optionalLookupDropdown(
label: 'Assigned To', label: 'Assigned To',
value: _assignedToUserId, 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( SidePanelSection(
title: 'PROCUREMENT', title: 'PROCUREMENT',
children: [ children: [
@ -779,11 +846,23 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
_pickDate((d) => _purchaseDate = d, _purchaseDate), _pickDate((d) => _purchaseDate = d, _purchaseDate),
), ),
right: _AssetFormDateField( right: _AssetFormDateField(
label: 'Commencement Date',
value: _commencementDate,
onPick: () => _pickDate(
(d) => _commencementDate = d,
_commencementDate,
),
),
),
const SizedBox(height: 12),
SidePanelFormRow(
left: _AssetFormDateField(
label: 'Warranty Expiry Date', label: 'Warranty Expiry Date',
value: _warrantyExpiry, value: _warrantyExpiry,
onPick: () => onPick: () =>
_pickDate((d) => _warrantyExpiry = d, _warrantyExpiry), _pickDate((d) => _warrantyExpiry = d, _warrantyExpiry),
), ),
right: const SizedBox.shrink(),
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
SidePanelFormRow( 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({ Widget _optionalLookupDropdown({
required String label, required String label,
required int? value, required int? value,
@ -1034,7 +1183,6 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
options: dropdownOptions, options: dropdownOptions,
refreshLookups: () { refreshLookups: () {
ref.invalidate(assetFormLookupsProvider); ref.invalidate(assetFormLookupsProvider);
ref.invalidate(assetPlantsProvider);
}, },
parseCreatedId: int.tryParse, parseCreatedId: int.tryParse,
onChanged: onChanged, onChanged: onChanged,
@ -1128,33 +1276,125 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
); );
} }
Widget _plantDropdown(List<FilterOptionModel> plants) { Widget _locationDropdown(List<FilterOptionModel> locations) {
final plantIds = plants final locationIds = locations
.map((p) => int.tryParse(p.id)) .map((location) => int.tryParse(location.id))
.whereType<int>() .whereType<int>()
.toList(); .toList();
return MasterQuickAddDropdown<int>( return AppSearchableDropdown<int>(
masterId: 'plants', label: 'Location *',
label: 'Plant *', value: _dropdownValue(_locationId, locationIds),
value: _dropdownValue(_plantId, plantIds), searchHint: 'Search plant or warehouse...',
searchHint: 'Search plant...',
isDense: true, isDense: true,
options: plants options: locations
.map( .map(
(p) => AppDropdownOption( (location) => AppDropdownOption(
value: int.tryParse(p.id) ?? 0, value: int.tryParse(location.id) ?? 0,
label: p.name, label: location.name,
), ),
) )
.where((option) => option.value != 0) .where((option) => option.value != 0)
.toList(), .toList(),
refreshLookups: () { onChanged: (v) => setState(() => _locationId = v),
ref.invalidate(assetPlantsProvider); validator: (v) => v == null ? 'Location is required' : null,
ref.invalidate(assetFormLookupsProvider); );
}, }
parseCreatedId: int.tryParse, }
onChanged: (v) => setState(() => _plantId = v),
validator: (v) => v == null ? 'Plant 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 = final itemSubcategoriesProvider =
FutureProvider.family<List<FilterOptionModel>, int?>((ref, categoryId) async { FutureProvider.family<List<FilterOptionModel>, int?>((ref, categoryId) async {
if (categoryId == null) return []; if (categoryId == null) return [];

View File

@ -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(),
),
],
],
),
);
}
}

View File

@ -1242,10 +1242,9 @@ class _TransferAssetPanelState extends ConsumerState<TransferAssetPanel> {
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
final _reasonController = TextEditingController(); final _reasonController = TextEditingController();
DateTime _transferDate = DateTime.now(); DateTime _transferDate = DateTime.now();
int? _toPlantId; int? _toLocationId;
int? _toDepartmentId; int? _toDepartmentId;
int? _toUserId; int? _toUserId;
int? _toWarehouseId;
bool _isSubmitting = false; bool _isSubmitting = false;
@override @override
@ -1271,10 +1270,9 @@ class _TransferAssetPanelState extends ConsumerState<TransferAssetPanel> {
if (!_formKey.currentState!.validate()) return; if (!_formKey.currentState!.validate()) return;
final hasDestination = final hasDestination =
_toPlantId != null || _toLocationId != null ||
_toDepartmentId != null || _toDepartmentId != null ||
_toUserId != null || _toUserId != null;
_toWarehouseId != null;
if (!hasDestination) { if (!hasDestination) {
showSidePanelSnackBar( showSidePanelSnackBar(
context, context,
@ -1287,10 +1285,9 @@ class _TransferAssetPanelState extends ConsumerState<TransferAssetPanel> {
try { try {
await ref.read(assetDetailProvider(widget.assetId).notifier).transferAsset({ await ref.read(assetDetailProvider(widget.assetId).notifier).transferAsset({
'transfer_date': DateFormatter.toApiDate(_transferDate), '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 (_toDepartmentId != null) 'to_department_id': _toDepartmentId,
if (_toUserId != null) 'to_user_id': _toUserId, if (_toUserId != null) 'to_user_id': _toUserId,
if (_toWarehouseId != null) 'to_warehouse_id': _toWarehouseId,
'reason': _reasonController.text.trim(), 'reason': _reasonController.text.trim(),
}); });
if (mounted) { if (mounted) {
@ -1331,12 +1328,11 @@ class _TransferAssetPanelState extends ConsumerState<TransferAssetPanel> {
onPick: _pickDate, onPick: _pickDate,
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
MasterQuickAddDropdown<int>( AppSearchableDropdown<int>(
masterId: 'plants', label: 'To Location',
label: 'To Plant', value: _toLocationId,
value: _toPlantId, searchHint: 'Search plant or warehouse...',
searchHint: 'Search plant...', options: lookups.locations
options: lookups.plants
.map((option) { .map((option) {
final id = int.tryParse(option.id); final id = int.tryParse(option.id);
if (id == null) return null; if (id == null) return null;
@ -1344,10 +1340,7 @@ class _TransferAssetPanelState extends ConsumerState<TransferAssetPanel> {
}) })
.whereType<AppDropdownOption<int>>() .whereType<AppDropdownOption<int>>()
.toList(), .toList(),
refreshLookups: () => onChanged: (v) => setState(() => _toLocationId = v),
ref.invalidate(assetFormLookupsProvider),
parseCreatedId: int.tryParse,
onChanged: (v) => setState(() => _toPlantId = v),
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
MasterQuickAddDropdown<int>( MasterQuickAddDropdown<int>(
@ -1384,25 +1377,6 @@ class _TransferAssetPanelState extends ConsumerState<TransferAssetPanel> {
onChanged: (v) => setState(() => _toUserId = v), onChanged: (v) => setState(() => _toUserId = v),
), ),
const SizedBox(height: 12), 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( AppTextField(
controller: _reasonController, controller: _reasonController,
label: 'Reason *', label: 'Reason *',
@ -1476,9 +1450,8 @@ class _TransferHistoryEntry extends StatelessWidget {
Expanded( Expanded(
child: _TransferLocationBlock( child: _TransferLocationBlock(
label: 'From', label: 'From',
plant: item.fromPlantName, location: item.fromLocationName,
department: item.fromDepartmentName, department: item.fromDepartmentName,
warehouse: item.fromWarehouseName,
user: item.fromUserName, user: item.fromUserName,
), ),
), ),
@ -1493,9 +1466,8 @@ class _TransferHistoryEntry extends StatelessWidget {
Expanded( Expanded(
child: _TransferLocationBlock( child: _TransferLocationBlock(
label: 'To', label: 'To',
plant: item.toPlantName, location: item.toLocationName,
department: item.toDepartmentName, department: item.toDepartmentName,
warehouse: item.toWarehouseName,
user: item.toUserName, user: item.toUserName,
), ),
), ),
@ -1535,16 +1507,14 @@ class _TransferHistoryEntry extends StatelessWidget {
class _TransferLocationBlock extends StatelessWidget { class _TransferLocationBlock extends StatelessWidget {
const _TransferLocationBlock({ const _TransferLocationBlock({
required this.label, required this.label,
this.plant, this.location,
this.department, this.department,
this.warehouse,
this.user, this.user,
}); });
final String label; final String label;
final String? plant; final String? location;
final String? department; final String? department;
final String? warehouse;
final String? user; final String? user;
@override @override
@ -1564,17 +1534,13 @@ class _TransferLocationBlock extends StatelessWidget {
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
_TransferLocationRow( _TransferLocationRow(
icon: Icons.factory_outlined, icon: Icons.place_outlined,
value: plant, value: location,
), ),
_TransferLocationRow( _TransferLocationRow(
icon: Icons.apartment_outlined, icon: Icons.apartment_outlined,
value: department, value: department,
), ),
_TransferLocationRow(
icon: Icons.warehouse_outlined,
value: warehouse,
),
_TransferLocationRow( _TransferLocationRow(
icon: Icons.person_outline, icon: Icons.person_outline,
value: user, value: user,

View File

@ -23,7 +23,12 @@ final grnLookupsProvider = FutureProvider.autoDispose<GrnLookups>((ref) async {
final master = ref.watch(masterRemoteDataSourceProvider); final master = ref.watch(masterRemoteDataSourceProvider);
final poRepo = ref.watch(purchaseOrderRepositoryProvider); 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 users = await _safeUserOptions(ref);
final receivablePos = <PurchaseOrderModel>[]; final receivablePos = <PurchaseOrderModel>[];

View File

@ -112,8 +112,8 @@ class _GrnFormScreenState extends ConsumerState<GrnFormScreen> {
_lines _lines
..clear() ..clear()
..addAll(draftsFromPurchaseOrder(po)); ..addAll(draftsFromPurchaseOrder(po));
if (_warehouseId == null && po.warehouseId != null) { if (_warehouseId == null && po.shippingId != null) {
_warehouseId = po.warehouseId; _warehouseId = po.shippingId;
} }
}); });
} }
@ -453,12 +453,13 @@ class _GrnFormScreenState extends ConsumerState<GrnFormScreen> {
value: existing?.poNumber ?? '', value: existing?.poNumber ?? '',
), ),
MasterQuickAddDropdown<int>( MasterQuickAddDropdown<int>(
masterId: 'warehouses', masterId: 'locations',
label: 'Warehouse *', label: 'Warehouse *',
value: _dropdownValue(_warehouseId, warehouseIds), value: _dropdownValue(_warehouseId, warehouseIds),
hint: 'Select warehouse', hint: 'Select warehouse',
searchHint: 'Search warehouse...', searchHint: 'Search warehouse...',
options: _intOptions(lookups.warehouses), options: _intOptions(lookups.warehouses),
initialValues: const {'type': 'warehouse'},
refreshLookups: () => refreshLookups: () =>
ref.invalidate(grnLookupsProvider), ref.invalidate(grnLookupsProvider),
parseCreatedId: int.tryParse, parseCreatedId: int.tryParse,

View File

@ -278,6 +278,12 @@ const masterDefinitions = <MasterDefinition>[
type: MasterFieldType.number, type: MasterFieldType.number,
required: true, required: true,
), ),
MasterFieldDef(
key: 'tags',
label: 'Tags',
multiline: true,
showInList: true,
),
MasterFieldDef(key: 'description', label: 'Description', multiline: true), MasterFieldDef(key: 'description', label: 'Description', multiline: true),
MasterFieldDef(key: 'specification', label: 'Specification', multiline: true), MasterFieldDef(key: 'specification', label: 'Specification', multiline: true),
_activeField, _activeField,
@ -341,47 +347,54 @@ const masterDefinitions = <MasterDefinition>[
], ],
), ),
MasterDefinition( MasterDefinition(
id: 'plants', id: 'locations',
title: 'Plants', title: 'Locations',
subtitle: 'Manufacturing plants and units', subtitle: 'Plants and warehouses',
category: 'Organization', category: 'Organization',
routeKey: 'plants', routeKey: 'locations',
apiPath: '/masters/plants', apiPath: '/masters/locations',
module: 'plants', module: 'locations',
icon: Icons.factory_outlined, icon: Icons.place_outlined,
fields: [ 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( MasterFieldDef(
key: 'plant_id', key: 'type',
label: 'Plant', label: 'Type',
type: MasterFieldType.dropdown, type: MasterFieldType.dropdown,
required: true, required: true,
showInList: 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, _activeField,
], ],
), ),
@ -521,6 +534,18 @@ String masterCellValue(Map<String, dynamic> row, MasterFieldDef field) {
final value = row[field.key]; final value = row[field.key];
if (value == null || value == '') return ''; 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) { if (field.type == MasterFieldType.boolean) {
return value == true ? 'Yes' : 'No'; return value == true ? 'Yes' : 'No';
} }

View File

@ -4,6 +4,7 @@ import '../../../../core/constants/app_constants.dart';
import '../../../../core/utils/table_search.dart'; import '../../../../core/utils/table_search.dart';
import '../../../../shared/models/export_file_result.dart'; import '../../../../shared/models/export_file_result.dart';
import '../../../assets/data/repositories/asset_repository_impl.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 '../../data/repositories/master_repository_impl.dart';
import '../../domain/entities/master_definition.dart'; import '../../domain/entities/master_definition.dart';
@ -259,6 +260,13 @@ class MasterFormNotifier extends FamilyAsyncNotifier<MasterFormState, MasterForm
.getById(_definition, arg.recordId!); .getById(_definition, arg.recordId!);
if (result.failure != null) throw result.failure!; if (result.failure != null) throw result.failure!;
values = Map<String, dynamic>.from(result.data ?? const {}); 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 { } else {
for (final field in _definition.formFields) { for (final field in _definition.formFields) {
if (field.type == MasterFieldType.boolean) { if (field.type == MasterFieldType.boolean) {
@ -342,6 +350,23 @@ class MasterFormNotifier extends FamilyAsyncNotifier<MasterFormState, MasterForm
continue; 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); final def = masterDefinitionById(key);
if (def == null) continue; if (def == null) continue;
final queryParameters = masterFieldOptionsQuery( final queryParameters = masterFieldOptionsQuery(
@ -481,10 +506,17 @@ class MasterFormNotifier extends FamilyAsyncNotifier<MasterFormState, MasterForm
final value = current.values[field.key]; final value = current.values[field.key];
if (value == null || value == '') continue; 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) { payload[field.key] = switch (field.type) {
MasterFieldType.number => num.tryParse(value.toString()) ?? value, MasterFieldType.number => num.tryParse(value.toString()) ?? value,
MasterFieldType.dropdown => field.staticOptions != null || MasterFieldType.dropdown => field.staticOptions != null ||
field.optionsMasterKey == 'asset_depreciation_methods' field.optionsMasterKey == 'asset_depreciation_methods' ||
field.optionsMasterKey == 'location_states'
? value.toString() ? value.toString()
: int.tryParse(value.toString()) ?? value, : int.tryParse(value.toString()) ?? value,
MasterFieldType.boolean => value == true, MasterFieldType.boolean => value == true,
@ -494,6 +526,26 @@ class MasterFormNotifier extends FamilyAsyncNotifier<MasterFormState, MasterForm
return payload; 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. /// Returns the created/updated record id on success, otherwise null.
Future<String?> submit() async { Future<String?> submit() async {
final current = state.valueOrNull ?? const MasterFormState(); final current = state.valueOrNull ?? const MasterFormState();

View File

@ -99,6 +99,9 @@ class _MasterFormPanelState extends ConsumerState<MasterFormPanel> {
labelText: _fieldLabel(field), labelText: _fieldLabel(field),
floatingLabelBehavior: FloatingLabelBehavior.always, floatingLabelBehavior: FloatingLabelBehavior.always,
alignLabelWithHint: true, alignLabelWithHint: true,
hintText: field.key == 'tags'
? 'Comma-separated, e.g. critical, imported, rm'
: null,
); );
} }

View File

@ -224,6 +224,7 @@ String masterQuickAddNoun(String masterId) {
'item_subcategories' => 'subcategory', 'item_subcategories' => 'subcategory',
'items' => 'item', 'items' => 'item',
'hsn_codes' => 'HSN code', 'hsn_codes' => 'HSN code',
'locations' => 'location',
'plants' => 'plant', 'plants' => 'plant',
'warehouses' => 'warehouse', 'warehouses' => 'warehouse',
'designations' => 'designation', 'designations' => 'designation',

View File

@ -23,6 +23,77 @@ class MasterRemoteDataSource {
Future<List<FilterOptionModel>> listPlants() => Future<List<FilterOptionModel>> listPlants() =>
_listOptions(ApiEndpoints.plants); _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() => Future<List<FilterOptionModel>> listDesignations() =>
_listOptions(ApiEndpoints.designations); _listOptions(ApiEndpoints.designations);

View File

@ -232,7 +232,8 @@ class PurchaseOrderRemoteDataSource {
if (query.search != null && query.search!.isNotEmpty) 'search': query.search, if (query.search != null && query.search!.isNotEmpty) 'search': query.search,
if (query.status != null) 'status': query.status, if (query.status != null) 'status': query.status,
if (query.vendorId != null) 'vendor_id': query.vendorId, 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.dateFrom != null) 'date_from': query.dateFrom,
if (query.dateTo != null) 'date_to': query.dateTo, if (query.dateTo != null) 'date_to': query.dateTo,
}; };

View File

@ -9,8 +9,9 @@ import '../../../vendors/domain/repositories/vendor_repository.dart';
class PurchaseOrderLookups { class PurchaseOrderLookups {
const PurchaseOrderLookups({ const PurchaseOrderLookups({
this.vendors = const [], this.vendors = const [],
this.plants = const [], this.locations = const [],
this.warehouses = const [], this.vendorSourceOfSupplyById = const {},
this.locationStateById = const {},
this.paymentTerms = const [], this.paymentTerms = const [],
this.deliveryTerms = const [], this.deliveryTerms = const [],
this.items = const [], this.items = const [],
@ -25,8 +26,12 @@ class PurchaseOrderLookups {
}); });
final List<FilterOptionModel> vendors; final List<FilterOptionModel> vendors;
final List<FilterOptionModel> plants; /// Billing / shipping options (plants + warehouses from locations master).
final List<FilterOptionModel> warehouses; 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> paymentTerms;
final List<FilterOptionModel> deliveryTerms; final List<FilterOptionModel> deliveryTerms;
final List<FilterOptionModel> items; final List<FilterOptionModel> items;
@ -50,31 +55,31 @@ final purchaseOrderLookupsProvider =
final master = ref.watch(masterRemoteDataSourceProvider); final master = ref.watch(masterRemoteDataSourceProvider);
final vendorRepo = ref.watch(vendorRepositoryProvider); 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 itemsWithDefaults = await _safeItemsWithDefaults(master.listItemsWithHsn);
final gstWithPct = await _safeGstRatesWithPct(master.listGstRatesWithPct); final gstWithPct = await _safeGstRatesWithPct(master.listGstRatesWithPct);
final hsnWithGst = await _safeHsnWithGst(master.listHsnCodesWithGstRate); final hsnWithGst = await _safeHsnWithGst(master.listHsnCodesWithGstRate);
final results = await Future.wait([ final results = await Future.wait([
_safeOptions(master.listPlants),
_safeOptions(master.listWarehouses),
_safeOptions(master.listPaymentTerms), _safeOptions(master.listPaymentTerms),
_safeOptions(master.listDeliveryTerms), _safeOptions(master.listDeliveryTerms),
_safeOptions(master.listUom), _safeOptions(master.listUom),
]); ]);
return PurchaseOrderLookups( return PurchaseOrderLookups(
vendors: vendors, vendors: vendorsWithSos.options,
plants: results[0], locations: locationsWithState.options,
warehouses: results[1], vendorSourceOfSupplyById: vendorsWithSos.sourceOfSupplyById,
paymentTerms: results[2], locationStateById: locationsWithState.stateById,
deliveryTerms: results[3], paymentTerms: results[0],
deliveryTerms: results[1],
items: itemsWithDefaults.options, items: itemsWithDefaults.options,
itemHsnById: itemsWithDefaults.hsnByItemId, itemHsnById: itemsWithDefaults.hsnByItemId,
itemUomById: itemsWithDefaults.uomByItemId, itemUomById: itemsWithDefaults.uomByItemId,
itemGstRateById: itemsWithDefaults.gstRateByItemId, itemGstRateById: itemsWithDefaults.gstRateByItemId,
uom: results[4], uom: results[2],
gstRates: gstWithPct.options, gstRates: gstWithPct.options,
gstRatePctById: gstWithPct.pctById, gstRatePctById: gstWithPct.pctById,
hsnCodes: hsnWithGst.options, 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< Future<
({ ({
List<FilterOptionModel> options, List<FilterOptionModel> options,
@ -146,24 +197,3 @@ Future<({List<FilterOptionModel> options, Map<String, int?> gstRateByHsnId})>
return (options: <FilterOptionModel>[], gstRateByHsnId: <String, int?>{}); 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();
}

View File

@ -421,7 +421,8 @@ class _DetailHeader extends StatelessWidget {
final subtitleParts = [ final subtitleParts = [
vendorTypeLabel(order.vendorType), vendorTypeLabel(order.vendorType),
if (order.vendorName?.trim().isNotEmpty == true) order.vendorName!.trim(), 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( final actions = Wrap(
@ -737,12 +738,12 @@ class _OrderDetailsCard extends StatelessWidget {
value: vendorTypeLabel(order.vendorType), value: vendorTypeLabel(order.vendorType),
), ),
_DetailField( _DetailField(
label: 'Plant', label: 'Billing',
value: _displayOrDash(order.plantName), value: _displayOrDash(order.billingName),
), ),
_DetailField( _DetailField(
label: 'Warehouse', label: 'Shipping',
value: _displayOrDash(order.warehouseName), value: _displayOrDash(order.shippingName),
), ),
_DetailField(label: 'Payment Term', value: paymentTerm), _DetailField(label: 'Payment Term', value: paymentTerm),
_DetailField(label: 'Delivery Term', value: deliveryTerm), _DetailField(label: 'Delivery Term', value: deliveryTerm),
@ -1150,10 +1151,47 @@ class _AmountSummaryCard extends StatelessWidget {
final PurchaseOrderModel order; 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
final discount = order.discountAmount ?? 0; 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); final primaryTint = theme.colorScheme.primary.withValues(alpha: 0.1);
return _SectionCard( return _SectionCard(
@ -1162,32 +1200,38 @@ class _AmountSummaryCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
_SummaryRow( _SummaryRow(
label: 'Taxable Amount', label: 'Sub Total',
value: CurrencyFormatter.format(order.taxableAmount), value: CurrencyFormatter.format(subTotal),
),
const SizedBox(height: 12),
_SummaryRow(
label: 'Tax (GST)',
value: CurrencyFormatter.format(order.taxAmount),
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
_SummaryRow( _SummaryRow(
label: 'Freight Charges', label: 'Freight Charges',
value: CurrencyFormatter.format(order.freightCharges ?? 0), value: CurrencyFormatter.format(freight),
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
_SummaryRow( _SummaryRow(
label: 'Other Charges', label: 'Other Charges',
value: CurrencyFormatter.format(order.otherCharges ?? 0), value: CurrencyFormatter.format(other),
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
_SummaryRow( _SummaryRow(
label: 'Discount', label: 'Discount Amount',
value: discount > 0 value: discount > 0
? '-${CurrencyFormatter.format(discount)}' ? '-${CurrencyFormatter.format(discount)}'
: CurrencyFormatter.format(0), : CurrencyFormatter.format(0),
valueColor: discount > 0 ? AppColors.error : null, 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), const SizedBox(height: 16),
Container( Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),

View File

@ -51,8 +51,8 @@ class _PurchaseOrderFormScreenState extends ConsumerState<PurchaseOrderFormScree
DateTime? _poDate; DateTime? _poDate;
DateTime? _expectedDeliveryDate; DateTime? _expectedDeliveryDate;
int? _vendorId; int? _vendorId;
int? _plantId; int? _billingId;
int? _warehouseId; int? _shippingId;
int? _paymentTermId; int? _paymentTermId;
int? _deliveryTermId; int? _deliveryTermId;
final List<PoLineItemDraft> _lines = []; final List<PoLineItemDraft> _lines = [];
@ -98,8 +98,8 @@ class _PurchaseOrderFormScreenState extends ConsumerState<PurchaseOrderFormScree
_poDate = order.poDate ?? DateTime.now(); _poDate = order.poDate ?? DateTime.now();
_expectedDeliveryDate = order.expectedDeliveryDate; _expectedDeliveryDate = order.expectedDeliveryDate;
_vendorId = order.vendorId; _vendorId = order.vendorId;
_plantId = order.plantId; _billingId = order.billingId;
_warehouseId = order.warehouseId; _shippingId = order.shippingId;
_paymentTermId = order.paymentTermId; _paymentTermId = order.paymentTermId;
_deliveryTermId = order.deliveryTermId; _deliveryTermId = order.deliveryTermId;
_discountController.text = _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() { Map<String, dynamic> _buildPayload() {
return { return {
'po_date': DateFormatter.toApiDate(_poDate ?? DateTime.now()), 'po_date': DateFormatter.toApiDate(_poDate ?? DateTime.now()),
'vendor_id': _vendorId, 'vendor_id': _vendorId,
'plant_id': _plantId, 'billing_id': _billingId,
if (_warehouseId != null) 'warehouse_id': _warehouseId, 'shipping_id': _shippingId,
if (_paymentTermId != null) 'payment_term_id': _paymentTermId, if (_paymentTermId != null) 'payment_term_id': _paymentTermId,
if (_deliveryTermId != null) 'delivery_term_id': _deliveryTermId, if (_deliveryTermId != null) 'delivery_term_id': _deliveryTermId,
if (_expectedDeliveryDate != null) if (_expectedDeliveryDate != null)
@ -238,7 +256,7 @@ class _PurchaseOrderFormScreenState extends ConsumerState<PurchaseOrderFormScree
return; return;
} }
if (_vendorId == null || _plantId == null) { if (_vendorId == null || _billingId == null || _shippingId == null) {
showAppToastFromSnackBar(context, showAppToastFromSnackBar(context,
const SnackBar(content: Text('Please complete all required fields')), const SnackBar(content: Text('Please complete all required fields')),
); );
@ -352,8 +370,8 @@ class _PurchaseOrderFormScreenState extends ConsumerState<PurchaseOrderFormScree
final vendorIds = final vendorIds =
lookups.vendors.map((e) => _parseId(e.id)).whereType<int>(); lookups.vendors.map((e) => _parseId(e.id)).whereType<int>();
final plantIds = final locationIds =
lookups.plants.map((e) => _parseId(e.id)).whereType<int>(); lookups.locations.map((e) => _parseId(e.id)).whereType<int>();
final totals = _computeTotals(lookups.gstRatePctById); final totals = _computeTotals(lookups.gstRatePctById);
final theme = Theme.of(context); final theme = Theme.of(context);
@ -373,105 +391,94 @@ class _PurchaseOrderFormScreenState extends ConsumerState<PurchaseOrderFormScree
const SizedBox(height: 16), const SizedBox(height: 16),
_SectionCard( _SectionCard(
title: 'ORDER DETAILS', title: 'ORDER DETAILS',
child: Column( child: QuickAddInlineHost(
children: [ child: ResponsiveFormGrid(
FormRowFour( smallColumns: 1,
children: [ mediumColumns: 2,
_DateField( largeColumns: 4,
label: 'PO Date *', mediumBreakpoint: 640,
value: _poDate, largeBreakpoint: 1100,
onTap: () => _pickDate( children: [
current: _poDate, _DateField(
onPicked: (d) => setState(() => _poDate = d), 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), const SizedBox(height: 16),
@ -518,6 +525,7 @@ class _PurchaseOrderFormScreenState extends ConsumerState<PurchaseOrderFormScree
otherChargesController: _otherChargesController, otherChargesController: _otherChargesController,
discountController: _discountController, discountController: _discountController,
isEditing: widget.isEditing, isEditing: widget.isEditing,
isInterState: _isInterStateGst(lookups),
); );
if (stack) { if (stack) {
@ -764,6 +772,7 @@ class _AmountSummaryCard extends StatelessWidget {
required this.otherChargesController, required this.otherChargesController,
required this.discountController, required this.discountController,
required this.isEditing, required this.isEditing,
required this.isInterState,
}); });
final PoOrderTotals totals; final PoOrderTotals totals;
@ -771,6 +780,7 @@ class _AmountSummaryCard extends StatelessWidget {
final TextEditingController otherChargesController; final TextEditingController otherChargesController;
final TextEditingController discountController; final TextEditingController discountController;
final bool isEditing; final bool isEditing;
final bool isInterState;
String? _validateDiscount(String? value) { String? _validateDiscount(String? value) {
final text = value?.trim() ?? ''; final text = value?.trim() ?? '';
@ -784,6 +794,34 @@ class _AmountSummaryCard extends StatelessWidget {
return null; 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
@ -800,13 +838,8 @@ class _AmountSummaryCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
_SummaryReadOnlyRow( _SummaryReadOnlyRow(
label: 'Taxable Amount', label: 'Sub Total',
value: CurrencyFormatter.format(totals.taxableAmount), value: CurrencyFormatter.format(totals.subTotal),
),
const SizedBox(height: 10),
_SummaryReadOnlyRow(
label: 'Tax (GST)',
value: CurrencyFormatter.format(totals.taxAmount),
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
_SummaryInputRow( _SummaryInputRow(
@ -826,6 +859,17 @@ class _AmountSummaryCard extends StatelessWidget {
validator: _validateDiscount, validator: _validateDiscount,
autovalidateMode: AutovalidateMode.always, 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), const SizedBox(height: 12),
Container( Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
@ -856,7 +900,9 @@ class _AmountSummaryCard extends StatelessWidget {
Text( Text(
isEditing isEditing
? 'Editing charges or discount recalculates the grand total immediately — matches what will print on the PDF.' ? '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( style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant, color: theme.colorScheme.onSurfaceVariant,
height: 1.4, height: 1.4,

View File

@ -57,49 +57,60 @@ class PoLineCalculation {
/// Order-level totals derived from line calculations + header charges. /// Order-level totals derived from line calculations + header charges.
class PoOrderTotals { class PoOrderTotals {
const PoOrderTotals({ const PoOrderTotals({
required this.subTotal,
required this.taxableAmount, required this.taxableAmount,
required this.taxAmount, required this.taxAmount,
required this.grandTotal, required this.grandTotal,
required this.maxDiscountAmount, required this.maxDiscountAmount,
}); });
/// Sum of line amounts (before freight / other / discount).
final double subTotal;
/// Sub Total + Freight + Other Discount.
final double taxableAmount; final double taxableAmount;
final double taxAmount; final double taxAmount;
final double grandTotal; final double grandTotal;
/// Taxable + Tax + Freight + Other (discount cannot exceed this). /// Sub Total + Tax + Freight + Other (discount cannot exceed this).
final double maxDiscountAmount; final double maxDiscountAmount;
static const zero = PoOrderTotals( static const zero = PoOrderTotals(
subTotal: 0,
taxableAmount: 0, taxableAmount: 0,
taxAmount: 0, taxAmount: 0,
grandTotal: 0, grandTotal: 0,
maxDiscountAmount: 0, maxDiscountAmount: 0,
); );
/// 3.5 Taxable = sum of Line Amounts /// Sub Total = sum of line amounts
/// 3.6 Tax = sum of GST Amounts /// Taxable = Sub Total + Freight + Other Discount
/// 3.7 Grand Total = Taxable + Tax + Freight + Other Discount /// Tax = sum of line GST amounts
/// Grand Total = Taxable + Tax
factory PoOrderTotals.compute({ factory PoOrderTotals.compute({
required Iterable<PoLineCalculation> lines, required Iterable<PoLineCalculation> lines,
required double freight, required double freight,
required double otherCharges, required double otherCharges,
required double discountAmount, required double discountAmount,
}) { }) {
var taxable = 0.0; var subTotal = 0.0;
var tax = 0.0; var tax = 0.0;
for (final line in lines) { for (final line in lines) {
taxable += line.lineAmount; subTotal += line.lineAmount;
tax += line.gstAmount; tax += line.gstAmount;
} }
final maxDiscount = taxable + tax + freight + otherCharges; final freightSafe = freight < 0 ? 0.0 : freight;
final clampedDiscount = final otherSafe = otherCharges < 0 ? 0.0 : otherCharges;
discountAmount < 0 ? 0.0 : discountAmount; final clampedDiscount = discountAmount < 0 ? 0.0 : discountAmount;
final grandTotalRaw = maxDiscount - clampedDiscount; 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( return PoOrderTotals(
subTotal: subTotal,
taxableAmount: taxable, taxableAmount: taxable,
taxAmount: tax, taxAmount: tax,
grandTotal: grandTotalRaw < 0 ? 0 : grandTotalRaw, grandTotal: grandTotal < 0 ? 0 : grandTotal,
maxDiscountAmount: maxDiscount < 0 ? 0 : maxDiscount, maxDiscountAmount: maxDiscount < 0 ? 0 : maxDiscount,
); );
} }
@ -521,120 +532,92 @@ class _LineItemCardState extends ConsumerState<_LineItemCard> {
].whereType<AppDropdownOption<int?>>().toList(); ].whereType<AppDropdownOption<int?>>().toList();
const spacing = 8.0; const spacing = 8.0;
const minRowWidth = 1040.0;
Widget flex({required int flex, required Widget child}) { final itemField = MasterQuickAddDropdown<int>(
return Expanded(flex: flex, child: child); key: ValueKey('$lineKey-item'),
} masterId: 'items',
label: 'Item *',
final fields = <Widget>[ value: line.itemId,
flex( hint: 'Select item',
flex: 3, searchHint: 'Search item name or code...',
child: MasterQuickAddDropdown<int>( options: itemOptions,
key: ValueKey('$lineKey-item'), refreshLookups: () async {
masterId: 'items', ref.invalidate(purchaseOrderLookupsProvider);
label: 'Item *', await ref.read(purchaseOrderLookupsProvider.future);
value: line.itemId, },
hint: 'Select item', parseCreatedId: int.tryParse,
searchHint: 'Search item name or code...', onChanged: _onItemChanged,
options: itemOptions, validator: (v) => v == null ? 'Item is required' : null,
refreshLookups: () async { );
ref.invalidate(purchaseOrderLookupsProvider); final qtyField = AppTextField(
await ref.read(purchaseOrderLookupsProvider.future); key: ValueKey('$lineKey-qty'),
}, controller: line.qtyController,
parseCreatedId: int.tryParse, label: 'Qty *',
onChanged: _onItemChanged, hint: '0',
validator: (v) => v == null ? 'Item is required' : null, keyboardType: const TextInputType.numberWithOptions(decimal: true),
), validator: (v) {
), if (v == null || v.trim().isEmpty) return 'Required';
flex( final qty = double.tryParse(v);
flex: 1, if (qty == null || qty <= 0) return 'Invalid';
child: AppTextField( return null;
key: ValueKey('$lineKey-qty'), },
controller: line.qtyController, );
label: 'Qty *', final uomField = MasterQuickAddDropdown<int>(
hint: '0', key: ValueKey('$lineKey-uom'),
keyboardType: const TextInputType.numberWithOptions(decimal: true), masterId: 'uom',
validator: (v) { label: 'UOM *',
if (v == null || v.trim().isEmpty) return 'Required'; value: line.uomId,
final qty = double.tryParse(v); hint: 'Select UOM',
if (qty == null || qty <= 0) return 'Invalid'; searchHint: 'Search UOM...',
return null; options: uomOptions,
}, refreshLookups: () async {
), ref.invalidate(purchaseOrderLookupsProvider);
), await ref.read(purchaseOrderLookupsProvider.future);
flex( },
flex: 2, parseCreatedId: int.tryParse,
child: MasterQuickAddDropdown<int>( onChanged: (v) => _updateLine(() => line.uomId = v),
key: ValueKey('$lineKey-uom'), validator: (v) => v == null ? 'Required' : null,
masterId: 'uom', );
label: 'UOM *', final rateField = AppTextField(
value: line.uomId, key: ValueKey('$lineKey-rate'),
hint: 'Select UOM', controller: line.rateController,
searchHint: 'Search UOM...', label: 'Rate *',
options: uomOptions, hint: '0.00',
refreshLookups: () async { keyboardType: const TextInputType.numberWithOptions(decimal: true),
ref.invalidate(purchaseOrderLookupsProvider); validator: (v) {
await ref.read(purchaseOrderLookupsProvider.future); if (v == null || v.trim().isEmpty) return 'Required';
}, final rate = double.tryParse(v);
parseCreatedId: int.tryParse, if (rate == null || rate < 0) return 'Invalid';
onChanged: (v) => _updateLine(() => line.uomId = v), return null;
validator: (v) => v == null ? 'Required' : null, },
), );
), final discField = AppTextField(
flex( key: ValueKey('$lineKey-discount'),
flex: 1, controller: line.discountController,
child: AppTextField( label: 'Disc %',
key: ValueKey('$lineKey-rate'), hint: '0',
controller: line.rateController, keyboardType: const TextInputType.numberWithOptions(decimal: true),
label: 'Rate *', );
hint: '0.00', final gstField = MasterQuickAddDropdown<int?>(
keyboardType: const TextInputType.numberWithOptions(decimal: true), key: ValueKey('$lineKey-gst'),
validator: (v) { masterId: 'gst_rates',
if (v == null || v.trim().isEmpty) return 'Required'; label: 'GST %',
final rate = double.tryParse(v); value: line.gstRateId,
if (rate == null || rate < 0) return 'Invalid'; hint: 'Select',
return null; searchHint: 'Search GST %...',
}, options: gstOptions,
), refreshLookups: () async {
), ref.invalidate(purchaseOrderLookupsProvider);
flex( await ref.read(purchaseOrderLookupsProvider.future);
flex: 1, },
child: AppTextField( parseCreatedId: int.tryParse,
key: ValueKey('$lineKey-discount'), onChanged: (v) => _updateLine(() => line.gstRateId = v),
controller: line.discountController, );
label: 'Disc %', final amountField = _AmountWithRemove(
hint: '0', amount: CurrencyFormatter.format(calc.lineAmount),
keyboardType: const TextInputType.numberWithOptions(decimal: true), backgroundColor: amountBg,
), onRemove: widget.onRemove,
), );
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,
),
),
];
return Container( return Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
@ -645,29 +628,48 @@ class _LineItemCardState extends ConsumerState<_LineItemCard> {
child: QuickAddInlineHost( child: QuickAddInlineHost(
child: LayoutBuilder( child: LayoutBuilder(
builder: (context, constraints) { builder: (context, constraints) {
final rowWidth = constraints.maxWidth < minRowWidth final width = constraints.maxWidth;
? minRowWidth
: constraints.maxWidth; // Wide: single flex row
final row = SizedBox( if (width >= 1100) {
width: rowWidth, return Row(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
for (var i = 0; i < fields.length; i++) ...[ Expanded(flex: 3, child: itemField),
if (i > 0) const SizedBox(width: spacing), const SizedBox(width: spacing),
fields[i], 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 (23 columns)
return ResponsiveFormGrid(
spacing: spacing,
smallColumns: 1,
mediumColumns: 2,
largeColumns: 3,
mediumBreakpoint: 520,
largeBreakpoint: 800,
children: [
itemField,
qtyField,
uomField,
rateField,
discField,
gstField,
amountField,
],
);
}, },
), ),
), ),

View File

@ -66,7 +66,11 @@ class AddUserFormNotifier extends FamilyAsyncNotifier<AddUserFormState, String?>
if (rolesResult.failure != null) throw rolesResult.failure!; if (rolesResult.failure != null) throw rolesResult.failure!;
final departments = await masterRemote.listDepartments(); 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 designations = await masterRemote.listDesignations();
final usersResult = await userRepository.listUserOptions(); final usersResult = await userRepository.listUserOptions();

View File

@ -187,6 +187,7 @@ class _AddUserPanelState extends ConsumerState<AddUserPanel> {
String? hint, String? hint,
bool required = false, bool required = false,
String? masterId, String? masterId,
Map<String, dynamic>? initialValues,
}) { }) {
final fieldLabel = required ? '$label *' : label; final fieldLabel = required ? '$label *' : label;
final fieldHint = hint ?? 'Select ${label.toLowerCase()}'; final fieldHint = hint ?? 'Select ${label.toLowerCase()}';
@ -215,6 +216,7 @@ class _AddUserPanelState extends ConsumerState<AddUserPanel> {
searchHint: 'Search $label...', searchHint: 'Search $label...',
enabled: fieldEnabled, enabled: fieldEnabled,
options: _toOptions(options), options: _toOptions(options),
initialValues: initialValues,
refreshLookups: () => refreshLookups: () =>
ref.invalidate(addUserFormProvider(widget.userId)), ref.invalidate(addUserFormProvider(widget.userId)),
parseCreatedId: (id) => id, parseCreatedId: (id) => id,
@ -357,7 +359,8 @@ class _AddUserPanelState extends ConsumerState<AddUserPanel> {
label: 'Plant / Unit', label: 'Plant / Unit',
value: _selectedPlantId, value: _selectedPlantId,
options: formState.plants, options: formState.plants,
masterId: 'plants', masterId: 'locations',
initialValues: const {'type': 'plant'},
onChanged: (v) => setState(() => _selectedPlantId = v), onChanged: (v) => setState(() => _selectedPlantId = v),
), ),
right: _buildDropdown( right: _buildDropdown(

View File

@ -106,8 +106,8 @@ class ReportsRemoteDataSource {
if (query.itemSubcategoryId != null && if (query.itemSubcategoryId != null &&
query.itemSubcategoryId!.isNotEmpty) query.itemSubcategoryId!.isNotEmpty)
'item_subcategory_id': query.itemSubcategoryId, 'item_subcategory_id': query.itemSubcategoryId,
if (query.plantId != null && query.plantId!.isNotEmpty) if (query.locationId != null && query.locationId!.isNotEmpty)
'plant_id': query.plantId, 'location_id': query.locationId,
if (query.departmentId != null && query.departmentId!.isNotEmpty) if (query.departmentId != null && query.departmentId!.isNotEmpty)
'department_id': query.departmentId, 'department_id': query.departmentId,
if (query.isActive != null) 'is_active': query.isActive, if (query.isActive != null) 'is_active': query.isActive,

View File

@ -22,12 +22,17 @@ class ReportFilterOption {
.toString() .toString()
.trim(); .trim();
final code = map['code']?.toString().trim(); final code = map['code']?.toString().trim();
final display = (code != null && final type = map['type']?.toString().trim();
var display = (code != null &&
code.isNotEmpty && code.isNotEmpty &&
label.isNotEmpty && label.isNotEmpty &&
code != label) code != label)
? '$label ($code)' ? '$label ($code)'
: (label.isEmpty ? value : label); : (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']) final parentId = (map['item_category_id'] ?? map['parent_id'])
?.toString() ?.toString()
.trim(); .trim();
@ -45,7 +50,7 @@ class ReportFilterOption {
class DepreciationReportFilters { class DepreciationReportFilters {
const DepreciationReportFilters({ const DepreciationReportFilters({
this.plants = const [], this.locations = const [],
this.categories = const [], this.categories = const [],
this.subcategories = const [], this.subcategories = const [],
this.departments = const [], this.departments = const [],
@ -53,7 +58,7 @@ class DepreciationReportFilters {
this.depreciationMethods = const [], this.depreciationMethods = const [],
}); });
final List<ReportFilterOption> plants; final List<ReportFilterOption> locations;
final List<ReportFilterOption> categories; final List<ReportFilterOption> categories;
final List<ReportFilterOption> subcategories; final List<ReportFilterOption> subcategories;
final List<ReportFilterOption> departments; final List<ReportFilterOption> departments;
@ -79,7 +84,7 @@ class DepreciationReportFilters {
} }
return DepreciationReportFilters( return DepreciationReportFilters(
plants: parse(['plants', 'plant_options', 'plant']), locations: parse(['locations', 'location_options', 'location']),
categories: parse([ categories: parse([
'item_categories', 'item_categories',
'categories', 'categories',
@ -155,7 +160,7 @@ class DepreciationReportRow {
this.assetName, this.assetName,
this.categoryName, this.categoryName,
this.subcategoryName, this.subcategoryName,
this.plantName, this.locationName,
this.departmentName, this.departmentName,
this.status, this.status,
this.purchaseDate, this.purchaseDate,
@ -175,7 +180,7 @@ class DepreciationReportRow {
final String? assetName; final String? assetName;
final String? categoryName; final String? categoryName;
final String? subcategoryName; final String? subcategoryName;
final String? plantName; final String? locationName;
final String? departmentName; final String? departmentName;
final String? status; final String? status;
final DateTime? purchaseDate; final DateTime? purchaseDate;
@ -262,8 +267,9 @@ class DepreciationReportRow {
]) ?? ]) ??
readNestedName('item_subcategory') ?? readNestedName('item_subcategory') ??
readNestedName('subcategory'), readNestedName('subcategory'),
plantName: locationName:
readString(['plant_name', 'plantName']) ?? readNestedName('plant'), readString(['location_name', 'locationName']) ??
readNestedName('location'),
departmentName: readString(['department_name', 'departmentName']) ?? departmentName: readString(['department_name', 'departmentName']) ??
readNestedName('department'), readNestedName('department'),
status: readString(['status']), status: readString(['status']),
@ -315,7 +321,7 @@ class DepreciationReportQuery {
this.page = 1, this.page = 1,
this.limit = 20, this.limit = 20,
this.search, this.search,
this.plantId, this.locationId,
this.itemCategoryId, this.itemCategoryId,
this.itemSubcategoryId, this.itemSubcategoryId,
this.departmentId, this.departmentId,
@ -330,7 +336,7 @@ class DepreciationReportQuery {
final int page; final int page;
final int limit; final int limit;
final String? search; final String? search;
final String? plantId; final String? locationId;
final String? itemCategoryId; final String? itemCategoryId;
final String? itemSubcategoryId; final String? itemSubcategoryId;
final String? departmentId; final String? departmentId;
@ -343,7 +349,7 @@ class DepreciationReportQuery {
bool get hasActiveFilter => bool get hasActiveFilter =>
(search?.isNotEmpty ?? false) || (search?.isNotEmpty ?? false) ||
(plantId?.isNotEmpty ?? false) || (locationId?.isNotEmpty ?? false) ||
(itemCategoryId?.isNotEmpty ?? false) || (itemCategoryId?.isNotEmpty ?? false) ||
(itemSubcategoryId?.isNotEmpty ?? false) || (itemSubcategoryId?.isNotEmpty ?? false) ||
(departmentId?.isNotEmpty ?? false) || (departmentId?.isNotEmpty ?? false) ||
@ -358,7 +364,7 @@ class DepreciationReportQuery {
int? page, int? page,
int? limit, int? limit,
String? search, String? search,
String? plantId, String? locationId,
String? itemCategoryId, String? itemCategoryId,
String? itemSubcategoryId, String? itemSubcategoryId,
String? departmentId, String? departmentId,
@ -369,7 +375,7 @@ class DepreciationReportQuery {
DateTime? purchaseDateFrom, DateTime? purchaseDateFrom,
DateTime? purchaseDateTo, DateTime? purchaseDateTo,
bool clearSearch = false, bool clearSearch = false,
bool clearPlantId = false, bool clearLocationId = false,
bool clearItemCategoryId = false, bool clearItemCategoryId = false,
bool clearItemSubcategoryId = false, bool clearItemSubcategoryId = false,
bool clearDepartmentId = false, bool clearDepartmentId = false,
@ -384,7 +390,7 @@ class DepreciationReportQuery {
page: page ?? this.page, page: page ?? this.page,
limit: limit ?? this.limit, limit: limit ?? this.limit,
search: clearSearch ? null : search ?? this.search, search: clearSearch ? null : search ?? this.search,
plantId: clearPlantId ? null : plantId ?? this.plantId, locationId: clearLocationId ? null : locationId ?? this.locationId,
itemCategoryId: clearItemCategoryId itemCategoryId: clearItemCategoryId
? null ? null
: itemCategoryId ?? this.itemCategoryId, : itemCategoryId ?? this.itemCategoryId,

View File

@ -145,14 +145,14 @@ class DepreciationReportNotifier
); );
} }
Future<void> setPlantId(String? value) async { Future<void> setLocationId(String? value) async {
final current = state.valueOrNull?.query ?? final current = state.valueOrNull?.query ??
const DepreciationReportQuery(limit: AppConstants.defaultPageSize); const DepreciationReportQuery(limit: AppConstants.defaultPageSize);
await applyQuery( await applyQuery(
current.copyWith( current.copyWith(
page: 1, page: 1,
plantId: value, locationId: value,
clearPlantId: value == null || value.isEmpty, clearLocationId: value == null || value.isEmpty,
), ),
); );
} }

View File

@ -174,7 +174,7 @@ class _DepreciationReportScreenState
filters: state.filters, filters: state.filters,
query: state.query, query: state.query,
onSearch: notifier.setSearch, onSearch: notifier.setSearch,
onPlantChanged: notifier.setPlantId, onLocationChanged: notifier.setLocationId,
onCategoryChanged: notifier.setItemCategoryId, onCategoryChanged: notifier.setItemCategoryId,
onSubcategoryChanged: notifier.setItemSubcategoryId, onSubcategoryChanged: notifier.setItemSubcategoryId,
onDepartmentChanged: notifier.setDepartmentId, onDepartmentChanged: notifier.setDepartmentId,
@ -324,7 +324,7 @@ class _FiltersBar extends StatefulWidget {
required this.filters, required this.filters,
required this.query, required this.query,
required this.onSearch, required this.onSearch,
required this.onPlantChanged, required this.onLocationChanged,
required this.onCategoryChanged, required this.onCategoryChanged,
required this.onSubcategoryChanged, required this.onSubcategoryChanged,
required this.onDepartmentChanged, required this.onDepartmentChanged,
@ -342,7 +342,7 @@ class _FiltersBar extends StatefulWidget {
final DepreciationReportFilters filters; final DepreciationReportFilters filters;
final DepreciationReportQuery query; final DepreciationReportQuery query;
final ValueChanged<String> onSearch; final ValueChanged<String> onSearch;
final ValueChanged<String?> onPlantChanged; final ValueChanged<String?> onLocationChanged;
final ValueChanged<String?> onCategoryChanged; final ValueChanged<String?> onCategoryChanged;
final ValueChanged<String?> onSubcategoryChanged; final ValueChanged<String?> onSubcategoryChanged;
final ValueChanged<String?> onDepartmentChanged; final ValueChanged<String?> onDepartmentChanged;
@ -425,11 +425,11 @@ class _FiltersBarState extends State<_FiltersBar> {
); );
} }
final plant = dropdown( final location = dropdown(
label: 'Plant', label: 'Location',
value: query.plantId, value: query.locationId,
options: filters.plants, options: filters.locations,
onChanged: widget.onPlantChanged, onChanged: widget.onLocationChanged,
); );
final category = dropdown( final category = dropdown(
label: 'Category', label: 'Category',
@ -549,7 +549,7 @@ class _FiltersBarState extends State<_FiltersBar> {
: null; : null;
return AppResponsiveFilterGrid( return AppResponsiveFilterGrid(
fields: [searchField, plant, category, asOfField], fields: [searchField, location, category, asOfField],
footer: Align( footer: Align(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
child: actions, child: actions,
@ -589,10 +589,10 @@ class _ReportTable extends StatelessWidget {
cellBuilder: (_, row) => AppTableCell.text(row.categoryName), cellBuilder: (_, row) => AppTableCell.text(row.categoryName),
), ),
AppDataColumn( AppDataColumn(
label: 'Plant', label: 'Location',
flex: 2, flex: 2,
searchText: (row) => row.plantName ?? '', searchText: (row) => row.locationName ?? '',
cellBuilder: (_, row) => AppTableCell.text(row.plantName), cellBuilder: (_, row) => AppTableCell.text(row.locationName),
), ),
AppDataColumn( AppDataColumn(
label: 'Purchase Date', label: 'Purchase Date',

View File

@ -45,10 +45,21 @@ Object? _readItemCategoryName(Map<dynamic, dynamic> json, String key) {
_readNestedName(json, 'asset_category'); _readNestedName(json, 'asset_category');
} }
Object? _readPlantName(Map<dynamic, dynamic> json, String key) { Object? _readLocationName(Map<dynamic, dynamic> json, String key) {
final flat = json['plant_name']; final flat = json['location_name'];
if (flat is String && flat.isNotEmpty) return flat; 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) { Object? _readItemCategoryId(Map<dynamic, dynamic> json, String key) {
@ -61,10 +72,10 @@ Object? _readItemCategoryId(Map<dynamic, dynamic> json, String key) {
return null; return null;
} }
Object? _readPlantId(Map<dynamic, dynamic> json, String key) { Object? _readLocationId(Map<dynamic, dynamic> json, String key) {
final flat = json['plant_id']; final flat = json['location_id'];
if (flat != null) return flat; if (flat != null) return flat;
final nested = json['plant']; final nested = json['location'];
if (nested is Map) return nested['id']; if (nested is Map) return nested['id'];
return null; return null;
} }
@ -94,12 +105,6 @@ Object? _readDepartmentName(Map<dynamic, dynamic> json, String key) {
return _readNestedName(json, 'department'); 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) { Object? _readVendorNameFromNested(Map<dynamic, dynamic> json, String key) {
final flat = json['vendor_name']; final flat = json['vendor_name'];
if (flat is String && flat.isNotEmpty) return flat; if (flat is String && flat.isNotEmpty) return flat;
@ -153,9 +158,14 @@ class AssetModel with _$AssetModel {
int? assetSubcategoryId, int? assetSubcategoryId,
@JsonKey(name: 'item_subcategory_name', readValue: _readItemSubcategoryName) @JsonKey(name: 'item_subcategory_name', readValue: _readItemSubcategoryName)
String? assetSubcategoryName, String? assetSubcategoryName,
@JsonKey(name: 'plant_id', readValue: _readPlantId, fromJson: _intFromJsonNullable) @JsonKey(
int? plantId, name: 'location_id',
@JsonKey(name: 'plant_name', readValue: _readPlantName) String? plantName, readValue: _readLocationId,
fromJson: _intFromJsonNullable,
)
int? locationId,
@JsonKey(name: 'location_name', readValue: _readLocationName)
String? locationName,
@JsonKey(name: 'brand_model') String? brandModel, @JsonKey(name: 'brand_model') String? brandModel,
String? manufacturer, String? manufacturer,
@JsonKey(name: 'serial_number') String? serialNumber, @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_id', fromJson: _intFromJsonNullable) int? departmentId,
@JsonKey(name: 'department_name', readValue: _readDepartmentName) @JsonKey(name: 'department_name', readValue: _readDepartmentName)
String? departmentName, 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: 'location_detail') String? locationDetail,
@JsonKey(name: 'assigned_to_user_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'assigned_to_user_id', fromJson: _intFromJsonNullable)
int? assignedToUserId, 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_id', fromJson: _intFromJsonNullable) int? vendorId,
@JsonKey(name: 'vendor_name', readValue: _readVendorNameFromNested) String? vendorName, @JsonKey(name: 'vendor_name', readValue: _readVendorNameFromNested) String? vendorName,
@JsonKey(name: 'po_id', fromJson: _intFromJsonNullable) int? poId, @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: 'is_active') @Default(true) bool isActive,
@JsonKey(name: 'created_at', fromJson: _dateFromJsonNullable) DateTime? createdAt, @JsonKey(name: 'created_at', fromJson: _dateFromJsonNullable) DateTime? createdAt,
@JsonKey(name: 'updated_at', fromJson: _dateFromJsonNullable) DateTime? updatedAt, @JsonKey(name: 'updated_at', fromJson: _dateFromJsonNullable) DateTime? updatedAt,
@JsonKey(
name: 'maintenance',
fromJson: AssetMaintenanceSummary.fromJsonNullable,
toJson: AssetMaintenanceSummary.toJsonNullable,
)
AssetMaintenanceSummary? maintenance,
}) = _AssetModel; }) = _AssetModel;
factory AssetModel.fromJson(Map<String, dynamic> json) => _$AssetModelFromJson(json); 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) { String assetConditionLabel(String? value) {
if (value == null || value.trim().isEmpty) return ''; if (value == null || value.trim().isEmpty) return '';
return value.replaceAll('_', ' '); return value.replaceAll('_', ' ');
@ -402,6 +572,35 @@ class InsurancePolicyModel with _$InsurancePolicyModel {
_$InsurancePolicyModelFromJson(json); _$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 @freezed
class AssetAlertModel with _$AssetAlertModel { class AssetAlertModel with _$AssetAlertModel {
const factory AssetAlertModel({ const factory AssetAlertModel({
@ -416,7 +615,8 @@ class AssetAlertModel with _$AssetAlertModel {
@JsonKey(name: 'due_date', fromJson: _dateFromJsonNullable) DateTime? dueDate, @JsonKey(name: 'due_date', fromJson: _dateFromJsonNullable) DateTime? dueDate,
@JsonKey(name: 'days_remaining', fromJson: _intFromJsonNullable) int? daysRemaining, @JsonKey(name: 'days_remaining', fromJson: _intFromJsonNullable) int? daysRemaining,
String? status, String? status,
@JsonKey(name: 'plant_name') String? plantName, @JsonKey(name: 'location_name', readValue: _readAlertLocationName)
String? locationName,
}) = _AssetAlertModel; }) = _AssetAlertModel;
factory AssetAlertModel.fromJson(Map<String, dynamic> json) => factory AssetAlertModel.fromJson(Map<String, dynamic> json) =>
@ -428,12 +628,10 @@ class AssetTransferHistoryModel {
required this.id, required this.id,
this.transferDate, this.transferDate,
this.reason, this.reason,
this.fromPlantName, this.fromLocationName,
this.toPlantName, this.toLocationName,
this.fromDepartmentName, this.fromDepartmentName,
this.toDepartmentName, this.toDepartmentName,
this.fromWarehouseName,
this.toWarehouseName,
this.fromUserName, this.fromUserName,
this.toUserName, this.toUserName,
this.createdAt, this.createdAt,
@ -443,12 +641,10 @@ class AssetTransferHistoryModel {
final String id; final String id;
final DateTime? transferDate; final DateTime? transferDate;
final String? reason; final String? reason;
final String? fromPlantName; final String? fromLocationName;
final String? toPlantName; final String? toLocationName;
final String? fromDepartmentName; final String? fromDepartmentName;
final String? toDepartmentName; final String? toDepartmentName;
final String? fromWarehouseName;
final String? toWarehouseName;
final String? fromUserName; final String? fromUserName;
final String? toUserName; final String? toUserName;
final DateTime? createdAt; final DateTime? createdAt;
@ -466,7 +662,15 @@ class AssetTransferHistoryModel {
if (nested is Map) { if (nested is Map) {
for (final field in ['name', 'full_name']) { for (final field in ['name', 'full_name']) {
final value = nested[field]; 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; return null;
@ -482,14 +686,13 @@ class AssetTransferHistoryModel {
id: _idFromJson(json['id']), id: _idFromJson(json['id']),
transferDate: parseDate(json['transfer_date']), transferDate: parseDate(json['transfer_date']),
reason: readName('reason'), reason: readName('reason'),
fromPlantName: readName('from_plant_name') ?? readNestedName('from_plant'), fromLocationName:
toPlantName: readName('to_plant_name') ?? readNestedName('to_plant'), readName('from_location_name') ?? readNestedName('from_location'),
toLocationName:
readName('to_location_name') ?? readNestedName('to_location'),
fromDepartmentName: fromDepartmentName:
readName('from_department_name') ?? readNestedName('from_department'), readName('from_department_name') ?? readNestedName('from_department'),
toDepartmentName: readName('to_department_name') ?? readNestedName('to_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'), fromUserName: readName('from_user_name') ?? readNestedName('from_user'),
toUserName: readName('to_user_name') ?? readNestedName('to_user'), toUserName: readName('to_user_name') ?? readNestedName('to_user'),
createdAt: parseDate(json['created_at']), createdAt: parseDate(json['created_at']),
@ -557,8 +760,10 @@ class AssetListQuery {
this.condition, this.condition,
this.itemCategoryId, this.itemCategoryId,
this.itemSubcategoryId, this.itemSubcategoryId,
this.plantId, this.locationId,
this.departmentId, this.departmentId,
this.maintenanceInchargeUserId,
this.dueOnly,
this.isActive, this.isActive,
}); });
@ -569,8 +774,10 @@ class AssetListQuery {
final String? condition; final String? condition;
final int? itemCategoryId; final int? itemCategoryId;
final int? itemSubcategoryId; final int? itemSubcategoryId;
final int? plantId; final int? locationId;
final int? departmentId; final int? departmentId;
final int? maintenanceInchargeUserId;
final bool? dueOnly;
final bool? isActive; final bool? isActive;
AssetListQuery copyWith({ AssetListQuery copyWith({
@ -581,8 +788,10 @@ class AssetListQuery {
Object? condition = _unset, Object? condition = _unset,
Object? itemCategoryId = _unset, Object? itemCategoryId = _unset,
Object? itemSubcategoryId = _unset, Object? itemSubcategoryId = _unset,
Object? plantId = _unset, Object? locationId = _unset,
Object? departmentId = _unset, Object? departmentId = _unset,
Object? maintenanceInchargeUserId = _unset,
Object? dueOnly = _unset,
Object? isActive = _unset, Object? isActive = _unset,
}) { }) {
return AssetListQuery( return AssetListQuery(
@ -598,10 +807,15 @@ class AssetListQuery {
itemSubcategoryId: identical(itemSubcategoryId, _unset) itemSubcategoryId: identical(itemSubcategoryId, _unset)
? this.itemSubcategoryId ? this.itemSubcategoryId
: itemSubcategoryId as int?, : 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) departmentId: identical(departmentId, _unset)
? this.departmentId ? this.departmentId
: departmentId as int?, : departmentId as int?,
maintenanceInchargeUserId: identical(maintenanceInchargeUserId, _unset)
? this.maintenanceInchargeUserId
: maintenanceInchargeUserId as int?,
dueOnly: identical(dueOnly, _unset) ? this.dueOnly : dueOnly as bool?,
isActive: isActive:
identical(isActive, _unset) ? this.isActive : isActive as bool?, identical(isActive, _unset) ? this.isActive : isActive as bool?,
); );

View File

@ -438,13 +438,13 @@ mixin _$AssetModel {
@JsonKey(name: 'item_subcategory_name', readValue: _readItemSubcategoryName) @JsonKey(name: 'item_subcategory_name', readValue: _readItemSubcategoryName)
String? get assetSubcategoryName => throw _privateConstructorUsedError; String? get assetSubcategoryName => throw _privateConstructorUsedError;
@JsonKey( @JsonKey(
name: 'plant_id', name: 'location_id',
readValue: _readPlantId, readValue: _readLocationId,
fromJson: _intFromJsonNullable, fromJson: _intFromJsonNullable,
) )
int? get plantId => throw _privateConstructorUsedError; int? get locationId => throw _privateConstructorUsedError;
@JsonKey(name: 'plant_name', readValue: _readPlantName) @JsonKey(name: 'location_name', readValue: _readLocationName)
String? get plantName => throw _privateConstructorUsedError; String? get locationName => throw _privateConstructorUsedError;
@JsonKey(name: 'brand_model') @JsonKey(name: 'brand_model')
String? get brandModel => throw _privateConstructorUsedError; String? get brandModel => throw _privateConstructorUsedError;
String? get manufacturer => throw _privateConstructorUsedError; String? get manufacturer => throw _privateConstructorUsedError;
@ -456,14 +456,26 @@ mixin _$AssetModel {
int? get departmentId => throw _privateConstructorUsedError; int? get departmentId => throw _privateConstructorUsedError;
@JsonKey(name: 'department_name', readValue: _readDepartmentName) @JsonKey(name: 'department_name', readValue: _readDepartmentName)
String? get departmentName => throw _privateConstructorUsedError; 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') @JsonKey(name: 'location_detail')
String? get locationDetail => throw _privateConstructorUsedError; String? get locationDetail => throw _privateConstructorUsedError;
@JsonKey(name: 'assigned_to_user_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'assigned_to_user_id', fromJson: _intFromJsonNullable)
int? get assignedToUserId => throw _privateConstructorUsedError; 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) @JsonKey(name: 'vendor_id', fromJson: _intFromJsonNullable)
int? get vendorId => throw _privateConstructorUsedError; int? get vendorId => throw _privateConstructorUsedError;
@JsonKey(name: 'vendor_name', readValue: _readVendorNameFromNested) @JsonKey(name: 'vendor_name', readValue: _readVendorNameFromNested)
@ -505,6 +517,13 @@ mixin _$AssetModel {
DateTime? get createdAt => throw _privateConstructorUsedError; DateTime? get createdAt => throw _privateConstructorUsedError;
@JsonKey(name: 'updated_at', fromJson: _dateFromJsonNullable) @JsonKey(name: 'updated_at', fromJson: _dateFromJsonNullable)
DateTime? get updatedAt => throw _privateConstructorUsedError; 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. /// Serializes this AssetModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError; Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@ -544,12 +563,13 @@ abstract class $AssetModelCopyWith<$Res> {
@JsonKey(name: 'item_subcategory_name', readValue: _readItemSubcategoryName) @JsonKey(name: 'item_subcategory_name', readValue: _readItemSubcategoryName)
String? assetSubcategoryName, String? assetSubcategoryName,
@JsonKey( @JsonKey(
name: 'plant_id', name: 'location_id',
readValue: _readPlantId, readValue: _readLocationId,
fromJson: _intFromJsonNullable, fromJson: _intFromJsonNullable,
) )
int? plantId, int? locationId,
@JsonKey(name: 'plant_name', readValue: _readPlantName) String? plantName, @JsonKey(name: 'location_name', readValue: _readLocationName)
String? locationName,
@JsonKey(name: 'brand_model') String? brandModel, @JsonKey(name: 'brand_model') String? brandModel,
String? manufacturer, String? manufacturer,
@JsonKey(name: 'serial_number') String? serialNumber, @JsonKey(name: 'serial_number') String? serialNumber,
@ -558,13 +578,27 @@ abstract class $AssetModelCopyWith<$Res> {
int? departmentId, int? departmentId,
@JsonKey(name: 'department_name', readValue: _readDepartmentName) @JsonKey(name: 'department_name', readValue: _readDepartmentName)
String? departmentName, 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: 'location_detail') String? locationDetail,
@JsonKey(name: 'assigned_to_user_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'assigned_to_user_id', fromJson: _intFromJsonNullable)
int? assignedToUserId, 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_id', fromJson: _intFromJsonNullable) int? vendorId,
@JsonKey(name: 'vendor_name', readValue: _readVendorNameFromNested) @JsonKey(name: 'vendor_name', readValue: _readVendorNameFromNested)
String? vendorName, String? vendorName,
@ -599,6 +633,12 @@ abstract class $AssetModelCopyWith<$Res> {
DateTime? createdAt, DateTime? createdAt,
@JsonKey(name: 'updated_at', fromJson: _dateFromJsonNullable) @JsonKey(name: 'updated_at', fromJson: _dateFromJsonNullable)
DateTime? updatedAt, 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? assetCategoryName = freezed,
Object? assetSubcategoryId = freezed, Object? assetSubcategoryId = freezed,
Object? assetSubcategoryName = freezed, Object? assetSubcategoryName = freezed,
Object? plantId = freezed, Object? locationId = freezed,
Object? plantName = freezed, Object? locationName = freezed,
Object? brandModel = freezed, Object? brandModel = freezed,
Object? manufacturer = freezed, Object? manufacturer = freezed,
Object? serialNumber = freezed, Object? serialNumber = freezed,
Object? partNumber = freezed, Object? partNumber = freezed,
Object? departmentId = freezed, Object? departmentId = freezed,
Object? departmentName = freezed, Object? departmentName = freezed,
Object? warehouseId = freezed,
Object? warehouseName = freezed,
Object? locationDetail = freezed, Object? locationDetail = freezed,
Object? assignedToUserId = freezed, Object? assignedToUserId = freezed,
Object? maintenanceInchargeUserId = freezed,
Object? maintenanceFrequencyInDays = freezed,
Object? maintenanceChecklistJson = freezed,
Object? commencementDate = freezed,
Object? vendorId = freezed, Object? vendorId = freezed,
Object? vendorName = freezed, Object? vendorName = freezed,
Object? poId = freezed, Object? poId = freezed,
@ -658,6 +700,7 @@ class _$AssetModelCopyWithImpl<$Res, $Val extends AssetModel>
Object? isActive = null, Object? isActive = null,
Object? createdAt = freezed, Object? createdAt = freezed,
Object? updatedAt = freezed, Object? updatedAt = freezed,
Object? maintenance = freezed,
}) { }) {
return _then( return _then(
_value.copyWith( _value.copyWith(
@ -689,13 +732,13 @@ class _$AssetModelCopyWithImpl<$Res, $Val extends AssetModel>
? _value.assetSubcategoryName ? _value.assetSubcategoryName
: assetSubcategoryName // ignore: cast_nullable_to_non_nullable : assetSubcategoryName // ignore: cast_nullable_to_non_nullable
as String?, as String?,
plantId: freezed == plantId locationId: freezed == locationId
? _value.plantId ? _value.locationId
: plantId // ignore: cast_nullable_to_non_nullable : locationId // ignore: cast_nullable_to_non_nullable
as int?, as int?,
plantName: freezed == plantName locationName: freezed == locationName
? _value.plantName ? _value.locationName
: plantName // ignore: cast_nullable_to_non_nullable : locationName // ignore: cast_nullable_to_non_nullable
as String?, as String?,
brandModel: freezed == brandModel brandModel: freezed == brandModel
? _value.brandModel ? _value.brandModel
@ -721,14 +764,6 @@ class _$AssetModelCopyWithImpl<$Res, $Val extends AssetModel>
? _value.departmentName ? _value.departmentName
: departmentName // ignore: cast_nullable_to_non_nullable : departmentName // ignore: cast_nullable_to_non_nullable
as String?, 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 locationDetail: freezed == locationDetail
? _value.locationDetail ? _value.locationDetail
: locationDetail // ignore: cast_nullable_to_non_nullable : locationDetail // ignore: cast_nullable_to_non_nullable
@ -737,6 +772,22 @@ class _$AssetModelCopyWithImpl<$Res, $Val extends AssetModel>
? _value.assignedToUserId ? _value.assignedToUserId
: assignedToUserId // ignore: cast_nullable_to_non_nullable : assignedToUserId // ignore: cast_nullable_to_non_nullable
as int?, 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 vendorId: freezed == vendorId
? _value.vendorId ? _value.vendorId
: vendorId // ignore: cast_nullable_to_non_nullable : vendorId // ignore: cast_nullable_to_non_nullable
@ -825,6 +876,10 @@ class _$AssetModelCopyWithImpl<$Res, $Val extends AssetModel>
? _value.updatedAt ? _value.updatedAt
: updatedAt // ignore: cast_nullable_to_non_nullable : updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime?, as DateTime?,
maintenance: freezed == maintenance
? _value.maintenance
: maintenance // ignore: cast_nullable_to_non_nullable
as AssetMaintenanceSummary?,
) )
as $Val, as $Val,
); );
@ -861,12 +916,13 @@ abstract class _$$AssetModelImplCopyWith<$Res>
@JsonKey(name: 'item_subcategory_name', readValue: _readItemSubcategoryName) @JsonKey(name: 'item_subcategory_name', readValue: _readItemSubcategoryName)
String? assetSubcategoryName, String? assetSubcategoryName,
@JsonKey( @JsonKey(
name: 'plant_id', name: 'location_id',
readValue: _readPlantId, readValue: _readLocationId,
fromJson: _intFromJsonNullable, fromJson: _intFromJsonNullable,
) )
int? plantId, int? locationId,
@JsonKey(name: 'plant_name', readValue: _readPlantName) String? plantName, @JsonKey(name: 'location_name', readValue: _readLocationName)
String? locationName,
@JsonKey(name: 'brand_model') String? brandModel, @JsonKey(name: 'brand_model') String? brandModel,
String? manufacturer, String? manufacturer,
@JsonKey(name: 'serial_number') String? serialNumber, @JsonKey(name: 'serial_number') String? serialNumber,
@ -875,13 +931,27 @@ abstract class _$$AssetModelImplCopyWith<$Res>
int? departmentId, int? departmentId,
@JsonKey(name: 'department_name', readValue: _readDepartmentName) @JsonKey(name: 'department_name', readValue: _readDepartmentName)
String? departmentName, 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: 'location_detail') String? locationDetail,
@JsonKey(name: 'assigned_to_user_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'assigned_to_user_id', fromJson: _intFromJsonNullable)
int? assignedToUserId, 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_id', fromJson: _intFromJsonNullable) int? vendorId,
@JsonKey(name: 'vendor_name', readValue: _readVendorNameFromNested) @JsonKey(name: 'vendor_name', readValue: _readVendorNameFromNested)
String? vendorName, String? vendorName,
@ -916,6 +986,12 @@ abstract class _$$AssetModelImplCopyWith<$Res>
DateTime? createdAt, DateTime? createdAt,
@JsonKey(name: 'updated_at', fromJson: _dateFromJsonNullable) @JsonKey(name: 'updated_at', fromJson: _dateFromJsonNullable)
DateTime? updatedAt, DateTime? updatedAt,
@JsonKey(
name: 'maintenance',
fromJson: AssetMaintenanceSummary.fromJsonNullable,
toJson: AssetMaintenanceSummary.toJsonNullable,
)
AssetMaintenanceSummary? maintenance,
}); });
} }
@ -940,18 +1016,20 @@ class __$$AssetModelImplCopyWithImpl<$Res>
Object? assetCategoryName = freezed, Object? assetCategoryName = freezed,
Object? assetSubcategoryId = freezed, Object? assetSubcategoryId = freezed,
Object? assetSubcategoryName = freezed, Object? assetSubcategoryName = freezed,
Object? plantId = freezed, Object? locationId = freezed,
Object? plantName = freezed, Object? locationName = freezed,
Object? brandModel = freezed, Object? brandModel = freezed,
Object? manufacturer = freezed, Object? manufacturer = freezed,
Object? serialNumber = freezed, Object? serialNumber = freezed,
Object? partNumber = freezed, Object? partNumber = freezed,
Object? departmentId = freezed, Object? departmentId = freezed,
Object? departmentName = freezed, Object? departmentName = freezed,
Object? warehouseId = freezed,
Object? warehouseName = freezed,
Object? locationDetail = freezed, Object? locationDetail = freezed,
Object? assignedToUserId = freezed, Object? assignedToUserId = freezed,
Object? maintenanceInchargeUserId = freezed,
Object? maintenanceFrequencyInDays = freezed,
Object? maintenanceChecklistJson = freezed,
Object? commencementDate = freezed,
Object? vendorId = freezed, Object? vendorId = freezed,
Object? vendorName = freezed, Object? vendorName = freezed,
Object? poId = freezed, Object? poId = freezed,
@ -974,6 +1052,7 @@ class __$$AssetModelImplCopyWithImpl<$Res>
Object? isActive = null, Object? isActive = null,
Object? createdAt = freezed, Object? createdAt = freezed,
Object? updatedAt = freezed, Object? updatedAt = freezed,
Object? maintenance = freezed,
}) { }) {
return _then( return _then(
_$AssetModelImpl( _$AssetModelImpl(
@ -1005,13 +1084,13 @@ class __$$AssetModelImplCopyWithImpl<$Res>
? _value.assetSubcategoryName ? _value.assetSubcategoryName
: assetSubcategoryName // ignore: cast_nullable_to_non_nullable : assetSubcategoryName // ignore: cast_nullable_to_non_nullable
as String?, as String?,
plantId: freezed == plantId locationId: freezed == locationId
? _value.plantId ? _value.locationId
: plantId // ignore: cast_nullable_to_non_nullable : locationId // ignore: cast_nullable_to_non_nullable
as int?, as int?,
plantName: freezed == plantName locationName: freezed == locationName
? _value.plantName ? _value.locationName
: plantName // ignore: cast_nullable_to_non_nullable : locationName // ignore: cast_nullable_to_non_nullable
as String?, as String?,
brandModel: freezed == brandModel brandModel: freezed == brandModel
? _value.brandModel ? _value.brandModel
@ -1037,14 +1116,6 @@ class __$$AssetModelImplCopyWithImpl<$Res>
? _value.departmentName ? _value.departmentName
: departmentName // ignore: cast_nullable_to_non_nullable : departmentName // ignore: cast_nullable_to_non_nullable
as String?, 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 locationDetail: freezed == locationDetail
? _value.locationDetail ? _value.locationDetail
: locationDetail // ignore: cast_nullable_to_non_nullable : locationDetail // ignore: cast_nullable_to_non_nullable
@ -1053,6 +1124,22 @@ class __$$AssetModelImplCopyWithImpl<$Res>
? _value.assignedToUserId ? _value.assignedToUserId
: assignedToUserId // ignore: cast_nullable_to_non_nullable : assignedToUserId // ignore: cast_nullable_to_non_nullable
as int?, 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 vendorId: freezed == vendorId
? _value.vendorId ? _value.vendorId
: vendorId // ignore: cast_nullable_to_non_nullable : vendorId // ignore: cast_nullable_to_non_nullable
@ -1141,6 +1228,10 @@ class __$$AssetModelImplCopyWithImpl<$Res>
? _value.updatedAt ? _value.updatedAt
: updatedAt // ignore: cast_nullable_to_non_nullable : updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime?, 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) @JsonKey(name: 'item_subcategory_name', readValue: _readItemSubcategoryName)
this.assetSubcategoryName, this.assetSubcategoryName,
@JsonKey( @JsonKey(
name: 'plant_id', name: 'location_id',
readValue: _readPlantId, readValue: _readLocationId,
fromJson: _intFromJsonNullable, fromJson: _intFromJsonNullable,
) )
this.plantId, this.locationId,
@JsonKey(name: 'plant_name', readValue: _readPlantName) this.plantName, @JsonKey(name: 'location_name', readValue: _readLocationName)
this.locationName,
@JsonKey(name: 'brand_model') this.brandModel, @JsonKey(name: 'brand_model') this.brandModel,
this.manufacturer, this.manufacturer,
@JsonKey(name: 'serial_number') this.serialNumber, @JsonKey(name: 'serial_number') this.serialNumber,
@ -1184,13 +1276,27 @@ class _$AssetModelImpl implements _AssetModel {
this.departmentId, this.departmentId,
@JsonKey(name: 'department_name', readValue: _readDepartmentName) @JsonKey(name: 'department_name', readValue: _readDepartmentName)
this.departmentName, 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: 'location_detail') this.locationDetail,
@JsonKey(name: 'assigned_to_user_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'assigned_to_user_id', fromJson: _intFromJsonNullable)
this.assignedToUserId, 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_id', fromJson: _intFromJsonNullable) this.vendorId,
@JsonKey(name: 'vendor_name', readValue: _readVendorNameFromNested) @JsonKey(name: 'vendor_name', readValue: _readVendorNameFromNested)
this.vendorName, this.vendorName,
@ -1225,7 +1331,13 @@ class _$AssetModelImpl implements _AssetModel {
this.createdAt, this.createdAt,
@JsonKey(name: 'updated_at', fromJson: _dateFromJsonNullable) @JsonKey(name: 'updated_at', fromJson: _dateFromJsonNullable)
this.updatedAt, this.updatedAt,
}); @JsonKey(
name: 'maintenance',
fromJson: AssetMaintenanceSummary.fromJsonNullable,
toJson: AssetMaintenanceSummary.toJsonNullable,
)
this.maintenance,
}) : _maintenanceChecklistJson = maintenanceChecklistJson;
factory _$AssetModelImpl.fromJson(Map<String, dynamic> json) => factory _$AssetModelImpl.fromJson(Map<String, dynamic> json) =>
_$$AssetModelImplFromJson(json); _$$AssetModelImplFromJson(json);
@ -1261,14 +1373,14 @@ class _$AssetModelImpl implements _AssetModel {
final String? assetSubcategoryName; final String? assetSubcategoryName;
@override @override
@JsonKey( @JsonKey(
name: 'plant_id', name: 'location_id',
readValue: _readPlantId, readValue: _readLocationId,
fromJson: _intFromJsonNullable, fromJson: _intFromJsonNullable,
) )
final int? plantId; final int? locationId;
@override @override
@JsonKey(name: 'plant_name', readValue: _readPlantName) @JsonKey(name: 'location_name', readValue: _readLocationName)
final String? plantName; final String? locationName;
@override @override
@JsonKey(name: 'brand_model') @JsonKey(name: 'brand_model')
final String? brandModel; final String? brandModel;
@ -1287,18 +1399,40 @@ class _$AssetModelImpl implements _AssetModel {
@JsonKey(name: 'department_name', readValue: _readDepartmentName) @JsonKey(name: 'department_name', readValue: _readDepartmentName)
final String? departmentName; final String? departmentName;
@override @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') @JsonKey(name: 'location_detail')
final String? locationDetail; final String? locationDetail;
@override @override
@JsonKey(name: 'assigned_to_user_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'assigned_to_user_id', fromJson: _intFromJsonNullable)
final int? assignedToUserId; final int? assignedToUserId;
@override @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) @JsonKey(name: 'vendor_id', fromJson: _intFromJsonNullable)
final int? vendorId; final int? vendorId;
@override @override
@ -1361,10 +1495,17 @@ class _$AssetModelImpl implements _AssetModel {
@override @override
@JsonKey(name: 'updated_at', fromJson: _dateFromJsonNullable) @JsonKey(name: 'updated_at', fromJson: _dateFromJsonNullable)
final DateTime? updatedAt; final DateTime? updatedAt;
@override
@JsonKey(
name: 'maintenance',
fromJson: AssetMaintenanceSummary.fromJsonNullable,
toJson: AssetMaintenanceSummary.toJsonNullable,
)
final AssetMaintenanceSummary? maintenance;
@override @override
String toString() { 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 @override
@ -1385,9 +1526,10 @@ class _$AssetModelImpl implements _AssetModel {
other.assetSubcategoryId == assetSubcategoryId) && other.assetSubcategoryId == assetSubcategoryId) &&
(identical(other.assetSubcategoryName, assetSubcategoryName) || (identical(other.assetSubcategoryName, assetSubcategoryName) ||
other.assetSubcategoryName == assetSubcategoryName) && other.assetSubcategoryName == assetSubcategoryName) &&
(identical(other.plantId, plantId) || other.plantId == plantId) && (identical(other.locationId, locationId) ||
(identical(other.plantName, plantName) || other.locationId == locationId) &&
other.plantName == plantName) && (identical(other.locationName, locationName) ||
other.locationName == locationName) &&
(identical(other.brandModel, brandModel) || (identical(other.brandModel, brandModel) ||
other.brandModel == brandModel) && other.brandModel == brandModel) &&
(identical(other.manufacturer, manufacturer) || (identical(other.manufacturer, manufacturer) ||
@ -1400,14 +1542,27 @@ class _$AssetModelImpl implements _AssetModel {
other.departmentId == departmentId) && other.departmentId == departmentId) &&
(identical(other.departmentName, departmentName) || (identical(other.departmentName, departmentName) ||
other.departmentName == departmentName) && other.departmentName == departmentName) &&
(identical(other.warehouseId, warehouseId) ||
other.warehouseId == warehouseId) &&
(identical(other.warehouseName, warehouseName) ||
other.warehouseName == warehouseName) &&
(identical(other.locationDetail, locationDetail) || (identical(other.locationDetail, locationDetail) ||
other.locationDetail == locationDetail) && other.locationDetail == locationDetail) &&
(identical(other.assignedToUserId, assignedToUserId) || (identical(other.assignedToUserId, assignedToUserId) ||
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) || (identical(other.vendorId, vendorId) ||
other.vendorId == vendorId) && other.vendorId == vendorId) &&
(identical(other.vendorName, vendorName) || (identical(other.vendorName, vendorName) ||
@ -1447,7 +1602,9 @@ class _$AssetModelImpl implements _AssetModel {
(identical(other.createdAt, createdAt) || (identical(other.createdAt, createdAt) ||
other.createdAt == createdAt) && other.createdAt == createdAt) &&
(identical(other.updatedAt, updatedAt) || (identical(other.updatedAt, updatedAt) ||
other.updatedAt == updatedAt)); other.updatedAt == updatedAt) &&
(identical(other.maintenance, maintenance) ||
other.maintenance == maintenance));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@ -1461,18 +1618,20 @@ class _$AssetModelImpl implements _AssetModel {
assetCategoryName, assetCategoryName,
assetSubcategoryId, assetSubcategoryId,
assetSubcategoryName, assetSubcategoryName,
plantId, locationId,
plantName, locationName,
brandModel, brandModel,
manufacturer, manufacturer,
serialNumber, serialNumber,
partNumber, partNumber,
departmentId, departmentId,
departmentName, departmentName,
warehouseId,
warehouseName,
locationDetail, locationDetail,
assignedToUserId, assignedToUserId,
maintenanceInchargeUserId,
maintenanceFrequencyInDays,
const DeepCollectionEquality().hash(_maintenanceChecklistJson),
commencementDate,
vendorId, vendorId,
vendorName, vendorName,
poId, poId,
@ -1495,6 +1654,7 @@ class _$AssetModelImpl implements _AssetModel {
isActive, isActive,
createdAt, createdAt,
updatedAt, updatedAt,
maintenance,
]); ]);
/// Create a copy of AssetModel /// Create a copy of AssetModel
@ -1533,13 +1693,13 @@ abstract class _AssetModel implements AssetModel {
@JsonKey(name: 'item_subcategory_name', readValue: _readItemSubcategoryName) @JsonKey(name: 'item_subcategory_name', readValue: _readItemSubcategoryName)
final String? assetSubcategoryName, final String? assetSubcategoryName,
@JsonKey( @JsonKey(
name: 'plant_id', name: 'location_id',
readValue: _readPlantId, readValue: _readLocationId,
fromJson: _intFromJsonNullable, fromJson: _intFromJsonNullable,
) )
final int? plantId, final int? locationId,
@JsonKey(name: 'plant_name', readValue: _readPlantName) @JsonKey(name: 'location_name', readValue: _readLocationName)
final String? plantName, final String? locationName,
@JsonKey(name: 'brand_model') final String? brandModel, @JsonKey(name: 'brand_model') final String? brandModel,
final String? manufacturer, final String? manufacturer,
@JsonKey(name: 'serial_number') final String? serialNumber, @JsonKey(name: 'serial_number') final String? serialNumber,
@ -1548,13 +1708,27 @@ abstract class _AssetModel implements AssetModel {
final int? departmentId, final int? departmentId,
@JsonKey(name: 'department_name', readValue: _readDepartmentName) @JsonKey(name: 'department_name', readValue: _readDepartmentName)
final String? departmentName, 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: 'location_detail') final String? locationDetail,
@JsonKey(name: 'assigned_to_user_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'assigned_to_user_id', fromJson: _intFromJsonNullable)
final int? assignedToUserId, 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) @JsonKey(name: 'vendor_id', fromJson: _intFromJsonNullable)
final int? vendorId, final int? vendorId,
@JsonKey(name: 'vendor_name', readValue: _readVendorNameFromNested) @JsonKey(name: 'vendor_name', readValue: _readVendorNameFromNested)
@ -1590,6 +1764,12 @@ abstract class _AssetModel implements AssetModel {
final DateTime? createdAt, final DateTime? createdAt,
@JsonKey(name: 'updated_at', fromJson: _dateFromJsonNullable) @JsonKey(name: 'updated_at', fromJson: _dateFromJsonNullable)
final DateTime? updatedAt, final DateTime? updatedAt,
@JsonKey(
name: 'maintenance',
fromJson: AssetMaintenanceSummary.fromJsonNullable,
toJson: AssetMaintenanceSummary.toJsonNullable,
)
final AssetMaintenanceSummary? maintenance,
}) = _$AssetModelImpl; }) = _$AssetModelImpl;
factory _AssetModel.fromJson(Map<String, dynamic> json) = factory _AssetModel.fromJson(Map<String, dynamic> json) =
@ -1626,14 +1806,14 @@ abstract class _AssetModel implements AssetModel {
String? get assetSubcategoryName; String? get assetSubcategoryName;
@override @override
@JsonKey( @JsonKey(
name: 'plant_id', name: 'location_id',
readValue: _readPlantId, readValue: _readLocationId,
fromJson: _intFromJsonNullable, fromJson: _intFromJsonNullable,
) )
int? get plantId; int? get locationId;
@override @override
@JsonKey(name: 'plant_name', readValue: _readPlantName) @JsonKey(name: 'location_name', readValue: _readLocationName)
String? get plantName; String? get locationName;
@override @override
@JsonKey(name: 'brand_model') @JsonKey(name: 'brand_model')
String? get brandModel; String? get brandModel;
@ -1652,18 +1832,31 @@ abstract class _AssetModel implements AssetModel {
@JsonKey(name: 'department_name', readValue: _readDepartmentName) @JsonKey(name: 'department_name', readValue: _readDepartmentName)
String? get departmentName; String? get departmentName;
@override @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') @JsonKey(name: 'location_detail')
String? get locationDetail; String? get locationDetail;
@override @override
@JsonKey(name: 'assigned_to_user_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'assigned_to_user_id', fromJson: _intFromJsonNullable)
int? get assignedToUserId; int? get assignedToUserId;
@override @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) @JsonKey(name: 'vendor_id', fromJson: _intFromJsonNullable)
int? get vendorId; int? get vendorId;
@override @override
@ -1726,6 +1919,13 @@ abstract class _AssetModel implements AssetModel {
@override @override
@JsonKey(name: 'updated_at', fromJson: _dateFromJsonNullable) @JsonKey(name: 'updated_at', fromJson: _dateFromJsonNullable)
DateTime? get updatedAt; DateTime? get updatedAt;
@override
@JsonKey(
name: 'maintenance',
fromJson: AssetMaintenanceSummary.fromJsonNullable,
toJson: AssetMaintenanceSummary.toJsonNullable,
)
AssetMaintenanceSummary? get maintenance;
/// Create a copy of AssetModel /// Create a copy of AssetModel
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@ -3913,8 +4113,8 @@ mixin _$AssetAlertModel {
@JsonKey(name: 'days_remaining', fromJson: _intFromJsonNullable) @JsonKey(name: 'days_remaining', fromJson: _intFromJsonNullable)
int? get daysRemaining => throw _privateConstructorUsedError; int? get daysRemaining => throw _privateConstructorUsedError;
String? get status => throw _privateConstructorUsedError; String? get status => throw _privateConstructorUsedError;
@JsonKey(name: 'plant_name') @JsonKey(name: 'location_name', readValue: _readAlertLocationName)
String? get plantName => throw _privateConstructorUsedError; String? get locationName => throw _privateConstructorUsedError;
/// Serializes this AssetAlertModel to a JSON map. /// Serializes this AssetAlertModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError; Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@ -3948,7 +4148,8 @@ abstract class $AssetAlertModelCopyWith<$Res> {
@JsonKey(name: 'days_remaining', fromJson: _intFromJsonNullable) @JsonKey(name: 'days_remaining', fromJson: _intFromJsonNullable)
int? daysRemaining, int? daysRemaining,
String? status, 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? dueDate = freezed,
Object? daysRemaining = freezed, Object? daysRemaining = freezed,
Object? status = freezed, Object? status = freezed,
Object? plantName = freezed, Object? locationName = freezed,
}) { }) {
return _then( return _then(
_value.copyWith( _value.copyWith(
@ -4026,9 +4227,9 @@ class _$AssetAlertModelCopyWithImpl<$Res, $Val extends AssetAlertModel>
? _value.status ? _value.status
: status // ignore: cast_nullable_to_non_nullable : status // ignore: cast_nullable_to_non_nullable
as String?, as String?,
plantName: freezed == plantName locationName: freezed == locationName
? _value.plantName ? _value.locationName
: plantName // ignore: cast_nullable_to_non_nullable : locationName // ignore: cast_nullable_to_non_nullable
as String?, as String?,
) )
as $Val, as $Val,
@ -4060,7 +4261,8 @@ abstract class _$$AssetAlertModelImplCopyWith<$Res>
@JsonKey(name: 'days_remaining', fromJson: _intFromJsonNullable) @JsonKey(name: 'days_remaining', fromJson: _intFromJsonNullable)
int? daysRemaining, int? daysRemaining,
String? status, 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? dueDate = freezed,
Object? daysRemaining = freezed, Object? daysRemaining = freezed,
Object? status = freezed, Object? status = freezed,
Object? plantName = freezed, Object? locationName = freezed,
}) { }) {
return _then( return _then(
_$AssetAlertModelImpl( _$AssetAlertModelImpl(
@ -4137,9 +4339,9 @@ class __$$AssetAlertModelImplCopyWithImpl<$Res>
? _value.status ? _value.status
: status // ignore: cast_nullable_to_non_nullable : status // ignore: cast_nullable_to_non_nullable
as String?, as String?,
plantName: freezed == plantName locationName: freezed == locationName
? _value.plantName ? _value.locationName
: plantName // ignore: cast_nullable_to_non_nullable : locationName // ignore: cast_nullable_to_non_nullable
as String?, as String?,
), ),
); );
@ -4163,7 +4365,8 @@ class _$AssetAlertModelImpl implements _AssetAlertModel {
@JsonKey(name: 'days_remaining', fromJson: _intFromJsonNullable) @JsonKey(name: 'days_remaining', fromJson: _intFromJsonNullable)
this.daysRemaining, this.daysRemaining,
this.status, this.status,
@JsonKey(name: 'plant_name') this.plantName, @JsonKey(name: 'location_name', readValue: _readAlertLocationName)
this.locationName,
}); });
factory _$AssetAlertModelImpl.fromJson(Map<String, dynamic> json) => factory _$AssetAlertModelImpl.fromJson(Map<String, dynamic> json) =>
@ -4199,12 +4402,12 @@ class _$AssetAlertModelImpl implements _AssetAlertModel {
@override @override
final String? status; final String? status;
@override @override
@JsonKey(name: 'plant_name') @JsonKey(name: 'location_name', readValue: _readAlertLocationName)
final String? plantName; final String? locationName;
@override @override
String toString() { 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 @override
@ -4227,8 +4430,8 @@ class _$AssetAlertModelImpl implements _AssetAlertModel {
(identical(other.daysRemaining, daysRemaining) || (identical(other.daysRemaining, daysRemaining) ||
other.daysRemaining == daysRemaining) && other.daysRemaining == daysRemaining) &&
(identical(other.status, status) || other.status == status) && (identical(other.status, status) || other.status == status) &&
(identical(other.plantName, plantName) || (identical(other.locationName, locationName) ||
other.plantName == plantName)); other.locationName == locationName));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@ -4246,7 +4449,7 @@ class _$AssetAlertModelImpl implements _AssetAlertModel {
dueDate, dueDate,
daysRemaining, daysRemaining,
status, status,
plantName, locationName,
); );
/// Create a copy of AssetAlertModel /// Create a copy of AssetAlertModel
@ -4282,7 +4485,8 @@ abstract class _AssetAlertModel implements AssetAlertModel {
@JsonKey(name: 'days_remaining', fromJson: _intFromJsonNullable) @JsonKey(name: 'days_remaining', fromJson: _intFromJsonNullable)
final int? daysRemaining, final int? daysRemaining,
final String? status, final String? status,
@JsonKey(name: 'plant_name') final String? plantName, @JsonKey(name: 'location_name', readValue: _readAlertLocationName)
final String? locationName,
}) = _$AssetAlertModelImpl; }) = _$AssetAlertModelImpl;
factory _AssetAlertModel.fromJson(Map<String, dynamic> json) = factory _AssetAlertModel.fromJson(Map<String, dynamic> json) =
@ -4318,8 +4522,8 @@ abstract class _AssetAlertModel implements AssetAlertModel {
@override @override
String? get status; String? get status;
@override @override
@JsonKey(name: 'plant_name') @JsonKey(name: 'location_name', readValue: _readAlertLocationName)
String? get plantName; String? get locationName;
/// Create a copy of AssetAlertModel /// Create a copy of AssetAlertModel
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.

View File

@ -57,18 +57,26 @@ _$AssetModelImpl _$$AssetModelImplFromJson(Map<String, dynamic> json) =>
), ),
assetSubcategoryName: assetSubcategoryName:
_readItemSubcategoryName(json, 'item_subcategory_name') as String?, _readItemSubcategoryName(json, 'item_subcategory_name') as String?,
plantId: _intFromJsonNullable(_readPlantId(json, 'plant_id')), locationId: _intFromJsonNullable(_readLocationId(json, 'location_id')),
plantName: _readPlantName(json, 'plant_name') as String?, locationName: _readLocationName(json, 'location_name') as String?,
brandModel: json['brand_model'] as String?, brandModel: json['brand_model'] as String?,
manufacturer: json['manufacturer'] as String?, manufacturer: json['manufacturer'] as String?,
serialNumber: json['serial_number'] as String?, serialNumber: json['serial_number'] as String?,
partNumber: json['part_number'] as String?, partNumber: json['part_number'] as String?,
departmentId: _intFromJsonNullable(json['department_id']), departmentId: _intFromJsonNullable(json['department_id']),
departmentName: _readDepartmentName(json, 'department_name') as String?, 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?, locationDetail: json['location_detail'] as String?,
assignedToUserId: _intFromJsonNullable(json['assigned_to_user_id']), 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']), vendorId: _intFromJsonNullable(json['vendor_id']),
vendorName: _readVendorNameFromNested(json, 'vendor_name') as String?, vendorName: _readVendorNameFromNested(json, 'vendor_name') as String?,
poId: _intFromJsonNullable(json['po_id']), poId: _intFromJsonNullable(json['po_id']),
@ -91,52 +99,61 @@ _$AssetModelImpl _$$AssetModelImplFromJson(Map<String, dynamic> json) =>
isActive: json['is_active'] as bool? ?? true, isActive: json['is_active'] as bool? ?? true,
createdAt: _dateFromJsonNullable(json['created_at']), createdAt: _dateFromJsonNullable(json['created_at']),
updatedAt: _dateFromJsonNullable(json['updated_at']), updatedAt: _dateFromJsonNullable(json['updated_at']),
maintenance: AssetMaintenanceSummary.fromJsonNullable(
json['maintenance'],
),
); );
Map<String, dynamic> _$$AssetModelImplToJson(_$AssetModelImpl instance) => Map<String, dynamic> _$$AssetModelImplToJson(
<String, dynamic>{ _$AssetModelImpl instance,
'id': instance.id, ) => <String, dynamic>{
'asset_name': instance.assetName, 'id': instance.id,
'asset_code': instance.assetCode, 'asset_name': instance.assetName,
'item_category_id': instance.assetCategoryId, 'asset_code': instance.assetCode,
'item_category_name': instance.assetCategoryName, 'item_category_id': instance.assetCategoryId,
'item_subcategory_id': instance.assetSubcategoryId, 'item_category_name': instance.assetCategoryName,
'item_subcategory_name': instance.assetSubcategoryName, 'item_subcategory_id': instance.assetSubcategoryId,
'plant_id': instance.plantId, 'item_subcategory_name': instance.assetSubcategoryName,
'plant_name': instance.plantName, 'location_id': instance.locationId,
'brand_model': instance.brandModel, 'location_name': instance.locationName,
'manufacturer': instance.manufacturer, 'brand_model': instance.brandModel,
'serial_number': instance.serialNumber, 'manufacturer': instance.manufacturer,
'part_number': instance.partNumber, 'serial_number': instance.serialNumber,
'department_id': instance.departmentId, 'part_number': instance.partNumber,
'department_name': instance.departmentName, 'department_id': instance.departmentId,
'warehouse_id': instance.warehouseId, 'department_name': instance.departmentName,
'warehouse_name': instance.warehouseName, 'location_detail': instance.locationDetail,
'location_detail': instance.locationDetail, 'assigned_to_user_id': instance.assignedToUserId,
'assigned_to_user_id': instance.assignedToUserId, 'maintenance_incharge_user_id': instance.maintenanceInchargeUserId,
'vendor_id': instance.vendorId, 'maintenance_frequency_in_days': instance.maintenanceFrequencyInDays,
'vendor_name': instance.vendorName, 'maintenance_checklist_json': _checklistToJson(
'po_id': instance.poId, instance.maintenanceChecklistJson,
'grn_id': instance.grnId, ),
'grn_item_id': instance.grnItemId, 'commencement_date': instance.commencementDate?.toIso8601String(),
'purchase_date': instance.purchaseDate?.toIso8601String(), 'vendor_id': instance.vendorId,
'purchase_cost': instance.purchaseCost, 'vendor_name': instance.vendorName,
'useful_life_years': instance.usefulLifeYears, 'po_id': instance.poId,
'depreciation_method': instance.depreciationMethod, 'grn_id': instance.grnId,
'depreciation_rate': instance.depreciationRate, 'grn_item_id': instance.grnItemId,
'salvage_value': instance.salvageValue, 'purchase_date': instance.purchaseDate?.toIso8601String(),
'warranty_expiry_date': instance.warrantyExpiryDate?.toIso8601String(), 'purchase_cost': instance.purchaseCost,
'condition': instance.condition, 'useful_life_years': instance.usefulLifeYears,
'status': instance.status, 'depreciation_method': instance.depreciationMethod,
'qr_code_value': instance.qrCodeValue, 'depreciation_rate': instance.depreciationRate,
'disposal_date': instance.disposalDate?.toIso8601String(), 'salvage_value': instance.salvageValue,
'disposal_reason': instance.disposalReason, 'warranty_expiry_date': instance.warrantyExpiryDate?.toIso8601String(),
'disposal_value': instance.disposalValue, 'condition': instance.condition,
'remarks': instance.remarks, 'status': instance.status,
'is_active': instance.isActive, 'qr_code_value': instance.qrCodeValue,
'created_at': instance.createdAt?.toIso8601String(), 'disposal_date': instance.disposalDate?.toIso8601String(),
'updated_at': instance.updatedAt?.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( _$AmcContractModelImpl _$$AmcContractModelImplFromJson(
Map<String, dynamic> json, Map<String, dynamic> json,
@ -314,7 +331,7 @@ _$AssetAlertModelImpl _$$AssetAlertModelImplFromJson(
dueDate: _dateFromJsonNullable(json['due_date']), dueDate: _dateFromJsonNullable(json['due_date']),
daysRemaining: _intFromJsonNullable(json['days_remaining']), daysRemaining: _intFromJsonNullable(json['days_remaining']),
status: json['status'] as String?, status: json['status'] as String?,
plantName: json['plant_name'] as String?, locationName: _readAlertLocationName(json, 'location_name') as String?,
); );
Map<String, dynamic> _$$AssetAlertModelImplToJson( Map<String, dynamic> _$$AssetAlertModelImplToJson(
@ -331,5 +348,5 @@ Map<String, dynamic> _$$AssetAlertModelImplToJson(
'due_date': instance.dueDate?.toIso8601String(), 'due_date': instance.dueDate?.toIso8601String(),
'days_remaining': instance.daysRemaining, 'days_remaining': instance.daysRemaining,
'status': instance.status, 'status': instance.status,
'plant_name': instance.plantName, 'location_name': instance.locationName,
}; };

View File

@ -49,11 +49,32 @@ Object? _readVendorType(Map<dynamic, dynamic> json, String key) {
return null; return null;
} }
Object? _readPlantName(Map<dynamic, dynamic> json, String key) => Object? _readLocationDisplayName(
_readNestedName(json, 'plant_name', 'plant'); 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) => Object? _readBillingName(Map<dynamic, dynamic> json, String key) =>
_readNestedName(json, 'warehouse_name', 'warehouse'); _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) { Object? _readItemName(Map<dynamic, dynamic> json, String key) {
final flat = json['item_name']; final flat = json['item_name'];
@ -154,10 +175,11 @@ class PurchaseOrderModel with _$PurchaseOrderModel {
@JsonKey(name: 'vendor_id', fromJson: _intFromJsonNullable) int? vendorId, @JsonKey(name: 'vendor_id', fromJson: _intFromJsonNullable) int? vendorId,
@JsonKey(name: 'vendor_name', readValue: _readVendorName) String? vendorName, @JsonKey(name: 'vendor_name', readValue: _readVendorName) String? vendorName,
@JsonKey(name: 'vendor_type', readValue: _readVendorType) String? vendorType, @JsonKey(name: 'vendor_type', readValue: _readVendorType) String? vendorType,
@JsonKey(name: 'plant_id', fromJson: _intFromJsonNullable) int? plantId, @JsonKey(name: 'billing_id', fromJson: _intFromJsonNullable) int? billingId,
@JsonKey(name: 'plant_name', readValue: _readPlantName) String? plantName, @JsonKey(name: 'billing_name', readValue: _readBillingName) String? billingName,
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable) int? warehouseId, @JsonKey(name: 'shipping_id', fromJson: _intFromJsonNullable) int? shippingId,
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName) String? warehouseName, @JsonKey(name: 'shipping_name', readValue: _readShippingName)
String? shippingName,
@JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable) int? paymentTermId, @JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable) int? paymentTermId,
@JsonKey(name: 'delivery_term_id', fromJson: _intFromJsonNullable) int? deliveryTermId, @JsonKey(name: 'delivery_term_id', fromJson: _intFromJsonNullable) int? deliveryTermId,
@JsonKey(name: 'expected_delivery_date', fromJson: _dateFromJsonNullable) @JsonKey(name: 'expected_delivery_date', fromJson: _dateFromJsonNullable)
@ -172,6 +194,9 @@ class PurchaseOrderModel with _$PurchaseOrderModel {
double? taxableAmount, double? taxableAmount,
@JsonKey(name: 'tax_total', readValue: _readTaxTotal) @JsonKey(name: 'tax_total', readValue: _readTaxTotal)
double? taxAmount, 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) @JsonKey(name: 'grand_total', readValue: _readGrandTotal)
double? totalAmount, double? totalAmount,
@JsonKey(name: 'terms_and_conditions') String? termsAndConditions, @JsonKey(name: 'terms_and_conditions') String? termsAndConditions,
@ -265,7 +290,8 @@ class PurchaseOrderListQuery with _$PurchaseOrderListQuery {
String? search, String? search,
String? status, String? status,
int? vendorId, int? vendorId,
int? plantId, int? billingId,
int? shippingId,
String? dateFrom, String? dateFrom,
String? dateTo, String? dateTo,
}) = _PurchaseOrderListQuery; }) = _PurchaseOrderListQuery;

View File

@ -34,14 +34,14 @@ mixin _$PurchaseOrderModel {
String? get vendorName => throw _privateConstructorUsedError; String? get vendorName => throw _privateConstructorUsedError;
@JsonKey(name: 'vendor_type', readValue: _readVendorType) @JsonKey(name: 'vendor_type', readValue: _readVendorType)
String? get vendorType => throw _privateConstructorUsedError; String? get vendorType => throw _privateConstructorUsedError;
@JsonKey(name: 'plant_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'billing_id', fromJson: _intFromJsonNullable)
int? get plantId => throw _privateConstructorUsedError; int? get billingId => throw _privateConstructorUsedError;
@JsonKey(name: 'plant_name', readValue: _readPlantName) @JsonKey(name: 'billing_name', readValue: _readBillingName)
String? get plantName => throw _privateConstructorUsedError; String? get billingName => throw _privateConstructorUsedError;
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'shipping_id', fromJson: _intFromJsonNullable)
int? get warehouseId => throw _privateConstructorUsedError; int? get shippingId => throw _privateConstructorUsedError;
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName) @JsonKey(name: 'shipping_name', readValue: _readShippingName)
String? get warehouseName => throw _privateConstructorUsedError; String? get shippingName => throw _privateConstructorUsedError;
@JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable)
int? get paymentTermId => throw _privateConstructorUsedError; int? get paymentTermId => throw _privateConstructorUsedError;
@JsonKey(name: 'delivery_term_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'delivery_term_id', fromJson: _intFromJsonNullable)
@ -58,6 +58,12 @@ mixin _$PurchaseOrderModel {
double? get taxableAmount => throw _privateConstructorUsedError; double? get taxableAmount => throw _privateConstructorUsedError;
@JsonKey(name: 'tax_total', readValue: _readTaxTotal) @JsonKey(name: 'tax_total', readValue: _readTaxTotal)
double? get taxAmount => throw _privateConstructorUsedError; 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) @JsonKey(name: 'grand_total', readValue: _readGrandTotal)
double? get totalAmount => throw _privateConstructorUsedError; double? get totalAmount => throw _privateConstructorUsedError;
@JsonKey(name: 'terms_and_conditions') @JsonKey(name: 'terms_and_conditions')
@ -98,12 +104,13 @@ abstract class $PurchaseOrderModelCopyWith<$Res> {
String? vendorName, String? vendorName,
@JsonKey(name: 'vendor_type', readValue: _readVendorType) @JsonKey(name: 'vendor_type', readValue: _readVendorType)
String? vendorType, String? vendorType,
@JsonKey(name: 'plant_id', fromJson: _intFromJsonNullable) int? plantId, @JsonKey(name: 'billing_id', fromJson: _intFromJsonNullable) int? billingId,
@JsonKey(name: 'plant_name', readValue: _readPlantName) String? plantName, @JsonKey(name: 'billing_name', readValue: _readBillingName)
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable) String? billingName,
int? warehouseId, @JsonKey(name: 'shipping_id', fromJson: _intFromJsonNullable)
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName) int? shippingId,
String? warehouseName, @JsonKey(name: 'shipping_name', readValue: _readShippingName)
String? shippingName,
@JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable)
int? paymentTermId, int? paymentTermId,
@JsonKey(name: 'delivery_term_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'delivery_term_id', fromJson: _intFromJsonNullable)
@ -118,6 +125,9 @@ abstract class $PurchaseOrderModelCopyWith<$Res> {
double? otherCharges, double? otherCharges,
@JsonKey(name: 'sub_total', readValue: _readSubTotal) double? taxableAmount, @JsonKey(name: 'sub_total', readValue: _readSubTotal) double? taxableAmount,
@JsonKey(name: 'tax_total', readValue: _readTaxTotal) double? taxAmount, @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) @JsonKey(name: 'grand_total', readValue: _readGrandTotal)
double? totalAmount, double? totalAmount,
@JsonKey(name: 'terms_and_conditions') String? termsAndConditions, @JsonKey(name: 'terms_and_conditions') String? termsAndConditions,
@ -154,10 +164,10 @@ class _$PurchaseOrderModelCopyWithImpl<$Res, $Val extends PurchaseOrderModel>
Object? vendorId = freezed, Object? vendorId = freezed,
Object? vendorName = freezed, Object? vendorName = freezed,
Object? vendorType = freezed, Object? vendorType = freezed,
Object? plantId = freezed, Object? billingId = freezed,
Object? plantName = freezed, Object? billingName = freezed,
Object? warehouseId = freezed, Object? shippingId = freezed,
Object? warehouseName = freezed, Object? shippingName = freezed,
Object? paymentTermId = freezed, Object? paymentTermId = freezed,
Object? deliveryTermId = freezed, Object? deliveryTermId = freezed,
Object? expectedDeliveryDate = freezed, Object? expectedDeliveryDate = freezed,
@ -166,6 +176,9 @@ class _$PurchaseOrderModelCopyWithImpl<$Res, $Val extends PurchaseOrderModel>
Object? otherCharges = freezed, Object? otherCharges = freezed,
Object? taxableAmount = freezed, Object? taxableAmount = freezed,
Object? taxAmount = freezed, Object? taxAmount = freezed,
Object? cgst = freezed,
Object? sgst = freezed,
Object? igst = freezed,
Object? totalAmount = freezed, Object? totalAmount = freezed,
Object? termsAndConditions = freezed, Object? termsAndConditions = freezed,
Object? remarks = freezed, Object? remarks = freezed,
@ -204,21 +217,21 @@ class _$PurchaseOrderModelCopyWithImpl<$Res, $Val extends PurchaseOrderModel>
? _value.vendorType ? _value.vendorType
: vendorType // ignore: cast_nullable_to_non_nullable : vendorType // ignore: cast_nullable_to_non_nullable
as String?, as String?,
plantId: freezed == plantId billingId: freezed == billingId
? _value.plantId ? _value.billingId
: plantId // ignore: cast_nullable_to_non_nullable : billingId // ignore: cast_nullable_to_non_nullable
as int?, as int?,
plantName: freezed == plantName billingName: freezed == billingName
? _value.plantName ? _value.billingName
: plantName // ignore: cast_nullable_to_non_nullable : billingName // ignore: cast_nullable_to_non_nullable
as String?, as String?,
warehouseId: freezed == warehouseId shippingId: freezed == shippingId
? _value.warehouseId ? _value.shippingId
: warehouseId // ignore: cast_nullable_to_non_nullable : shippingId // ignore: cast_nullable_to_non_nullable
as int?, as int?,
warehouseName: freezed == warehouseName shippingName: freezed == shippingName
? _value.warehouseName ? _value.shippingName
: warehouseName // ignore: cast_nullable_to_non_nullable : shippingName // ignore: cast_nullable_to_non_nullable
as String?, as String?,
paymentTermId: freezed == paymentTermId paymentTermId: freezed == paymentTermId
? _value.paymentTermId ? _value.paymentTermId
@ -252,6 +265,18 @@ class _$PurchaseOrderModelCopyWithImpl<$Res, $Val extends PurchaseOrderModel>
? _value.taxAmount ? _value.taxAmount
: taxAmount // ignore: cast_nullable_to_non_nullable : taxAmount // ignore: cast_nullable_to_non_nullable
as double?, 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 totalAmount: freezed == totalAmount
? _value.totalAmount ? _value.totalAmount
: totalAmount // ignore: cast_nullable_to_non_nullable : totalAmount // ignore: cast_nullable_to_non_nullable
@ -305,12 +330,13 @@ abstract class _$$PurchaseOrderModelImplCopyWith<$Res>
String? vendorName, String? vendorName,
@JsonKey(name: 'vendor_type', readValue: _readVendorType) @JsonKey(name: 'vendor_type', readValue: _readVendorType)
String? vendorType, String? vendorType,
@JsonKey(name: 'plant_id', fromJson: _intFromJsonNullable) int? plantId, @JsonKey(name: 'billing_id', fromJson: _intFromJsonNullable) int? billingId,
@JsonKey(name: 'plant_name', readValue: _readPlantName) String? plantName, @JsonKey(name: 'billing_name', readValue: _readBillingName)
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable) String? billingName,
int? warehouseId, @JsonKey(name: 'shipping_id', fromJson: _intFromJsonNullable)
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName) int? shippingId,
String? warehouseName, @JsonKey(name: 'shipping_name', readValue: _readShippingName)
String? shippingName,
@JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable)
int? paymentTermId, int? paymentTermId,
@JsonKey(name: 'delivery_term_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'delivery_term_id', fromJson: _intFromJsonNullable)
@ -325,6 +351,9 @@ abstract class _$$PurchaseOrderModelImplCopyWith<$Res>
double? otherCharges, double? otherCharges,
@JsonKey(name: 'sub_total', readValue: _readSubTotal) double? taxableAmount, @JsonKey(name: 'sub_total', readValue: _readSubTotal) double? taxableAmount,
@JsonKey(name: 'tax_total', readValue: _readTaxTotal) double? taxAmount, @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) @JsonKey(name: 'grand_total', readValue: _readGrandTotal)
double? totalAmount, double? totalAmount,
@JsonKey(name: 'terms_and_conditions') String? termsAndConditions, @JsonKey(name: 'terms_and_conditions') String? termsAndConditions,
@ -360,10 +389,10 @@ class __$$PurchaseOrderModelImplCopyWithImpl<$Res>
Object? vendorId = freezed, Object? vendorId = freezed,
Object? vendorName = freezed, Object? vendorName = freezed,
Object? vendorType = freezed, Object? vendorType = freezed,
Object? plantId = freezed, Object? billingId = freezed,
Object? plantName = freezed, Object? billingName = freezed,
Object? warehouseId = freezed, Object? shippingId = freezed,
Object? warehouseName = freezed, Object? shippingName = freezed,
Object? paymentTermId = freezed, Object? paymentTermId = freezed,
Object? deliveryTermId = freezed, Object? deliveryTermId = freezed,
Object? expectedDeliveryDate = freezed, Object? expectedDeliveryDate = freezed,
@ -372,6 +401,9 @@ class __$$PurchaseOrderModelImplCopyWithImpl<$Res>
Object? otherCharges = freezed, Object? otherCharges = freezed,
Object? taxableAmount = freezed, Object? taxableAmount = freezed,
Object? taxAmount = freezed, Object? taxAmount = freezed,
Object? cgst = freezed,
Object? sgst = freezed,
Object? igst = freezed,
Object? totalAmount = freezed, Object? totalAmount = freezed,
Object? termsAndConditions = freezed, Object? termsAndConditions = freezed,
Object? remarks = freezed, Object? remarks = freezed,
@ -410,21 +442,21 @@ class __$$PurchaseOrderModelImplCopyWithImpl<$Res>
? _value.vendorType ? _value.vendorType
: vendorType // ignore: cast_nullable_to_non_nullable : vendorType // ignore: cast_nullable_to_non_nullable
as String?, as String?,
plantId: freezed == plantId billingId: freezed == billingId
? _value.plantId ? _value.billingId
: plantId // ignore: cast_nullable_to_non_nullable : billingId // ignore: cast_nullable_to_non_nullable
as int?, as int?,
plantName: freezed == plantName billingName: freezed == billingName
? _value.plantName ? _value.billingName
: plantName // ignore: cast_nullable_to_non_nullable : billingName // ignore: cast_nullable_to_non_nullable
as String?, as String?,
warehouseId: freezed == warehouseId shippingId: freezed == shippingId
? _value.warehouseId ? _value.shippingId
: warehouseId // ignore: cast_nullable_to_non_nullable : shippingId // ignore: cast_nullable_to_non_nullable
as int?, as int?,
warehouseName: freezed == warehouseName shippingName: freezed == shippingName
? _value.warehouseName ? _value.shippingName
: warehouseName // ignore: cast_nullable_to_non_nullable : shippingName // ignore: cast_nullable_to_non_nullable
as String?, as String?,
paymentTermId: freezed == paymentTermId paymentTermId: freezed == paymentTermId
? _value.paymentTermId ? _value.paymentTermId
@ -458,6 +490,18 @@ class __$$PurchaseOrderModelImplCopyWithImpl<$Res>
? _value.taxAmount ? _value.taxAmount
: taxAmount // ignore: cast_nullable_to_non_nullable : taxAmount // ignore: cast_nullable_to_non_nullable
as double?, 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 totalAmount: freezed == totalAmount
? _value.totalAmount ? _value.totalAmount
: totalAmount // ignore: cast_nullable_to_non_nullable : 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_id', fromJson: _intFromJsonNullable) this.vendorId,
@JsonKey(name: 'vendor_name', readValue: _readVendorName) this.vendorName, @JsonKey(name: 'vendor_name', readValue: _readVendorName) this.vendorName,
@JsonKey(name: 'vendor_type', readValue: _readVendorType) this.vendorType, @JsonKey(name: 'vendor_type', readValue: _readVendorType) this.vendorType,
@JsonKey(name: 'plant_id', fromJson: _intFromJsonNullable) this.plantId, @JsonKey(name: 'billing_id', fromJson: _intFromJsonNullable) this.billingId,
@JsonKey(name: 'plant_name', readValue: _readPlantName) this.plantName, @JsonKey(name: 'billing_name', readValue: _readBillingName)
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable) this.billingName,
this.warehouseId, @JsonKey(name: 'shipping_id', fromJson: _intFromJsonNullable)
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName) this.shippingId,
this.warehouseName, @JsonKey(name: 'shipping_name', readValue: _readShippingName)
this.shippingName,
@JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable)
this.paymentTermId, this.paymentTermId,
@JsonKey(name: 'delivery_term_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'delivery_term_id', fromJson: _intFromJsonNullable)
@ -522,6 +567,9 @@ class _$PurchaseOrderModelImpl extends _PurchaseOrderModel {
this.otherCharges, this.otherCharges,
@JsonKey(name: 'sub_total', readValue: _readSubTotal) this.taxableAmount, @JsonKey(name: 'sub_total', readValue: _readSubTotal) this.taxableAmount,
@JsonKey(name: 'tax_total', readValue: _readTaxTotal) this.taxAmount, @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: 'grand_total', readValue: _readGrandTotal) this.totalAmount,
@JsonKey(name: 'terms_and_conditions') this.termsAndConditions, @JsonKey(name: 'terms_and_conditions') this.termsAndConditions,
this.remarks, this.remarks,
@ -560,17 +608,17 @@ class _$PurchaseOrderModelImpl extends _PurchaseOrderModel {
@JsonKey(name: 'vendor_type', readValue: _readVendorType) @JsonKey(name: 'vendor_type', readValue: _readVendorType)
final String? vendorType; final String? vendorType;
@override @override
@JsonKey(name: 'plant_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'billing_id', fromJson: _intFromJsonNullable)
final int? plantId; final int? billingId;
@override @override
@JsonKey(name: 'plant_name', readValue: _readPlantName) @JsonKey(name: 'billing_name', readValue: _readBillingName)
final String? plantName; final String? billingName;
@override @override
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'shipping_id', fromJson: _intFromJsonNullable)
final int? warehouseId; final int? shippingId;
@override @override
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName) @JsonKey(name: 'shipping_name', readValue: _readShippingName)
final String? warehouseName; final String? shippingName;
@override @override
@JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable)
final int? paymentTermId; final int? paymentTermId;
@ -596,6 +644,15 @@ class _$PurchaseOrderModelImpl extends _PurchaseOrderModel {
@JsonKey(name: 'tax_total', readValue: _readTaxTotal) @JsonKey(name: 'tax_total', readValue: _readTaxTotal)
final double? taxAmount; final double? taxAmount;
@override @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) @JsonKey(name: 'grand_total', readValue: _readGrandTotal)
final double? totalAmount; final double? totalAmount;
@override @override
@ -623,7 +680,7 @@ class _$PurchaseOrderModelImpl extends _PurchaseOrderModel {
@override @override
String toString() { 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 @override
@ -641,13 +698,14 @@ class _$PurchaseOrderModelImpl extends _PurchaseOrderModel {
other.vendorName == vendorName) && other.vendorName == vendorName) &&
(identical(other.vendorType, vendorType) || (identical(other.vendorType, vendorType) ||
other.vendorType == vendorType) && other.vendorType == vendorType) &&
(identical(other.plantId, plantId) || other.plantId == plantId) && (identical(other.billingId, billingId) ||
(identical(other.plantName, plantName) || other.billingId == billingId) &&
other.plantName == plantName) && (identical(other.billingName, billingName) ||
(identical(other.warehouseId, warehouseId) || other.billingName == billingName) &&
other.warehouseId == warehouseId) && (identical(other.shippingId, shippingId) ||
(identical(other.warehouseName, warehouseName) || other.shippingId == shippingId) &&
other.warehouseName == warehouseName) && (identical(other.shippingName, shippingName) ||
other.shippingName == shippingName) &&
(identical(other.paymentTermId, paymentTermId) || (identical(other.paymentTermId, paymentTermId) ||
other.paymentTermId == paymentTermId) && other.paymentTermId == paymentTermId) &&
(identical(other.deliveryTermId, deliveryTermId) || (identical(other.deliveryTermId, deliveryTermId) ||
@ -664,6 +722,9 @@ class _$PurchaseOrderModelImpl extends _PurchaseOrderModel {
other.taxableAmount == taxableAmount) && other.taxableAmount == taxableAmount) &&
(identical(other.taxAmount, taxAmount) || (identical(other.taxAmount, taxAmount) ||
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) || (identical(other.totalAmount, totalAmount) ||
other.totalAmount == totalAmount) && other.totalAmount == totalAmount) &&
(identical(other.termsAndConditions, termsAndConditions) || (identical(other.termsAndConditions, termsAndConditions) ||
@ -689,10 +750,10 @@ class _$PurchaseOrderModelImpl extends _PurchaseOrderModel {
vendorId, vendorId,
vendorName, vendorName,
vendorType, vendorType,
plantId, billingId,
plantName, billingName,
warehouseId, shippingId,
warehouseName, shippingName,
paymentTermId, paymentTermId,
deliveryTermId, deliveryTermId,
expectedDeliveryDate, expectedDeliveryDate,
@ -701,6 +762,9 @@ class _$PurchaseOrderModelImpl extends _PurchaseOrderModel {
otherCharges, otherCharges,
taxableAmount, taxableAmount,
taxAmount, taxAmount,
cgst,
sgst,
igst,
totalAmount, totalAmount,
termsAndConditions, termsAndConditions,
remarks, remarks,
@ -740,14 +804,14 @@ abstract class _PurchaseOrderModel extends PurchaseOrderModel {
final String? vendorName, final String? vendorName,
@JsonKey(name: 'vendor_type', readValue: _readVendorType) @JsonKey(name: 'vendor_type', readValue: _readVendorType)
final String? vendorType, final String? vendorType,
@JsonKey(name: 'plant_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'billing_id', fromJson: _intFromJsonNullable)
final int? plantId, final int? billingId,
@JsonKey(name: 'plant_name', readValue: _readPlantName) @JsonKey(name: 'billing_name', readValue: _readBillingName)
final String? plantName, final String? billingName,
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'shipping_id', fromJson: _intFromJsonNullable)
final int? warehouseId, final int? shippingId,
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName) @JsonKey(name: 'shipping_name', readValue: _readShippingName)
final String? warehouseName, final String? shippingName,
@JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable)
final int? paymentTermId, final int? paymentTermId,
@JsonKey(name: 'delivery_term_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'delivery_term_id', fromJson: _intFromJsonNullable)
@ -764,6 +828,12 @@ abstract class _PurchaseOrderModel extends PurchaseOrderModel {
final double? taxableAmount, final double? taxableAmount,
@JsonKey(name: 'tax_total', readValue: _readTaxTotal) @JsonKey(name: 'tax_total', readValue: _readTaxTotal)
final double? taxAmount, 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) @JsonKey(name: 'grand_total', readValue: _readGrandTotal)
final double? totalAmount, final double? totalAmount,
@JsonKey(name: 'terms_and_conditions') final String? termsAndConditions, @JsonKey(name: 'terms_and_conditions') final String? termsAndConditions,
@ -802,17 +872,17 @@ abstract class _PurchaseOrderModel extends PurchaseOrderModel {
@JsonKey(name: 'vendor_type', readValue: _readVendorType) @JsonKey(name: 'vendor_type', readValue: _readVendorType)
String? get vendorType; String? get vendorType;
@override @override
@JsonKey(name: 'plant_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'billing_id', fromJson: _intFromJsonNullable)
int? get plantId; int? get billingId;
@override @override
@JsonKey(name: 'plant_name', readValue: _readPlantName) @JsonKey(name: 'billing_name', readValue: _readBillingName)
String? get plantName; String? get billingName;
@override @override
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'shipping_id', fromJson: _intFromJsonNullable)
int? get warehouseId; int? get shippingId;
@override @override
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName) @JsonKey(name: 'shipping_name', readValue: _readShippingName)
String? get warehouseName; String? get shippingName;
@override @override
@JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable) @JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable)
int? get paymentTermId; int? get paymentTermId;
@ -838,6 +908,15 @@ abstract class _PurchaseOrderModel extends PurchaseOrderModel {
@JsonKey(name: 'tax_total', readValue: _readTaxTotal) @JsonKey(name: 'tax_total', readValue: _readTaxTotal)
double? get taxAmount; double? get taxAmount;
@override @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) @JsonKey(name: 'grand_total', readValue: _readGrandTotal)
double? get totalAmount; double? get totalAmount;
@override @override
@ -1642,7 +1721,8 @@ mixin _$PurchaseOrderListQuery {
String? get search => throw _privateConstructorUsedError; String? get search => throw _privateConstructorUsedError;
String? get status => throw _privateConstructorUsedError; String? get status => throw _privateConstructorUsedError;
int? get vendorId => 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 dateFrom => throw _privateConstructorUsedError;
String? get dateTo => throw _privateConstructorUsedError; String? get dateTo => throw _privateConstructorUsedError;
@ -1666,7 +1746,8 @@ abstract class $PurchaseOrderListQueryCopyWith<$Res> {
String? search, String? search,
String? status, String? status,
int? vendorId, int? vendorId,
int? plantId, int? billingId,
int? shippingId,
String? dateFrom, String? dateFrom,
String? dateTo, String? dateTo,
}); });
@ -1695,7 +1776,8 @@ class _$PurchaseOrderListQueryCopyWithImpl<
Object? search = freezed, Object? search = freezed,
Object? status = freezed, Object? status = freezed,
Object? vendorId = freezed, Object? vendorId = freezed,
Object? plantId = freezed, Object? billingId = freezed,
Object? shippingId = freezed,
Object? dateFrom = freezed, Object? dateFrom = freezed,
Object? dateTo = freezed, Object? dateTo = freezed,
}) { }) {
@ -1721,9 +1803,13 @@ class _$PurchaseOrderListQueryCopyWithImpl<
? _value.vendorId ? _value.vendorId
: vendorId // ignore: cast_nullable_to_non_nullable : vendorId // ignore: cast_nullable_to_non_nullable
as int?, as int?,
plantId: freezed == plantId billingId: freezed == billingId
? _value.plantId ? _value.billingId
: plantId // ignore: cast_nullable_to_non_nullable : billingId // ignore: cast_nullable_to_non_nullable
as int?,
shippingId: freezed == shippingId
? _value.shippingId
: shippingId // ignore: cast_nullable_to_non_nullable
as int?, as int?,
dateFrom: freezed == dateFrom dateFrom: freezed == dateFrom
? _value.dateFrom ? _value.dateFrom
@ -1754,7 +1840,8 @@ abstract class _$$PurchaseOrderListQueryImplCopyWith<$Res>
String? search, String? search,
String? status, String? status,
int? vendorId, int? vendorId,
int? plantId, int? billingId,
int? shippingId,
String? dateFrom, String? dateFrom,
String? dateTo, String? dateTo,
}); });
@ -1780,7 +1867,8 @@ class __$$PurchaseOrderListQueryImplCopyWithImpl<$Res>
Object? search = freezed, Object? search = freezed,
Object? status = freezed, Object? status = freezed,
Object? vendorId = freezed, Object? vendorId = freezed,
Object? plantId = freezed, Object? billingId = freezed,
Object? shippingId = freezed,
Object? dateFrom = freezed, Object? dateFrom = freezed,
Object? dateTo = freezed, Object? dateTo = freezed,
}) { }) {
@ -1806,9 +1894,13 @@ class __$$PurchaseOrderListQueryImplCopyWithImpl<$Res>
? _value.vendorId ? _value.vendorId
: vendorId // ignore: cast_nullable_to_non_nullable : vendorId // ignore: cast_nullable_to_non_nullable
as int?, as int?,
plantId: freezed == plantId billingId: freezed == billingId
? _value.plantId ? _value.billingId
: plantId // ignore: cast_nullable_to_non_nullable : billingId // ignore: cast_nullable_to_non_nullable
as int?,
shippingId: freezed == shippingId
? _value.shippingId
: shippingId // ignore: cast_nullable_to_non_nullable
as int?, as int?,
dateFrom: freezed == dateFrom dateFrom: freezed == dateFrom
? _value.dateFrom ? _value.dateFrom
@ -1832,7 +1924,8 @@ class _$PurchaseOrderListQueryImpl implements _PurchaseOrderListQuery {
this.search, this.search,
this.status, this.status,
this.vendorId, this.vendorId,
this.plantId, this.billingId,
this.shippingId,
this.dateFrom, this.dateFrom,
this.dateTo, this.dateTo,
}); });
@ -1850,7 +1943,9 @@ class _$PurchaseOrderListQueryImpl implements _PurchaseOrderListQuery {
@override @override
final int? vendorId; final int? vendorId;
@override @override
final int? plantId; final int? billingId;
@override
final int? shippingId;
@override @override
final String? dateFrom; final String? dateFrom;
@override @override
@ -1858,7 +1953,7 @@ class _$PurchaseOrderListQueryImpl implements _PurchaseOrderListQuery {
@override @override
String toString() { 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 @override
@ -1872,7 +1967,10 @@ class _$PurchaseOrderListQueryImpl implements _PurchaseOrderListQuery {
(identical(other.status, status) || other.status == status) && (identical(other.status, status) || other.status == status) &&
(identical(other.vendorId, vendorId) || (identical(other.vendorId, vendorId) ||
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) || (identical(other.dateFrom, dateFrom) ||
other.dateFrom == dateFrom) && other.dateFrom == dateFrom) &&
(identical(other.dateTo, dateTo) || other.dateTo == dateTo)); (identical(other.dateTo, dateTo) || other.dateTo == dateTo));
@ -1886,7 +1984,8 @@ class _$PurchaseOrderListQueryImpl implements _PurchaseOrderListQuery {
search, search,
status, status,
vendorId, vendorId,
plantId, billingId,
shippingId,
dateFrom, dateFrom,
dateTo, dateTo,
); );
@ -1911,7 +2010,8 @@ abstract class _PurchaseOrderListQuery implements PurchaseOrderListQuery {
final String? search, final String? search,
final String? status, final String? status,
final int? vendorId, final int? vendorId,
final int? plantId, final int? billingId,
final int? shippingId,
final String? dateFrom, final String? dateFrom,
final String? dateTo, final String? dateTo,
}) = _$PurchaseOrderListQueryImpl; }) = _$PurchaseOrderListQueryImpl;
@ -1927,7 +2027,9 @@ abstract class _PurchaseOrderListQuery implements PurchaseOrderListQuery {
@override @override
int? get vendorId; int? get vendorId;
@override @override
int? get plantId; int? get billingId;
@override
int? get shippingId;
@override @override
String? get dateFrom; String? get dateFrom;
@override @override

View File

@ -16,10 +16,10 @@ _$PurchaseOrderModelImpl _$$PurchaseOrderModelImplFromJson(
vendorId: _intFromJsonNullable(json['vendor_id']), vendorId: _intFromJsonNullable(json['vendor_id']),
vendorName: _readVendorName(json, 'vendor_name') as String?, vendorName: _readVendorName(json, 'vendor_name') as String?,
vendorType: _readVendorType(json, 'vendor_type') as String?, vendorType: _readVendorType(json, 'vendor_type') as String?,
plantId: _intFromJsonNullable(json['plant_id']), billingId: _intFromJsonNullable(json['billing_id']),
plantName: _readPlantName(json, 'plant_name') as String?, billingName: _readBillingName(json, 'billing_name') as String?,
warehouseId: _intFromJsonNullable(json['warehouse_id']), shippingId: _intFromJsonNullable(json['shipping_id']),
warehouseName: _readWarehouseName(json, 'warehouse_name') as String?, shippingName: _readShippingName(json, 'shipping_name') as String?,
paymentTermId: _intFromJsonNullable(json['payment_term_id']), paymentTermId: _intFromJsonNullable(json['payment_term_id']),
deliveryTermId: _intFromJsonNullable(json['delivery_term_id']), deliveryTermId: _intFromJsonNullable(json['delivery_term_id']),
expectedDeliveryDate: _dateFromJsonNullable(json['expected_delivery_date']), expectedDeliveryDate: _dateFromJsonNullable(json['expected_delivery_date']),
@ -28,6 +28,9 @@ _$PurchaseOrderModelImpl _$$PurchaseOrderModelImplFromJson(
otherCharges: _doubleFromJsonNullable(json['other_charges']), otherCharges: _doubleFromJsonNullable(json['other_charges']),
taxableAmount: (_readSubTotal(json, 'sub_total') as num?)?.toDouble(), taxableAmount: (_readSubTotal(json, 'sub_total') as num?)?.toDouble(),
taxAmount: (_readTaxTotal(json, 'tax_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(), totalAmount: (_readGrandTotal(json, 'grand_total') as num?)?.toDouble(),
termsAndConditions: json['terms_and_conditions'] as String?, termsAndConditions: json['terms_and_conditions'] as String?,
remarks: json['remarks'] as String?, remarks: json['remarks'] as String?,
@ -53,10 +56,10 @@ Map<String, dynamic> _$$PurchaseOrderModelImplToJson(
'vendor_id': instance.vendorId, 'vendor_id': instance.vendorId,
'vendor_name': instance.vendorName, 'vendor_name': instance.vendorName,
'vendor_type': instance.vendorType, 'vendor_type': instance.vendorType,
'plant_id': instance.plantId, 'billing_id': instance.billingId,
'plant_name': instance.plantName, 'billing_name': instance.billingName,
'warehouse_id': instance.warehouseId, 'shipping_id': instance.shippingId,
'warehouse_name': instance.warehouseName, 'shipping_name': instance.shippingName,
'payment_term_id': instance.paymentTermId, 'payment_term_id': instance.paymentTermId,
'delivery_term_id': instance.deliveryTermId, 'delivery_term_id': instance.deliveryTermId,
'expected_delivery_date': instance.expectedDeliveryDate?.toIso8601String(), 'expected_delivery_date': instance.expectedDeliveryDate?.toIso8601String(),
@ -65,6 +68,9 @@ Map<String, dynamic> _$$PurchaseOrderModelImplToJson(
'other_charges': instance.otherCharges, 'other_charges': instance.otherCharges,
'sub_total': instance.taxableAmount, 'sub_total': instance.taxableAmount,
'tax_total': instance.taxAmount, 'tax_total': instance.taxAmount,
'cgst': instance.cgst,
'sgst': instance.sgst,
'igst': instance.igst,
'grand_total': instance.totalAmount, 'grand_total': instance.totalAmount,
'terms_and_conditions': instance.termsAndConditions, 'terms_and_conditions': instance.termsAndConditions,
'remarks': instance.remarks, 'remarks': instance.remarks,

View File

@ -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_alerts_screen.dart';
import '../../modules/assets/presentation/screens/asset_detail_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_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/change_password_screen.dart';
import '../../modules/auth/presentation/screens/forgot_password_screen.dart'; import '../../modules/auth/presentation/screens/forgot_password_screen.dart';
import '../../modules/auth/presentation/screens/login_screen.dart'; import '../../modules/auth/presentation/screens/login_screen.dart';
@ -298,6 +299,10 @@ final routerProvider = Provider<GoRouter>((ref) {
path: 'alerts', path: 'alerts',
builder: (context, state) => const AssetAlertsScreen(), builder: (context, state) => const AssetAlertsScreen(),
), ),
GoRoute(
path: 'maintenance',
builder: (context, state) => const AssetMaintenanceScreen(),
),
GoRoute( GoRoute(
path: ':id', path: ':id',
builder: (context, state) => builder: (context, state) =>

View File

@ -60,6 +60,12 @@ const List<MenuItem> appMenuItems = [
route: RouteConstants.assetAlerts, route: RouteConstants.assetAlerts,
module: 'assets', module: 'assets',
), ),
MenuItem(
label: 'My Maintenance',
icon: Icons.build_outlined,
route: RouteConstants.assetMaintenance,
module: 'assets',
),
], ],
), ),
MenuItem( MenuItem(

View File

@ -41,8 +41,11 @@ class AppResponsiveFilterBar extends StatelessWidget {
final hasTrailing = trailing != null; final hasTrailing = trailing != null;
if (maxWidth >= _rowBreakpoint) { if (maxWidth >= _rowBreakpoint) {
final alignWithLabeledFilters = filters.isNotEmpty;
return Row( return Row(
crossAxisAlignment: crossAxisAlignment, crossAxisAlignment: alignWithLabeledFilters
? crossAxisAlignment
: CrossAxisAlignment.center,
children: [ children: [
Expanded(flex: searchFlex, child: search), Expanded(flex: searchFlex, child: search),
for (final filter in filters) ...[ for (final filter in filters) ...[
@ -52,7 +55,7 @@ class AppResponsiveFilterBar extends StatelessWidget {
if (hasTrailing) ...[ if (hasTrailing) ...[
SizedBox(width: spacing), SizedBox(width: spacing),
Padding( Padding(
padding: const EdgeInsets.only(top: 8), padding: EdgeInsets.only(top: alignWithLabeledFilters ? 8 : 0),
child: trailing!, child: trailing!,
), ),
], ],
@ -61,6 +64,7 @@ class AppResponsiveFilterBar extends StatelessWidget {
} }
if (maxWidth >= _stackBreakpoint) { if (maxWidth >= _stackBreakpoint) {
final alignWithLabeledFilters = filters.isNotEmpty;
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
@ -69,6 +73,7 @@ class AppResponsiveFilterBar extends StatelessWidget {
Wrap( Wrap(
spacing: spacing, spacing: spacing,
runSpacing: runSpacing, runSpacing: runSpacing,
crossAxisAlignment: WrapCrossAlignment.center,
children: [ children: [
for (final filter in filters) for (final filter in filters)
SizedBox( SizedBox(
@ -87,7 +92,9 @@ class AppResponsiveFilterBar extends StatelessWidget {
child: Align( child: Align(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
child: Padding( child: Padding(
padding: const EdgeInsets.only(top: 8), padding: EdgeInsets.only(
top: alignWithLabeledFilters ? 8 : 0,
),
child: trailing!, child: trailing!,
), ),
), ),

View File

@ -34,6 +34,7 @@ class AppSearchExportBar extends StatelessWidget {
hintText: searchHint, hintText: searchHint,
prefixIcon: const Icon(Icons.search, size: 20), prefixIcon: const Icon(Icons.search, size: 20),
isDense: true, isDense: true,
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
), ),
onChanged: onSearch, onChanged: onSearch,
); );
@ -41,16 +42,19 @@ class AppSearchExportBar extends StatelessWidget {
return AppResponsiveFilterBar( return AppResponsiveFilterBar(
search: searchField, search: searchField,
trailing: showExport trailing: showExport
? OutlinedButton.icon( ? SizedBox(
onPressed: isExporting ? null : onExport, height: 40,
icon: isExporting child: OutlinedButton.icon(
? const SizedBox( onPressed: isExporting ? null : onExport,
width: 18, icon: isExporting
height: 18, ? const SizedBox(
child: CircularProgressIndicator(strokeWidth: 2), width: 18,
) height: 18,
: const Icon(Icons.download_outlined, size: 18), child: CircularProgressIndicator(strokeWidth: 2),
label: Text(isExporting ? 'Exporting...' : 'Export'), )
: const Icon(Icons.download_outlined, size: 18),
label: Text(isExporting ? 'Exporting...' : 'Export'),
),
) )
: null, : null,
); );