item level change
This commit is contained in:
parent
3914e78465
commit
36847aaf87
@ -56,8 +56,6 @@ class ApiEndpoints {
|
||||
'/masters/item-subcategories/$id';
|
||||
static const String items = '/masters/items';
|
||||
static String itemById(String id) => '/masters/items/$id';
|
||||
static const String brands = '/masters/brands';
|
||||
static String brandById(String id) => '/masters/brands/$id';
|
||||
static const String documentSeries = '/masters/document-series';
|
||||
static String documentSeriesById(String id) => '/masters/document-series/$id';
|
||||
static const String deliveryTerms = '/masters/delivery-terms';
|
||||
|
||||
@ -104,10 +104,20 @@ class AssetRemoteDataSource {
|
||||
return _parseList(response.data, AssetTransferHistoryModel.fromJson);
|
||||
}
|
||||
|
||||
Future<List<AssetCategoryModel>> getCategories() async {
|
||||
Future<List<AssetCategoryModel>> getCategories({
|
||||
bool dropdownCall = false,
|
||||
}) async {
|
||||
final response = await dio.get(
|
||||
ApiEndpoints.itemCategories,
|
||||
queryParameters: const {'limit': 100, 'is_active': true},
|
||||
queryParameters: {
|
||||
if (dropdownCall)
|
||||
'dropdown_call': true
|
||||
else ...const {
|
||||
'limit': 100,
|
||||
'is_active': true,
|
||||
},
|
||||
'category_type': 'ASSET',
|
||||
},
|
||||
);
|
||||
return _parseList(response.data, AssetCategoryModel.fromJson)
|
||||
.where((category) => category.isActive)
|
||||
|
||||
@ -63,8 +63,10 @@ class AssetRepositoryImpl implements AssetRepository {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<List<AssetCategoryModel>>> getCategories() {
|
||||
return safeApiCall(() => dataSource.getCategories());
|
||||
Future<Result<List<AssetCategoryModel>>> getCategories({
|
||||
bool dropdownCall = false,
|
||||
}) {
|
||||
return safeApiCall(() => dataSource.getCategories(dropdownCall: dropdownCall));
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@ -13,7 +13,9 @@ abstract class AssetRepository {
|
||||
Future<Result<void>> deleteAsset(String id);
|
||||
Future<Result<void>> transferAsset(String id, Map<String, dynamic> data);
|
||||
Future<Result<List<AssetTransferHistoryModel>>> getTransferHistory(String assetId);
|
||||
Future<Result<List<AssetCategoryModel>>> getCategories();
|
||||
Future<Result<List<AssetCategoryModel>>> getCategories({
|
||||
bool dropdownCall = false,
|
||||
});
|
||||
Future<Result<AssetDropdownOptionsModel>> getAssetOptions();
|
||||
Future<Result<List<AssetDropdownOption>>> getContractTypes();
|
||||
Future<Result<List<AssetDropdownOption>>> getVisitTypes();
|
||||
|
||||
@ -3,7 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import '../../data/repositories/asset_repository_impl.dart';
|
||||
import '../../../../shared/models/asset_model.dart';
|
||||
|
||||
/// Item categories from `/masters/item-categories` (shared for items and assets).
|
||||
/// Categories for Assets list filter (paginated list API — no dropdown_call).
|
||||
final itemCategoriesProvider = FutureProvider<List<AssetCategoryModel>>((ref) async {
|
||||
final repository = ref.watch(assetRepositoryProvider);
|
||||
final result = await repository.getCategories();
|
||||
@ -11,5 +11,14 @@ final itemCategoriesProvider = FutureProvider<List<AssetCategoryModel>>((ref) as
|
||||
return result.data ?? [];
|
||||
});
|
||||
|
||||
/// Categories for Asset form dropdowns (`dropdown_call=true`).
|
||||
final itemCategoriesFormProvider =
|
||||
FutureProvider<List<AssetCategoryModel>>((ref) async {
|
||||
final repository = ref.watch(assetRepositoryProvider);
|
||||
final result = await repository.getCategories(dropdownCall: true);
|
||||
if (result.failure != null) throw result.failure!;
|
||||
return result.data ?? [];
|
||||
});
|
||||
|
||||
@Deprecated('Use itemCategoriesProvider')
|
||||
final assetCategoriesProvider = itemCategoriesProvider;
|
||||
|
||||
@ -1,19 +1,15 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../../core/constants/app_constants.dart';
|
||||
import '../../../../core/network/api_handler.dart';
|
||||
import '../../../../core/utils/active_option.dart';
|
||||
import '../../../../shared/models/asset_model.dart';
|
||||
import '../../../../shared/models/grn_model.dart';
|
||||
import '../../../../shared/models/purchase_order_model.dart';
|
||||
import '../../../../shared/models/user_management_models.dart';
|
||||
import '../../../../shared/models/vendor_model.dart';
|
||||
import '../../../../shared/widgets/app_dropdown.dart';
|
||||
import '../../../assets/data/repositories/asset_repository_impl.dart';
|
||||
import '../../../grn/data/repositories/grn_repository_impl.dart';
|
||||
import '../../../masters/data/datasources/master_remote_data_source.dart';
|
||||
import '../../../purchase_orders/data/repositories/purchase_order_repository_impl.dart';
|
||||
import '../../../users/presentation/providers/users_provider.dart';
|
||||
import '../../../users/data/repositories/user_repository_impl.dart';
|
||||
import '../../../vendors/data/repositories/vendor_repository_impl.dart';
|
||||
|
||||
class AssetFormLookups {
|
||||
@ -117,41 +113,24 @@ Future<List<FilterOptionModel>> _safeOptions(
|
||||
|
||||
Future<List<FilterOptionModel>> _safeVendorOptions(Ref ref) async {
|
||||
try {
|
||||
final vendors = <FilterOptionModel>[];
|
||||
var page = 1;
|
||||
var totalPages = 1;
|
||||
final result =
|
||||
await ref.read(vendorRepositoryProvider).listVendorOptions();
|
||||
if (result.failure != null || result.data == null) return const [];
|
||||
|
||||
while (page <= totalPages) {
|
||||
final result = await ref.read(vendorRepositoryProvider).getVendors(
|
||||
VendorListQuery(
|
||||
page: page,
|
||||
limit: AppConstants.maxPageSize,
|
||||
isActive: true,
|
||||
),
|
||||
);
|
||||
if (result.failure != null || result.data == null) return vendors;
|
||||
|
||||
final data = result.data!;
|
||||
vendors.addAll(
|
||||
data.items
|
||||
.where(
|
||||
(vendor) => isActiveVendorOption(
|
||||
isActive: vendor.isActive,
|
||||
status: vendor.status,
|
||||
),
|
||||
)
|
||||
.map(
|
||||
(vendor) => FilterOptionModel(
|
||||
id: vendor.id,
|
||||
name: vendor.vendorName,
|
||||
),
|
||||
),
|
||||
);
|
||||
totalPages = data.totalPages;
|
||||
page++;
|
||||
}
|
||||
|
||||
return vendors;
|
||||
return result.data!
|
||||
.where(
|
||||
(vendor) => isActiveVendorOption(
|
||||
isActive: vendor.isActive,
|
||||
status: vendor.status,
|
||||
),
|
||||
)
|
||||
.map(
|
||||
(vendor) => FilterOptionModel(
|
||||
id: vendor.id,
|
||||
name: vendor.vendorName,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
} catch (_) {
|
||||
return const [];
|
||||
}
|
||||
@ -159,16 +138,9 @@ Future<List<FilterOptionModel>> _safeVendorOptions(Ref ref) async {
|
||||
|
||||
Future<List<FilterOptionModel>> _safeUserOptions(Ref ref) async {
|
||||
try {
|
||||
final result = await ref.read(getUsersUseCaseProvider)(
|
||||
const UserListQuery(
|
||||
page: 1,
|
||||
limit: AppConstants.maxPageSize,
|
||||
status: 'active',
|
||||
isActive: true,
|
||||
),
|
||||
);
|
||||
final result = await ref.read(userRepositoryProvider).listUserOptions();
|
||||
if (result.failure != null || result.data == null) return const [];
|
||||
return result.data!.items
|
||||
return result.data!
|
||||
.where(
|
||||
(user) => isActiveUserOption(
|
||||
status: user.status,
|
||||
@ -189,15 +161,11 @@ Future<List<FilterOptionModel>> _safeUserOptions(Ref ref) async {
|
||||
|
||||
Future<List<FilterOptionModel>> _safePurchaseOrderOptions(Ref ref) async {
|
||||
try {
|
||||
final result =
|
||||
await ref.read(purchaseOrderRepositoryProvider).getPurchaseOrders(
|
||||
const PurchaseOrderListQuery(
|
||||
page: 1,
|
||||
limit: AppConstants.maxPageSize,
|
||||
),
|
||||
);
|
||||
final result = await ref
|
||||
.read(purchaseOrderRepositoryProvider)
|
||||
.listPurchaseOrderOptions();
|
||||
if (result.failure != null || result.data == null) return const [];
|
||||
return result.data!.items
|
||||
return result.data!
|
||||
.map(
|
||||
(po) => FilterOptionModel(
|
||||
id: po.id,
|
||||
@ -212,11 +180,9 @@ Future<List<FilterOptionModel>> _safePurchaseOrderOptions(Ref ref) async {
|
||||
|
||||
Future<List<FilterOptionModel>> _safeGrnOptions(Ref ref) async {
|
||||
try {
|
||||
final result = await ref.read(grnRepositoryProvider).getGrns(
|
||||
const GrnListQuery(page: 1, limit: AppConstants.maxPageSize),
|
||||
);
|
||||
final result = await ref.read(grnRepositoryProvider).listGrnOptions();
|
||||
if (result.failure != null || result.data == null) return const [];
|
||||
return result.data!.items
|
||||
return result.data!
|
||||
.map(
|
||||
(grn) => FilterOptionModel(
|
||||
id: grn.id,
|
||||
|
||||
@ -466,7 +466,7 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final categoriesAsync = ref.watch(itemCategoriesProvider);
|
||||
final categoriesAsync = ref.watch(itemCategoriesFormProvider);
|
||||
final plantsAsync = ref.watch(assetPlantsProvider);
|
||||
|
||||
if (widget.isEditing) {
|
||||
@ -1091,6 +1091,7 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
|
||||
value: _dropdownValue(_categoryId, categoryIds),
|
||||
searchHint: 'Search category...',
|
||||
isDense: true,
|
||||
initialValues: const {'category_type': 'ASSET'},
|
||||
options: activeCategories
|
||||
.map(
|
||||
(c) => AppDropdownOption(
|
||||
@ -1101,7 +1102,7 @@ class _AssetFormPanelState extends ConsumerState<AssetFormPanel> {
|
||||
.where((option) => option.value != 0)
|
||||
.toList(),
|
||||
refreshLookups: () {
|
||||
ref.invalidate(itemCategoriesProvider);
|
||||
ref.invalidate(itemCategoriesFormProvider);
|
||||
},
|
||||
parseCreatedId: int.tryParse,
|
||||
onChanged: (v) => setState(() {
|
||||
|
||||
@ -19,6 +19,26 @@ class GrnRemoteDataSource {
|
||||
return _parsePaginated(response.data, GrnModel.fromJson);
|
||||
}
|
||||
|
||||
/// Form-dropdown loader: all active GRNs (`dropdown_call=true`).
|
||||
Future<List<GrnModel>> listGrnOptions() async {
|
||||
final response = await dio.get(
|
||||
ApiEndpoints.grn,
|
||||
queryParameters: const {'dropdown_call': true},
|
||||
);
|
||||
final body = response.data;
|
||||
if (body is! Map) return const [];
|
||||
final raw = body['data'];
|
||||
final list = raw is List
|
||||
? raw
|
||||
: raw is Map
|
||||
? (raw['items'] as List?) ?? const []
|
||||
: const [];
|
||||
return list
|
||||
.whereType<Map>()
|
||||
.map((item) => GrnModel.fromJson(Map<String, dynamic>.from(item)))
|
||||
.toList();
|
||||
}
|
||||
|
||||
Future<GrnModel> getGrnById(String id) async {
|
||||
final response = await dio.get(ApiEndpoints.grnById(id));
|
||||
return GrnModel.fromJson(response.data['data'] as Map<String, dynamic>);
|
||||
|
||||
@ -26,6 +26,11 @@ class GrnRepositoryImpl implements GrnRepository {
|
||||
return safeApiCall(() => dataSource.getGrns(query));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<List<GrnModel>>> listGrnOptions() {
|
||||
return safeApiCall(() => dataSource.listGrnOptions());
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<ExportFileResult>> exportGrns(GrnListQuery query) {
|
||||
return safeApiCall(() => dataSource.exportGrns(query));
|
||||
|
||||
@ -5,6 +5,7 @@ import '../../../../shared/models/grn_model.dart';
|
||||
|
||||
abstract class GrnRepository {
|
||||
Future<Result<PaginatedResponse<GrnModel>>> getGrns(GrnListQuery query);
|
||||
Future<Result<List<GrnModel>>> listGrnOptions();
|
||||
Future<Result<ExportFileResult>> exportGrns(GrnListQuery query);
|
||||
Future<Result<GrnModel>> getGrnById(String id);
|
||||
Future<Result<GrnModel>> createGrn(Map<String, dynamic> data);
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../../core/constants/app_constants.dart';
|
||||
import '../../../../core/utils/active_option.dart';
|
||||
import '../../../../shared/models/purchase_order_model.dart';
|
||||
import '../../../../shared/models/user_management_models.dart';
|
||||
import '../../../masters/data/datasources/master_remote_data_source.dart';
|
||||
import '../../../purchase_orders/data/repositories/purchase_order_repository_impl.dart';
|
||||
import '../../../users/presentation/providers/users_provider.dart';
|
||||
import '../../../users/data/repositories/user_repository_impl.dart';
|
||||
|
||||
class GrnLookups {
|
||||
const GrnLookups({
|
||||
@ -29,15 +28,9 @@ final grnLookupsProvider = FutureProvider.autoDispose<GrnLookups>((ref) async {
|
||||
|
||||
final receivablePos = <PurchaseOrderModel>[];
|
||||
for (final status in ['APPROVED', 'PARTIALLY_RECEIVED']) {
|
||||
final result = await poRepo.getPurchaseOrders(
|
||||
PurchaseOrderListQuery(
|
||||
page: 1,
|
||||
limit: AppConstants.maxPageSize,
|
||||
status: status,
|
||||
),
|
||||
);
|
||||
final result = await poRepo.listPurchaseOrderOptions(status: status);
|
||||
if (result.failure == null && result.data != null) {
|
||||
receivablePos.addAll(result.data!.items);
|
||||
receivablePos.addAll(result.data!);
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,16 +53,9 @@ Future<List<FilterOptionModel>> _safeOptions(
|
||||
|
||||
Future<List<FilterOptionModel>> _safeUserOptions(Ref ref) async {
|
||||
try {
|
||||
final result = await ref.read(getUsersUseCaseProvider)(
|
||||
const UserListQuery(
|
||||
page: 1,
|
||||
limit: AppConstants.maxPageSize,
|
||||
status: 'active',
|
||||
isActive: true,
|
||||
),
|
||||
);
|
||||
final result = await ref.read(userRepositoryProvider).listUserOptions();
|
||||
if (result.failure != null || result.data == null) return const [];
|
||||
return result.data!.items
|
||||
return result.data!
|
||||
.where(
|
||||
(user) => isActiveUserOption(
|
||||
status: user.status,
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../../core/constants/app_constants.dart';
|
||||
import '../../../../core/network/dio_client.dart';
|
||||
import '../../../../core/utils/active_option.dart';
|
||||
import '../../../../shared/models/export_file_result.dart';
|
||||
@ -43,6 +42,7 @@ class MasterCrudRemoteDataSource {
|
||||
int limit = 20,
|
||||
String? search,
|
||||
bool? isActive,
|
||||
Map<String, dynamic>? extraQueryParameters,
|
||||
}) async {
|
||||
final response = await dio.get(
|
||||
definition.apiPath,
|
||||
@ -51,6 +51,7 @@ class MasterCrudRemoteDataSource {
|
||||
'limit': limit,
|
||||
if (search != null && search.isNotEmpty) 'search': search,
|
||||
if (isActive != null) 'is_active': isActive,
|
||||
...?extraQueryParameters,
|
||||
},
|
||||
);
|
||||
|
||||
@ -80,21 +81,31 @@ class MasterCrudRemoteDataSource {
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<Map<String, dynamic>>> listOptions(MasterDefinition definition) async {
|
||||
final allItems = <Map<String, dynamic>>[];
|
||||
var page = 1;
|
||||
while (true) {
|
||||
final result = await list(
|
||||
definition,
|
||||
page: page,
|
||||
limit: AppConstants.defaultPageSize,
|
||||
isActive: true,
|
||||
);
|
||||
allItems.addAll(result.items.where(isActiveOptionRow));
|
||||
if (page >= result.totalPages) break;
|
||||
page++;
|
||||
}
|
||||
return allItems;
|
||||
Future<List<Map<String, dynamic>>> listOptions(
|
||||
MasterDefinition definition, {
|
||||
Map<String, dynamic>? queryParameters,
|
||||
}) async {
|
||||
final response = await dio.get(
|
||||
definition.apiPath,
|
||||
queryParameters: {
|
||||
'dropdown_call': true,
|
||||
...?queryParameters,
|
||||
},
|
||||
);
|
||||
|
||||
final body = response.data as Map<String, dynamic>;
|
||||
final raw = body['data'];
|
||||
final list = raw is List<dynamic>
|
||||
? raw
|
||||
: raw is Map<String, dynamic>
|
||||
? raw['items'] as List<dynamic>? ?? const []
|
||||
: const [];
|
||||
|
||||
return list
|
||||
.whereType<Map>()
|
||||
.map((item) => Map<String, dynamic>.from(item))
|
||||
.where(isActiveOptionRow)
|
||||
.toList();
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getById(
|
||||
|
||||
@ -33,9 +33,12 @@ class MasterRepositoryImpl implements MasterRepository {
|
||||
|
||||
@override
|
||||
Future<Result<List<Map<String, dynamic>>>> listOptions(
|
||||
MasterDefinition definition,
|
||||
) =>
|
||||
safeApiCall(() => remote.listOptions(definition));
|
||||
MasterDefinition definition, {
|
||||
Map<String, dynamic>? queryParameters,
|
||||
}) =>
|
||||
safeApiCall(
|
||||
() => remote.listOptions(definition, queryParameters: queryParameters),
|
||||
);
|
||||
|
||||
@override
|
||||
Future<Result<Map<String, dynamic>>> getById(
|
||||
|
||||
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||
|
||||
enum MasterFieldType { text, number, boolean, dropdown }
|
||||
|
||||
const brandTypes = ['OWN', 'OEM', 'THIRD_PARTY'];
|
||||
const categoryTypeOptions = ['STOCK', 'ASSET'];
|
||||
|
||||
class MasterFieldDef {
|
||||
const MasterFieldDef({
|
||||
@ -12,11 +12,15 @@ class MasterFieldDef {
|
||||
this.required = false,
|
||||
this.showInList = false,
|
||||
this.showInForm = true,
|
||||
this.readOnly = false,
|
||||
this.optionsMasterKey,
|
||||
this.optionsQueryParams,
|
||||
this.staticOptions,
|
||||
this.multiline = false,
|
||||
this.filterByFieldKey,
|
||||
this.filterByOptionKey,
|
||||
this.visibleWhenFieldKey,
|
||||
this.visibleWhenValue,
|
||||
});
|
||||
|
||||
final String key;
|
||||
@ -26,15 +30,40 @@ class MasterFieldDef {
|
||||
final bool showInList;
|
||||
/// When false, field is list/display-only and excluded from create/update payloads.
|
||||
final bool showInForm;
|
||||
/// When true, shown in the form but not editable (value set by other fields).
|
||||
final bool readOnly;
|
||||
/// Master key used to populate dropdown options (e.g. `plants` for plant_id).
|
||||
final String? optionsMasterKey;
|
||||
/// Fixed dropdown choices (e.g. brand type) — no API lookup.
|
||||
/// Extra query parameters when loading [optionsMasterKey] options.
|
||||
final Map<String, dynamic>? optionsQueryParams;
|
||||
/// Fixed dropdown choices (e.g. category type) — no API lookup.
|
||||
final List<String>? staticOptions;
|
||||
final bool multiline;
|
||||
/// Form field whose value filters this dropdown (e.g. `item_category_id`).
|
||||
final String? filterByFieldKey;
|
||||
/// Option-row key matched against [filterByFieldKey] (defaults to same key).
|
||||
final String? filterByOptionKey;
|
||||
/// Show this field only when [visibleWhenFieldKey] equals [visibleWhenValue].
|
||||
final String? visibleWhenFieldKey;
|
||||
final String? visibleWhenValue;
|
||||
|
||||
/// Cache key for dropdown option rows (includes query params when set).
|
||||
String get dropdownLookupKey {
|
||||
final masterKey = optionsMasterKey;
|
||||
if (masterKey == null) return key;
|
||||
final params = optionsQueryParams;
|
||||
if (params == null || params.isEmpty) return masterKey;
|
||||
final parts = params.entries.toList()
|
||||
..sort((a, b) => a.key.compareTo(b.key));
|
||||
final query = parts.map((e) => '${e.key}=${e.value}').join('&');
|
||||
return '$masterKey?$query';
|
||||
}
|
||||
|
||||
bool isVisibleInForm(Map<String, dynamic> values) {
|
||||
final whenKey = visibleWhenFieldKey;
|
||||
if (whenKey == null) return true;
|
||||
return values[whenKey]?.toString() == visibleWhenValue;
|
||||
}
|
||||
}
|
||||
|
||||
class MasterDefinition {
|
||||
@ -119,11 +148,27 @@ const masterDefinitions = <MasterDefinition>[
|
||||
fields: [
|
||||
MasterFieldDef(key: 'code', label: 'Code', required: true, showInList: true),
|
||||
MasterFieldDef(key: 'name', label: 'Name', required: true, showInList: true),
|
||||
MasterFieldDef(key: 'code_prefix', label: 'Code Prefix', showInList: true),
|
||||
MasterFieldDef(
|
||||
key: 'category_type',
|
||||
label: 'Category Type',
|
||||
type: MasterFieldType.dropdown,
|
||||
required: true,
|
||||
showInList: true,
|
||||
staticOptions: categoryTypeOptions,
|
||||
),
|
||||
MasterFieldDef(
|
||||
key: 'code_prefix',
|
||||
label: 'Code Prefix',
|
||||
showInList: true,
|
||||
visibleWhenFieldKey: 'category_type',
|
||||
visibleWhenValue: 'ASSET',
|
||||
),
|
||||
MasterFieldDef(
|
||||
key: 'default_useful_life_years',
|
||||
label: 'Useful Life (Years)',
|
||||
type: MasterFieldType.number,
|
||||
visibleWhenFieldKey: 'category_type',
|
||||
visibleWhenValue: 'ASSET',
|
||||
),
|
||||
MasterFieldDef(
|
||||
key: 'default_depreciation_method',
|
||||
@ -131,6 +176,8 @@ const masterDefinitions = <MasterDefinition>[
|
||||
type: MasterFieldType.dropdown,
|
||||
showInList: true,
|
||||
optionsMasterKey: 'asset_depreciation_methods',
|
||||
visibleWhenFieldKey: 'category_type',
|
||||
visibleWhenValue: 'ASSET',
|
||||
),
|
||||
_activeField,
|
||||
],
|
||||
@ -175,6 +222,12 @@ const masterDefinitions = <MasterDefinition>[
|
||||
showInList: true,
|
||||
showInForm: false,
|
||||
),
|
||||
MasterFieldDef(
|
||||
key: 'is_asset_item',
|
||||
label: 'Asset Item',
|
||||
type: MasterFieldType.boolean,
|
||||
required: true,
|
||||
),
|
||||
MasterFieldDef(key: 'item_name', label: 'Item Name', required: true, showInList: true),
|
||||
MasterFieldDef(
|
||||
key: 'item_category_id',
|
||||
@ -182,6 +235,7 @@ const masterDefinitions = <MasterDefinition>[
|
||||
type: MasterFieldType.dropdown,
|
||||
required: true,
|
||||
optionsMasterKey: 'item_categories',
|
||||
// Resolved at runtime from is_asset_item (STOCK / ASSET).
|
||||
),
|
||||
MasterFieldDef(
|
||||
key: 'item_subcategory_id',
|
||||
@ -208,19 +262,9 @@ const masterDefinitions = <MasterDefinition>[
|
||||
key: 'gst_rate_id',
|
||||
label: 'GST Rate',
|
||||
type: MasterFieldType.dropdown,
|
||||
optionsMasterKey: 'gst_rates',
|
||||
),
|
||||
MasterFieldDef(
|
||||
key: 'brand_id',
|
||||
label: 'Brand',
|
||||
type: MasterFieldType.dropdown,
|
||||
optionsMasterKey: 'brands',
|
||||
),
|
||||
MasterFieldDef(
|
||||
key: 'is_asset_item',
|
||||
label: 'Asset Item',
|
||||
type: MasterFieldType.boolean,
|
||||
required: true,
|
||||
showInList: true,
|
||||
readOnly: true,
|
||||
// Filled from selected HSN's nested gst_rate — no gst-rates API.
|
||||
),
|
||||
MasterFieldDef(
|
||||
key: 'min_order_qty',
|
||||
@ -257,32 +301,13 @@ const masterDefinitions = <MasterDefinition>[
|
||||
showInList: true,
|
||||
multiline: true,
|
||||
),
|
||||
_activeField,
|
||||
],
|
||||
),
|
||||
MasterDefinition(
|
||||
id: 'brands',
|
||||
title: 'Brands',
|
||||
subtitle: 'Own and third-party brands',
|
||||
category: 'Inventory & Items',
|
||||
routeKey: 'brands',
|
||||
apiPath: '/masters/brands',
|
||||
module: 'brands',
|
||||
icon: Icons.loyalty_outlined,
|
||||
fields: [
|
||||
MasterFieldDef(key: 'code', label: 'Code', required: true, showInList: true),
|
||||
MasterFieldDef(key: 'name', label: 'Name', required: true, showInList: true),
|
||||
MasterFieldDef(
|
||||
key: 'brand_type',
|
||||
label: 'Brand Type',
|
||||
key: 'gst_rate_id',
|
||||
label: 'GST Rate',
|
||||
type: MasterFieldType.dropdown,
|
||||
required: true,
|
||||
showInList: true,
|
||||
staticOptions: brandTypes,
|
||||
optionsMasterKey: 'gst_rates',
|
||||
),
|
||||
MasterFieldDef(key: 'contact_person', label: 'Contact Person'),
|
||||
MasterFieldDef(key: 'phone', label: 'Phone'),
|
||||
MasterFieldDef(key: 'email', label: 'Email', required: true),
|
||||
_activeField,
|
||||
],
|
||||
),
|
||||
@ -504,6 +529,21 @@ String masterCellValue(Map<String, dynamic> row, MasterFieldDef field) {
|
||||
final label = row['${field.key}_label'];
|
||||
if (label != null && label.toString().isNotEmpty) return label.toString();
|
||||
|
||||
if (field.key == 'gst_rate_id') {
|
||||
final nested = row['gst_rate'];
|
||||
if (nested is Map) {
|
||||
final pct = nested['rate_pct'];
|
||||
if (pct != null) {
|
||||
final rate = pct is num
|
||||
? pct.toDouble()
|
||||
: double.tryParse(pct.toString());
|
||||
if (rate != null) {
|
||||
return rate % 1 == 0 ? '${rate.toInt()}%' : '$rate%';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prefer explicit "<base>_name" or nested "<base>.name" / flat code from API
|
||||
// (e.g. asset_category_id -> asset_category_name; hsn_code_id -> hsn_code)
|
||||
if (field.key.endsWith('_id')) {
|
||||
@ -540,3 +580,61 @@ String masterStatusValue(Map<String, dynamic> row) {
|
||||
if (active == false) return 'inactive';
|
||||
return 'active';
|
||||
}
|
||||
|
||||
/// Category list filter for Items form: ASSET when Asset Item is checked.
|
||||
String itemCategoryTypeForValues(Map<String, dynamic> values) =>
|
||||
values['is_asset_item'] == true ? 'ASSET' : 'STOCK';
|
||||
|
||||
/// Effective options query for a form field (may depend on other values).
|
||||
Map<String, dynamic>? masterFieldOptionsQuery({
|
||||
required String masterId,
|
||||
required MasterFieldDef field,
|
||||
required Map<String, dynamic> values,
|
||||
}) {
|
||||
if (masterId == 'items' && field.key == 'item_category_id') {
|
||||
return {'category_type': itemCategoryTypeForValues(values)};
|
||||
}
|
||||
return field.optionsQueryParams;
|
||||
}
|
||||
|
||||
String masterFieldDropdownLookupKey({
|
||||
required String masterId,
|
||||
required MasterFieldDef field,
|
||||
required Map<String, dynamic> values,
|
||||
}) {
|
||||
final masterKey = field.optionsMasterKey;
|
||||
if (masterKey == null) return field.key;
|
||||
final params = masterFieldOptionsQuery(
|
||||
masterId: masterId,
|
||||
field: field,
|
||||
values: values,
|
||||
);
|
||||
if (params == null || params.isEmpty) return masterKey;
|
||||
final parts = params.entries.toList()
|
||||
..sort((a, b) => a.key.compareTo(b.key));
|
||||
final query = parts.map((e) => '${e.key}=${e.value}').join('&');
|
||||
return '$masterKey?$query';
|
||||
}
|
||||
|
||||
/// Display label for GST filled from HSN nested `gst_rate.description`.
|
||||
String? gstRateDisplayFromValues(Map<String, dynamic> values) {
|
||||
final nested = values['gst_rate'];
|
||||
if (nested is Map) {
|
||||
final desc = nested['description'];
|
||||
if (desc != null && desc.toString().trim().isNotEmpty) {
|
||||
return desc.toString().trim();
|
||||
}
|
||||
final pct = nested['rate_pct'];
|
||||
if (pct != null) {
|
||||
final rate = pct is num ? pct.toDouble() : double.tryParse(pct.toString());
|
||||
if (rate != null) {
|
||||
return rate % 1 == 0 ? '${rate.toInt()}%' : '$rate%';
|
||||
}
|
||||
}
|
||||
}
|
||||
final label = values['gst_rate_label'];
|
||||
if (label != null && label.toString().trim().isNotEmpty) {
|
||||
return label.toString().trim();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -11,7 +11,10 @@ abstract class MasterRepository {
|
||||
String? search,
|
||||
});
|
||||
|
||||
Future<Result<List<Map<String, dynamic>>>> listOptions(MasterDefinition definition);
|
||||
Future<Result<List<Map<String, dynamic>>>> listOptions(
|
||||
MasterDefinition definition, {
|
||||
Map<String, dynamic>? queryParameters,
|
||||
});
|
||||
|
||||
Future<Result<Map<String, dynamic>>> getById(
|
||||
MasterDefinition definition,
|
||||
|
||||
@ -250,7 +250,6 @@ class MasterFormNotifier extends FamilyAsyncNotifier<MasterFormState, MasterForm
|
||||
|
||||
@override
|
||||
Future<MasterFormState> build(MasterFormArgs arg) async {
|
||||
final dropdownOptions = await _loadDropdownOptions();
|
||||
final existingRecords = await _loadExistingRecords();
|
||||
Map<String, dynamic> values = {};
|
||||
|
||||
@ -272,6 +271,18 @@ class MasterFormNotifier extends FamilyAsyncNotifier<MasterFormState, MasterForm
|
||||
}
|
||||
}
|
||||
|
||||
// Load after values so Items category options use is_asset_item.
|
||||
final dropdownOptions = await _loadDropdownOptions(values: values);
|
||||
|
||||
if (_definition.id == 'items') {
|
||||
final syncState = MasterFormState(
|
||||
values: values,
|
||||
dropdownOptions: dropdownOptions,
|
||||
existingRecords: existingRecords,
|
||||
);
|
||||
_applyGstFromHsn(values, syncState);
|
||||
}
|
||||
|
||||
return MasterFormState(
|
||||
values: values,
|
||||
dropdownOptions: dropdownOptions,
|
||||
@ -301,18 +312,25 @@ class MasterFormNotifier extends FamilyAsyncNotifier<MasterFormState, MasterForm
|
||||
}
|
||||
|
||||
Future<Map<String, List<Map<String, dynamic>>>>
|
||||
_loadDropdownOptions() async {
|
||||
_loadDropdownOptions({Map<String, dynamic>? values}) async {
|
||||
final formValues = values ?? state.valueOrNull?.values ?? const {};
|
||||
final options = <String, List<Map<String, dynamic>>>{};
|
||||
final keys = _definition.formFields
|
||||
.where((field) => field.optionsMasterKey != null)
|
||||
.map((field) => field.optionsMasterKey!)
|
||||
.toSet();
|
||||
final fields = _definition.formFields
|
||||
.where((field) => field.optionsMasterKey != null);
|
||||
|
||||
for (final key in keys) {
|
||||
for (final field in fields) {
|
||||
final lookupKey = masterFieldDropdownLookupKey(
|
||||
masterId: _definition.id,
|
||||
field: field,
|
||||
values: formValues,
|
||||
);
|
||||
if (options.containsKey(lookupKey)) continue;
|
||||
|
||||
final key = field.optionsMasterKey!;
|
||||
if (key == 'asset_depreciation_methods') {
|
||||
final result = await ref.read(assetRepositoryProvider).getDepreciationMethods();
|
||||
if (result.failure != null) continue;
|
||||
options[key] = (result.data ?? const [])
|
||||
options[lookupKey] = (result.data ?? const [])
|
||||
.where((option) => option.value.trim().isNotEmpty)
|
||||
.map(
|
||||
(option) => <String, dynamic>{
|
||||
@ -326,19 +344,93 @@ class MasterFormNotifier extends FamilyAsyncNotifier<MasterFormState, MasterForm
|
||||
|
||||
final def = masterDefinitionById(key);
|
||||
if (def == null) continue;
|
||||
final result = await ref.read(masterRepositoryProvider).listOptions(def);
|
||||
final queryParameters = masterFieldOptionsQuery(
|
||||
masterId: _definition.id,
|
||||
field: field,
|
||||
values: formValues,
|
||||
);
|
||||
final result = await ref.read(masterRepositoryProvider).listOptions(
|
||||
def,
|
||||
queryParameters: queryParameters,
|
||||
);
|
||||
if (result.failure != null) continue;
|
||||
options[key] = result.data ?? const [];
|
||||
options[lookupKey] = result.data ?? const [];
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
void _applyGstFromHsn(Map<String, dynamic> values, MasterFormState current) {
|
||||
if (_definition.id != 'items') return;
|
||||
final hsnId = values['hsn_code_id']?.toString();
|
||||
if (hsnId == null || hsnId.isEmpty) {
|
||||
values['gst_rate_id'] = null;
|
||||
values['gst_rate'] = null;
|
||||
values['gst_rate_label'] = null;
|
||||
return;
|
||||
}
|
||||
|
||||
final hsnOptions = current.dropdownOptions['hsn_codes'] ?? const [];
|
||||
Map<String, dynamic>? hsnRow;
|
||||
for (final row in hsnOptions) {
|
||||
if (row['id']?.toString() == hsnId) {
|
||||
hsnRow = row;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hsnRow == null) return;
|
||||
|
||||
final nestedGst = hsnRow['gst_rate'];
|
||||
final gstId = hsnRow['gst_rate_id'] ??
|
||||
(nestedGst is Map ? nestedGst['id'] : null);
|
||||
if (gstId != null && gstId.toString().isNotEmpty) {
|
||||
values['gst_rate_id'] = gstId.toString();
|
||||
} else {
|
||||
values['gst_rate_id'] = null;
|
||||
}
|
||||
|
||||
if (nestedGst is Map) {
|
||||
values['gst_rate'] = Map<String, dynamic>.from(nestedGst);
|
||||
final desc = nestedGst['description'];
|
||||
values['gst_rate_label'] =
|
||||
desc != null && desc.toString().trim().isNotEmpty
|
||||
? desc.toString().trim()
|
||||
: gstRateDisplayFromValues(values);
|
||||
} else {
|
||||
values['gst_rate'] = null;
|
||||
values['gst_rate_label'] = null;
|
||||
}
|
||||
}
|
||||
|
||||
void _clearHiddenFieldValues(Map<String, dynamic> values) {
|
||||
for (final field in _definition.formFields) {
|
||||
if (field.isVisibleInForm(values)) continue;
|
||||
values[field.key] = null;
|
||||
}
|
||||
}
|
||||
|
||||
void updateValue(String key, dynamic value) {
|
||||
final current = state.valueOrNull;
|
||||
if (current == null) return;
|
||||
final values = Map<String, dynamic>.from(current.values);
|
||||
values[key] = value;
|
||||
|
||||
if (key == 'hsn_code_id') {
|
||||
_applyGstFromHsn(values, current);
|
||||
}
|
||||
|
||||
if (key == 'category_type') {
|
||||
_clearHiddenFieldValues(values);
|
||||
}
|
||||
|
||||
// Asset Item toggles Items category list between STOCK / ASSET.
|
||||
if (key == 'is_asset_item' && _definition.id == 'items') {
|
||||
values['item_category_id'] = null;
|
||||
values['item_subcategory_id'] = null;
|
||||
state = AsyncData(current.copyWith(values: values));
|
||||
_reloadItemCategoryOptions(values);
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear dependent dropdowns when their parent filter value changes
|
||||
// (e.g. item_category_id → item_subcategory_id).
|
||||
for (final field in _definition.formFields) {
|
||||
@ -347,8 +439,12 @@ class MasterFormNotifier extends FamilyAsyncNotifier<MasterFormState, MasterForm
|
||||
if (dependentValue == null || dependentValue == '') continue;
|
||||
|
||||
final optionKey = field.filterByOptionKey ?? field.filterByFieldKey!;
|
||||
final options =
|
||||
current.dropdownOptions[field.optionsMasterKey] ?? const [];
|
||||
final lookupKey = masterFieldDropdownLookupKey(
|
||||
masterId: _definition.id,
|
||||
field: field,
|
||||
values: values,
|
||||
);
|
||||
final options = current.dropdownOptions[lookupKey] ?? const [];
|
||||
final stillValid = options.any(
|
||||
(item) =>
|
||||
item['id']?.toString() == dependentValue.toString() &&
|
||||
@ -362,22 +458,35 @@ class MasterFormNotifier extends FamilyAsyncNotifier<MasterFormState, MasterForm
|
||||
state = AsyncData(current.copyWith(values: values));
|
||||
}
|
||||
|
||||
Future<void> _reloadItemCategoryOptions(Map<String, dynamic> values) async {
|
||||
final current = state.valueOrNull;
|
||||
if (current == null) return;
|
||||
final options = await _loadDropdownOptions(values: values);
|
||||
final latest = state.valueOrNull;
|
||||
if (latest == null) return;
|
||||
state = AsyncData(latest.copyWith(dropdownOptions: options));
|
||||
}
|
||||
|
||||
Future<void> reloadDropdownOptions() async {
|
||||
final current = state.valueOrNull;
|
||||
if (current == null) return;
|
||||
final options = await _loadDropdownOptions();
|
||||
final options = await _loadDropdownOptions(values: current.values);
|
||||
state = AsyncData(current.copyWith(dropdownOptions: options));
|
||||
}
|
||||
|
||||
Map<String, dynamic> _buildPayload(MasterFormState current) {
|
||||
final payload = <String, dynamic>{};
|
||||
for (final field in _definition.formFields) {
|
||||
if (!field.isVisibleInForm(current.values)) continue;
|
||||
final value = current.values[field.key];
|
||||
if (value == null || value == '') continue;
|
||||
|
||||
payload[field.key] = switch (field.type) {
|
||||
MasterFieldType.number => num.tryParse(value.toString()) ?? value,
|
||||
MasterFieldType.dropdown => int.tryParse(value.toString()) ?? value,
|
||||
MasterFieldType.dropdown => field.staticOptions != null ||
|
||||
field.optionsMasterKey == 'asset_depreciation_methods'
|
||||
? value.toString()
|
||||
: int.tryParse(value.toString()) ?? value,
|
||||
MasterFieldType.boolean => value == true,
|
||||
MasterFieldType.text => value.toString().trim(),
|
||||
};
|
||||
|
||||
@ -119,20 +119,74 @@ class _MasterFormPanelState extends ConsumerState<MasterFormPanel> {
|
||||
onChanged: (checked) => notifier.updateValue(field.key, checked),
|
||||
);
|
||||
}
|
||||
return CheckboxListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: Text(field.label),
|
||||
value: value == true,
|
||||
onChanged: (checked) =>
|
||||
notifier.updateValue(field.key, checked ?? false),
|
||||
return InkWell(
|
||||
onTap: () => notifier.updateValue(field.key, value != true),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: Checkbox(
|
||||
value: value == true,
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
visualDensity: VisualDensity.compact,
|
||||
onChanged: (checked) =>
|
||||
notifier.updateValue(field.key, checked ?? false),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
field.label,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
case MasterFieldType.dropdown:
|
||||
if (field.readOnly) {
|
||||
final display = field.key == 'gst_rate_id'
|
||||
? (gstRateDisplayFromValues(formState.values) ?? '')
|
||||
: (value?.toString() ?? '');
|
||||
final theme = Theme.of(context);
|
||||
return _wrapField(
|
||||
TextFormField(
|
||||
key: ValueKey(
|
||||
'$_formSessionId-${field.key}-readonly-$display',
|
||||
),
|
||||
initialValue: display.isEmpty ? '' : display,
|
||||
readOnly: true,
|
||||
enableInteractiveSelection: false,
|
||||
style: theme.textTheme.bodyLarge?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
decoration: _inputDecoration(field).copyWith(
|
||||
hintText: display.isEmpty
|
||||
? 'Select HSN code first'
|
||||
: null,
|
||||
filled: true,
|
||||
fillColor: theme.colorScheme.surfaceContainerHighest
|
||||
.withValues(alpha: 0.35),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final List<AppDropdownOption<String>> dropdownOptions;
|
||||
if (field.staticOptions != null) {
|
||||
dropdownOptions = stringDropdownOptions(field.staticOptions!);
|
||||
} else {
|
||||
var options = formState.dropdownOptions[field.optionsMasterKey] ??
|
||||
final lookupKey = masterFieldDropdownLookupKey(
|
||||
masterId: widget.masterId,
|
||||
field: field,
|
||||
values: formState.values,
|
||||
);
|
||||
var options = formState.dropdownOptions[lookupKey] ??
|
||||
const <Map<String, dynamic>>[];
|
||||
final filterField = field.filterByFieldKey;
|
||||
if (filterField != null) {
|
||||
@ -178,7 +232,9 @@ class _MasterFormPanelState extends ConsumerState<MasterFormPanel> {
|
||||
if (canQuickAdd) {
|
||||
return MasterQuickAddDropdown<String>(
|
||||
key: ValueKey(
|
||||
'$_formSessionId-${field.key}-${filterField == null ? '' : formState.values[filterField]}',
|
||||
'$_formSessionId-${field.key}-'
|
||||
'${filterField == null ? '' : formState.values[filterField]}-'
|
||||
'${formState.values['is_asset_item']}',
|
||||
),
|
||||
masterId: optionsMasterKey,
|
||||
label: _fieldLabel(field),
|
||||
@ -191,9 +247,19 @@ class _MasterFormPanelState extends ConsumerState<MasterFormPanel> {
|
||||
: 'Select ${field.label.toLowerCase()}',
|
||||
searchHint: 'Search ${field.label.toLowerCase()}...',
|
||||
enabled: parentSelected,
|
||||
initialValues: filterField == null
|
||||
? null
|
||||
: {filterField: formState.values[filterField]},
|
||||
initialValues: () {
|
||||
final values = <String, dynamic>{};
|
||||
final params = masterFieldOptionsQuery(
|
||||
masterId: widget.masterId,
|
||||
field: field,
|
||||
values: formState.values,
|
||||
);
|
||||
if (params != null) values.addAll(params);
|
||||
if (filterField != null) {
|
||||
values[filterField] = formState.values[filterField];
|
||||
}
|
||||
return values.isEmpty ? null : values;
|
||||
}(),
|
||||
refreshLookups: () {
|
||||
ref
|
||||
.read(masterFormProvider(_args).notifier)
|
||||
@ -323,8 +389,10 @@ class _MasterFormPanelState extends ConsumerState<MasterFormPanel> {
|
||||
MasterFormState formState,
|
||||
) {
|
||||
final def = _definition;
|
||||
final regularFields =
|
||||
def.formFields.where((field) => field.key != 'is_active').toList();
|
||||
final regularFields = def.formFields
|
||||
.where((field) => field.key != 'is_active')
|
||||
.where((field) => field.isVisibleInForm(formState.values))
|
||||
.toList();
|
||||
MasterFieldDef? activeField;
|
||||
for (final field in def.formFields) {
|
||||
if (field.key == 'is_active') {
|
||||
@ -339,8 +407,9 @@ class _MasterFormPanelState extends ConsumerState<MasterFormPanel> {
|
||||
while (i < regularFields.length) {
|
||||
final left = regularFields[i];
|
||||
|
||||
// Multiline fields (e.g. HSN Description) always take a full row.
|
||||
if (left.multiline) {
|
||||
// Multiline / Asset Item take a full row.
|
||||
final fullWidth = left.multiline || left.key == 'is_asset_item';
|
||||
if (fullWidth) {
|
||||
widgets.add(
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
@ -354,8 +423,9 @@ class _MasterFormPanelState extends ConsumerState<MasterFormPanel> {
|
||||
continue;
|
||||
}
|
||||
|
||||
final hasRight =
|
||||
i + 1 < regularFields.length && !regularFields[i + 1].multiline;
|
||||
final hasRight = i + 1 < regularFields.length &&
|
||||
!regularFields[i + 1].multiline &&
|
||||
regularFields[i + 1].key != 'is_asset_item';
|
||||
if (hasRight) {
|
||||
final right = regularFields[i + 1];
|
||||
widgets.add(
|
||||
|
||||
@ -124,7 +124,12 @@ class _MasterInlineCreateFormState
|
||||
if (field.staticOptions != null) {
|
||||
dropdownOptions = stringDropdownOptions(field.staticOptions!);
|
||||
} else {
|
||||
var options = formState.dropdownOptions[field.optionsMasterKey] ??
|
||||
final lookupKey = masterFieldDropdownLookupKey(
|
||||
masterId: widget.masterId,
|
||||
field: field,
|
||||
values: formState.values,
|
||||
);
|
||||
var options = formState.dropdownOptions[lookupKey] ??
|
||||
const <Map<String, dynamic>>[];
|
||||
final filterField = field.filterByFieldKey;
|
||||
if (filterField != null) {
|
||||
@ -444,6 +449,7 @@ class _MasterInlineCreateFormState
|
||||
data: (formState) {
|
||||
final fields = def.formFields
|
||||
.where((f) => f.key != 'is_active')
|
||||
.where((f) => f.isVisibleInForm(formState.values))
|
||||
.toList();
|
||||
final active = def.formFields
|
||||
.where((f) => f.key == 'is_active')
|
||||
|
||||
@ -224,7 +224,6 @@ String masterQuickAddNoun(String masterId) {
|
||||
'item_subcategories' => 'subcategory',
|
||||
'items' => 'item',
|
||||
'hsn_codes' => 'HSN code',
|
||||
'brands' => 'brand',
|
||||
'plants' => 'plant',
|
||||
'warehouses' => 'warehouse',
|
||||
'designations' => 'designation',
|
||||
|
||||
@ -35,9 +35,6 @@ class MasterRemoteDataSource {
|
||||
Future<List<FilterOptionModel>> listWarehouses() =>
|
||||
_listOptions(ApiEndpoints.warehouses);
|
||||
|
||||
Future<List<FilterOptionModel>> listBrands() =>
|
||||
_listOptions(ApiEndpoints.brands);
|
||||
|
||||
Future<List<FilterOptionModel>> listUom() => _listOptions(ApiEndpoints.uom);
|
||||
|
||||
Future<List<FilterOptionModel>> listItems() => _listOptions(ApiEndpoints.items);
|
||||
@ -48,6 +45,32 @@ class MasterRemoteDataSource {
|
||||
Future<List<FilterOptionModel>> listHsnCodes() =>
|
||||
_listOptions(ApiEndpoints.hsnCodes);
|
||||
|
||||
/// HSN options with default `gst_rate_id` for GST autofill on item/PO lines.
|
||||
Future<({List<FilterOptionModel> options, Map<String, int?> gstRateByHsnId})>
|
||||
listHsnCodesWithGstRate() async {
|
||||
final rows = await _listAllMaps(ApiEndpoints.hsnCodes);
|
||||
final options = <FilterOptionModel>[];
|
||||
final gstRateByHsnId = <String, int?>{};
|
||||
|
||||
for (final item in rows) {
|
||||
if (!isActiveOptionRow(item)) continue;
|
||||
final id = item['id']?.toString() ?? '';
|
||||
if (id.isEmpty) continue;
|
||||
|
||||
final nestedGst = item['gst_rate'];
|
||||
gstRateByHsnId[id] = _asInt(
|
||||
item['gst_rate_id'] ??
|
||||
(nestedGst is Map ? nestedGst['id'] : null),
|
||||
);
|
||||
|
||||
final name = _optionLabel(item);
|
||||
if (name.isEmpty) continue;
|
||||
options.add(FilterOptionModel(id: id, name: name));
|
||||
}
|
||||
|
||||
return (options: options, gstRateByHsnId: gstRateByHsnId);
|
||||
}
|
||||
|
||||
/// Active items with default HSN / UOM / GST for PO line autofill.
|
||||
Future<
|
||||
({
|
||||
@ -56,10 +79,7 @@ class MasterRemoteDataSource {
|
||||
Map<String, int?> uomByItemId,
|
||||
Map<String, int?> gstRateByItemId,
|
||||
})> listItemsWithHsn() async {
|
||||
final rows = await _listAllMaps(
|
||||
ApiEndpoints.items,
|
||||
queryParameters: {'is_active': true},
|
||||
);
|
||||
final rows = await _listAllMaps(ApiEndpoints.items);
|
||||
final hsnByItemId = <String, int?>{};
|
||||
final uomByItemId = <String, int?>{};
|
||||
final gstRateByItemId = <String, int?>{};
|
||||
@ -97,10 +117,7 @@ class MasterRemoteDataSource {
|
||||
/// GST rate options with numeric `rate_pct` for tax calculations.
|
||||
Future<({List<FilterOptionModel> options, Map<String, double> pctById})>
|
||||
listGstRatesWithPct() async {
|
||||
final rows = await _listAllMaps(
|
||||
ApiEndpoints.gstRates,
|
||||
queryParameters: {'is_active': true},
|
||||
);
|
||||
final rows = await _listAllMaps(ApiEndpoints.gstRates);
|
||||
final options = <FilterOptionModel>[];
|
||||
final pctById = <String, double>{};
|
||||
|
||||
@ -123,14 +140,29 @@ class MasterRemoteDataSource {
|
||||
return (options: options, pctById: pctById);
|
||||
}
|
||||
|
||||
Future<List<FilterOptionModel>> listItemCategories() =>
|
||||
_listOptions(ApiEndpoints.itemCategories);
|
||||
Future<List<FilterOptionModel>> listItemCategories({String? categoryType}) async {
|
||||
final rows = await _listAllMaps(
|
||||
ApiEndpoints.itemCategories,
|
||||
queryParameters: {
|
||||
if (categoryType != null) 'category_type': categoryType,
|
||||
},
|
||||
);
|
||||
return rows
|
||||
.where(isActiveOptionRow)
|
||||
.map(
|
||||
(item) => FilterOptionModel(
|
||||
id: item['id']?.toString() ?? '',
|
||||
name: _optionLabel(item),
|
||||
),
|
||||
)
|
||||
.where((item) => item.id.isNotEmpty && item.name.isNotEmpty)
|
||||
.toList();
|
||||
}
|
||||
|
||||
Future<List<FilterOptionModel>> listItemSubcategories({int? itemCategoryId}) async {
|
||||
final rows = await _listAllMaps(
|
||||
ApiEndpoints.itemSubcategories,
|
||||
queryParameters: {
|
||||
'is_active': true,
|
||||
if (itemCategoryId != null) 'item_category_id': itemCategoryId,
|
||||
},
|
||||
);
|
||||
@ -153,10 +185,7 @@ class MasterRemoteDataSource {
|
||||
}
|
||||
|
||||
Future<List<FilterOptionModel>> _listOptions(String endpoint) async {
|
||||
final rows = await _listAllMaps(
|
||||
endpoint,
|
||||
queryParameters: {'is_active': true},
|
||||
);
|
||||
final rows = await _listAllMaps(endpoint);
|
||||
return rows
|
||||
.where(isActiveOptionRow)
|
||||
.map(
|
||||
@ -169,33 +198,23 @@ class MasterRemoteDataSource {
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// Loads every page of a masters list (API default page size = 20).
|
||||
/// Form-dropdown loader: uses `dropdown_call=true` (all active, no pagination).
|
||||
Future<List<Map<String, dynamic>>> _listAllMaps(
|
||||
String endpoint, {
|
||||
Map<String, dynamic>? queryParameters,
|
||||
}) async {
|
||||
final all = <Map<String, dynamic>>[];
|
||||
var page = 1;
|
||||
var totalPages = 1;
|
||||
final pageSize = AppConstants.defaultPageSize;
|
||||
|
||||
while (page <= totalPages) {
|
||||
final response = await dio.get(
|
||||
endpoint,
|
||||
queryParameters: {
|
||||
'page': page,
|
||||
'limit': pageSize,
|
||||
...?queryParameters,
|
||||
},
|
||||
);
|
||||
final parsed = _parsePage(response.data, fallbackLimit: pageSize);
|
||||
all.addAll(parsed.items);
|
||||
totalPages = parsed.totalPages;
|
||||
if (parsed.items.isEmpty) break;
|
||||
page++;
|
||||
}
|
||||
|
||||
return all;
|
||||
final response = await dio.get(
|
||||
endpoint,
|
||||
queryParameters: {
|
||||
'dropdown_call': true,
|
||||
...?queryParameters,
|
||||
},
|
||||
);
|
||||
final parsed = _parsePage(
|
||||
response.data,
|
||||
fallbackLimit: AppConstants.defaultPageSize,
|
||||
);
|
||||
return parsed.items;
|
||||
}
|
||||
|
||||
({List<Map<String, dynamic>> items, int totalPages}) _parsePage(
|
||||
|
||||
@ -22,6 +22,33 @@ class PurchaseOrderRemoteDataSource {
|
||||
return _parsePaginated(response.data, PurchaseOrderModel.fromJson);
|
||||
}
|
||||
|
||||
/// Form-dropdown loader (`dropdown_call=true`). Optional status filter.
|
||||
Future<List<PurchaseOrderModel>> listPurchaseOrderOptions({
|
||||
String? status,
|
||||
}) async {
|
||||
final response = await dio.get(
|
||||
ApiEndpoints.purchaseOrders,
|
||||
queryParameters: {
|
||||
'dropdown_call': true,
|
||||
if (status != null) 'status': status,
|
||||
},
|
||||
);
|
||||
final body = response.data;
|
||||
if (body is! Map) return const [];
|
||||
final raw = body['data'];
|
||||
final list = raw is List
|
||||
? raw
|
||||
: raw is Map
|
||||
? (raw['items'] as List?) ?? const []
|
||||
: const [];
|
||||
return list
|
||||
.whereType<Map>()
|
||||
.map(
|
||||
(item) => PurchaseOrderModel.fromJson(Map<String, dynamic>.from(item)),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
Future<PurchaseOrderModel> getPurchaseOrderById(String id) async {
|
||||
final response = await dio.get(ApiEndpoints.purchaseOrderById(id));
|
||||
final raw = response.data['data'];
|
||||
@ -204,7 +231,6 @@ class PurchaseOrderRemoteDataSource {
|
||||
return {
|
||||
if (query.search != null && query.search!.isNotEmpty) 'search': query.search,
|
||||
if (query.status != null) 'status': query.status,
|
||||
if (query.poType != null) 'po_type': query.poType,
|
||||
if (query.vendorId != null) 'vendor_id': query.vendorId,
|
||||
if (query.plantId != null) 'plant_id': query.plantId,
|
||||
if (query.dateFrom != null) 'date_from': query.dateFrom,
|
||||
|
||||
@ -32,6 +32,15 @@ class PurchaseOrderRepositoryImpl implements PurchaseOrderRepository {
|
||||
return safeApiCall(() => dataSource.getPurchaseOrders(query));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<List<PurchaseOrderModel>>> listPurchaseOrderOptions({
|
||||
String? status,
|
||||
}) {
|
||||
return safeApiCall(
|
||||
() => dataSource.listPurchaseOrderOptions(status: status),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<ExportFileResult>> exportPurchaseOrders(
|
||||
PurchaseOrderListQuery query,
|
||||
|
||||
@ -8,6 +8,9 @@ abstract class PurchaseOrderRepository {
|
||||
Future<Result<PaginatedResponse<PurchaseOrderModel>>> getPurchaseOrders(
|
||||
PurchaseOrderListQuery query,
|
||||
);
|
||||
Future<Result<List<PurchaseOrderModel>>> listPurchaseOrderOptions({
|
||||
String? status,
|
||||
});
|
||||
Future<Result<ExportFileResult>> exportPurchaseOrders(
|
||||
PurchaseOrderListQuery query,
|
||||
);
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../../core/constants/app_constants.dart';
|
||||
import '../../../../core/utils/active_option.dart';
|
||||
import '../../../../shared/models/user_management_models.dart';
|
||||
import '../../../../shared/models/vendor_model.dart';
|
||||
import '../../../masters/data/datasources/master_remote_data_source.dart';
|
||||
import '../../../vendors/data/repositories/vendor_repository_impl.dart';
|
||||
import '../../../vendors/domain/repositories/vendor_repository.dart';
|
||||
@ -13,7 +11,6 @@ class PurchaseOrderLookups {
|
||||
this.vendors = const [],
|
||||
this.plants = const [],
|
||||
this.warehouses = const [],
|
||||
this.brands = const [],
|
||||
this.paymentTerms = const [],
|
||||
this.deliveryTerms = const [],
|
||||
this.items = const [],
|
||||
@ -24,12 +21,12 @@ class PurchaseOrderLookups {
|
||||
this.gstRates = const [],
|
||||
this.gstRatePctById = const {},
|
||||
this.hsnCodes = const [],
|
||||
this.hsnGstRateById = const {},
|
||||
});
|
||||
|
||||
final List<FilterOptionModel> vendors;
|
||||
final List<FilterOptionModel> plants;
|
||||
final List<FilterOptionModel> warehouses;
|
||||
final List<FilterOptionModel> brands;
|
||||
final List<FilterOptionModel> paymentTerms;
|
||||
final List<FilterOptionModel> deliveryTerms;
|
||||
final List<FilterOptionModel> items;
|
||||
@ -44,6 +41,8 @@ class PurchaseOrderLookups {
|
||||
/// GST rate id → `rate_pct` for tax calculations.
|
||||
final Map<String, double> gstRatePctById;
|
||||
final List<FilterOptionModel> hsnCodes;
|
||||
/// HSN code id → default `gst_rate_id` from HSN master.
|
||||
final Map<String, int?> hsnGstRateById;
|
||||
}
|
||||
|
||||
final purchaseOrderLookupsProvider =
|
||||
@ -55,32 +54,31 @@ final purchaseOrderLookupsProvider =
|
||||
|
||||
final itemsWithDefaults = await _safeItemsWithDefaults(master.listItemsWithHsn);
|
||||
final gstWithPct = await _safeGstRatesWithPct(master.listGstRatesWithPct);
|
||||
final hsnWithGst = await _safeHsnWithGst(master.listHsnCodesWithGstRate);
|
||||
|
||||
final results = await Future.wait([
|
||||
_safeOptions(master.listPlants),
|
||||
_safeOptions(master.listWarehouses),
|
||||
_safeOptions(master.listBrands),
|
||||
_safeOptions(master.listPaymentTerms),
|
||||
_safeOptions(master.listDeliveryTerms),
|
||||
_safeOptions(master.listUom),
|
||||
_safeOptions(master.listHsnCodes),
|
||||
]);
|
||||
|
||||
return PurchaseOrderLookups(
|
||||
vendors: vendors,
|
||||
plants: results[0],
|
||||
warehouses: results[1],
|
||||
brands: results[2],
|
||||
paymentTerms: results[3],
|
||||
deliveryTerms: results[4],
|
||||
paymentTerms: results[2],
|
||||
deliveryTerms: results[3],
|
||||
items: itemsWithDefaults.options,
|
||||
itemHsnById: itemsWithDefaults.hsnByItemId,
|
||||
itemUomById: itemsWithDefaults.uomByItemId,
|
||||
itemGstRateById: itemsWithDefaults.gstRateByItemId,
|
||||
uom: results[5],
|
||||
uom: results[4],
|
||||
gstRates: gstWithPct.options,
|
||||
gstRatePctById: gstWithPct.pctById,
|
||||
hsnCodes: results[6],
|
||||
hsnCodes: hsnWithGst.options,
|
||||
hsnGstRateById: hsnWithGst.gstRateByHsnId,
|
||||
);
|
||||
});
|
||||
|
||||
@ -136,42 +134,36 @@ Future<({List<FilterOptionModel> options, Map<String, double> pctById})>
|
||||
}
|
||||
}
|
||||
|
||||
Future<({List<FilterOptionModel> options, Map<String, int?> gstRateByHsnId})>
|
||||
_safeHsnWithGst(
|
||||
Future<({List<FilterOptionModel> options, Map<String, int?> gstRateByHsnId})>
|
||||
Function()
|
||||
load,
|
||||
) async {
|
||||
try {
|
||||
return await load();
|
||||
} catch (_) {
|
||||
return (options: <FilterOptionModel>[], gstRateByHsnId: <String, int?>{});
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<FilterOptionModel>> _fetchActiveVendors(
|
||||
VendorRepository vendorRepo,
|
||||
) async {
|
||||
final vendors = <FilterOptionModel>[];
|
||||
var page = 1;
|
||||
var totalPages = 1;
|
||||
|
||||
while (page <= totalPages) {
|
||||
final result = await vendorRepo.getVendors(
|
||||
VendorListQuery(
|
||||
page: page,
|
||||
limit: AppConstants.maxPageSize,
|
||||
isActive: true,
|
||||
),
|
||||
);
|
||||
if (result.failure != null) {
|
||||
throw result.failure!;
|
||||
}
|
||||
|
||||
final data = result.data!;
|
||||
vendors.addAll(
|
||||
data.items
|
||||
.where(
|
||||
(vendor) => isActiveVendorOption(
|
||||
isActive: vendor.isActive,
|
||||
status: vendor.status,
|
||||
),
|
||||
)
|
||||
.map(
|
||||
(vendor) =>
|
||||
FilterOptionModel(id: vendor.id, name: vendor.vendorName),
|
||||
),
|
||||
);
|
||||
totalPages = data.totalPages;
|
||||
page++;
|
||||
final result = await vendorRepo.listVendorOptions();
|
||||
if (result.failure != null) {
|
||||
throw result.failure!;
|
||||
}
|
||||
|
||||
return vendors;
|
||||
return (result.data ?? const [])
|
||||
.where(
|
||||
(vendor) => isActiveVendorOption(
|
||||
isActive: vendor.isActive,
|
||||
status: vendor.status,
|
||||
),
|
||||
)
|
||||
.map(
|
||||
(vendor) => FilterOptionModel(id: vendor.id, name: vendor.vendorName),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@ -112,12 +112,6 @@ class PurchaseOrdersListNotifier
|
||||
applyQuery(current.query.copyWith(status: status, page: 1));
|
||||
}
|
||||
|
||||
void setPoTypeFilter(String? poType) {
|
||||
final current = state.valueOrNull;
|
||||
if (current == null) return;
|
||||
applyQuery(current.query.copyWith(poType: poType, page: 1));
|
||||
}
|
||||
|
||||
void setPage(int page) {
|
||||
final current = state.valueOrNull;
|
||||
if (current == null) return;
|
||||
|
||||
@ -8,6 +8,7 @@ import '../../../../core/errors/failure.dart';
|
||||
import '../../../../core/theme/app_colors.dart';
|
||||
import '../../../../core/utils/formatters.dart';
|
||||
import '../../../../shared/models/purchase_order_model.dart';
|
||||
import '../../../../shared/models/vendor_model.dart';
|
||||
import '../../../../shared/models/user_management_models.dart';
|
||||
import '../../../../shared/providers/permissions_provider.dart';
|
||||
import '../../../../shared/utils/file_download_helper.dart';
|
||||
@ -418,7 +419,7 @@ class _DetailHeader extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final subtitleParts = [
|
||||
poTypeLabel(order.poType),
|
||||
vendorTypeLabel(order.vendorType),
|
||||
if (order.vendorName?.trim().isNotEmpty == true) order.vendorName!.trim(),
|
||||
if (order.plantName?.trim().isNotEmpty == true) order.plantName!.trim(),
|
||||
];
|
||||
@ -702,7 +703,6 @@ class _OrderDetailsCard extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final brand = _lookupName(lookups?.brands, order.brandId);
|
||||
final paymentTerm =
|
||||
_lookupName(lookups?.paymentTerms, order.paymentTermId);
|
||||
final deliveryTerm =
|
||||
@ -733,8 +733,8 @@ class _OrderDetailsCard extends StatelessWidget {
|
||||
value: _displayOrDash(order.vendorName),
|
||||
),
|
||||
_DetailField(
|
||||
label: 'PO Type',
|
||||
value: poTypeLabel(order.poType),
|
||||
label: 'Vendor Type',
|
||||
value: vendorTypeLabel(order.vendorType),
|
||||
),
|
||||
_DetailField(
|
||||
label: 'Plant',
|
||||
@ -744,7 +744,6 @@ class _OrderDetailsCard extends StatelessWidget {
|
||||
label: 'Warehouse',
|
||||
value: _displayOrDash(order.warehouseName),
|
||||
),
|
||||
_DetailField(label: 'Brand', value: brand),
|
||||
_DetailField(label: 'Payment Term', value: paymentTerm),
|
||||
_DetailField(label: 'Delivery Term', value: deliveryTerm),
|
||||
];
|
||||
@ -1275,7 +1274,7 @@ class _DetailFooter extends StatelessWidget {
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
'${poTypeLabel(order.poType)} · ${poStatusLabel(order.status)}',
|
||||
'${vendorTypeLabel(order.vendorType)} · ${poStatusLabel(order.status)}',
|
||||
style: style,
|
||||
),
|
||||
const Spacer(),
|
||||
|
||||
@ -50,11 +50,9 @@ class _PurchaseOrderFormScreenState extends ConsumerState<PurchaseOrderFormScree
|
||||
|
||||
DateTime? _poDate;
|
||||
DateTime? _expectedDeliveryDate;
|
||||
String? _poType;
|
||||
int? _vendorId;
|
||||
int? _plantId;
|
||||
int? _warehouseId;
|
||||
int? _brandId;
|
||||
int? _paymentTermId;
|
||||
int? _deliveryTermId;
|
||||
final List<PoLineItemDraft> _lines = [];
|
||||
@ -99,11 +97,9 @@ class _PurchaseOrderFormScreenState extends ConsumerState<PurchaseOrderFormScree
|
||||
setState(() {
|
||||
_poDate = order.poDate ?? DateTime.now();
|
||||
_expectedDeliveryDate = order.expectedDeliveryDate;
|
||||
_poType = order.poType;
|
||||
_vendorId = order.vendorId;
|
||||
_plantId = order.plantId;
|
||||
_warehouseId = order.warehouseId;
|
||||
_brandId = order.brandId;
|
||||
_paymentTermId = order.paymentTermId;
|
||||
_deliveryTermId = order.deliveryTermId;
|
||||
_discountController.text =
|
||||
@ -190,11 +186,9 @@ class _PurchaseOrderFormScreenState extends ConsumerState<PurchaseOrderFormScree
|
||||
Map<String, dynamic> _buildPayload() {
|
||||
return {
|
||||
'po_date': DateFormatter.toApiDate(_poDate ?? DateTime.now()),
|
||||
'po_type': _poType,
|
||||
'vendor_id': _vendorId,
|
||||
'plant_id': _plantId,
|
||||
if (_warehouseId != null) 'warehouse_id': _warehouseId,
|
||||
if (_brandId != null) 'brand_id': _brandId,
|
||||
if (_paymentTermId != null) 'payment_term_id': _paymentTermId,
|
||||
if (_deliveryTermId != null) 'delivery_term_id': _deliveryTermId,
|
||||
if (_expectedDeliveryDate != null)
|
||||
@ -244,7 +238,7 @@ class _PurchaseOrderFormScreenState extends ConsumerState<PurchaseOrderFormScree
|
||||
return;
|
||||
}
|
||||
|
||||
if (_poType == null || _vendorId == null || _plantId == null) {
|
||||
if (_vendorId == null || _plantId == null) {
|
||||
showAppToastFromSnackBar(context,
|
||||
const SnackBar(content: Text('Please complete all required fields')),
|
||||
);
|
||||
@ -391,23 +385,6 @@ class _PurchaseOrderFormScreenState extends ConsumerState<PurchaseOrderFormScree
|
||||
onPicked: (d) => setState(() => _poDate = d),
|
||||
),
|
||||
),
|
||||
AppSearchableDropdown<String>(
|
||||
label: 'PO Type *',
|
||||
value: _poType,
|
||||
hint: 'Select PO type',
|
||||
searchHint: 'Search type...',
|
||||
options: poTypeOptions
|
||||
.map(
|
||||
(e) => AppDropdownOption(
|
||||
value: e.$1,
|
||||
label: e.$2,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onChanged: (v) => setState(() => _poType = v),
|
||||
validator: (v) =>
|
||||
v == null ? 'PO type is required' : null,
|
||||
),
|
||||
AppSearchableDropdown<int>(
|
||||
label: 'Vendor *',
|
||||
value: _dropdownValue(_vendorId, vendorIds),
|
||||
@ -432,10 +409,6 @@ class _PurchaseOrderFormScreenState extends ConsumerState<PurchaseOrderFormScree
|
||||
validator: (v) =>
|
||||
v == null ? 'Plant is required' : null,
|
||||
),
|
||||
],
|
||||
),
|
||||
FormRowFour(
|
||||
children: [
|
||||
MasterQuickAddDropdown<int?>(
|
||||
masterId: 'warehouses',
|
||||
label: 'Warehouse',
|
||||
@ -449,18 +422,10 @@ class _PurchaseOrderFormScreenState extends ConsumerState<PurchaseOrderFormScree
|
||||
onChanged: (v) =>
|
||||
setState(() => _warehouseId = v),
|
||||
),
|
||||
MasterQuickAddDropdown<int?>(
|
||||
masterId: 'brands',
|
||||
label: 'Brand',
|
||||
value: _brandId,
|
||||
hint: 'Select brand',
|
||||
searchHint: 'Search brand...',
|
||||
options: _nullableIntOptions(lookups.brands),
|
||||
refreshLookups: () =>
|
||||
ref.invalidate(purchaseOrderLookupsProvider),
|
||||
parseCreatedId: int.tryParse,
|
||||
onChanged: (v) => setState(() => _brandId = v),
|
||||
),
|
||||
],
|
||||
),
|
||||
FormRowFour(
|
||||
children: [
|
||||
MasterQuickAddDropdown<int?>(
|
||||
masterId: 'payment_terms',
|
||||
label: 'Payment Term',
|
||||
|
||||
@ -8,6 +8,7 @@ import '../../../../core/errors/failure.dart';
|
||||
import '../../../../core/utils/formatters.dart';
|
||||
import '../../../../core/utils/responsive_utils.dart';
|
||||
import '../../../../shared/models/purchase_order_model.dart';
|
||||
import '../../../../shared/models/vendor_model.dart';
|
||||
import '../../../../shared/providers/permissions_provider.dart';
|
||||
import '../../../../shared/widgets/app_card.dart';
|
||||
import '../../../../shared/widgets/app_confirmation_dialog.dart';
|
||||
@ -114,8 +115,6 @@ class _PurchaseOrderListScreenState extends ConsumerState<PurchaseOrderListScree
|
||||
onSearch: ref.read(purchaseOrdersListProvider.notifier).setSearch,
|
||||
onStatusChanged:
|
||||
ref.read(purchaseOrdersListProvider.notifier).setStatusFilter,
|
||||
onPoTypeChanged:
|
||||
ref.read(purchaseOrdersListProvider.notifier).setPoTypeFilter,
|
||||
),
|
||||
footer: AppPagination(
|
||||
currentPage: state.query.page,
|
||||
@ -273,7 +272,6 @@ class _FiltersBar extends StatelessWidget {
|
||||
required this.statusOptions,
|
||||
required this.onSearch,
|
||||
required this.onStatusChanged,
|
||||
required this.onPoTypeChanged,
|
||||
this.showExport = false,
|
||||
this.isExporting = false,
|
||||
this.onExport,
|
||||
@ -284,7 +282,6 @@ class _FiltersBar extends StatelessWidget {
|
||||
final List<AppDropdownOption<String?>> statusOptions;
|
||||
final ValueChanged<String> onSearch;
|
||||
final ValueChanged<String?> onStatusChanged;
|
||||
final ValueChanged<String?> onPoTypeChanged;
|
||||
final bool showExport;
|
||||
final bool isExporting;
|
||||
final VoidCallback? onExport;
|
||||
@ -306,19 +303,6 @@ class _FiltersBar extends StatelessWidget {
|
||||
options: statusOptions,
|
||||
onChanged: onStatusChanged,
|
||||
),
|
||||
AppSearchableDropdown<String?>(
|
||||
label: 'PO Type',
|
||||
value: query.poType,
|
||||
searchHint: 'Search type...',
|
||||
isDense: true,
|
||||
options: [
|
||||
const AppDropdownOption(value: null, label: 'All Types'),
|
||||
...poTypeOptions.map(
|
||||
(e) => AppDropdownOption(value: e.$1, label: e.$2),
|
||||
),
|
||||
],
|
||||
onChanged: onPoTypeChanged,
|
||||
),
|
||||
];
|
||||
|
||||
return AppResponsiveFilterBar(
|
||||
@ -456,7 +440,7 @@ class _PoCardList extends StatelessWidget {
|
||||
child: ListTile(
|
||||
title: Text(order.poNo ?? 'PO #${order.id}'),
|
||||
subtitle: Text(
|
||||
'${order.vendorName ?? '—'} · ${poTypeLabel(order.poType)}',
|
||||
'${order.vendorName ?? '—'} · ${vendorTypeLabel(order.vendorType)}',
|
||||
),
|
||||
trailing: PoStatusChip(status: order.status, compact: true),
|
||||
onTap: () => onView(order),
|
||||
|
||||
@ -394,6 +394,18 @@ class _LineItemCardState extends ConsumerState<_LineItemCard> {
|
||||
ref.read(purchaseOrderLookupsProvider).valueOrNull?.itemHsnById ??
|
||||
widget.itemHsnById;
|
||||
|
||||
Map<String, int?> get _hsnGstRateById =>
|
||||
ref.read(purchaseOrderLookupsProvider).valueOrNull?.hsnGstRateById ??
|
||||
const {};
|
||||
|
||||
void _applyGstFromHsn(int? hsnId) {
|
||||
if (hsnId == null) return;
|
||||
final gst = _hsnGstRateById[hsnId.toString()];
|
||||
if (gst != null) {
|
||||
widget.line.gstRateId = gst;
|
||||
}
|
||||
}
|
||||
|
||||
bool _fillMissingItemDefaults() {
|
||||
final itemId = widget.line.itemId;
|
||||
if (itemId == null) return false;
|
||||
@ -415,6 +427,10 @@ class _LineItemCardState extends ConsumerState<_LineItemCard> {
|
||||
widget.line.hsnCodeId = defaultHsn;
|
||||
changed = true;
|
||||
}
|
||||
if (widget.line.gstRateId == null && widget.line.hsnCodeId != null) {
|
||||
_applyGstFromHsn(widget.line.hsnCodeId);
|
||||
if (widget.line.gstRateId != null) changed = true;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
@ -434,6 +450,9 @@ class _LineItemCardState extends ConsumerState<_LineItemCard> {
|
||||
final defaultHsn = _itemHsnById[key];
|
||||
if (defaultHsn != null) {
|
||||
widget.line.hsnCodeId = defaultHsn;
|
||||
if (widget.line.gstRateId == null) {
|
||||
_applyGstFromHsn(defaultHsn);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../../core/constants/app_constants.dart';
|
||||
import '../../../../core/utils/active_option.dart';
|
||||
import '../../../../shared/models/user_management_models.dart';
|
||||
import '../../../masters/data/datasources/master_remote_data_source.dart';
|
||||
import '../../../roles/data/repositories/role_repository_impl.dart';
|
||||
import '../../../users/data/repositories/user_repository_impl.dart';
|
||||
import '../../../users/presentation/providers/users_provider.dart';
|
||||
|
||||
class AddUserFormState {
|
||||
@ -60,7 +60,7 @@ class AddUserFormNotifier extends FamilyAsyncNotifier<AddUserFormState, String?>
|
||||
Future<AddUserFormState> build(String? userId) async {
|
||||
final masterRemote = ref.read(masterRemoteDataSourceProvider);
|
||||
final roleRepository = ref.read(roleRepositoryProvider);
|
||||
final getUsers = ref.read(getUsersUseCaseProvider);
|
||||
final userRepository = ref.read(userRepositoryProvider);
|
||||
|
||||
final rolesResult = await roleRepository.listRoleOptions();
|
||||
if (rolesResult.failure != null) throw rolesResult.failure!;
|
||||
@ -69,16 +69,10 @@ class AddUserFormNotifier extends FamilyAsyncNotifier<AddUserFormState, String?>
|
||||
final plants = await masterRemote.listPlants();
|
||||
final designations = await masterRemote.listDesignations();
|
||||
|
||||
final usersResult = await getUsers(
|
||||
const UserListQuery(
|
||||
limit: AppConstants.maxPageSize,
|
||||
status: 'active',
|
||||
isActive: true,
|
||||
),
|
||||
);
|
||||
final usersResult = await userRepository.listUserOptions();
|
||||
if (usersResult.failure != null) throw usersResult.failure!;
|
||||
|
||||
final managers = usersResult.data!.items
|
||||
final managers = (usersResult.data ?? const [])
|
||||
.where(
|
||||
(user) =>
|
||||
isReportingManagerRole(user.roleName) &&
|
||||
|
||||
@ -37,9 +37,7 @@ class RoleRemoteDataSource {
|
||||
final response = await dio.get(
|
||||
ApiEndpoints.roles,
|
||||
queryParameters: {
|
||||
'page': 1,
|
||||
'limit': 100,
|
||||
'is_active': true,
|
||||
'dropdown_call': true,
|
||||
if (search != null && search.isNotEmpty) 'search': search,
|
||||
},
|
||||
);
|
||||
|
||||
@ -96,6 +96,26 @@ class UserRemoteDataSource {
|
||||
);
|
||||
}
|
||||
|
||||
/// Form-dropdown loader: all active users (`dropdown_call=true`).
|
||||
Future<List<ManagedUserModel>> listUserOptions() async {
|
||||
final response = await dio.get(
|
||||
ApiEndpoints.users,
|
||||
queryParameters: const {'dropdown_call': true},
|
||||
);
|
||||
final body = response.data;
|
||||
if (body is! Map) return const [];
|
||||
final raw = body['data'];
|
||||
final list = raw is List
|
||||
? raw
|
||||
: raw is Map
|
||||
? (raw['items'] as List?) ?? const []
|
||||
: const [];
|
||||
return list
|
||||
.whereType<Map>()
|
||||
.map((item) => ManagedUserModel.fromJson(Map<String, dynamic>.from(item)))
|
||||
.toList();
|
||||
}
|
||||
|
||||
Future<ManagedUserModel> getUserById(String id) async {
|
||||
final response = await dio.get(ApiEndpoints.userById(id));
|
||||
return ManagedUserModel.fromJson(response.data['data'] as Map<String, dynamic>);
|
||||
|
||||
@ -35,6 +35,10 @@ class UserRepositoryImpl implements UserRepository {
|
||||
) =>
|
||||
safeApiCall(() => remote.getUsers(query));
|
||||
|
||||
@override
|
||||
Future<Result<List<ManagedUserModel>>> listUserOptions() =>
|
||||
safeApiCall(() => remote.listUserOptions());
|
||||
|
||||
@override
|
||||
Future<Result<ManagedUserModel>> getUserById(String id) =>
|
||||
safeApiCall(() => remote.getUserById(id));
|
||||
|
||||
@ -7,6 +7,7 @@ abstract class UserRepository {
|
||||
Future<Result<UserSummaryModel>> getSummary();
|
||||
Future<Result<UserFiltersModel>> getFilters();
|
||||
Future<Result<PaginatedResponse<ManagedUserModel>>> getUsers(UserListQuery query);
|
||||
Future<Result<List<ManagedUserModel>>> listUserOptions();
|
||||
Future<Result<ManagedUserModel>> getUserById(String id);
|
||||
Future<Result<ManagedUserModel>> createUser(CreateUserRequest request);
|
||||
Future<Result<ManagedUserModel>> updateUser(String id, UpdateUserRequest request);
|
||||
|
||||
@ -20,6 +20,15 @@ class VendorRemoteDataSource {
|
||||
return _parsePaginated(response.data, VendorModel.fromJson);
|
||||
}
|
||||
|
||||
/// Form-dropdown loader: all active vendors (`dropdown_call=true`).
|
||||
Future<List<VendorModel>> listVendorOptions() async {
|
||||
final response = await dio.get(
|
||||
ApiEndpoints.vendors,
|
||||
queryParameters: const {'dropdown_call': true},
|
||||
);
|
||||
return _parseList(response.data, VendorModel.fromJson);
|
||||
}
|
||||
|
||||
Future<VendorModel> getVendorById(String id) async {
|
||||
final response = await dio.get(ApiEndpoints.vendorById(id));
|
||||
return VendorModel.fromJson(response.data['data'] as Map<String, dynamic>);
|
||||
|
||||
@ -26,6 +26,11 @@ class VendorRepositoryImpl implements VendorRepository {
|
||||
return safeApiCall(() => dataSource.getVendors(query));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<List<VendorModel>>> listVendorOptions() {
|
||||
return safeApiCall(() => dataSource.listVendorOptions());
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<ExportFileResult>> exportVendors(VendorListQuery query) {
|
||||
return safeApiCall(() => dataSource.exportVendors(query));
|
||||
|
||||
@ -5,6 +5,7 @@ import '../../../../shared/models/vendor_model.dart';
|
||||
|
||||
abstract class VendorRepository {
|
||||
Future<Result<PaginatedResponse<VendorModel>>> getVendors(VendorListQuery query);
|
||||
Future<Result<List<VendorModel>>> listVendorOptions();
|
||||
Future<Result<ExportFileResult>> exportVendors(VendorListQuery query);
|
||||
Future<Result<VendorModel>> getVendorById(String id);
|
||||
Future<Result<VendorModel>> createVendor(Map<String, dynamic> data);
|
||||
|
||||
@ -117,6 +117,7 @@ class AssetCategoryModel with _$AssetCategoryModel {
|
||||
@JsonKey(fromJson: _idFromJson) required String id,
|
||||
required String code,
|
||||
required String name,
|
||||
@JsonKey(name: 'category_type') String? categoryType,
|
||||
@JsonKey(name: 'code_prefix') String? codePrefix,
|
||||
@JsonKey(name: 'default_useful_life_years', fromJson: _intFromJsonNullable)
|
||||
int? defaultUsefulLifeYears,
|
||||
|
||||
@ -25,6 +25,8 @@ mixin _$AssetCategoryModel {
|
||||
String get id => throw _privateConstructorUsedError;
|
||||
String get code => throw _privateConstructorUsedError;
|
||||
String get name => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'category_type')
|
||||
String? get categoryType => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'code_prefix')
|
||||
String? get codePrefix => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'default_useful_life_years', fromJson: _intFromJsonNullable)
|
||||
@ -57,6 +59,7 @@ abstract class $AssetCategoryModelCopyWith<$Res> {
|
||||
@JsonKey(fromJson: _idFromJson) String id,
|
||||
String code,
|
||||
String name,
|
||||
@JsonKey(name: 'category_type') String? categoryType,
|
||||
@JsonKey(name: 'code_prefix') String? codePrefix,
|
||||
@JsonKey(name: 'default_useful_life_years', fromJson: _intFromJsonNullable)
|
||||
int? defaultUsefulLifeYears,
|
||||
@ -86,6 +89,7 @@ class _$AssetCategoryModelCopyWithImpl<$Res, $Val extends AssetCategoryModel>
|
||||
Object? id = null,
|
||||
Object? code = null,
|
||||
Object? name = null,
|
||||
Object? categoryType = freezed,
|
||||
Object? codePrefix = freezed,
|
||||
Object? defaultUsefulLifeYears = freezed,
|
||||
Object? defaultDepreciationMethod = freezed,
|
||||
@ -107,6 +111,10 @@ class _$AssetCategoryModelCopyWithImpl<$Res, $Val extends AssetCategoryModel>
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
categoryType: freezed == categoryType
|
||||
? _value.categoryType
|
||||
: categoryType // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
codePrefix: freezed == codePrefix
|
||||
? _value.codePrefix
|
||||
: codePrefix // ignore: cast_nullable_to_non_nullable
|
||||
@ -150,6 +158,7 @@ abstract class _$$AssetCategoryModelImplCopyWith<$Res>
|
||||
@JsonKey(fromJson: _idFromJson) String id,
|
||||
String code,
|
||||
String name,
|
||||
@JsonKey(name: 'category_type') String? categoryType,
|
||||
@JsonKey(name: 'code_prefix') String? codePrefix,
|
||||
@JsonKey(name: 'default_useful_life_years', fromJson: _intFromJsonNullable)
|
||||
int? defaultUsefulLifeYears,
|
||||
@ -178,6 +187,7 @@ class __$$AssetCategoryModelImplCopyWithImpl<$Res>
|
||||
Object? id = null,
|
||||
Object? code = null,
|
||||
Object? name = null,
|
||||
Object? categoryType = freezed,
|
||||
Object? codePrefix = freezed,
|
||||
Object? defaultUsefulLifeYears = freezed,
|
||||
Object? defaultDepreciationMethod = freezed,
|
||||
@ -199,6 +209,10 @@ class __$$AssetCategoryModelImplCopyWithImpl<$Res>
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
categoryType: freezed == categoryType
|
||||
? _value.categoryType
|
||||
: categoryType // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
codePrefix: freezed == codePrefix
|
||||
? _value.codePrefix
|
||||
: codePrefix // ignore: cast_nullable_to_non_nullable
|
||||
@ -235,6 +249,7 @@ class _$AssetCategoryModelImpl implements _AssetCategoryModel {
|
||||
@JsonKey(fromJson: _idFromJson) required this.id,
|
||||
required this.code,
|
||||
required this.name,
|
||||
@JsonKey(name: 'category_type') this.categoryType,
|
||||
@JsonKey(name: 'code_prefix') this.codePrefix,
|
||||
@JsonKey(name: 'default_useful_life_years', fromJson: _intFromJsonNullable)
|
||||
this.defaultUsefulLifeYears,
|
||||
@ -256,6 +271,9 @@ class _$AssetCategoryModelImpl implements _AssetCategoryModel {
|
||||
@override
|
||||
final String name;
|
||||
@override
|
||||
@JsonKey(name: 'category_type')
|
||||
final String? categoryType;
|
||||
@override
|
||||
@JsonKey(name: 'code_prefix')
|
||||
final String? codePrefix;
|
||||
@override
|
||||
@ -274,7 +292,7 @@ class _$AssetCategoryModelImpl implements _AssetCategoryModel {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AssetCategoryModel(id: $id, code: $code, name: $name, codePrefix: $codePrefix, defaultUsefulLifeYears: $defaultUsefulLifeYears, defaultDepreciationMethod: $defaultDepreciationMethod, isActive: $isActive, createdAt: $createdAt, updatedAt: $updatedAt)';
|
||||
return 'AssetCategoryModel(id: $id, code: $code, name: $name, categoryType: $categoryType, codePrefix: $codePrefix, defaultUsefulLifeYears: $defaultUsefulLifeYears, defaultDepreciationMethod: $defaultDepreciationMethod, isActive: $isActive, createdAt: $createdAt, updatedAt: $updatedAt)';
|
||||
}
|
||||
|
||||
@override
|
||||
@ -285,6 +303,8 @@ class _$AssetCategoryModelImpl implements _AssetCategoryModel {
|
||||
(identical(other.id, id) || other.id == id) &&
|
||||
(identical(other.code, code) || other.code == code) &&
|
||||
(identical(other.name, name) || other.name == name) &&
|
||||
(identical(other.categoryType, categoryType) ||
|
||||
other.categoryType == categoryType) &&
|
||||
(identical(other.codePrefix, codePrefix) ||
|
||||
other.codePrefix == codePrefix) &&
|
||||
(identical(other.defaultUsefulLifeYears, defaultUsefulLifeYears) ||
|
||||
@ -309,6 +329,7 @@ class _$AssetCategoryModelImpl implements _AssetCategoryModel {
|
||||
id,
|
||||
code,
|
||||
name,
|
||||
categoryType,
|
||||
codePrefix,
|
||||
defaultUsefulLifeYears,
|
||||
defaultDepreciationMethod,
|
||||
@ -339,6 +360,7 @@ abstract class _AssetCategoryModel implements AssetCategoryModel {
|
||||
@JsonKey(fromJson: _idFromJson) required final String id,
|
||||
required final String code,
|
||||
required final String name,
|
||||
@JsonKey(name: 'category_type') final String? categoryType,
|
||||
@JsonKey(name: 'code_prefix') final String? codePrefix,
|
||||
@JsonKey(name: 'default_useful_life_years', fromJson: _intFromJsonNullable)
|
||||
final int? defaultUsefulLifeYears,
|
||||
@ -360,6 +382,9 @@ abstract class _AssetCategoryModel implements AssetCategoryModel {
|
||||
@override
|
||||
String get name;
|
||||
@override
|
||||
@JsonKey(name: 'category_type')
|
||||
String? get categoryType;
|
||||
@override
|
||||
@JsonKey(name: 'code_prefix')
|
||||
String? get codePrefix;
|
||||
@override
|
||||
|
||||
@ -12,6 +12,7 @@ _$AssetCategoryModelImpl _$$AssetCategoryModelImplFromJson(
|
||||
id: _idFromJson(json['id']),
|
||||
code: json['code'] as String,
|
||||
name: json['name'] as String,
|
||||
categoryType: json['category_type'] as String?,
|
||||
codePrefix: json['code_prefix'] as String?,
|
||||
defaultUsefulLifeYears: _intFromJsonNullable(
|
||||
json['default_useful_life_years'],
|
||||
@ -32,6 +33,7 @@ Map<String, dynamic> _$$AssetCategoryModelImplToJson(
|
||||
'id': instance.id,
|
||||
'code': instance.code,
|
||||
'name': instance.name,
|
||||
'category_type': instance.categoryType,
|
||||
'code_prefix': instance.codePrefix,
|
||||
'default_useful_life_years': instance.defaultUsefulLifeYears,
|
||||
'default_depreciation_method': instance.defaultDepreciationMethod,
|
||||
|
||||
@ -38,6 +38,17 @@ Object? _readNestedName(Map<dynamic, dynamic> json, String flatKey, String neste
|
||||
Object? _readVendorName(Map<dynamic, dynamic> json, String key) =>
|
||||
_readNestedName(json, 'vendor_name', 'vendor');
|
||||
|
||||
Object? _readVendorType(Map<dynamic, dynamic> json, String key) {
|
||||
final flat = json['vendor_type'];
|
||||
if (flat is String && flat.isNotEmpty) return flat;
|
||||
final nested = json['vendor'];
|
||||
if (nested is Map) {
|
||||
final type = nested['vendor_type'];
|
||||
if (type != null && type.toString().isNotEmpty) return type;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Object? _readPlantName(Map<dynamic, dynamic> json, String key) =>
|
||||
_readNestedName(json, 'plant_name', 'plant');
|
||||
|
||||
@ -139,15 +150,14 @@ class PurchaseOrderModel with _$PurchaseOrderModel {
|
||||
@JsonKey(fromJson: _idFromJson) required String id,
|
||||
@JsonKey(name: 'po_number', readValue: _readPoNumber) String? poNo,
|
||||
@JsonKey(name: 'po_date', fromJson: _dateFromJsonNullable) DateTime? poDate,
|
||||
@JsonKey(name: 'po_type') String? poType,
|
||||
@Default('DRAFT') String status,
|
||||
@JsonKey(name: 'vendor_id', fromJson: _intFromJsonNullable) int? vendorId,
|
||||
@JsonKey(name: 'vendor_name', readValue: _readVendorName) String? vendorName,
|
||||
@JsonKey(name: 'vendor_type', readValue: _readVendorType) String? vendorType,
|
||||
@JsonKey(name: 'plant_id', fromJson: _intFromJsonNullable) int? plantId,
|
||||
@JsonKey(name: 'plant_name', readValue: _readPlantName) String? plantName,
|
||||
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable) int? warehouseId,
|
||||
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName) String? warehouseName,
|
||||
@JsonKey(name: 'brand_id', fromJson: _intFromJsonNullable) int? brandId,
|
||||
@JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable) int? paymentTermId,
|
||||
@JsonKey(name: 'delivery_term_id', fromJson: _intFromJsonNullable) int? deliveryTermId,
|
||||
@JsonKey(name: 'expected_delivery_date', fromJson: _dateFromJsonNullable)
|
||||
@ -254,7 +264,6 @@ class PurchaseOrderListQuery with _$PurchaseOrderListQuery {
|
||||
@Default(20) int limit,
|
||||
String? search,
|
||||
String? status,
|
||||
String? poType,
|
||||
int? vendorId,
|
||||
int? plantId,
|
||||
String? dateFrom,
|
||||
@ -262,14 +271,6 @@ class PurchaseOrderListQuery with _$PurchaseOrderListQuery {
|
||||
}) = _PurchaseOrderListQuery;
|
||||
}
|
||||
|
||||
const poTypeOptions = [
|
||||
('RAW_MATERIAL', 'Raw Material'),
|
||||
('PACKING_MATERIAL', 'Packing Material'),
|
||||
('ASSET_CAPITAL', 'Asset / Capital'),
|
||||
('SERVICE', 'Service'),
|
||||
('GENERAL', 'General'),
|
||||
];
|
||||
|
||||
const poStatusOptions = [
|
||||
('DRAFT', 'Draft'),
|
||||
// ('SUBMITTED', 'Submitted'),
|
||||
@ -281,15 +282,6 @@ const poStatusOptions = [
|
||||
('FULLY_RECEIVED', 'Fully Received'),
|
||||
];
|
||||
|
||||
String poTypeLabel(String? value) {
|
||||
if (value == null) return '—';
|
||||
return poTypeOptions
|
||||
.where((e) => e.$1 == value)
|
||||
.map((e) => e.$2)
|
||||
.firstOrNull ??
|
||||
value;
|
||||
}
|
||||
|
||||
String poStatusLabel(String? value) {
|
||||
if (value == null) return '—';
|
||||
final normalizedKey = value.trim().toUpperCase().replaceAll(' ', '_');
|
||||
|
||||
@ -27,13 +27,13 @@ mixin _$PurchaseOrderModel {
|
||||
String? get poNo => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'po_date', fromJson: _dateFromJsonNullable)
|
||||
DateTime? get poDate => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'po_type')
|
||||
String? get poType => throw _privateConstructorUsedError;
|
||||
String get status => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'vendor_id', fromJson: _intFromJsonNullable)
|
||||
int? get vendorId => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'vendor_name', readValue: _readVendorName)
|
||||
String? get vendorName => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'vendor_type', readValue: _readVendorType)
|
||||
String? get vendorType => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'plant_id', fromJson: _intFromJsonNullable)
|
||||
int? get plantId => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'plant_name', readValue: _readPlantName)
|
||||
@ -42,8 +42,6 @@ mixin _$PurchaseOrderModel {
|
||||
int? get warehouseId => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName)
|
||||
String? get warehouseName => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'brand_id', fromJson: _intFromJsonNullable)
|
||||
int? get brandId => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable)
|
||||
int? get paymentTermId => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'delivery_term_id', fromJson: _intFromJsonNullable)
|
||||
@ -94,18 +92,18 @@ abstract class $PurchaseOrderModelCopyWith<$Res> {
|
||||
@JsonKey(fromJson: _idFromJson) String id,
|
||||
@JsonKey(name: 'po_number', readValue: _readPoNumber) String? poNo,
|
||||
@JsonKey(name: 'po_date', fromJson: _dateFromJsonNullable) DateTime? poDate,
|
||||
@JsonKey(name: 'po_type') String? poType,
|
||||
String status,
|
||||
@JsonKey(name: 'vendor_id', fromJson: _intFromJsonNullable) int? vendorId,
|
||||
@JsonKey(name: 'vendor_name', readValue: _readVendorName)
|
||||
String? vendorName,
|
||||
@JsonKey(name: 'vendor_type', readValue: _readVendorType)
|
||||
String? vendorType,
|
||||
@JsonKey(name: 'plant_id', fromJson: _intFromJsonNullable) int? plantId,
|
||||
@JsonKey(name: 'plant_name', readValue: _readPlantName) String? plantName,
|
||||
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable)
|
||||
int? warehouseId,
|
||||
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName)
|
||||
String? warehouseName,
|
||||
@JsonKey(name: 'brand_id', fromJson: _intFromJsonNullable) int? brandId,
|
||||
@JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable)
|
||||
int? paymentTermId,
|
||||
@JsonKey(name: 'delivery_term_id', fromJson: _intFromJsonNullable)
|
||||
@ -152,15 +150,14 @@ class _$PurchaseOrderModelCopyWithImpl<$Res, $Val extends PurchaseOrderModel>
|
||||
Object? id = null,
|
||||
Object? poNo = freezed,
|
||||
Object? poDate = freezed,
|
||||
Object? poType = freezed,
|
||||
Object? status = null,
|
||||
Object? vendorId = freezed,
|
||||
Object? vendorName = freezed,
|
||||
Object? vendorType = freezed,
|
||||
Object? plantId = freezed,
|
||||
Object? plantName = freezed,
|
||||
Object? warehouseId = freezed,
|
||||
Object? warehouseName = freezed,
|
||||
Object? brandId = freezed,
|
||||
Object? paymentTermId = freezed,
|
||||
Object? deliveryTermId = freezed,
|
||||
Object? expectedDeliveryDate = freezed,
|
||||
@ -191,10 +188,6 @@ class _$PurchaseOrderModelCopyWithImpl<$Res, $Val extends PurchaseOrderModel>
|
||||
? _value.poDate
|
||||
: poDate // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
poType: freezed == poType
|
||||
? _value.poType
|
||||
: poType // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
status: null == status
|
||||
? _value.status
|
||||
: status // ignore: cast_nullable_to_non_nullable
|
||||
@ -207,6 +200,10 @@ class _$PurchaseOrderModelCopyWithImpl<$Res, $Val extends PurchaseOrderModel>
|
||||
? _value.vendorName
|
||||
: vendorName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
vendorType: freezed == vendorType
|
||||
? _value.vendorType
|
||||
: vendorType // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
plantId: freezed == plantId
|
||||
? _value.plantId
|
||||
: plantId // ignore: cast_nullable_to_non_nullable
|
||||
@ -223,10 +220,6 @@ class _$PurchaseOrderModelCopyWithImpl<$Res, $Val extends PurchaseOrderModel>
|
||||
? _value.warehouseName
|
||||
: warehouseName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
brandId: freezed == brandId
|
||||
? _value.brandId
|
||||
: brandId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
paymentTermId: freezed == paymentTermId
|
||||
? _value.paymentTermId
|
||||
: paymentTermId // ignore: cast_nullable_to_non_nullable
|
||||
@ -306,18 +299,18 @@ abstract class _$$PurchaseOrderModelImplCopyWith<$Res>
|
||||
@JsonKey(fromJson: _idFromJson) String id,
|
||||
@JsonKey(name: 'po_number', readValue: _readPoNumber) String? poNo,
|
||||
@JsonKey(name: 'po_date', fromJson: _dateFromJsonNullable) DateTime? poDate,
|
||||
@JsonKey(name: 'po_type') String? poType,
|
||||
String status,
|
||||
@JsonKey(name: 'vendor_id', fromJson: _intFromJsonNullable) int? vendorId,
|
||||
@JsonKey(name: 'vendor_name', readValue: _readVendorName)
|
||||
String? vendorName,
|
||||
@JsonKey(name: 'vendor_type', readValue: _readVendorType)
|
||||
String? vendorType,
|
||||
@JsonKey(name: 'plant_id', fromJson: _intFromJsonNullable) int? plantId,
|
||||
@JsonKey(name: 'plant_name', readValue: _readPlantName) String? plantName,
|
||||
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable)
|
||||
int? warehouseId,
|
||||
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName)
|
||||
String? warehouseName,
|
||||
@JsonKey(name: 'brand_id', fromJson: _intFromJsonNullable) int? brandId,
|
||||
@JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable)
|
||||
int? paymentTermId,
|
||||
@JsonKey(name: 'delivery_term_id', fromJson: _intFromJsonNullable)
|
||||
@ -363,15 +356,14 @@ class __$$PurchaseOrderModelImplCopyWithImpl<$Res>
|
||||
Object? id = null,
|
||||
Object? poNo = freezed,
|
||||
Object? poDate = freezed,
|
||||
Object? poType = freezed,
|
||||
Object? status = null,
|
||||
Object? vendorId = freezed,
|
||||
Object? vendorName = freezed,
|
||||
Object? vendorType = freezed,
|
||||
Object? plantId = freezed,
|
||||
Object? plantName = freezed,
|
||||
Object? warehouseId = freezed,
|
||||
Object? warehouseName = freezed,
|
||||
Object? brandId = freezed,
|
||||
Object? paymentTermId = freezed,
|
||||
Object? deliveryTermId = freezed,
|
||||
Object? expectedDeliveryDate = freezed,
|
||||
@ -402,10 +394,6 @@ class __$$PurchaseOrderModelImplCopyWithImpl<$Res>
|
||||
? _value.poDate
|
||||
: poDate // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
poType: freezed == poType
|
||||
? _value.poType
|
||||
: poType // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
status: null == status
|
||||
? _value.status
|
||||
: status // ignore: cast_nullable_to_non_nullable
|
||||
@ -418,6 +406,10 @@ class __$$PurchaseOrderModelImplCopyWithImpl<$Res>
|
||||
? _value.vendorName
|
||||
: vendorName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
vendorType: freezed == vendorType
|
||||
? _value.vendorType
|
||||
: vendorType // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
plantId: freezed == plantId
|
||||
? _value.plantId
|
||||
: plantId // ignore: cast_nullable_to_non_nullable
|
||||
@ -434,10 +426,6 @@ class __$$PurchaseOrderModelImplCopyWithImpl<$Res>
|
||||
? _value.warehouseName
|
||||
: warehouseName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
brandId: freezed == brandId
|
||||
? _value.brandId
|
||||
: brandId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
paymentTermId: freezed == paymentTermId
|
||||
? _value.paymentTermId
|
||||
: paymentTermId // ignore: cast_nullable_to_non_nullable
|
||||
@ -510,17 +498,16 @@ class _$PurchaseOrderModelImpl extends _PurchaseOrderModel {
|
||||
@JsonKey(fromJson: _idFromJson) required this.id,
|
||||
@JsonKey(name: 'po_number', readValue: _readPoNumber) this.poNo,
|
||||
@JsonKey(name: 'po_date', fromJson: _dateFromJsonNullable) this.poDate,
|
||||
@JsonKey(name: 'po_type') this.poType,
|
||||
this.status = 'DRAFT',
|
||||
@JsonKey(name: 'vendor_id', fromJson: _intFromJsonNullable) this.vendorId,
|
||||
@JsonKey(name: 'vendor_name', readValue: _readVendorName) this.vendorName,
|
||||
@JsonKey(name: 'vendor_type', readValue: _readVendorType) this.vendorType,
|
||||
@JsonKey(name: 'plant_id', fromJson: _intFromJsonNullable) this.plantId,
|
||||
@JsonKey(name: 'plant_name', readValue: _readPlantName) this.plantName,
|
||||
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable)
|
||||
this.warehouseId,
|
||||
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName)
|
||||
this.warehouseName,
|
||||
@JsonKey(name: 'brand_id', fromJson: _intFromJsonNullable) this.brandId,
|
||||
@JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable)
|
||||
this.paymentTermId,
|
||||
@JsonKey(name: 'delivery_term_id', fromJson: _intFromJsonNullable)
|
||||
@ -561,9 +548,6 @@ class _$PurchaseOrderModelImpl extends _PurchaseOrderModel {
|
||||
@JsonKey(name: 'po_date', fromJson: _dateFromJsonNullable)
|
||||
final DateTime? poDate;
|
||||
@override
|
||||
@JsonKey(name: 'po_type')
|
||||
final String? poType;
|
||||
@override
|
||||
@JsonKey()
|
||||
final String status;
|
||||
@override
|
||||
@ -573,6 +557,9 @@ class _$PurchaseOrderModelImpl extends _PurchaseOrderModel {
|
||||
@JsonKey(name: 'vendor_name', readValue: _readVendorName)
|
||||
final String? vendorName;
|
||||
@override
|
||||
@JsonKey(name: 'vendor_type', readValue: _readVendorType)
|
||||
final String? vendorType;
|
||||
@override
|
||||
@JsonKey(name: 'plant_id', fromJson: _intFromJsonNullable)
|
||||
final int? plantId;
|
||||
@override
|
||||
@ -585,9 +572,6 @@ class _$PurchaseOrderModelImpl extends _PurchaseOrderModel {
|
||||
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName)
|
||||
final String? warehouseName;
|
||||
@override
|
||||
@JsonKey(name: 'brand_id', fromJson: _intFromJsonNullable)
|
||||
final int? brandId;
|
||||
@override
|
||||
@JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable)
|
||||
final int? paymentTermId;
|
||||
@override
|
||||
@ -639,7 +623,7 @@ class _$PurchaseOrderModelImpl extends _PurchaseOrderModel {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'PurchaseOrderModel(id: $id, poNo: $poNo, poDate: $poDate, poType: $poType, status: $status, vendorId: $vendorId, vendorName: $vendorName, plantId: $plantId, plantName: $plantName, warehouseId: $warehouseId, warehouseName: $warehouseName, brandId: $brandId, 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, 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)';
|
||||
}
|
||||
|
||||
@override
|
||||
@ -650,12 +634,13 @@ class _$PurchaseOrderModelImpl extends _PurchaseOrderModel {
|
||||
(identical(other.id, id) || other.id == id) &&
|
||||
(identical(other.poNo, poNo) || other.poNo == poNo) &&
|
||||
(identical(other.poDate, poDate) || other.poDate == poDate) &&
|
||||
(identical(other.poType, poType) || other.poType == poType) &&
|
||||
(identical(other.status, status) || other.status == status) &&
|
||||
(identical(other.vendorId, vendorId) ||
|
||||
other.vendorId == vendorId) &&
|
||||
(identical(other.vendorName, vendorName) ||
|
||||
other.vendorName == vendorName) &&
|
||||
(identical(other.vendorType, vendorType) ||
|
||||
other.vendorType == vendorType) &&
|
||||
(identical(other.plantId, plantId) || other.plantId == plantId) &&
|
||||
(identical(other.plantName, plantName) ||
|
||||
other.plantName == plantName) &&
|
||||
@ -663,7 +648,6 @@ class _$PurchaseOrderModelImpl extends _PurchaseOrderModel {
|
||||
other.warehouseId == warehouseId) &&
|
||||
(identical(other.warehouseName, warehouseName) ||
|
||||
other.warehouseName == warehouseName) &&
|
||||
(identical(other.brandId, brandId) || other.brandId == brandId) &&
|
||||
(identical(other.paymentTermId, paymentTermId) ||
|
||||
other.paymentTermId == paymentTermId) &&
|
||||
(identical(other.deliveryTermId, deliveryTermId) ||
|
||||
@ -701,15 +685,14 @@ class _$PurchaseOrderModelImpl extends _PurchaseOrderModel {
|
||||
id,
|
||||
poNo,
|
||||
poDate,
|
||||
poType,
|
||||
status,
|
||||
vendorId,
|
||||
vendorName,
|
||||
vendorType,
|
||||
plantId,
|
||||
plantName,
|
||||
warehouseId,
|
||||
warehouseName,
|
||||
brandId,
|
||||
paymentTermId,
|
||||
deliveryTermId,
|
||||
expectedDeliveryDate,
|
||||
@ -750,12 +733,13 @@ abstract class _PurchaseOrderModel extends PurchaseOrderModel {
|
||||
@JsonKey(name: 'po_number', readValue: _readPoNumber) final String? poNo,
|
||||
@JsonKey(name: 'po_date', fromJson: _dateFromJsonNullable)
|
||||
final DateTime? poDate,
|
||||
@JsonKey(name: 'po_type') final String? poType,
|
||||
final String status,
|
||||
@JsonKey(name: 'vendor_id', fromJson: _intFromJsonNullable)
|
||||
final int? vendorId,
|
||||
@JsonKey(name: 'vendor_name', readValue: _readVendorName)
|
||||
final String? vendorName,
|
||||
@JsonKey(name: 'vendor_type', readValue: _readVendorType)
|
||||
final String? vendorType,
|
||||
@JsonKey(name: 'plant_id', fromJson: _intFromJsonNullable)
|
||||
final int? plantId,
|
||||
@JsonKey(name: 'plant_name', readValue: _readPlantName)
|
||||
@ -764,8 +748,6 @@ abstract class _PurchaseOrderModel extends PurchaseOrderModel {
|
||||
final int? warehouseId,
|
||||
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName)
|
||||
final String? warehouseName,
|
||||
@JsonKey(name: 'brand_id', fromJson: _intFromJsonNullable)
|
||||
final int? brandId,
|
||||
@JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable)
|
||||
final int? paymentTermId,
|
||||
@JsonKey(name: 'delivery_term_id', fromJson: _intFromJsonNullable)
|
||||
@ -809,9 +791,6 @@ abstract class _PurchaseOrderModel extends PurchaseOrderModel {
|
||||
@JsonKey(name: 'po_date', fromJson: _dateFromJsonNullable)
|
||||
DateTime? get poDate;
|
||||
@override
|
||||
@JsonKey(name: 'po_type')
|
||||
String? get poType;
|
||||
@override
|
||||
String get status;
|
||||
@override
|
||||
@JsonKey(name: 'vendor_id', fromJson: _intFromJsonNullable)
|
||||
@ -820,6 +799,9 @@ abstract class _PurchaseOrderModel extends PurchaseOrderModel {
|
||||
@JsonKey(name: 'vendor_name', readValue: _readVendorName)
|
||||
String? get vendorName;
|
||||
@override
|
||||
@JsonKey(name: 'vendor_type', readValue: _readVendorType)
|
||||
String? get vendorType;
|
||||
@override
|
||||
@JsonKey(name: 'plant_id', fromJson: _intFromJsonNullable)
|
||||
int? get plantId;
|
||||
@override
|
||||
@ -832,9 +814,6 @@ abstract class _PurchaseOrderModel extends PurchaseOrderModel {
|
||||
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName)
|
||||
String? get warehouseName;
|
||||
@override
|
||||
@JsonKey(name: 'brand_id', fromJson: _intFromJsonNullable)
|
||||
int? get brandId;
|
||||
@override
|
||||
@JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable)
|
||||
int? get paymentTermId;
|
||||
@override
|
||||
@ -1662,7 +1641,6 @@ mixin _$PurchaseOrderListQuery {
|
||||
int get limit => throw _privateConstructorUsedError;
|
||||
String? get search => throw _privateConstructorUsedError;
|
||||
String? get status => throw _privateConstructorUsedError;
|
||||
String? get poType => throw _privateConstructorUsedError;
|
||||
int? get vendorId => throw _privateConstructorUsedError;
|
||||
int? get plantId => throw _privateConstructorUsedError;
|
||||
String? get dateFrom => throw _privateConstructorUsedError;
|
||||
@ -1687,7 +1665,6 @@ abstract class $PurchaseOrderListQueryCopyWith<$Res> {
|
||||
int limit,
|
||||
String? search,
|
||||
String? status,
|
||||
String? poType,
|
||||
int? vendorId,
|
||||
int? plantId,
|
||||
String? dateFrom,
|
||||
@ -1717,7 +1694,6 @@ class _$PurchaseOrderListQueryCopyWithImpl<
|
||||
Object? limit = null,
|
||||
Object? search = freezed,
|
||||
Object? status = freezed,
|
||||
Object? poType = freezed,
|
||||
Object? vendorId = freezed,
|
||||
Object? plantId = freezed,
|
||||
Object? dateFrom = freezed,
|
||||
@ -1741,10 +1717,6 @@ class _$PurchaseOrderListQueryCopyWithImpl<
|
||||
? _value.status
|
||||
: status // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
poType: freezed == poType
|
||||
? _value.poType
|
||||
: poType // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
vendorId: freezed == vendorId
|
||||
? _value.vendorId
|
||||
: vendorId // ignore: cast_nullable_to_non_nullable
|
||||
@ -1781,7 +1753,6 @@ abstract class _$$PurchaseOrderListQueryImplCopyWith<$Res>
|
||||
int limit,
|
||||
String? search,
|
||||
String? status,
|
||||
String? poType,
|
||||
int? vendorId,
|
||||
int? plantId,
|
||||
String? dateFrom,
|
||||
@ -1808,7 +1779,6 @@ class __$$PurchaseOrderListQueryImplCopyWithImpl<$Res>
|
||||
Object? limit = null,
|
||||
Object? search = freezed,
|
||||
Object? status = freezed,
|
||||
Object? poType = freezed,
|
||||
Object? vendorId = freezed,
|
||||
Object? plantId = freezed,
|
||||
Object? dateFrom = freezed,
|
||||
@ -1832,10 +1802,6 @@ class __$$PurchaseOrderListQueryImplCopyWithImpl<$Res>
|
||||
? _value.status
|
||||
: status // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
poType: freezed == poType
|
||||
? _value.poType
|
||||
: poType // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
vendorId: freezed == vendorId
|
||||
? _value.vendorId
|
||||
: vendorId // ignore: cast_nullable_to_non_nullable
|
||||
@ -1865,7 +1831,6 @@ class _$PurchaseOrderListQueryImpl implements _PurchaseOrderListQuery {
|
||||
this.limit = 20,
|
||||
this.search,
|
||||
this.status,
|
||||
this.poType,
|
||||
this.vendorId,
|
||||
this.plantId,
|
||||
this.dateFrom,
|
||||
@ -1883,8 +1848,6 @@ class _$PurchaseOrderListQueryImpl implements _PurchaseOrderListQuery {
|
||||
@override
|
||||
final String? status;
|
||||
@override
|
||||
final String? poType;
|
||||
@override
|
||||
final int? vendorId;
|
||||
@override
|
||||
final int? plantId;
|
||||
@ -1895,7 +1858,7 @@ class _$PurchaseOrderListQueryImpl implements _PurchaseOrderListQuery {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'PurchaseOrderListQuery(page: $page, limit: $limit, search: $search, status: $status, poType: $poType, vendorId: $vendorId, plantId: $plantId, dateFrom: $dateFrom, dateTo: $dateTo)';
|
||||
return 'PurchaseOrderListQuery(page: $page, limit: $limit, search: $search, status: $status, vendorId: $vendorId, plantId: $plantId, dateFrom: $dateFrom, dateTo: $dateTo)';
|
||||
}
|
||||
|
||||
@override
|
||||
@ -1907,7 +1870,6 @@ class _$PurchaseOrderListQueryImpl implements _PurchaseOrderListQuery {
|
||||
(identical(other.limit, limit) || other.limit == limit) &&
|
||||
(identical(other.search, search) || other.search == search) &&
|
||||
(identical(other.status, status) || other.status == status) &&
|
||||
(identical(other.poType, poType) || other.poType == poType) &&
|
||||
(identical(other.vendorId, vendorId) ||
|
||||
other.vendorId == vendorId) &&
|
||||
(identical(other.plantId, plantId) || other.plantId == plantId) &&
|
||||
@ -1923,7 +1885,6 @@ class _$PurchaseOrderListQueryImpl implements _PurchaseOrderListQuery {
|
||||
limit,
|
||||
search,
|
||||
status,
|
||||
poType,
|
||||
vendorId,
|
||||
plantId,
|
||||
dateFrom,
|
||||
@ -1949,7 +1910,6 @@ abstract class _PurchaseOrderListQuery implements PurchaseOrderListQuery {
|
||||
final int limit,
|
||||
final String? search,
|
||||
final String? status,
|
||||
final String? poType,
|
||||
final int? vendorId,
|
||||
final int? plantId,
|
||||
final String? dateFrom,
|
||||
@ -1965,8 +1925,6 @@ abstract class _PurchaseOrderListQuery implements PurchaseOrderListQuery {
|
||||
@override
|
||||
String? get status;
|
||||
@override
|
||||
String? get poType;
|
||||
@override
|
||||
int? get vendorId;
|
||||
@override
|
||||
int? get plantId;
|
||||
|
||||
@ -12,15 +12,14 @@ _$PurchaseOrderModelImpl _$$PurchaseOrderModelImplFromJson(
|
||||
id: _idFromJson(json['id']),
|
||||
poNo: _readPoNumber(json, 'po_number') as String?,
|
||||
poDate: _dateFromJsonNullable(json['po_date']),
|
||||
poType: json['po_type'] as String?,
|
||||
status: json['status'] as String? ?? 'DRAFT',
|
||||
vendorId: _intFromJsonNullable(json['vendor_id']),
|
||||
vendorName: _readVendorName(json, 'vendor_name') as String?,
|
||||
vendorType: _readVendorType(json, 'vendor_type') as String?,
|
||||
plantId: _intFromJsonNullable(json['plant_id']),
|
||||
plantName: _readPlantName(json, 'plant_name') as String?,
|
||||
warehouseId: _intFromJsonNullable(json['warehouse_id']),
|
||||
warehouseName: _readWarehouseName(json, 'warehouse_name') as String?,
|
||||
brandId: _intFromJsonNullable(json['brand_id']),
|
||||
paymentTermId: _intFromJsonNullable(json['payment_term_id']),
|
||||
deliveryTermId: _intFromJsonNullable(json['delivery_term_id']),
|
||||
expectedDeliveryDate: _dateFromJsonNullable(json['expected_delivery_date']),
|
||||
@ -50,15 +49,14 @@ Map<String, dynamic> _$$PurchaseOrderModelImplToJson(
|
||||
'id': instance.id,
|
||||
'po_number': instance.poNo,
|
||||
'po_date': instance.poDate?.toIso8601String(),
|
||||
'po_type': instance.poType,
|
||||
'status': instance.status,
|
||||
'vendor_id': instance.vendorId,
|
||||
'vendor_name': instance.vendorName,
|
||||
'vendor_type': instance.vendorType,
|
||||
'plant_id': instance.plantId,
|
||||
'plant_name': instance.plantName,
|
||||
'warehouse_id': instance.warehouseId,
|
||||
'warehouse_name': instance.warehouseName,
|
||||
'brand_id': instance.brandId,
|
||||
'payment_term_id': instance.paymentTermId,
|
||||
'delivery_term_id': instance.deliveryTermId,
|
||||
'expected_delivery_date': instance.expectedDeliveryDate?.toIso8601String(),
|
||||
|
||||
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../core/utils/validators.dart';
|
||||
import '../../modules/assets/data/repositories/asset_repository_impl.dart';
|
||||
import '../../modules/master_data/data/repositories/master_repository_impl.dart';
|
||||
import '../../modules/master_data/domain/entities/master_definition.dart';
|
||||
import 'app_form_toggle_field.dart';
|
||||
@ -77,17 +78,46 @@ class _MasterInlineQuickAddFormState
|
||||
|
||||
Future<void> _loadOptions() async {
|
||||
final options = <String, List<Map<String, dynamic>>>{};
|
||||
final keys = _definition.formFields
|
||||
.where((f) => f.optionsMasterKey != null && f.staticOptions == null)
|
||||
.map((f) => f.optionsMasterKey!)
|
||||
.toSet();
|
||||
final fields = _definition.formFields
|
||||
.where((f) => f.optionsMasterKey != null && f.staticOptions == null);
|
||||
|
||||
for (final field in fields) {
|
||||
final key = field.optionsMasterKey!;
|
||||
final lookupKey = masterFieldDropdownLookupKey(
|
||||
masterId: widget.masterId,
|
||||
field: field,
|
||||
values: _values,
|
||||
);
|
||||
if (options.containsKey(lookupKey)) continue;
|
||||
|
||||
if (key == 'asset_depreciation_methods') {
|
||||
final result =
|
||||
await ref.read(assetRepositoryProvider).getDepreciationMethods();
|
||||
if (result.failure != null) continue;
|
||||
options[lookupKey] = (result.data ?? const [])
|
||||
.where((option) => option.value.trim().isNotEmpty)
|
||||
.map(
|
||||
(option) => <String, dynamic>{
|
||||
'id': option.value,
|
||||
'name': option.label,
|
||||
},
|
||||
)
|
||||
.toList();
|
||||
continue;
|
||||
}
|
||||
|
||||
for (final key in keys) {
|
||||
final def = masterDefinitionById(key);
|
||||
if (def == null) continue;
|
||||
final result = await ref.read(masterRepositoryProvider).listOptions(def);
|
||||
final result = await ref.read(masterRepositoryProvider).listOptions(
|
||||
def,
|
||||
queryParameters: masterFieldOptionsQuery(
|
||||
masterId: widget.masterId,
|
||||
field: field,
|
||||
values: _values,
|
||||
),
|
||||
);
|
||||
if (result.failure == null) {
|
||||
options[key] = result.data ?? const [];
|
||||
options[lookupKey] = result.data ?? const [];
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,9 +128,23 @@ class _MasterInlineQuickAddFormState
|
||||
});
|
||||
}
|
||||
|
||||
void _updateValue(String key, dynamic value) {
|
||||
setState(() {
|
||||
_values[key] = value;
|
||||
if (key == 'category_type') {
|
||||
for (final field in _definition.formFields) {
|
||||
if (field.isVisibleInForm(_values)) continue;
|
||||
_values[field.key] = null;
|
||||
_controllers[field.key]?.text = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Map<String, dynamic> _buildPayload() {
|
||||
final payload = <String, dynamic>{};
|
||||
for (final field in _definition.formFields) {
|
||||
if (!field.isVisibleInForm(_values)) continue;
|
||||
var value = _values[field.key];
|
||||
if (field.type == MasterFieldType.text ||
|
||||
field.type == MasterFieldType.number) {
|
||||
@ -109,7 +153,11 @@ class _MasterInlineQuickAddFormState
|
||||
if (value == null || value == '') continue;
|
||||
payload[field.key] = switch (field.type) {
|
||||
MasterFieldType.number => num.tryParse(value.toString()) ?? value,
|
||||
MasterFieldType.dropdown => int.tryParse(value.toString()) ?? value,
|
||||
MasterFieldType.dropdown => field.staticOptions != null
|
||||
? value.toString()
|
||||
: (field.optionsMasterKey == 'asset_depreciation_methods'
|
||||
? value.toString()
|
||||
: int.tryParse(value.toString()) ?? value),
|
||||
MasterFieldType.boolean => value == true,
|
||||
MasterFieldType.text => value.toString().trim(),
|
||||
};
|
||||
@ -157,8 +205,13 @@ class _MasterInlineQuickAddFormState
|
||||
.map((v) => <String, dynamic>{'id': v, 'name': v})
|
||||
.toList();
|
||||
}
|
||||
final lookupKey = masterFieldDropdownLookupKey(
|
||||
masterId: widget.masterId,
|
||||
field: field,
|
||||
values: _values,
|
||||
);
|
||||
var options =
|
||||
_dropdownOptions[field.optionsMasterKey] ?? const <Map<String, dynamic>>[];
|
||||
_dropdownOptions[lookupKey] ?? const <Map<String, dynamic>>[];
|
||||
final filterField = field.filterByFieldKey;
|
||||
if (filterField != null) {
|
||||
final parentId = _values[filterField]?.toString();
|
||||
@ -182,7 +235,7 @@ class _MasterInlineQuickAddFormState
|
||||
value: _values[field.key] == true,
|
||||
onChanged: _submitting
|
||||
? null
|
||||
: (v) => setState(() => _values[field.key] = v),
|
||||
: (v) => _updateValue(field.key, v),
|
||||
);
|
||||
}
|
||||
return CheckboxListTile(
|
||||
@ -192,7 +245,7 @@ class _MasterInlineQuickAddFormState
|
||||
value: _values[field.key] == true,
|
||||
onChanged: _submitting
|
||||
? null
|
||||
: (v) => setState(() => _values[field.key] = v ?? false),
|
||||
: (v) => _updateValue(field.key, v ?? false),
|
||||
);
|
||||
|
||||
case MasterFieldType.dropdown:
|
||||
@ -221,7 +274,7 @@ class _MasterInlineQuickAddFormState
|
||||
],
|
||||
onChanged: (_submitting || prefilled)
|
||||
? null
|
||||
: (v) => setState(() => _values[field.key] = v),
|
||||
: (v) => _updateValue(field.key, v),
|
||||
validator: field.required
|
||||
? (v) => v == null ? '${field.label} is required' : null
|
||||
: null,
|
||||
@ -252,9 +305,12 @@ class _MasterInlineQuickAddFormState
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final fields = _definition.formFields;
|
||||
final activeField = fields.where((field) => field.key == 'is_active').firstOrNull;
|
||||
final regularFields =
|
||||
fields.where((field) => field.key != 'is_active').toList();
|
||||
final activeField =
|
||||
fields.where((field) => field.key == 'is_active').firstOrNull;
|
||||
final regularFields = fields
|
||||
.where((field) => field.key != 'is_active')
|
||||
.where((field) => field.isVisibleInForm(_values))
|
||||
.toList();
|
||||
|
||||
return Material(
|
||||
color: theme.colorScheme.surfaceContainerHighest.withValues(alpha: 0.45),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user