feat(inventory): 재고 현황 요약/상세 플로우를 릴리스
- lib/features/inventory/summary 계층과 warehouse select 위젯을 추가해 목록/상세, 자동 새로고침, 필터, 상세 시트를 구현 - PermissionBootstrapper, scope 파서, 라우트 가드로 inventory.view 기반 권한 부여와 메뉴 노출을 통합(lib/core, lib/main.dart 등) - Inventory Summary API/QA/Audit 문서와 PR 템플릿, CHANGELOG를 신규 스펙과 검증 커맨드로 업데이트 - DTO 직렬화 의존성을 추가하고 Golden·Widget·단위 테스트를 작성했으며 flutter analyze / flutter test --coverage를 통과
This commit is contained in:
@@ -0,0 +1,249 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
import '../../domain/entities/inventory_counterparty.dart';
|
||||
import '../../domain/entities/inventory_event.dart';
|
||||
import '../../domain/entities/inventory_product.dart';
|
||||
import '../../domain/entities/inventory_transaction_reference.dart';
|
||||
import '../../domain/entities/inventory_vendor.dart';
|
||||
import '../../domain/entities/inventory_warehouse.dart';
|
||||
import '../../domain/entities/inventory_warehouse_balance.dart';
|
||||
|
||||
part 'inventory_common_dtos.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake, explicitToJson: true)
|
||||
class InventoryVendorDto {
|
||||
const InventoryVendorDto({this.id, this.vendorName});
|
||||
|
||||
final int? id;
|
||||
final String? vendorName;
|
||||
|
||||
factory InventoryVendorDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$InventoryVendorDtoFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$InventoryVendorDtoToJson(this);
|
||||
|
||||
InventoryVendor toEntity() {
|
||||
return InventoryVendor(id: id, name: (vendorName ?? '').trim());
|
||||
}
|
||||
}
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake, explicitToJson: true)
|
||||
class InventoryProductDto {
|
||||
const InventoryProductDto({
|
||||
required this.id,
|
||||
this.productCode,
|
||||
this.productName,
|
||||
this.vendor,
|
||||
});
|
||||
|
||||
final int id;
|
||||
final String? productCode;
|
||||
final String? productName;
|
||||
final InventoryVendorDto? vendor;
|
||||
|
||||
factory InventoryProductDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$InventoryProductDtoFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$InventoryProductDtoToJson(this);
|
||||
|
||||
InventoryProduct toEntity() {
|
||||
return InventoryProduct(
|
||||
id: id,
|
||||
code: (productCode ?? '').trim(),
|
||||
name: (productName ?? '').trim(),
|
||||
vendor: vendor?.toEntity(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake, explicitToJson: true)
|
||||
class InventoryWarehouseDto {
|
||||
const InventoryWarehouseDto({
|
||||
required this.id,
|
||||
this.warehouseCode,
|
||||
this.warehouseName,
|
||||
});
|
||||
|
||||
final int id;
|
||||
final String? warehouseCode;
|
||||
final String? warehouseName;
|
||||
|
||||
factory InventoryWarehouseDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$InventoryWarehouseDtoFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$InventoryWarehouseDtoToJson(this);
|
||||
|
||||
InventoryWarehouse toEntity() => InventoryWarehouse(
|
||||
id: id,
|
||||
code: (warehouseCode ?? '').trim(),
|
||||
name: (warehouseName ?? '').trim(),
|
||||
);
|
||||
}
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake, explicitToJson: true)
|
||||
class InventoryWarehouseBalanceDto {
|
||||
const InventoryWarehouseBalanceDto({
|
||||
required this.warehouse,
|
||||
required this.quantity,
|
||||
});
|
||||
|
||||
final InventoryWarehouseDto warehouse;
|
||||
@JsonKey(fromJson: _parseQuantity)
|
||||
final int quantity;
|
||||
|
||||
factory InventoryWarehouseBalanceDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$InventoryWarehouseBalanceDtoFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$InventoryWarehouseBalanceDtoToJson(this);
|
||||
|
||||
InventoryWarehouseBalance toEntity() => InventoryWarehouseBalance(
|
||||
warehouse: warehouse.toEntity(),
|
||||
quantity: quantity,
|
||||
);
|
||||
}
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class InventoryCounterpartyDto {
|
||||
const InventoryCounterpartyDto({this.type, this.name});
|
||||
|
||||
final String? type;
|
||||
final String? name;
|
||||
|
||||
factory InventoryCounterpartyDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$InventoryCounterpartyDtoFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$InventoryCounterpartyDtoToJson(this);
|
||||
|
||||
InventoryCounterparty toEntity() {
|
||||
final normalized = (type ?? '').toLowerCase();
|
||||
InventoryCounterpartyType resolvedType;
|
||||
switch (normalized) {
|
||||
case 'vendor':
|
||||
resolvedType = InventoryCounterpartyType.vendor;
|
||||
break;
|
||||
case 'customer':
|
||||
resolvedType = InventoryCounterpartyType.customer;
|
||||
break;
|
||||
default:
|
||||
resolvedType = InventoryCounterpartyType.unknown;
|
||||
break;
|
||||
}
|
||||
return InventoryCounterparty(type: resolvedType, name: name?.trim());
|
||||
}
|
||||
}
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class InventoryTransactionRefDto {
|
||||
const InventoryTransactionRefDto({required this.id, this.transactionNo});
|
||||
|
||||
final int id;
|
||||
final String? transactionNo;
|
||||
|
||||
factory InventoryTransactionRefDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$InventoryTransactionRefDtoFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$InventoryTransactionRefDtoToJson(this);
|
||||
|
||||
InventoryTransactionReference toEntity() => InventoryTransactionReference(
|
||||
id: id,
|
||||
transactionNo: (transactionNo ?? '').trim(),
|
||||
);
|
||||
}
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class InventoryEventLineRefDto {
|
||||
const InventoryEventLineRefDto({
|
||||
required this.id,
|
||||
this.lineNo,
|
||||
this.quantity,
|
||||
});
|
||||
|
||||
final int id;
|
||||
final int? lineNo;
|
||||
@JsonKey(fromJson: _parseNullableQuantity)
|
||||
final int? quantity;
|
||||
|
||||
factory InventoryEventLineRefDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$InventoryEventLineRefDtoFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$InventoryEventLineRefDtoToJson(this);
|
||||
|
||||
InventoryEventLineReference toEntity() => InventoryEventLineReference(
|
||||
id: id,
|
||||
lineNo: lineNo ?? 0,
|
||||
quantity: quantity ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake, explicitToJson: true)
|
||||
class InventoryEventDto {
|
||||
const InventoryEventDto({
|
||||
required this.eventId,
|
||||
required this.eventKind,
|
||||
required this.eventLabel,
|
||||
required this.deltaQuantity,
|
||||
required this.occurredAt,
|
||||
this.counterparty,
|
||||
this.warehouse,
|
||||
this.transaction,
|
||||
this.line,
|
||||
});
|
||||
|
||||
final int eventId;
|
||||
final String eventKind;
|
||||
final String eventLabel;
|
||||
@JsonKey(fromJson: _parseQuantity)
|
||||
final int deltaQuantity;
|
||||
final DateTime occurredAt;
|
||||
final InventoryCounterpartyDto? counterparty;
|
||||
final InventoryWarehouseDto? warehouse;
|
||||
final InventoryTransactionRefDto? transaction;
|
||||
final InventoryEventLineRefDto? line;
|
||||
|
||||
factory InventoryEventDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$InventoryEventDtoFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$InventoryEventDtoToJson(this);
|
||||
|
||||
InventoryEvent toEntity() => InventoryEvent(
|
||||
eventId: eventId,
|
||||
eventKind: eventKind,
|
||||
eventLabel: eventLabel,
|
||||
deltaQuantity: deltaQuantity,
|
||||
occurredAt: occurredAt,
|
||||
counterparty: counterparty?.toEntity(),
|
||||
warehouse: warehouse?.toEntity(),
|
||||
transaction: transaction?.toEntity(),
|
||||
line: line?.toEntity(),
|
||||
);
|
||||
}
|
||||
|
||||
int _parseQuantity(dynamic value) {
|
||||
if (value == null) {
|
||||
return 0;
|
||||
}
|
||||
if (value is int) {
|
||||
return value;
|
||||
}
|
||||
if (value is num) {
|
||||
return value.round();
|
||||
}
|
||||
if (value is String) {
|
||||
final sanitized = value.replaceAll(',', '').trim();
|
||||
if (sanitized.isEmpty) {
|
||||
return 0;
|
||||
}
|
||||
final parsed = num.tryParse(sanitized);
|
||||
if (parsed != null) {
|
||||
return parsed.round();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int? _parseNullableQuantity(dynamic value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return _parseQuantity(value);
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'inventory_common_dtos.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
InventoryVendorDto _$InventoryVendorDtoFromJson(Map<String, dynamic> json) =>
|
||||
InventoryVendorDto(
|
||||
id: (json['id'] as num?)?.toInt(),
|
||||
vendorName: json['vendor_name'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$InventoryVendorDtoToJson(InventoryVendorDto instance) =>
|
||||
<String, dynamic>{'id': instance.id, 'vendor_name': instance.vendorName};
|
||||
|
||||
InventoryProductDto _$InventoryProductDtoFromJson(Map<String, dynamic> json) =>
|
||||
InventoryProductDto(
|
||||
id: (json['id'] as num).toInt(),
|
||||
productCode: json['product_code'] as String?,
|
||||
productName: json['product_name'] as String?,
|
||||
vendor: json['vendor'] == null
|
||||
? null
|
||||
: InventoryVendorDto.fromJson(json['vendor'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$InventoryProductDtoToJson(
|
||||
InventoryProductDto instance,
|
||||
) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'product_code': instance.productCode,
|
||||
'product_name': instance.productName,
|
||||
'vendor': instance.vendor?.toJson(),
|
||||
};
|
||||
|
||||
InventoryWarehouseDto _$InventoryWarehouseDtoFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => InventoryWarehouseDto(
|
||||
id: (json['id'] as num).toInt(),
|
||||
warehouseCode: json['warehouse_code'] as String?,
|
||||
warehouseName: json['warehouse_name'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$InventoryWarehouseDtoToJson(
|
||||
InventoryWarehouseDto instance,
|
||||
) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'warehouse_code': instance.warehouseCode,
|
||||
'warehouse_name': instance.warehouseName,
|
||||
};
|
||||
|
||||
InventoryWarehouseBalanceDto _$InventoryWarehouseBalanceDtoFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => InventoryWarehouseBalanceDto(
|
||||
warehouse: InventoryWarehouseDto.fromJson(
|
||||
json['warehouse'] as Map<String, dynamic>,
|
||||
),
|
||||
quantity: _parseQuantity(json['quantity']),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$InventoryWarehouseBalanceDtoToJson(
|
||||
InventoryWarehouseBalanceDto instance,
|
||||
) => <String, dynamic>{
|
||||
'warehouse': instance.warehouse.toJson(),
|
||||
'quantity': instance.quantity,
|
||||
};
|
||||
|
||||
InventoryCounterpartyDto _$InventoryCounterpartyDtoFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => InventoryCounterpartyDto(
|
||||
type: json['type'] as String?,
|
||||
name: json['name'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$InventoryCounterpartyDtoToJson(
|
||||
InventoryCounterpartyDto instance,
|
||||
) => <String, dynamic>{'type': instance.type, 'name': instance.name};
|
||||
|
||||
InventoryTransactionRefDto _$InventoryTransactionRefDtoFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => InventoryTransactionRefDto(
|
||||
id: (json['id'] as num).toInt(),
|
||||
transactionNo: json['transaction_no'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$InventoryTransactionRefDtoToJson(
|
||||
InventoryTransactionRefDto instance,
|
||||
) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'transaction_no': instance.transactionNo,
|
||||
};
|
||||
|
||||
InventoryEventLineRefDto _$InventoryEventLineRefDtoFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => InventoryEventLineRefDto(
|
||||
id: (json['id'] as num).toInt(),
|
||||
lineNo: (json['line_no'] as num?)?.toInt(),
|
||||
quantity: _parseNullableQuantity(json['quantity']),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$InventoryEventLineRefDtoToJson(
|
||||
InventoryEventLineRefDto instance,
|
||||
) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'line_no': instance.lineNo,
|
||||
'quantity': instance.quantity,
|
||||
};
|
||||
|
||||
InventoryEventDto _$InventoryEventDtoFromJson(Map<String, dynamic> json) =>
|
||||
InventoryEventDto(
|
||||
eventId: (json['event_id'] as num).toInt(),
|
||||
eventKind: json['event_kind'] as String,
|
||||
eventLabel: json['event_label'] as String,
|
||||
deltaQuantity: _parseQuantity(json['delta_quantity']),
|
||||
occurredAt: DateTime.parse(json['occurred_at'] as String),
|
||||
counterparty: json['counterparty'] == null
|
||||
? null
|
||||
: InventoryCounterpartyDto.fromJson(
|
||||
json['counterparty'] as Map<String, dynamic>,
|
||||
),
|
||||
warehouse: json['warehouse'] == null
|
||||
? null
|
||||
: InventoryWarehouseDto.fromJson(
|
||||
json['warehouse'] as Map<String, dynamic>,
|
||||
),
|
||||
transaction: json['transaction'] == null
|
||||
? null
|
||||
: InventoryTransactionRefDto.fromJson(
|
||||
json['transaction'] as Map<String, dynamic>,
|
||||
),
|
||||
line: json['line'] == null
|
||||
? null
|
||||
: InventoryEventLineRefDto.fromJson(
|
||||
json['line'] as Map<String, dynamic>,
|
||||
),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$InventoryEventDtoToJson(InventoryEventDto instance) =>
|
||||
<String, dynamic>{
|
||||
'event_id': instance.eventId,
|
||||
'event_kind': instance.eventKind,
|
||||
'event_label': instance.eventLabel,
|
||||
'delta_quantity': instance.deltaQuantity,
|
||||
'occurred_at': instance.occurredAt.toIso8601String(),
|
||||
'counterparty': instance.counterparty?.toJson(),
|
||||
'warehouse': instance.warehouse?.toJson(),
|
||||
'transaction': instance.transaction?.toJson(),
|
||||
'line': instance.line?.toJson(),
|
||||
};
|
||||
@@ -0,0 +1,84 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
import '../../domain/entities/inventory_detail.dart';
|
||||
import 'inventory_common_dtos.dart';
|
||||
|
||||
part 'inventory_detail_response.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake, explicitToJson: true)
|
||||
class InventoryDetailResponse {
|
||||
const InventoryDetailResponse({required this.data});
|
||||
|
||||
final InventoryDetailDataDto data;
|
||||
|
||||
factory InventoryDetailResponse.fromJson(Map<String, dynamic> json) =>
|
||||
_$InventoryDetailResponseFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$InventoryDetailResponseToJson(this);
|
||||
|
||||
InventoryDetail toEntity() => data.toEntity();
|
||||
}
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake, explicitToJson: true)
|
||||
class InventoryDetailDataDto {
|
||||
const InventoryDetailDataDto({
|
||||
required this.product,
|
||||
required this.totalQuantity,
|
||||
List<InventoryWarehouseBalanceDto>? warehouseBalances,
|
||||
List<InventoryEventDto>? recentEvents,
|
||||
this.updatedAt,
|
||||
this.lastRefreshedAt,
|
||||
}) : warehouseBalances = warehouseBalances ?? const [],
|
||||
recentEvents = recentEvents ?? const [];
|
||||
|
||||
final InventoryProductDto product;
|
||||
@JsonKey(fromJson: _parseQuantity)
|
||||
final int totalQuantity;
|
||||
@JsonKey(defaultValue: <InventoryWarehouseBalanceDto>[])
|
||||
final List<InventoryWarehouseBalanceDto> warehouseBalances;
|
||||
@JsonKey(defaultValue: <InventoryEventDto>[])
|
||||
final List<InventoryEventDto> recentEvents;
|
||||
final DateTime? updatedAt;
|
||||
final DateTime? lastRefreshedAt;
|
||||
|
||||
factory InventoryDetailDataDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$InventoryDetailDataDtoFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$InventoryDetailDataDtoToJson(this);
|
||||
|
||||
InventoryDetail toEntity() => InventoryDetail(
|
||||
product: product.toEntity(),
|
||||
totalQuantity: totalQuantity,
|
||||
warehouseBalances: warehouseBalances
|
||||
.map((balance) => balance.toEntity())
|
||||
.toList(growable: false),
|
||||
recentEvents: recentEvents
|
||||
.map((event) => event.toEntity())
|
||||
.toList(growable: false),
|
||||
updatedAt: updatedAt,
|
||||
lastRefreshedAt: lastRefreshedAt,
|
||||
);
|
||||
}
|
||||
|
||||
int _parseQuantity(dynamic value) {
|
||||
if (value == null) {
|
||||
return 0;
|
||||
}
|
||||
if (value is int) {
|
||||
return value;
|
||||
}
|
||||
if (value is num) {
|
||||
return value.round();
|
||||
}
|
||||
if (value is String) {
|
||||
final sanitized = value.replaceAll(',', '').trim();
|
||||
if (sanitized.isEmpty) {
|
||||
return 0;
|
||||
}
|
||||
final parsed = num.tryParse(sanitized);
|
||||
if (parsed != null) {
|
||||
return parsed.round();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'inventory_detail_response.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
InventoryDetailResponse _$InventoryDetailResponseFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => InventoryDetailResponse(
|
||||
data: InventoryDetailDataDto.fromJson(json['data'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$InventoryDetailResponseToJson(
|
||||
InventoryDetailResponse instance,
|
||||
) => <String, dynamic>{'data': instance.data.toJson()};
|
||||
|
||||
InventoryDetailDataDto _$InventoryDetailDataDtoFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => InventoryDetailDataDto(
|
||||
product: InventoryProductDto.fromJson(
|
||||
json['product'] as Map<String, dynamic>,
|
||||
),
|
||||
totalQuantity: _parseQuantity(json['total_quantity']),
|
||||
warehouseBalances:
|
||||
(json['warehouse_balances'] as List<dynamic>?)
|
||||
?.map(
|
||||
(e) => InventoryWarehouseBalanceDto.fromJson(
|
||||
e as Map<String, dynamic>,
|
||||
),
|
||||
)
|
||||
.toList() ??
|
||||
[],
|
||||
recentEvents:
|
||||
(json['recent_events'] as List<dynamic>?)
|
||||
?.map((e) => InventoryEventDto.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
[],
|
||||
updatedAt: json['updated_at'] == null
|
||||
? null
|
||||
: DateTime.parse(json['updated_at'] as String),
|
||||
lastRefreshedAt: json['last_refreshed_at'] == null
|
||||
? null
|
||||
: DateTime.parse(json['last_refreshed_at'] as String),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$InventoryDetailDataDtoToJson(
|
||||
InventoryDetailDataDto instance,
|
||||
) => <String, dynamic>{
|
||||
'product': instance.product.toJson(),
|
||||
'total_quantity': instance.totalQuantity,
|
||||
'warehouse_balances': instance.warehouseBalances
|
||||
.map((e) => e.toJson())
|
||||
.toList(),
|
||||
'recent_events': instance.recentEvents.map((e) => e.toJson()).toList(),
|
||||
'updated_at': instance.updatedAt?.toIso8601String(),
|
||||
'last_refreshed_at': instance.lastRefreshedAt?.toIso8601String(),
|
||||
};
|
||||
@@ -0,0 +1,112 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:superport_v2/core/common/models/paginated_result.dart';
|
||||
|
||||
import '../../domain/entities/inventory_summary.dart';
|
||||
import '../../domain/entities/inventory_summary_list_result.dart';
|
||||
import 'inventory_common_dtos.dart';
|
||||
|
||||
part 'inventory_summary_response.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake, explicitToJson: true)
|
||||
class InventorySummaryResponse {
|
||||
const InventorySummaryResponse({
|
||||
List<InventorySummaryItemDto>? items,
|
||||
this.page = 1,
|
||||
this.pageSize = 0,
|
||||
this.total = 0,
|
||||
this.lastRefreshedAt,
|
||||
}) : items = items ?? const [];
|
||||
|
||||
@JsonKey(defaultValue: <InventorySummaryItemDto>[])
|
||||
final List<InventorySummaryItemDto> items;
|
||||
final int page;
|
||||
final int pageSize;
|
||||
final int total;
|
||||
final DateTime? lastRefreshedAt;
|
||||
|
||||
factory InventorySummaryResponse.fromJson(Map<String, dynamic> json) =>
|
||||
_$InventorySummaryResponseFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$InventorySummaryResponseToJson(this);
|
||||
|
||||
InventorySummaryListResult toEntity() {
|
||||
final summaries = items
|
||||
.map((item) => item.toEntity())
|
||||
.toList(growable: false);
|
||||
final paginated = PaginatedResult<InventorySummary>(
|
||||
items: summaries,
|
||||
page: page,
|
||||
pageSize: pageSize,
|
||||
total: total,
|
||||
);
|
||||
return InventorySummaryListResult(
|
||||
result: paginated,
|
||||
lastRefreshedAt:
|
||||
lastRefreshedAt ??
|
||||
(summaries.isNotEmpty ? summaries.first.lastRefreshedAt : null),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake, explicitToJson: true)
|
||||
class InventorySummaryItemDto {
|
||||
const InventorySummaryItemDto({
|
||||
required this.product,
|
||||
required this.totalQuantity,
|
||||
List<InventoryWarehouseBalanceDto>? warehouseBalances,
|
||||
this.recentEvent,
|
||||
this.updatedAt,
|
||||
this.lastRefreshedAt,
|
||||
}) : warehouseBalances = warehouseBalances ?? const [];
|
||||
|
||||
final InventoryProductDto product;
|
||||
@JsonKey(fromJson: _parseQuantity)
|
||||
final int totalQuantity;
|
||||
@JsonKey(defaultValue: <InventoryWarehouseBalanceDto>[])
|
||||
final List<InventoryWarehouseBalanceDto> warehouseBalances;
|
||||
final InventoryEventDto? recentEvent;
|
||||
final DateTime? updatedAt;
|
||||
final DateTime? lastRefreshedAt;
|
||||
|
||||
factory InventorySummaryItemDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$InventorySummaryItemDtoFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$InventorySummaryItemDtoToJson(this);
|
||||
|
||||
InventorySummary toEntity() {
|
||||
final balances = warehouseBalances
|
||||
.map((balance) => balance.toEntity())
|
||||
.toList(growable: false);
|
||||
return InventorySummary(
|
||||
product: product.toEntity(),
|
||||
totalQuantity: totalQuantity,
|
||||
warehouseBalances: balances,
|
||||
recentEvent: recentEvent?.toEntity(),
|
||||
updatedAt: updatedAt,
|
||||
lastRefreshedAt: lastRefreshedAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
int _parseQuantity(dynamic value) {
|
||||
if (value == null) {
|
||||
return 0;
|
||||
}
|
||||
if (value is int) {
|
||||
return value;
|
||||
}
|
||||
if (value is num) {
|
||||
return value.round();
|
||||
}
|
||||
if (value is String) {
|
||||
final sanitized = value.replaceAll(',', '').trim();
|
||||
if (sanitized.isEmpty) {
|
||||
return 0;
|
||||
}
|
||||
final parsed = num.tryParse(sanitized);
|
||||
if (parsed != null) {
|
||||
return parsed.round();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'inventory_summary_response.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
InventorySummaryResponse _$InventorySummaryResponseFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => InventorySummaryResponse(
|
||||
items:
|
||||
(json['items'] as List<dynamic>?)
|
||||
?.map(
|
||||
(e) => InventorySummaryItemDto.fromJson(e as Map<String, dynamic>),
|
||||
)
|
||||
.toList() ??
|
||||
[],
|
||||
page: (json['page'] as num?)?.toInt() ?? 1,
|
||||
pageSize: (json['page_size'] as num?)?.toInt() ?? 0,
|
||||
total: (json['total'] as num?)?.toInt() ?? 0,
|
||||
lastRefreshedAt: json['last_refreshed_at'] == null
|
||||
? null
|
||||
: DateTime.parse(json['last_refreshed_at'] as String),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$InventorySummaryResponseToJson(
|
||||
InventorySummaryResponse instance,
|
||||
) => <String, dynamic>{
|
||||
'items': instance.items.map((e) => e.toJson()).toList(),
|
||||
'page': instance.page,
|
||||
'page_size': instance.pageSize,
|
||||
'total': instance.total,
|
||||
'last_refreshed_at': instance.lastRefreshedAt?.toIso8601String(),
|
||||
};
|
||||
|
||||
InventorySummaryItemDto _$InventorySummaryItemDtoFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => InventorySummaryItemDto(
|
||||
product: InventoryProductDto.fromJson(
|
||||
json['product'] as Map<String, dynamic>,
|
||||
),
|
||||
totalQuantity: _parseQuantity(json['total_quantity']),
|
||||
warehouseBalances:
|
||||
(json['warehouse_balances'] as List<dynamic>?)
|
||||
?.map(
|
||||
(e) => InventoryWarehouseBalanceDto.fromJson(
|
||||
e as Map<String, dynamic>,
|
||||
),
|
||||
)
|
||||
.toList() ??
|
||||
[],
|
||||
recentEvent: json['recent_event'] == null
|
||||
? null
|
||||
: InventoryEventDto.fromJson(
|
||||
json['recent_event'] as Map<String, dynamic>,
|
||||
),
|
||||
updatedAt: json['updated_at'] == null
|
||||
? null
|
||||
: DateTime.parse(json['updated_at'] as String),
|
||||
lastRefreshedAt: json['last_refreshed_at'] == null
|
||||
? null
|
||||
: DateTime.parse(json['last_refreshed_at'] as String),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$InventorySummaryItemDtoToJson(
|
||||
InventorySummaryItemDto instance,
|
||||
) => <String, dynamic>{
|
||||
'product': instance.product.toJson(),
|
||||
'total_quantity': instance.totalQuantity,
|
||||
'warehouse_balances': instance.warehouseBalances
|
||||
.map((e) => e.toJson())
|
||||
.toList(),
|
||||
'recent_event': instance.recentEvent?.toJson(),
|
||||
'updated_at': instance.updatedAt?.toIso8601String(),
|
||||
'last_refreshed_at': instance.lastRefreshedAt?.toIso8601String(),
|
||||
};
|
||||
Reference in New Issue
Block a user