report inventory
This commit is contained in:
parent
2a44ce023e
commit
dca0f546f9
@ -0,0 +1,47 @@
|
|||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:dartz/dartz.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:injectable/injectable.dart';
|
||||||
|
|
||||||
|
import '../../../domain/analytic/analytic.dart';
|
||||||
|
|
||||||
|
part 'inventory_analytic_loader_event.dart';
|
||||||
|
part 'inventory_analytic_loader_state.dart';
|
||||||
|
part 'inventory_analytic_loader_bloc.freezed.dart';
|
||||||
|
|
||||||
|
@injectable
|
||||||
|
class InventoryAnalyticLoaderBloc
|
||||||
|
extends Bloc<InventoryAnalyticLoaderEvent, InventoryAnalyticLoaderState> {
|
||||||
|
final IAnalyticRepository _analyticRepository;
|
||||||
|
InventoryAnalyticLoaderBloc(this._analyticRepository)
|
||||||
|
: super(InventoryAnalyticLoaderState.initial()) {
|
||||||
|
on<InventoryAnalyticLoaderEvent>(_onInventoryAnalyticLoaderEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onInventoryAnalyticLoaderEvent(
|
||||||
|
InventoryAnalyticLoaderEvent event,
|
||||||
|
Emitter<InventoryAnalyticLoaderState> emit,
|
||||||
|
) {
|
||||||
|
return event.map(
|
||||||
|
fetched: (e) async {
|
||||||
|
emit(state.copyWith(isFetching: true, failureOption: none()));
|
||||||
|
|
||||||
|
final result = await _analyticRepository.getInventory(
|
||||||
|
dateFrom: e.startDate,
|
||||||
|
dateTo: e.endDate,
|
||||||
|
);
|
||||||
|
|
||||||
|
emit(
|
||||||
|
result.fold(
|
||||||
|
(l) => state.copyWith(isFetching: false, failureOption: some(l)),
|
||||||
|
(r) => state.copyWith(
|
||||||
|
isFetching: false,
|
||||||
|
failureOption: none(),
|
||||||
|
inventoryAnalytic: r,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,480 @@
|
|||||||
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'inventory_analytic_loader_bloc.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models',
|
||||||
|
);
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$InventoryAnalyticLoaderEvent {
|
||||||
|
DateTime get startDate => throw _privateConstructorUsedError;
|
||||||
|
DateTime get endDate => throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({
|
||||||
|
required TResult Function(DateTime startDate, DateTime endDate) fetched,
|
||||||
|
}) => throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(DateTime startDate, DateTime endDate)? fetched,
|
||||||
|
}) => throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function(DateTime startDate, DateTime endDate)? fetched,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) => throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(_Fetched value) fetched,
|
||||||
|
}) => throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(_Fetched value)? fetched,
|
||||||
|
}) => throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(_Fetched value)? fetched,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Create a copy of InventoryAnalyticLoaderEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
$InventoryAnalyticLoaderEventCopyWith<InventoryAnalyticLoaderEvent>
|
||||||
|
get copyWith => throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $InventoryAnalyticLoaderEventCopyWith<$Res> {
|
||||||
|
factory $InventoryAnalyticLoaderEventCopyWith(
|
||||||
|
InventoryAnalyticLoaderEvent value,
|
||||||
|
$Res Function(InventoryAnalyticLoaderEvent) then,
|
||||||
|
) =
|
||||||
|
_$InventoryAnalyticLoaderEventCopyWithImpl<
|
||||||
|
$Res,
|
||||||
|
InventoryAnalyticLoaderEvent
|
||||||
|
>;
|
||||||
|
@useResult
|
||||||
|
$Res call({DateTime startDate, DateTime endDate});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$InventoryAnalyticLoaderEventCopyWithImpl<
|
||||||
|
$Res,
|
||||||
|
$Val extends InventoryAnalyticLoaderEvent
|
||||||
|
>
|
||||||
|
implements $InventoryAnalyticLoaderEventCopyWith<$Res> {
|
||||||
|
_$InventoryAnalyticLoaderEventCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
/// Create a copy of InventoryAnalyticLoaderEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({Object? startDate = null, Object? endDate = null}) {
|
||||||
|
return _then(
|
||||||
|
_value.copyWith(
|
||||||
|
startDate: null == startDate
|
||||||
|
? _value.startDate
|
||||||
|
: startDate // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,
|
||||||
|
endDate: null == endDate
|
||||||
|
? _value.endDate
|
||||||
|
: endDate // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,
|
||||||
|
)
|
||||||
|
as $Val,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$FetchedImplCopyWith<$Res>
|
||||||
|
implements $InventoryAnalyticLoaderEventCopyWith<$Res> {
|
||||||
|
factory _$$FetchedImplCopyWith(
|
||||||
|
_$FetchedImpl value,
|
||||||
|
$Res Function(_$FetchedImpl) then,
|
||||||
|
) = __$$FetchedImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call({DateTime startDate, DateTime endDate});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$FetchedImplCopyWithImpl<$Res>
|
||||||
|
extends _$InventoryAnalyticLoaderEventCopyWithImpl<$Res, _$FetchedImpl>
|
||||||
|
implements _$$FetchedImplCopyWith<$Res> {
|
||||||
|
__$$FetchedImplCopyWithImpl(
|
||||||
|
_$FetchedImpl _value,
|
||||||
|
$Res Function(_$FetchedImpl) _then,
|
||||||
|
) : super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of InventoryAnalyticLoaderEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({Object? startDate = null, Object? endDate = null}) {
|
||||||
|
return _then(
|
||||||
|
_$FetchedImpl(
|
||||||
|
startDate: null == startDate
|
||||||
|
? _value.startDate
|
||||||
|
: startDate // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,
|
||||||
|
endDate: null == endDate
|
||||||
|
? _value.endDate
|
||||||
|
: endDate // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$FetchedImpl implements _Fetched {
|
||||||
|
const _$FetchedImpl({required this.startDate, required this.endDate});
|
||||||
|
|
||||||
|
@override
|
||||||
|
final DateTime startDate;
|
||||||
|
@override
|
||||||
|
final DateTime endDate;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'InventoryAnalyticLoaderEvent.fetched(startDate: $startDate, endDate: $endDate)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$FetchedImpl &&
|
||||||
|
(identical(other.startDate, startDate) ||
|
||||||
|
other.startDate == startDate) &&
|
||||||
|
(identical(other.endDate, endDate) || other.endDate == endDate));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType, startDate, endDate);
|
||||||
|
|
||||||
|
/// Create a copy of InventoryAnalyticLoaderEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$FetchedImplCopyWith<_$FetchedImpl> get copyWith =>
|
||||||
|
__$$FetchedImplCopyWithImpl<_$FetchedImpl>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({
|
||||||
|
required TResult Function(DateTime startDate, DateTime endDate) fetched,
|
||||||
|
}) {
|
||||||
|
return fetched(startDate, endDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(DateTime startDate, DateTime endDate)? fetched,
|
||||||
|
}) {
|
||||||
|
return fetched?.call(startDate, endDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function(DateTime startDate, DateTime endDate)? fetched,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (fetched != null) {
|
||||||
|
return fetched(startDate, endDate);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(_Fetched value) fetched,
|
||||||
|
}) {
|
||||||
|
return fetched(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(_Fetched value)? fetched,
|
||||||
|
}) {
|
||||||
|
return fetched?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(_Fetched value)? fetched,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (fetched != null) {
|
||||||
|
return fetched(this);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _Fetched implements InventoryAnalyticLoaderEvent {
|
||||||
|
const factory _Fetched({
|
||||||
|
required final DateTime startDate,
|
||||||
|
required final DateTime endDate,
|
||||||
|
}) = _$FetchedImpl;
|
||||||
|
|
||||||
|
@override
|
||||||
|
DateTime get startDate;
|
||||||
|
@override
|
||||||
|
DateTime get endDate;
|
||||||
|
|
||||||
|
/// Create a copy of InventoryAnalyticLoaderEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$FetchedImplCopyWith<_$FetchedImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$InventoryAnalyticLoaderState {
|
||||||
|
InventoryAnalytic get inventoryAnalytic => throw _privateConstructorUsedError;
|
||||||
|
Option<AnalyticFailure> get failureOption =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
bool get isFetching => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Create a copy of InventoryAnalyticLoaderState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
$InventoryAnalyticLoaderStateCopyWith<InventoryAnalyticLoaderState>
|
||||||
|
get copyWith => throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $InventoryAnalyticLoaderStateCopyWith<$Res> {
|
||||||
|
factory $InventoryAnalyticLoaderStateCopyWith(
|
||||||
|
InventoryAnalyticLoaderState value,
|
||||||
|
$Res Function(InventoryAnalyticLoaderState) then,
|
||||||
|
) =
|
||||||
|
_$InventoryAnalyticLoaderStateCopyWithImpl<
|
||||||
|
$Res,
|
||||||
|
InventoryAnalyticLoaderState
|
||||||
|
>;
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
InventoryAnalytic inventoryAnalytic,
|
||||||
|
Option<AnalyticFailure> failureOption,
|
||||||
|
bool isFetching,
|
||||||
|
});
|
||||||
|
|
||||||
|
$InventoryAnalyticCopyWith<$Res> get inventoryAnalytic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$InventoryAnalyticLoaderStateCopyWithImpl<
|
||||||
|
$Res,
|
||||||
|
$Val extends InventoryAnalyticLoaderState
|
||||||
|
>
|
||||||
|
implements $InventoryAnalyticLoaderStateCopyWith<$Res> {
|
||||||
|
_$InventoryAnalyticLoaderStateCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
/// Create a copy of InventoryAnalyticLoaderState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? inventoryAnalytic = null,
|
||||||
|
Object? failureOption = null,
|
||||||
|
Object? isFetching = null,
|
||||||
|
}) {
|
||||||
|
return _then(
|
||||||
|
_value.copyWith(
|
||||||
|
inventoryAnalytic: null == inventoryAnalytic
|
||||||
|
? _value.inventoryAnalytic
|
||||||
|
: inventoryAnalytic // ignore: cast_nullable_to_non_nullable
|
||||||
|
as InventoryAnalytic,
|
||||||
|
failureOption: null == failureOption
|
||||||
|
? _value.failureOption
|
||||||
|
: failureOption // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Option<AnalyticFailure>,
|
||||||
|
isFetching: null == isFetching
|
||||||
|
? _value.isFetching
|
||||||
|
: isFetching // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
)
|
||||||
|
as $Val,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a copy of InventoryAnalyticLoaderState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$InventoryAnalyticCopyWith<$Res> get inventoryAnalytic {
|
||||||
|
return $InventoryAnalyticCopyWith<$Res>(_value.inventoryAnalytic, (value) {
|
||||||
|
return _then(_value.copyWith(inventoryAnalytic: value) as $Val);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$InventoryAnalyticLoaderStateImplCopyWith<$Res>
|
||||||
|
implements $InventoryAnalyticLoaderStateCopyWith<$Res> {
|
||||||
|
factory _$$InventoryAnalyticLoaderStateImplCopyWith(
|
||||||
|
_$InventoryAnalyticLoaderStateImpl value,
|
||||||
|
$Res Function(_$InventoryAnalyticLoaderStateImpl) then,
|
||||||
|
) = __$$InventoryAnalyticLoaderStateImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
InventoryAnalytic inventoryAnalytic,
|
||||||
|
Option<AnalyticFailure> failureOption,
|
||||||
|
bool isFetching,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
$InventoryAnalyticCopyWith<$Res> get inventoryAnalytic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$InventoryAnalyticLoaderStateImplCopyWithImpl<$Res>
|
||||||
|
extends
|
||||||
|
_$InventoryAnalyticLoaderStateCopyWithImpl<
|
||||||
|
$Res,
|
||||||
|
_$InventoryAnalyticLoaderStateImpl
|
||||||
|
>
|
||||||
|
implements _$$InventoryAnalyticLoaderStateImplCopyWith<$Res> {
|
||||||
|
__$$InventoryAnalyticLoaderStateImplCopyWithImpl(
|
||||||
|
_$InventoryAnalyticLoaderStateImpl _value,
|
||||||
|
$Res Function(_$InventoryAnalyticLoaderStateImpl) _then,
|
||||||
|
) : super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of InventoryAnalyticLoaderState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? inventoryAnalytic = null,
|
||||||
|
Object? failureOption = null,
|
||||||
|
Object? isFetching = null,
|
||||||
|
}) {
|
||||||
|
return _then(
|
||||||
|
_$InventoryAnalyticLoaderStateImpl(
|
||||||
|
inventoryAnalytic: null == inventoryAnalytic
|
||||||
|
? _value.inventoryAnalytic
|
||||||
|
: inventoryAnalytic // ignore: cast_nullable_to_non_nullable
|
||||||
|
as InventoryAnalytic,
|
||||||
|
failureOption: null == failureOption
|
||||||
|
? _value.failureOption
|
||||||
|
: failureOption // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Option<AnalyticFailure>,
|
||||||
|
isFetching: null == isFetching
|
||||||
|
? _value.isFetching
|
||||||
|
: isFetching // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$InventoryAnalyticLoaderStateImpl
|
||||||
|
implements _InventoryAnalyticLoaderState {
|
||||||
|
_$InventoryAnalyticLoaderStateImpl({
|
||||||
|
required this.inventoryAnalytic,
|
||||||
|
required this.failureOption,
|
||||||
|
this.isFetching = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
final InventoryAnalytic inventoryAnalytic;
|
||||||
|
@override
|
||||||
|
final Option<AnalyticFailure> failureOption;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final bool isFetching;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'InventoryAnalyticLoaderState(inventoryAnalytic: $inventoryAnalytic, failureOption: $failureOption, isFetching: $isFetching)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$InventoryAnalyticLoaderStateImpl &&
|
||||||
|
(identical(other.inventoryAnalytic, inventoryAnalytic) ||
|
||||||
|
other.inventoryAnalytic == inventoryAnalytic) &&
|
||||||
|
(identical(other.failureOption, failureOption) ||
|
||||||
|
other.failureOption == failureOption) &&
|
||||||
|
(identical(other.isFetching, isFetching) ||
|
||||||
|
other.isFetching == isFetching));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
Object.hash(runtimeType, inventoryAnalytic, failureOption, isFetching);
|
||||||
|
|
||||||
|
/// Create a copy of InventoryAnalyticLoaderState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$InventoryAnalyticLoaderStateImplCopyWith<
|
||||||
|
_$InventoryAnalyticLoaderStateImpl
|
||||||
|
>
|
||||||
|
get copyWith =>
|
||||||
|
__$$InventoryAnalyticLoaderStateImplCopyWithImpl<
|
||||||
|
_$InventoryAnalyticLoaderStateImpl
|
||||||
|
>(this, _$identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _InventoryAnalyticLoaderState
|
||||||
|
implements InventoryAnalyticLoaderState {
|
||||||
|
factory _InventoryAnalyticLoaderState({
|
||||||
|
required final InventoryAnalytic inventoryAnalytic,
|
||||||
|
required final Option<AnalyticFailure> failureOption,
|
||||||
|
final bool isFetching,
|
||||||
|
}) = _$InventoryAnalyticLoaderStateImpl;
|
||||||
|
|
||||||
|
@override
|
||||||
|
InventoryAnalytic get inventoryAnalytic;
|
||||||
|
@override
|
||||||
|
Option<AnalyticFailure> get failureOption;
|
||||||
|
@override
|
||||||
|
bool get isFetching;
|
||||||
|
|
||||||
|
/// Create a copy of InventoryAnalyticLoaderState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$InventoryAnalyticLoaderStateImplCopyWith<
|
||||||
|
_$InventoryAnalyticLoaderStateImpl
|
||||||
|
>
|
||||||
|
get copyWith => throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
part of 'inventory_analytic_loader_bloc.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class InventoryAnalyticLoaderEvent with _$InventoryAnalyticLoaderEvent {
|
||||||
|
const factory InventoryAnalyticLoaderEvent.fetched({
|
||||||
|
required DateTime startDate,
|
||||||
|
required DateTime endDate,
|
||||||
|
}) = _Fetched;
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
part of 'inventory_analytic_loader_bloc.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class InventoryAnalyticLoaderState with _$InventoryAnalyticLoaderState {
|
||||||
|
factory InventoryAnalyticLoaderState({
|
||||||
|
required InventoryAnalytic inventoryAnalytic,
|
||||||
|
required Option<AnalyticFailure> failureOption,
|
||||||
|
@Default(false) bool isFetching,
|
||||||
|
}) = _InventoryAnalyticLoaderState;
|
||||||
|
|
||||||
|
factory InventoryAnalyticLoaderState.initial() =>
|
||||||
|
InventoryAnalyticLoaderState(
|
||||||
|
inventoryAnalytic: InventoryAnalytic.empty(),
|
||||||
|
failureOption: none(),
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -15,4 +15,5 @@ class ApiPath {
|
|||||||
'/api/v1/analytics/payment-methods';
|
'/api/v1/analytics/payment-methods';
|
||||||
static const String analyticProfitLoss = '/api/v1/analytics/profit-loss';
|
static const String analyticProfitLoss = '/api/v1/analytics/profit-loss';
|
||||||
static const String analyticCategories = '/api/v1/analytics/categories';
|
static const String analyticCategories = '/api/v1/analytics/categories';
|
||||||
|
static const String analyticInventory = '/api/v1/inventory/report/details';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,5 +11,6 @@ part 'entities/product_analytic_entity.dart';
|
|||||||
part 'entities/payment_method_analytic_entity.dart';
|
part 'entities/payment_method_analytic_entity.dart';
|
||||||
part 'entities/profit_loss_analytic_entity.dart';
|
part 'entities/profit_loss_analytic_entity.dart';
|
||||||
part 'entities/category_analytic_entity.dart';
|
part 'entities/category_analytic_entity.dart';
|
||||||
|
part 'entities/inventory_analytic_entity.dart';
|
||||||
part 'failures/analytic_failure.dart';
|
part 'failures/analytic_failure.dart';
|
||||||
part 'repositories/i_analytic_repository.dart';
|
part 'repositories/i_analytic_repository.dart';
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
120
lib/domain/analytic/entities/inventory_analytic_entity.dart
Normal file
120
lib/domain/analytic/entities/inventory_analytic_entity.dart
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
part of '../analytic.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class InventoryAnalytic with _$InventoryAnalytic {
|
||||||
|
const factory InventoryAnalytic({
|
||||||
|
required InventoryAnalyticSummary summary,
|
||||||
|
required List<InventoryAnalyticProductItem> products,
|
||||||
|
required List<InventoryAnalyticIngredientItem> ingredients,
|
||||||
|
}) = _InventoryAnalytic;
|
||||||
|
|
||||||
|
factory InventoryAnalytic.empty() => InventoryAnalytic(
|
||||||
|
summary: InventoryAnalyticSummary.empty(),
|
||||||
|
products: const [],
|
||||||
|
ingredients: const [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class InventoryAnalyticSummary with _$InventoryAnalyticSummary {
|
||||||
|
const factory InventoryAnalyticSummary({
|
||||||
|
required int totalProducts,
|
||||||
|
required int totalIngredients,
|
||||||
|
required int totalValue,
|
||||||
|
required int lowStockProducts,
|
||||||
|
required int lowStockIngredients,
|
||||||
|
required int zeroStockProducts,
|
||||||
|
required int zeroStockIngredients,
|
||||||
|
required int totalSoldProducts,
|
||||||
|
required int totalSoldIngredients,
|
||||||
|
required String outletId,
|
||||||
|
required String outletName,
|
||||||
|
required DateTime generatedAt,
|
||||||
|
}) = _InventoryAnalyticSummary;
|
||||||
|
|
||||||
|
factory InventoryAnalyticSummary.empty() => InventoryAnalyticSummary(
|
||||||
|
totalProducts: 0,
|
||||||
|
totalIngredients: 0,
|
||||||
|
totalValue: 0,
|
||||||
|
lowStockProducts: 0,
|
||||||
|
lowStockIngredients: 0,
|
||||||
|
zeroStockProducts: 0,
|
||||||
|
zeroStockIngredients: 0,
|
||||||
|
totalSoldProducts: 0,
|
||||||
|
totalSoldIngredients: 0,
|
||||||
|
outletId: '',
|
||||||
|
outletName: '',
|
||||||
|
generatedAt: DateTime.now(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class InventoryAnalyticProductItem with _$InventoryAnalyticProductItem {
|
||||||
|
const factory InventoryAnalyticProductItem({
|
||||||
|
required String id,
|
||||||
|
required String productId,
|
||||||
|
required String productName,
|
||||||
|
required String categoryName,
|
||||||
|
required int quantity,
|
||||||
|
required int reorderLevel,
|
||||||
|
required num unitCost,
|
||||||
|
required int totalValue,
|
||||||
|
required int totalIn,
|
||||||
|
required int totalOut,
|
||||||
|
required bool isLowStock,
|
||||||
|
required bool isZeroStock,
|
||||||
|
required DateTime updatedAt,
|
||||||
|
}) = _InventoryAnalyticProductItem;
|
||||||
|
|
||||||
|
factory InventoryAnalyticProductItem.empty() => InventoryAnalyticProductItem(
|
||||||
|
id: '',
|
||||||
|
productId: '',
|
||||||
|
productName: '',
|
||||||
|
categoryName: '',
|
||||||
|
quantity: 0,
|
||||||
|
reorderLevel: 0,
|
||||||
|
unitCost: 0,
|
||||||
|
totalValue: 0,
|
||||||
|
totalIn: 0,
|
||||||
|
totalOut: 0,
|
||||||
|
isLowStock: false,
|
||||||
|
isZeroStock: false,
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class InventoryAnalyticIngredientItem with _$InventoryAnalyticIngredientItem {
|
||||||
|
const factory InventoryAnalyticIngredientItem({
|
||||||
|
required String id,
|
||||||
|
required String ingredientId,
|
||||||
|
required String ingredientName,
|
||||||
|
required String unitName,
|
||||||
|
required int quantity,
|
||||||
|
required int reorderLevel,
|
||||||
|
required num unitCost,
|
||||||
|
required int totalValue,
|
||||||
|
required int totalIn,
|
||||||
|
required int totalOut,
|
||||||
|
required bool isLowStock,
|
||||||
|
required bool isZeroStock,
|
||||||
|
required DateTime updatedAt,
|
||||||
|
}) = _InventoryAnalyticIngredientItem;
|
||||||
|
|
||||||
|
factory InventoryAnalyticIngredientItem.empty() =>
|
||||||
|
InventoryAnalyticIngredientItem(
|
||||||
|
id: '',
|
||||||
|
ingredientId: '',
|
||||||
|
ingredientName: '',
|
||||||
|
unitName: '',
|
||||||
|
quantity: 0,
|
||||||
|
reorderLevel: 0,
|
||||||
|
unitCost: 0,
|
||||||
|
totalValue: 0,
|
||||||
|
totalIn: 0,
|
||||||
|
totalOut: 0,
|
||||||
|
isLowStock: false,
|
||||||
|
isZeroStock: false,
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -25,4 +25,8 @@ abstract class IAnalyticRepository {
|
|||||||
required DateTime dateFrom,
|
required DateTime dateFrom,
|
||||||
required DateTime dateTo,
|
required DateTime dateTo,
|
||||||
});
|
});
|
||||||
|
Future<Either<AnalyticFailure, InventoryAnalytic>> getInventory({
|
||||||
|
required DateTime dateFrom,
|
||||||
|
required DateTime dateTo,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,3 +11,4 @@ part 'dtos/product_analytic_dto.dart';
|
|||||||
part 'dtos/payment_method_analytic_dto.dart';
|
part 'dtos/payment_method_analytic_dto.dart';
|
||||||
part 'dtos/profit_loss_analytic_dto.dart';
|
part 'dtos/profit_loss_analytic_dto.dart';
|
||||||
part 'dtos/category_analytic_dto.dart';
|
part 'dtos/category_analytic_dto.dart';
|
||||||
|
part 'dtos/inventory_analytic_dto.dart';
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -527,3 +527,146 @@ Map<String, dynamic> _$$CategoryAnalyticItemDtoImplToJson(
|
|||||||
'product_count': instance.productCount,
|
'product_count': instance.productCount,
|
||||||
'order_count': instance.orderCount,
|
'order_count': instance.orderCount,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_$InventoryAnalyticDtoImpl _$$InventoryAnalyticDtoImplFromJson(
|
||||||
|
Map<String, dynamic> json,
|
||||||
|
) => _$InventoryAnalyticDtoImpl(
|
||||||
|
summary: json['summary'] == null
|
||||||
|
? null
|
||||||
|
: InventoryAnalyticSummaryDto.fromJson(
|
||||||
|
json['summary'] as Map<String, dynamic>,
|
||||||
|
),
|
||||||
|
products: (json['products'] as List<dynamic>?)
|
||||||
|
?.map(
|
||||||
|
(e) =>
|
||||||
|
InventoryAnalyticProductItemDto.fromJson(e as Map<String, dynamic>),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
ingredients: (json['ingredients'] as List<dynamic>?)
|
||||||
|
?.map(
|
||||||
|
(e) => InventoryAnalyticIngredientItemDto.fromJson(
|
||||||
|
e as Map<String, dynamic>,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$InventoryAnalyticDtoImplToJson(
|
||||||
|
_$InventoryAnalyticDtoImpl instance,
|
||||||
|
) => <String, dynamic>{
|
||||||
|
'summary': instance.summary,
|
||||||
|
'products': instance.products,
|
||||||
|
'ingredients': instance.ingredients,
|
||||||
|
};
|
||||||
|
|
||||||
|
_$InventoryAnalyticSummaryDtoImpl _$$InventoryAnalyticSummaryDtoImplFromJson(
|
||||||
|
Map<String, dynamic> json,
|
||||||
|
) => _$InventoryAnalyticSummaryDtoImpl(
|
||||||
|
totalProducts: (json['total_products'] as num?)?.toInt(),
|
||||||
|
totalIngredients: (json['total_ingredients'] as num?)?.toInt(),
|
||||||
|
totalValue: (json['total_value'] as num?)?.toInt(),
|
||||||
|
lowStockProducts: (json['low_stock_products'] as num?)?.toInt(),
|
||||||
|
lowStockIngredients: (json['low_stock_ingredients'] as num?)?.toInt(),
|
||||||
|
zeroStockProducts: (json['zero_stock_products'] as num?)?.toInt(),
|
||||||
|
zeroStockIngredients: (json['zero_stock_ingredients'] as num?)?.toInt(),
|
||||||
|
totalSoldProducts: (json['total_sold_products'] as num?)?.toInt(),
|
||||||
|
totalSoldIngredients: (json['total_sold_ingredients'] as num?)?.toInt(),
|
||||||
|
outletId: json['outlet_id'] as String?,
|
||||||
|
outletName: json['outlet_name'] as String?,
|
||||||
|
generatedAt: json['generated_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['generated_at'] as String),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$InventoryAnalyticSummaryDtoImplToJson(
|
||||||
|
_$InventoryAnalyticSummaryDtoImpl instance,
|
||||||
|
) => <String, dynamic>{
|
||||||
|
'total_products': instance.totalProducts,
|
||||||
|
'total_ingredients': instance.totalIngredients,
|
||||||
|
'total_value': instance.totalValue,
|
||||||
|
'low_stock_products': instance.lowStockProducts,
|
||||||
|
'low_stock_ingredients': instance.lowStockIngredients,
|
||||||
|
'zero_stock_products': instance.zeroStockProducts,
|
||||||
|
'zero_stock_ingredients': instance.zeroStockIngredients,
|
||||||
|
'total_sold_products': instance.totalSoldProducts,
|
||||||
|
'total_sold_ingredients': instance.totalSoldIngredients,
|
||||||
|
'outlet_id': instance.outletId,
|
||||||
|
'outlet_name': instance.outletName,
|
||||||
|
'generated_at': instance.generatedAt?.toIso8601String(),
|
||||||
|
};
|
||||||
|
|
||||||
|
_$InventoryAnalyticProductItemDtoImpl
|
||||||
|
_$$InventoryAnalyticProductItemDtoImplFromJson(Map<String, dynamic> json) =>
|
||||||
|
_$InventoryAnalyticProductItemDtoImpl(
|
||||||
|
id: json['id'] as String?,
|
||||||
|
productId: json['product_id'] as String?,
|
||||||
|
productName: json['product_name'] as String?,
|
||||||
|
categoryName: json['category_name'] as String?,
|
||||||
|
quantity: (json['quantity'] as num?)?.toInt(),
|
||||||
|
reorderLevel: (json['reorder_level'] as num?)?.toInt(),
|
||||||
|
unitCost: json['unit_cost'] as num?,
|
||||||
|
totalValue: (json['total_value'] as num?)?.toInt(),
|
||||||
|
totalIn: (json['total_in'] as num?)?.toInt(),
|
||||||
|
totalOut: (json['total_out'] as num?)?.toInt(),
|
||||||
|
isLowStock: json['is_low_stock'] as bool?,
|
||||||
|
isZeroStock: json['is_zero_stock'] as bool?,
|
||||||
|
updatedAt: json['updated_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['updated_at'] as String),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$InventoryAnalyticProductItemDtoImplToJson(
|
||||||
|
_$InventoryAnalyticProductItemDtoImpl instance,
|
||||||
|
) => <String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'product_id': instance.productId,
|
||||||
|
'product_name': instance.productName,
|
||||||
|
'category_name': instance.categoryName,
|
||||||
|
'quantity': instance.quantity,
|
||||||
|
'reorder_level': instance.reorderLevel,
|
||||||
|
'unit_cost': instance.unitCost,
|
||||||
|
'total_value': instance.totalValue,
|
||||||
|
'total_in': instance.totalIn,
|
||||||
|
'total_out': instance.totalOut,
|
||||||
|
'is_low_stock': instance.isLowStock,
|
||||||
|
'is_zero_stock': instance.isZeroStock,
|
||||||
|
'updated_at': instance.updatedAt?.toIso8601String(),
|
||||||
|
};
|
||||||
|
|
||||||
|
_$InventoryAnalyticIngredientItemDtoImpl
|
||||||
|
_$$InventoryAnalyticIngredientItemDtoImplFromJson(Map<String, dynamic> json) =>
|
||||||
|
_$InventoryAnalyticIngredientItemDtoImpl(
|
||||||
|
id: json['id'] as String?,
|
||||||
|
ingredientId: json['ingredient_id'] as String?,
|
||||||
|
ingredientName: json['ingredient_name'] as String?,
|
||||||
|
unitName: json['unit_name'] as String?,
|
||||||
|
quantity: (json['quantity'] as num?)?.toInt(),
|
||||||
|
reorderLevel: (json['reorder_level'] as num?)?.toInt(),
|
||||||
|
unitCost: json['unit_cost'] as num?,
|
||||||
|
totalValue: (json['total_value'] as num?)?.toInt(),
|
||||||
|
totalIn: (json['total_in'] as num?)?.toInt(),
|
||||||
|
totalOut: (json['total_out'] as num?)?.toInt(),
|
||||||
|
isLowStock: json['is_low_stock'] as bool?,
|
||||||
|
isZeroStock: json['is_zero_stock'] as bool?,
|
||||||
|
updatedAt: json['updated_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['updated_at'] as String),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$InventoryAnalyticIngredientItemDtoImplToJson(
|
||||||
|
_$InventoryAnalyticIngredientItemDtoImpl instance,
|
||||||
|
) => <String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'ingredient_id': instance.ingredientId,
|
||||||
|
'ingredient_name': instance.ingredientName,
|
||||||
|
'unit_name': instance.unitName,
|
||||||
|
'quantity': instance.quantity,
|
||||||
|
'reorder_level': instance.reorderLevel,
|
||||||
|
'unit_cost': instance.unitCost,
|
||||||
|
'total_value': instance.totalValue,
|
||||||
|
'total_in': instance.totalIn,
|
||||||
|
'total_out': instance.totalOut,
|
||||||
|
'is_low_stock': instance.isLowStock,
|
||||||
|
'is_zero_stock': instance.isZeroStock,
|
||||||
|
'updated_at': instance.updatedAt?.toIso8601String(),
|
||||||
|
};
|
||||||
|
|||||||
@ -9,6 +9,8 @@ import '../../../common/extension/extension.dart';
|
|||||||
import '../../../common/function/app_function.dart';
|
import '../../../common/function/app_function.dart';
|
||||||
import '../../../common/url/api_path.dart';
|
import '../../../common/url/api_path.dart';
|
||||||
import '../../../domain/analytic/analytic.dart';
|
import '../../../domain/analytic/analytic.dart';
|
||||||
|
import '../../../injection.dart';
|
||||||
|
import '../../outlet/datasources/local_data_provider.dart';
|
||||||
import '../analytic_dtos.dart';
|
import '../analytic_dtos.dart';
|
||||||
|
|
||||||
@injectable
|
@injectable
|
||||||
@ -192,4 +194,34 @@ class AnalyticRemoteDataProvider {
|
|||||||
return DC.error(AnalyticFailure.serverError(e));
|
return DC.error(AnalyticFailure.serverError(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<DC<AnalyticFailure, InventoryAnalyticDto>> fetchInventory({
|
||||||
|
required DateTime dateFrom,
|
||||||
|
required DateTime dateTo,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
final user = await getIt<OutletLocalDatasource>().currentOutlet();
|
||||||
|
final response = await _apiClient.get(
|
||||||
|
'${ApiPath.analyticInventory}/${user.id}',
|
||||||
|
params: {
|
||||||
|
'date_from': dateFrom.toServerDate(),
|
||||||
|
'date_to': dateTo.toServerDate(),
|
||||||
|
},
|
||||||
|
headers: getAuthorizationHeader(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.data['success'] == false) {
|
||||||
|
return DC.error(AnalyticFailure.unexpectedError());
|
||||||
|
}
|
||||||
|
|
||||||
|
final inventory = InventoryAnalyticDto.fromJson(
|
||||||
|
response.data['data'] as Map<String, dynamic>,
|
||||||
|
);
|
||||||
|
|
||||||
|
return DC.data(inventory);
|
||||||
|
} on ApiFailure catch (e, s) {
|
||||||
|
log('fetchInventory', name: _logName, error: e, stackTrace: s);
|
||||||
|
return DC.error(AnalyticFailure.serverError(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
146
lib/infrastructure/analytic/dtos/inventory_analytic_dto.dart
Normal file
146
lib/infrastructure/analytic/dtos/inventory_analytic_dto.dart
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
part of '../analytic_dtos.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class InventoryAnalyticDto with _$InventoryAnalyticDto {
|
||||||
|
const InventoryAnalyticDto._();
|
||||||
|
|
||||||
|
const factory InventoryAnalyticDto({
|
||||||
|
@JsonKey(name: "summary") InventoryAnalyticSummaryDto? summary,
|
||||||
|
@JsonKey(name: "products") List<InventoryAnalyticProductItemDto>? products,
|
||||||
|
@JsonKey(name: "ingredients")
|
||||||
|
List<InventoryAnalyticIngredientItemDto>? ingredients,
|
||||||
|
}) = _InventoryAnalyticDto;
|
||||||
|
|
||||||
|
factory InventoryAnalyticDto.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$InventoryAnalyticDtoFromJson(json);
|
||||||
|
|
||||||
|
// Mapper ke domain (opsional)
|
||||||
|
InventoryAnalytic toDomain() => InventoryAnalytic(
|
||||||
|
summary: summary?.toDomain() ?? InventoryAnalyticSummary.empty(),
|
||||||
|
products: products?.map((e) => e.toDomain()).toList() ?? [],
|
||||||
|
ingredients: ingredients?.map((e) => e.toDomain()).toList() ?? [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class InventoryAnalyticSummaryDto with _$InventoryAnalyticSummaryDto {
|
||||||
|
const InventoryAnalyticSummaryDto._();
|
||||||
|
|
||||||
|
const factory InventoryAnalyticSummaryDto({
|
||||||
|
@JsonKey(name: "total_products") int? totalProducts,
|
||||||
|
@JsonKey(name: "total_ingredients") int? totalIngredients,
|
||||||
|
@JsonKey(name: "total_value") int? totalValue,
|
||||||
|
@JsonKey(name: "low_stock_products") int? lowStockProducts,
|
||||||
|
@JsonKey(name: "low_stock_ingredients") int? lowStockIngredients,
|
||||||
|
@JsonKey(name: "zero_stock_products") int? zeroStockProducts,
|
||||||
|
@JsonKey(name: "zero_stock_ingredients") int? zeroStockIngredients,
|
||||||
|
@JsonKey(name: "total_sold_products") int? totalSoldProducts,
|
||||||
|
@JsonKey(name: "total_sold_ingredients") int? totalSoldIngredients,
|
||||||
|
@JsonKey(name: "outlet_id") String? outletId,
|
||||||
|
@JsonKey(name: "outlet_name") String? outletName,
|
||||||
|
@JsonKey(name: "generated_at") DateTime? generatedAt,
|
||||||
|
}) = _InventoryAnalyticSummaryDto;
|
||||||
|
|
||||||
|
factory InventoryAnalyticSummaryDto.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$InventoryAnalyticSummaryDtoFromJson(json);
|
||||||
|
|
||||||
|
// Optional mapper ke entity
|
||||||
|
InventoryAnalyticSummary toDomain() => InventoryAnalyticSummary(
|
||||||
|
totalProducts: totalProducts ?? 0,
|
||||||
|
totalIngredients: totalIngredients ?? 0,
|
||||||
|
totalValue: totalValue ?? 0,
|
||||||
|
lowStockProducts: lowStockProducts ?? 0,
|
||||||
|
lowStockIngredients: lowStockIngredients ?? 0,
|
||||||
|
zeroStockProducts: zeroStockProducts ?? 0,
|
||||||
|
zeroStockIngredients: zeroStockIngredients ?? 0,
|
||||||
|
totalSoldProducts: totalSoldProducts ?? 0,
|
||||||
|
totalSoldIngredients: totalSoldIngredients ?? 0,
|
||||||
|
outletId: outletId ?? '',
|
||||||
|
outletName: outletName ?? '',
|
||||||
|
generatedAt: generatedAt ?? DateTime.now(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class InventoryAnalyticProductItemDto with _$InventoryAnalyticProductItemDto {
|
||||||
|
const InventoryAnalyticProductItemDto._();
|
||||||
|
|
||||||
|
const factory InventoryAnalyticProductItemDto({
|
||||||
|
@JsonKey(name: "id") String? id,
|
||||||
|
@JsonKey(name: "product_id") String? productId,
|
||||||
|
@JsonKey(name: "product_name") String? productName,
|
||||||
|
@JsonKey(name: "category_name") String? categoryName,
|
||||||
|
@JsonKey(name: "quantity") int? quantity,
|
||||||
|
@JsonKey(name: "reorder_level") int? reorderLevel,
|
||||||
|
@JsonKey(name: "unit_cost") num? unitCost,
|
||||||
|
@JsonKey(name: "total_value") int? totalValue,
|
||||||
|
@JsonKey(name: "total_in") int? totalIn,
|
||||||
|
@JsonKey(name: "total_out") int? totalOut,
|
||||||
|
@JsonKey(name: "is_low_stock") bool? isLowStock,
|
||||||
|
@JsonKey(name: "is_zero_stock") bool? isZeroStock,
|
||||||
|
@JsonKey(name: "updated_at") DateTime? updatedAt,
|
||||||
|
}) = _InventoryAnalyticProductItemDto;
|
||||||
|
|
||||||
|
factory InventoryAnalyticProductItemDto.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$InventoryAnalyticProductItemDtoFromJson(json);
|
||||||
|
|
||||||
|
// Mapper ke domain (opsional)
|
||||||
|
InventoryAnalyticProductItem toDomain() => InventoryAnalyticProductItem(
|
||||||
|
id: id ?? '',
|
||||||
|
productId: productId ?? '',
|
||||||
|
productName: productName ?? '',
|
||||||
|
categoryName: categoryName ?? '',
|
||||||
|
quantity: quantity ?? 0,
|
||||||
|
reorderLevel: reorderLevel ?? 0,
|
||||||
|
unitCost: unitCost ?? 0,
|
||||||
|
totalValue: totalValue ?? 0,
|
||||||
|
totalIn: totalIn ?? 0,
|
||||||
|
totalOut: totalOut ?? 0,
|
||||||
|
isLowStock: isLowStock ?? false,
|
||||||
|
isZeroStock: isZeroStock ?? false,
|
||||||
|
updatedAt: updatedAt ?? DateTime.now(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class InventoryAnalyticIngredientItemDto
|
||||||
|
with _$InventoryAnalyticIngredientItemDto {
|
||||||
|
const InventoryAnalyticIngredientItemDto._();
|
||||||
|
|
||||||
|
const factory InventoryAnalyticIngredientItemDto({
|
||||||
|
@JsonKey(name: "id") String? id,
|
||||||
|
@JsonKey(name: "ingredient_id") String? ingredientId,
|
||||||
|
@JsonKey(name: "ingredient_name") String? ingredientName,
|
||||||
|
@JsonKey(name: "unit_name") String? unitName,
|
||||||
|
@JsonKey(name: "quantity") int? quantity,
|
||||||
|
@JsonKey(name: "reorder_level") int? reorderLevel,
|
||||||
|
@JsonKey(name: "unit_cost") num? unitCost,
|
||||||
|
@JsonKey(name: "total_value") int? totalValue,
|
||||||
|
@JsonKey(name: "total_in") int? totalIn,
|
||||||
|
@JsonKey(name: "total_out") int? totalOut,
|
||||||
|
@JsonKey(name: "is_low_stock") bool? isLowStock,
|
||||||
|
@JsonKey(name: "is_zero_stock") bool? isZeroStock,
|
||||||
|
@JsonKey(name: "updated_at") DateTime? updatedAt,
|
||||||
|
}) = _InventoryAnalyticIngredientItemDto;
|
||||||
|
|
||||||
|
factory InventoryAnalyticIngredientItemDto.fromJson(
|
||||||
|
Map<String, dynamic> json,
|
||||||
|
) => _$InventoryAnalyticIngredientItemDtoFromJson(json);
|
||||||
|
|
||||||
|
// Mapper ke domain (opsional)
|
||||||
|
InventoryAnalyticIngredientItem toDomain() => InventoryAnalyticIngredientItem(
|
||||||
|
id: id ?? '',
|
||||||
|
ingredientId: ingredientId ?? '',
|
||||||
|
ingredientName: ingredientName ?? '',
|
||||||
|
unitName: unitName ?? '',
|
||||||
|
quantity: quantity ?? 0,
|
||||||
|
reorderLevel: reorderLevel ?? 0,
|
||||||
|
unitCost: unitCost ?? 0,
|
||||||
|
totalValue: totalValue ?? 0,
|
||||||
|
totalIn: totalIn ?? 0,
|
||||||
|
totalOut: totalOut ?? 0,
|
||||||
|
isLowStock: isLowStock ?? false,
|
||||||
|
isZeroStock: isZeroStock ?? false,
|
||||||
|
updatedAt: updatedAt ?? DateTime.now(),
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -157,4 +157,28 @@ class AnalyticRepository implements IAnalyticRepository {
|
|||||||
return left(const AnalyticFailure.unexpectedError());
|
return left(const AnalyticFailure.unexpectedError());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Either<AnalyticFailure, InventoryAnalytic>> getInventory({
|
||||||
|
required DateTime dateFrom,
|
||||||
|
required DateTime dateTo,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
final result = await _dataProvider.fetchInventory(
|
||||||
|
dateFrom: dateFrom,
|
||||||
|
dateTo: dateTo,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.hasError) {
|
||||||
|
return left(result.error!);
|
||||||
|
}
|
||||||
|
|
||||||
|
final inventory = result.data!.toDomain();
|
||||||
|
|
||||||
|
return right(inventory);
|
||||||
|
} catch (e) {
|
||||||
|
log('getInventoryError', name: _logName, error: e);
|
||||||
|
return left(const AnalyticFailure.unexpectedError());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,8 @@ import 'package:apskel_pos_flutter_v2/application/analytic/category_analytic_loa
|
|||||||
as _i911;
|
as _i911;
|
||||||
import 'package:apskel_pos_flutter_v2/application/analytic/dashboard_analytic_loader/dashboard_analytic_loader_bloc.dart'
|
import 'package:apskel_pos_flutter_v2/application/analytic/dashboard_analytic_loader/dashboard_analytic_loader_bloc.dart'
|
||||||
as _i80;
|
as _i80;
|
||||||
|
import 'package:apskel_pos_flutter_v2/application/analytic/inventory_analytic_loader/inventory_analytic_loader_bloc.dart'
|
||||||
|
as _i651;
|
||||||
import 'package:apskel_pos_flutter_v2/application/analytic/payment_method_analytic_loader/payment_method_analytic_loader_bloc.dart'
|
import 'package:apskel_pos_flutter_v2/application/analytic/payment_method_analytic_loader/payment_method_analytic_loader_bloc.dart'
|
||||||
as _i733;
|
as _i733;
|
||||||
import 'package:apskel_pos_flutter_v2/application/analytic/product_analytic_loader/product_analytic_loader_bloc.dart'
|
import 'package:apskel_pos_flutter_v2/application/analytic/product_analytic_loader/product_analytic_loader_bloc.dart'
|
||||||
@ -320,6 +322,9 @@ extension GetItInjectableX on _i174.GetIt {
|
|||||||
gh.factory<_i911.CategoryAnalyticLoaderBloc>(
|
gh.factory<_i911.CategoryAnalyticLoaderBloc>(
|
||||||
() => _i911.CategoryAnalyticLoaderBloc(gh<_i346.IAnalyticRepository>()),
|
() => _i911.CategoryAnalyticLoaderBloc(gh<_i346.IAnalyticRepository>()),
|
||||||
);
|
);
|
||||||
|
gh.factory<_i651.InventoryAnalyticLoaderBloc>(
|
||||||
|
() => _i651.InventoryAnalyticLoaderBloc(gh<_i346.IAnalyticRepository>()),
|
||||||
|
);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
|
|
||||||
import '../../../../../application/analytic/category_analytic_loader/category_analytic_loader_bloc.dart';
|
import '../../../../../application/analytic/category_analytic_loader/category_analytic_loader_bloc.dart';
|
||||||
import '../../../../../application/analytic/dashboard_analytic_loader/dashboard_analytic_loader_bloc.dart';
|
import '../../../../../application/analytic/dashboard_analytic_loader/dashboard_analytic_loader_bloc.dart';
|
||||||
|
import '../../../../../application/analytic/inventory_analytic_loader/inventory_analytic_loader_bloc.dart';
|
||||||
import '../../../../../application/analytic/payment_method_analytic_loader/payment_method_analytic_loader_bloc.dart';
|
import '../../../../../application/analytic/payment_method_analytic_loader/payment_method_analytic_loader_bloc.dart';
|
||||||
import '../../../../../application/analytic/product_analytic_loader/product_analytic_loader_bloc.dart';
|
import '../../../../../application/analytic/product_analytic_loader/product_analytic_loader_bloc.dart';
|
||||||
import '../../../../../application/analytic/profit_loss_analytic_loader/profit_loss_analytic_loader_bloc.dart';
|
import '../../../../../application/analytic/profit_loss_analytic_loader/profit_loss_analytic_loader_bloc.dart';
|
||||||
@ -19,6 +20,7 @@ import '../../../../components/spaces/space.dart';
|
|||||||
import '../../../../router/app_router.gr.dart';
|
import '../../../../router/app_router.gr.dart';
|
||||||
import 'sections/report_category_section.dart';
|
import 'sections/report_category_section.dart';
|
||||||
import 'sections/report_dashboard_section.dart';
|
import 'sections/report_dashboard_section.dart';
|
||||||
|
import 'sections/report_inventory_section.dart';
|
||||||
import 'sections/report_payment_method_section.dart';
|
import 'sections/report_payment_method_section.dart';
|
||||||
import 'sections/report_product_section.dart';
|
import 'sections/report_product_section.dart';
|
||||||
import 'sections/report_profit_loss_section.dart';
|
import 'sections/report_profit_loss_section.dart';
|
||||||
@ -185,7 +187,20 @@ class ReportPage extends StatelessWidget implements AutoRouteWrapper {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
6 => Text(state.title),
|
6 =>
|
||||||
|
BlocBuilder<
|
||||||
|
InventoryAnalyticLoaderBloc,
|
||||||
|
InventoryAnalyticLoaderState
|
||||||
|
>(
|
||||||
|
builder: (context, inventory) {
|
||||||
|
return ReportInventorySection(
|
||||||
|
menu: reportMenus[state.selectedMenu],
|
||||||
|
state: inventory,
|
||||||
|
startDate: state.startDate,
|
||||||
|
endDate: state.endDate,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
7 =>
|
7 =>
|
||||||
BlocBuilder<
|
BlocBuilder<
|
||||||
CategoryAnalyticLoaderBloc,
|
CategoryAnalyticLoaderBloc,
|
||||||
@ -255,6 +270,12 @@ class ReportPage extends StatelessWidget implements AutoRouteWrapper {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
case 6:
|
case 6:
|
||||||
|
return context.read<InventoryAnalyticLoaderBloc>().add(
|
||||||
|
InventoryAnalyticLoaderEvent.fetched(
|
||||||
|
startDate: state.startDate,
|
||||||
|
endDate: state.endDate,
|
||||||
|
),
|
||||||
|
);
|
||||||
case 7:
|
case 7:
|
||||||
return context.read<CategoryAnalyticLoaderBloc>().add(
|
return context.read<CategoryAnalyticLoaderBloc>().add(
|
||||||
CategoryAnalyticLoaderEvent.fetched(
|
CategoryAnalyticLoaderEvent.fetched(
|
||||||
@ -316,7 +337,6 @@ class ReportPage extends StatelessWidget implements AutoRouteWrapper {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => getIt<CategoryAnalyticLoaderBloc>()
|
create: (context) => getIt<CategoryAnalyticLoaderBloc>()
|
||||||
..add(
|
..add(
|
||||||
@ -326,6 +346,15 @@ class ReportPage extends StatelessWidget implements AutoRouteWrapper {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
BlocProvider(
|
||||||
|
create: (context) => getIt<InventoryAnalyticLoaderBloc>()
|
||||||
|
..add(
|
||||||
|
InventoryAnalyticLoaderEvent.fetched(
|
||||||
|
startDate: DateTime.now().subtract(const Duration(days: 30)),
|
||||||
|
endDate: DateTime.now(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: this,
|
child: this,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -0,0 +1,171 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
import '../../../../../../application/analytic/inventory_analytic_loader/inventory_analytic_loader_bloc.dart';
|
||||||
|
import '../../../../../../common/data/report_menu.dart';
|
||||||
|
import '../../../../../../common/extension/extension.dart';
|
||||||
|
import '../../../../../../common/theme/theme.dart';
|
||||||
|
import '../../../../../components/error/analytic_error_state_widget.dart';
|
||||||
|
import '../../../../../components/loader/loader_with_text.dart';
|
||||||
|
import '../../../../../components/spaces/space.dart';
|
||||||
|
import '../../../../../components/widgets/report/report_header.dart';
|
||||||
|
import '../../../../../components/widgets/report/report_summary_card.dart';
|
||||||
|
import '../widgets/inventory/report_inventory_ingredient_tab.dart';
|
||||||
|
import '../widgets/inventory/report_inventory_product_tab.dart';
|
||||||
|
|
||||||
|
class ReportInventorySection extends StatefulWidget {
|
||||||
|
final ReportMenu menu;
|
||||||
|
final InventoryAnalyticLoaderState state;
|
||||||
|
final DateTime startDate;
|
||||||
|
final DateTime endDate;
|
||||||
|
|
||||||
|
const ReportInventorySection({
|
||||||
|
super.key,
|
||||||
|
required this.menu,
|
||||||
|
required this.state,
|
||||||
|
required this.startDate,
|
||||||
|
required this.endDate,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ReportInventorySection> createState() => _ReportInventorySectionState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ReportInventorySectionState extends State<ReportInventorySection> {
|
||||||
|
int _selectedTabIndex = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (widget.state.isFetching) {
|
||||||
|
return const Center(child: LoaderWithText());
|
||||||
|
}
|
||||||
|
return widget.state.failureOption.fold(
|
||||||
|
() => RefreshIndicator(
|
||||||
|
onRefresh: () {
|
||||||
|
context.read<InventoryAnalyticLoaderBloc>().add(
|
||||||
|
InventoryAnalyticLoaderEvent.fetched(
|
||||||
|
startDate: widget.startDate,
|
||||||
|
endDate: widget.endDate,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return Future.value();
|
||||||
|
},
|
||||||
|
child: ListView(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
children: [
|
||||||
|
ReportHeader(
|
||||||
|
menu: widget.menu,
|
||||||
|
endDate: widget.endDate,
|
||||||
|
startDate: widget.startDate,
|
||||||
|
),
|
||||||
|
_buildSummary(),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.only(top: 16),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
_buildTab('Produk', 0),
|
||||||
|
SpaceWidth(12),
|
||||||
|
_buildTab('Bahan Baku', 1),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SpaceHeight(16),
|
||||||
|
|
||||||
|
_selectedTabIndex == 0
|
||||||
|
? ReportInventoryProductTab(
|
||||||
|
data: widget.state.inventoryAnalytic,
|
||||||
|
)
|
||||||
|
: ReportInventoryIngredientTab(
|
||||||
|
data: widget.state.inventoryAnalytic,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(f) => AnalyticErrorStateWidget(
|
||||||
|
failure: f,
|
||||||
|
menu: widget.menu,
|
||||||
|
onRefresh: () {
|
||||||
|
context.read<InventoryAnalyticLoaderBloc>().add(
|
||||||
|
InventoryAnalyticLoaderEvent.fetched(
|
||||||
|
startDate: widget.startDate,
|
||||||
|
endDate: widget.endDate,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Padding _buildSummary() {
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsetsGeometry.only(top: 16),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: ReportSummaryCard(
|
||||||
|
color: AppColor.success,
|
||||||
|
icon: Icons.inventory_2_outlined,
|
||||||
|
title: 'Total Produk',
|
||||||
|
value: widget.state.inventoryAnalytic.summary.totalProducts
|
||||||
|
.toString(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SpaceWidth(12),
|
||||||
|
Expanded(
|
||||||
|
child: ReportSummaryCard(
|
||||||
|
color: AppColor.info,
|
||||||
|
icon: Icons.list_alt_outlined,
|
||||||
|
title: 'Total Bahan',
|
||||||
|
value: widget.state.inventoryAnalytic.summary.totalIngredients
|
||||||
|
.toString(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SpaceWidth(12),
|
||||||
|
Expanded(
|
||||||
|
child: ReportSummaryCard(
|
||||||
|
color: AppColor.primary,
|
||||||
|
icon: Icons.monetization_on_outlined,
|
||||||
|
title: 'Total Nilai',
|
||||||
|
value: widget
|
||||||
|
.state
|
||||||
|
.inventoryAnalytic
|
||||||
|
.summary
|
||||||
|
.totalValue
|
||||||
|
.currencyFormatRpV2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildTab(String title, int index) {
|
||||||
|
bool isActive = _selectedTabIndex == index;
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_selectedTabIndex = index;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: isActive ? AppColor.primary : AppColor.white,
|
||||||
|
borderRadius: BorderRadius.circular(25),
|
||||||
|
border: Border.all(
|
||||||
|
color: isActive ? AppColor.primary : AppColor.border,
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
title,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: isActive ? AppColor.white : AppColor.textSecondary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,210 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../../../../../../../common/theme/theme.dart';
|
||||||
|
import '../../../../../../../domain/analytic/analytic.dart';
|
||||||
|
import '../../../../../../components/spaces/space.dart';
|
||||||
|
import '../../../../../../components/widgets/report/report_summary_card.dart';
|
||||||
|
|
||||||
|
class ReportInventoryIngredientTab extends StatelessWidget {
|
||||||
|
final InventoryAnalytic data;
|
||||||
|
const ReportInventoryIngredientTab({super.key, required this.data});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: ReportSummaryCard(
|
||||||
|
color: AppColor.success,
|
||||||
|
icon: Icons.warning_amber_outlined,
|
||||||
|
title: 'Low Stok',
|
||||||
|
value: data.summary.lowStockIngredients.toString(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SpaceWidth(12),
|
||||||
|
Expanded(
|
||||||
|
child: ReportSummaryCard(
|
||||||
|
color: AppColor.error,
|
||||||
|
icon: Icons.block_outlined,
|
||||||
|
title: 'Zero Stok',
|
||||||
|
value: data.summary.zeroStockIngredients.toString(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SpaceWidth(12),
|
||||||
|
Expanded(
|
||||||
|
child: ReportSummaryCard(
|
||||||
|
color: AppColor.error,
|
||||||
|
icon: Icons.shopping_cart_checkout_outlined,
|
||||||
|
title: 'Total Sold',
|
||||||
|
value: data.summary.totalSoldIngredients.toString(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColor.primary, // Purple color
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(8),
|
||||||
|
topRight: Radius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Table(
|
||||||
|
columnWidths: const {
|
||||||
|
0: FlexColumnWidth(2.5), // Name
|
||||||
|
1: FlexColumnWidth(1), // Stock
|
||||||
|
2: FlexColumnWidth(2), // Masuk
|
||||||
|
3: FlexColumnWidth(2), // Keluar
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
TableRow(
|
||||||
|
children: [
|
||||||
|
_buildHeaderCell('Nama'),
|
||||||
|
_buildHeaderCell('Stock'),
|
||||||
|
_buildHeaderCell('Masuk'),
|
||||||
|
_buildHeaderCell('Keluar'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(color: AppColor.white),
|
||||||
|
child: Table(
|
||||||
|
columnWidths: {
|
||||||
|
0: FlexColumnWidth(2.5), // Name
|
||||||
|
1: FlexColumnWidth(1), // Stock
|
||||||
|
2: FlexColumnWidth(2), // Masuk
|
||||||
|
3: FlexColumnWidth(2), // Keluar
|
||||||
|
},
|
||||||
|
children: data.ingredients
|
||||||
|
.map(
|
||||||
|
(item) => _buildIngredientsDataRow(
|
||||||
|
item,
|
||||||
|
data.ingredients.indexOf(item) % 2 == 0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColor.primary, // Purple color
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
bottomLeft: Radius.circular(8),
|
||||||
|
bottomRight: Radius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Table(
|
||||||
|
columnWidths: const {
|
||||||
|
0: FlexColumnWidth(2.5), // Name
|
||||||
|
1: FlexColumnWidth(1), // Stock
|
||||||
|
2: FlexColumnWidth(2), // Masuk
|
||||||
|
3: FlexColumnWidth(2), // Keluar
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
TableRow(
|
||||||
|
children: [
|
||||||
|
_buildTotalCell('TOTAL'),
|
||||||
|
_buildTotalCell(
|
||||||
|
(data.ingredients.fold<num>(
|
||||||
|
0,
|
||||||
|
(sum, item) => sum + (item.quantity),
|
||||||
|
)).toString(),
|
||||||
|
),
|
||||||
|
_buildTotalCell(
|
||||||
|
(data.ingredients.fold<num>(
|
||||||
|
0,
|
||||||
|
(sum, item) => sum + (item.totalIn),
|
||||||
|
)).toString(),
|
||||||
|
),
|
||||||
|
_buildTotalCell(
|
||||||
|
(data.ingredients.fold<num>(
|
||||||
|
0,
|
||||||
|
(sum, item) => sum + (item.totalOut),
|
||||||
|
)).toString(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TableRow _buildIngredientsDataRow(
|
||||||
|
InventoryAnalyticIngredientItem item,
|
||||||
|
bool isEven,
|
||||||
|
) {
|
||||||
|
return TableRow(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: item.isZeroStock
|
||||||
|
? Colors.red.shade100
|
||||||
|
: item.isLowStock
|
||||||
|
? Colors.yellow.shade100
|
||||||
|
: isEven
|
||||||
|
? Colors.grey.shade50
|
||||||
|
: AppColor.white,
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
_buildDataCell(item.ingredientName, alignment: Alignment.centerLeft),
|
||||||
|
_buildDataCell(item.quantity.toString()),
|
||||||
|
_buildDataCell(item.totalIn.toString()),
|
||||||
|
_buildDataCell(item.totalOut.toString()),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildDataCell(
|
||||||
|
String text, {
|
||||||
|
Alignment alignment = Alignment.center,
|
||||||
|
Color? textColor,
|
||||||
|
}) {
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 16),
|
||||||
|
alignment: alignment,
|
||||||
|
child: Text(
|
||||||
|
text,
|
||||||
|
style: AppStyle.sm.copyWith(
|
||||||
|
color: textColor ?? AppColor.black,
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
),
|
||||||
|
textAlign: alignment == Alignment.centerLeft
|
||||||
|
? TextAlign.left
|
||||||
|
: TextAlign.center,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildHeaderCell(String text) {
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 16),
|
||||||
|
child: Text(
|
||||||
|
text,
|
||||||
|
style: AppStyle.sm.copyWith(
|
||||||
|
color: AppColor.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildTotalCell(String text) {
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 16),
|
||||||
|
child: Text(
|
||||||
|
text,
|
||||||
|
style: AppStyle.sm.copyWith(
|
||||||
|
color: AppColor.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,216 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../../../../../../../common/theme/theme.dart';
|
||||||
|
import '../../../../../../../domain/analytic/analytic.dart';
|
||||||
|
import '../../../../../../components/spaces/space.dart';
|
||||||
|
import '../../../../../../components/widgets/report/report_summary_card.dart';
|
||||||
|
|
||||||
|
class ReportInventoryProductTab extends StatelessWidget {
|
||||||
|
final InventoryAnalytic data;
|
||||||
|
const ReportInventoryProductTab({super.key, required this.data});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: ReportSummaryCard(
|
||||||
|
color: AppColor.success,
|
||||||
|
icon: Icons.warning_amber_outlined,
|
||||||
|
title: 'Low Stok',
|
||||||
|
value: data.summary.lowStockProducts.toString(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SpaceWidth(12),
|
||||||
|
Expanded(
|
||||||
|
child: ReportSummaryCard(
|
||||||
|
color: AppColor.error,
|
||||||
|
icon: Icons.block_outlined,
|
||||||
|
title: 'Zero Stok',
|
||||||
|
value: data.summary.zeroStockProducts.toString(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SpaceWidth(12),
|
||||||
|
Expanded(
|
||||||
|
child: ReportSummaryCard(
|
||||||
|
color: AppColor.error,
|
||||||
|
icon: Icons.shopping_cart_checkout_outlined,
|
||||||
|
title: 'Total Sold',
|
||||||
|
value: data.summary.totalSoldProducts.toString(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColor.primary, // Purple color
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(8),
|
||||||
|
topRight: Radius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Table(
|
||||||
|
columnWidths: const {
|
||||||
|
0: FlexColumnWidth(2.5), // Produk
|
||||||
|
1: FlexColumnWidth(2), // Kategori
|
||||||
|
2: FlexColumnWidth(1), // Stock
|
||||||
|
3: FlexColumnWidth(2), // Masuk
|
||||||
|
4: FlexColumnWidth(2), // Keluar
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
TableRow(
|
||||||
|
children: [
|
||||||
|
_buildHeaderCell('Nama'),
|
||||||
|
_buildHeaderCell('Kategori'),
|
||||||
|
_buildHeaderCell('Stock'),
|
||||||
|
_buildHeaderCell('Masuk'),
|
||||||
|
_buildHeaderCell('Keluar'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(color: AppColor.white),
|
||||||
|
child: Table(
|
||||||
|
columnWidths: {
|
||||||
|
0: FlexColumnWidth(2.5), // Produk
|
||||||
|
1: FlexColumnWidth(2), // Kategori
|
||||||
|
2: FlexColumnWidth(1), // Stock
|
||||||
|
3: FlexColumnWidth(2), // Masuk
|
||||||
|
4: FlexColumnWidth(2), // Keluar
|
||||||
|
},
|
||||||
|
children: data.products
|
||||||
|
.map(
|
||||||
|
(item) => _buildProductDataRow(
|
||||||
|
item,
|
||||||
|
data.products.indexOf(item) % 2 == 0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColor.primary, // Purple color
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
bottomLeft: Radius.circular(8),
|
||||||
|
bottomRight: Radius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Table(
|
||||||
|
columnWidths: const {
|
||||||
|
0: FlexColumnWidth(2.5), // Produk
|
||||||
|
1: FlexColumnWidth(2), // Kategori
|
||||||
|
2: FlexColumnWidth(1), // Stock
|
||||||
|
3: FlexColumnWidth(2), // Masuk
|
||||||
|
4: FlexColumnWidth(2), // Keluar
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
TableRow(
|
||||||
|
children: [
|
||||||
|
_buildTotalCell('TOTAL'),
|
||||||
|
_buildTotalCell(''),
|
||||||
|
_buildTotalCell(
|
||||||
|
(data.products.fold<num>(
|
||||||
|
0,
|
||||||
|
(sum, item) => sum + (item.quantity),
|
||||||
|
)).toString(),
|
||||||
|
),
|
||||||
|
_buildTotalCell(
|
||||||
|
(data.products.fold<num>(
|
||||||
|
0,
|
||||||
|
(sum, item) => sum + (item.totalIn),
|
||||||
|
)).toString(),
|
||||||
|
),
|
||||||
|
_buildTotalCell(
|
||||||
|
(data.products.fold<num>(
|
||||||
|
0,
|
||||||
|
(sum, item) => sum + (item.totalOut),
|
||||||
|
)).toString(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TableRow _buildProductDataRow(
|
||||||
|
InventoryAnalyticProductItem product,
|
||||||
|
bool isEven,
|
||||||
|
) {
|
||||||
|
return TableRow(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: product.isZeroStock
|
||||||
|
? Colors.red.shade100
|
||||||
|
: product.isLowStock
|
||||||
|
? Colors.yellow.shade100
|
||||||
|
: isEven
|
||||||
|
? Colors.grey.shade50
|
||||||
|
: AppColor.white,
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
_buildDataCell(product.productName, alignment: Alignment.centerLeft),
|
||||||
|
_buildDataCell(product.categoryName, alignment: Alignment.centerLeft),
|
||||||
|
_buildDataCell(product.quantity.toString()),
|
||||||
|
_buildDataCell(product.totalIn.toString()),
|
||||||
|
_buildDataCell(product.totalOut.toString()),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildDataCell(
|
||||||
|
String text, {
|
||||||
|
Alignment alignment = Alignment.center,
|
||||||
|
Color? textColor,
|
||||||
|
}) {
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 16),
|
||||||
|
alignment: alignment,
|
||||||
|
child: Text(
|
||||||
|
text,
|
||||||
|
style: AppStyle.sm.copyWith(
|
||||||
|
color: textColor ?? AppColor.black,
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
),
|
||||||
|
textAlign: alignment == Alignment.centerLeft
|
||||||
|
? TextAlign.left
|
||||||
|
: TextAlign.center,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildHeaderCell(String text) {
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 16),
|
||||||
|
child: Text(
|
||||||
|
text,
|
||||||
|
style: AppStyle.sm.copyWith(
|
||||||
|
color: AppColor.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildTotalCell(String text) {
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 16),
|
||||||
|
child: Text(
|
||||||
|
text,
|
||||||
|
style: AppStyle.sm.copyWith(
|
||||||
|
color: AppColor.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user