feat(approvals): 결재 접근 차단 대응과 전표 전이 메모 전달 강화
- approvals 모듈에서 APPROVAL_ACCESS_DENIED 응답을 포착하여 ApprovalAccessDeniedException으로 변환하고 접근 거부 시 토스트·대시보드 리다이렉트를 처리 - approval history 조회가 서버 action id에 맞춰 필터링되도록 repository·controller·테스트를 보강 - 재고 트랜잭션 상태 전이 API 호출에 note를 전달하도록 repository·컨트롤러·통합/단위 테스트를 업데이트 - 승인 플로우 QA 체크리스트 및 연동 문서를 최신 계약과 테스트 흐름으로 업데이트
This commit is contained in:
@@ -400,11 +400,12 @@ class InboundController extends ChangeNotifier {
|
||||
/// 재고 트랜잭션을 상신(submit)한다.
|
||||
Future<InboundRecord> submitTransaction(
|
||||
int id, {
|
||||
String? note,
|
||||
bool refreshAfter = true,
|
||||
StockTransactionListFilter? refreshFilter,
|
||||
}) {
|
||||
return _executeMutation(
|
||||
() => _transactionRepository.submit(id),
|
||||
() => _transactionRepository.submit(id, note: note),
|
||||
trackedId: id,
|
||||
refreshAfter: refreshAfter,
|
||||
refreshFilter: refreshFilter,
|
||||
@@ -414,11 +415,12 @@ class InboundController extends ChangeNotifier {
|
||||
/// 재고 트랜잭션을 완료 처리한다.
|
||||
Future<InboundRecord> completeTransaction(
|
||||
int id, {
|
||||
String? note,
|
||||
bool refreshAfter = true,
|
||||
StockTransactionListFilter? refreshFilter,
|
||||
}) {
|
||||
return _executeMutation(
|
||||
() => _transactionRepository.complete(id),
|
||||
() => _transactionRepository.complete(id, note: note),
|
||||
trackedId: id,
|
||||
refreshAfter: refreshAfter,
|
||||
refreshFilter: refreshFilter,
|
||||
@@ -428,11 +430,12 @@ class InboundController extends ChangeNotifier {
|
||||
/// 재고 트랜잭션을 승인 처리한다.
|
||||
Future<InboundRecord> approveTransaction(
|
||||
int id, {
|
||||
String? note,
|
||||
bool refreshAfter = true,
|
||||
StockTransactionListFilter? refreshFilter,
|
||||
}) {
|
||||
return _executeMutation(
|
||||
() => _transactionRepository.approve(id),
|
||||
() => _transactionRepository.approve(id, note: note),
|
||||
trackedId: id,
|
||||
refreshAfter: refreshAfter,
|
||||
refreshFilter: refreshFilter,
|
||||
@@ -442,11 +445,12 @@ class InboundController extends ChangeNotifier {
|
||||
/// 재고 트랜잭션을 반려 처리한다.
|
||||
Future<InboundRecord> rejectTransaction(
|
||||
int id, {
|
||||
String? note,
|
||||
bool refreshAfter = true,
|
||||
StockTransactionListFilter? refreshFilter,
|
||||
}) {
|
||||
return _executeMutation(
|
||||
() => _transactionRepository.reject(id),
|
||||
() => _transactionRepository.reject(id, note: note),
|
||||
trackedId: id,
|
||||
refreshAfter: refreshAfter,
|
||||
refreshFilter: refreshFilter,
|
||||
@@ -456,11 +460,12 @@ class InboundController extends ChangeNotifier {
|
||||
/// 재고 트랜잭션을 취소 처리한다.
|
||||
Future<InboundRecord> cancelTransaction(
|
||||
int id, {
|
||||
String? note,
|
||||
bool refreshAfter = true,
|
||||
StockTransactionListFilter? refreshFilter,
|
||||
}) {
|
||||
return _executeMutation(
|
||||
() => _transactionRepository.cancel(id),
|
||||
() => _transactionRepository.cancel(id, note: note),
|
||||
trackedId: id,
|
||||
refreshAfter: refreshAfter,
|
||||
refreshFilter: refreshFilter,
|
||||
|
||||
@@ -371,11 +371,12 @@ class OutboundController extends ChangeNotifier {
|
||||
/// 출고 트랜잭션을 상신한다.
|
||||
Future<OutboundRecord> submitTransaction(
|
||||
int id, {
|
||||
String? note,
|
||||
bool refreshAfter = true,
|
||||
StockTransactionListFilter? refreshFilter,
|
||||
}) {
|
||||
return _executeMutation(
|
||||
() => _transactionRepository.submit(id),
|
||||
() => _transactionRepository.submit(id, note: note),
|
||||
trackedId: id,
|
||||
refreshAfter: refreshAfter,
|
||||
refreshFilter: refreshFilter,
|
||||
@@ -385,11 +386,12 @@ class OutboundController extends ChangeNotifier {
|
||||
/// 출고 트랜잭션을 완료 처리한다.
|
||||
Future<OutboundRecord> completeTransaction(
|
||||
int id, {
|
||||
String? note,
|
||||
bool refreshAfter = true,
|
||||
StockTransactionListFilter? refreshFilter,
|
||||
}) {
|
||||
return _executeMutation(
|
||||
() => _transactionRepository.complete(id),
|
||||
() => _transactionRepository.complete(id, note: note),
|
||||
trackedId: id,
|
||||
refreshAfter: refreshAfter,
|
||||
refreshFilter: refreshFilter,
|
||||
@@ -399,11 +401,12 @@ class OutboundController extends ChangeNotifier {
|
||||
/// 출고 트랜잭션을 승인 처리한다.
|
||||
Future<OutboundRecord> approveTransaction(
|
||||
int id, {
|
||||
String? note,
|
||||
bool refreshAfter = true,
|
||||
StockTransactionListFilter? refreshFilter,
|
||||
}) {
|
||||
return _executeMutation(
|
||||
() => _transactionRepository.approve(id),
|
||||
() => _transactionRepository.approve(id, note: note),
|
||||
trackedId: id,
|
||||
refreshAfter: refreshAfter,
|
||||
refreshFilter: refreshFilter,
|
||||
@@ -413,11 +416,12 @@ class OutboundController extends ChangeNotifier {
|
||||
/// 출고 트랜잭션을 반려 처리한다.
|
||||
Future<OutboundRecord> rejectTransaction(
|
||||
int id, {
|
||||
String? note,
|
||||
bool refreshAfter = true,
|
||||
StockTransactionListFilter? refreshFilter,
|
||||
}) {
|
||||
return _executeMutation(
|
||||
() => _transactionRepository.reject(id),
|
||||
() => _transactionRepository.reject(id, note: note),
|
||||
trackedId: id,
|
||||
refreshAfter: refreshAfter,
|
||||
refreshFilter: refreshFilter,
|
||||
@@ -427,11 +431,12 @@ class OutboundController extends ChangeNotifier {
|
||||
/// 출고 트랜잭션을 취소 처리한다.
|
||||
Future<OutboundRecord> cancelTransaction(
|
||||
int id, {
|
||||
String? note,
|
||||
bool refreshAfter = true,
|
||||
StockTransactionListFilter? refreshFilter,
|
||||
}) {
|
||||
return _executeMutation(
|
||||
() => _transactionRepository.cancel(id),
|
||||
() => _transactionRepository.cancel(id, note: note),
|
||||
trackedId: id,
|
||||
refreshAfter: refreshAfter,
|
||||
refreshFilter: refreshFilter,
|
||||
|
||||
@@ -408,12 +408,13 @@ class RentalController extends ChangeNotifier {
|
||||
/// 대여/반납 트랜잭션을 상신한다.
|
||||
Future<RentalRecord> submitTransaction(
|
||||
int id, {
|
||||
String? note,
|
||||
bool refreshAfter = true,
|
||||
StockTransactionListFilter? refreshFilter,
|
||||
bool? refreshFilterByRentalTypes,
|
||||
}) {
|
||||
return _executeMutation(
|
||||
() => _transactionRepository.submit(id),
|
||||
() => _transactionRepository.submit(id, note: note),
|
||||
trackedId: id,
|
||||
refreshAfter: refreshAfter,
|
||||
refreshFilter: refreshFilter,
|
||||
@@ -424,12 +425,13 @@ class RentalController extends ChangeNotifier {
|
||||
/// 대여/반납 트랜잭션을 완료 처리한다.
|
||||
Future<RentalRecord> completeTransaction(
|
||||
int id, {
|
||||
String? note,
|
||||
bool refreshAfter = true,
|
||||
StockTransactionListFilter? refreshFilter,
|
||||
bool? refreshFilterByRentalTypes,
|
||||
}) {
|
||||
return _executeMutation(
|
||||
() => _transactionRepository.complete(id),
|
||||
() => _transactionRepository.complete(id, note: note),
|
||||
trackedId: id,
|
||||
refreshAfter: refreshAfter,
|
||||
refreshFilter: refreshFilter,
|
||||
@@ -440,12 +442,13 @@ class RentalController extends ChangeNotifier {
|
||||
/// 대여/반납 트랜잭션을 승인 처리한다.
|
||||
Future<RentalRecord> approveTransaction(
|
||||
int id, {
|
||||
String? note,
|
||||
bool refreshAfter = true,
|
||||
StockTransactionListFilter? refreshFilter,
|
||||
bool? refreshFilterByRentalTypes,
|
||||
}) {
|
||||
return _executeMutation(
|
||||
() => _transactionRepository.approve(id),
|
||||
() => _transactionRepository.approve(id, note: note),
|
||||
trackedId: id,
|
||||
refreshAfter: refreshAfter,
|
||||
refreshFilter: refreshFilter,
|
||||
@@ -456,12 +459,13 @@ class RentalController extends ChangeNotifier {
|
||||
/// 대여/반납 트랜잭션을 반려 처리한다.
|
||||
Future<RentalRecord> rejectTransaction(
|
||||
int id, {
|
||||
String? note,
|
||||
bool refreshAfter = true,
|
||||
StockTransactionListFilter? refreshFilter,
|
||||
bool? refreshFilterByRentalTypes,
|
||||
}) {
|
||||
return _executeMutation(
|
||||
() => _transactionRepository.reject(id),
|
||||
() => _transactionRepository.reject(id, note: note),
|
||||
trackedId: id,
|
||||
refreshAfter: refreshAfter,
|
||||
refreshFilter: refreshFilter,
|
||||
@@ -472,12 +476,13 @@ class RentalController extends ChangeNotifier {
|
||||
/// 대여/반납 트랜잭션을 취소 처리한다.
|
||||
Future<RentalRecord> cancelTransaction(
|
||||
int id, {
|
||||
String? note,
|
||||
bool refreshAfter = true,
|
||||
StockTransactionListFilter? refreshFilter,
|
||||
bool? refreshFilterByRentalTypes,
|
||||
}) {
|
||||
return _executeMutation(
|
||||
() => _transactionRepository.cancel(id),
|
||||
() => _transactionRepository.cancel(id, note: note),
|
||||
trackedId: id,
|
||||
refreshAfter: refreshAfter,
|
||||
refreshFilter: refreshFilter,
|
||||
|
||||
@@ -82,50 +82,43 @@ class StockTransactionRepositoryRemote implements StockTransactionRepository {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<StockTransaction> submit(int id) async {
|
||||
final response = await _api.post<Map<String, dynamic>>(
|
||||
'$_basePath/$id/submit',
|
||||
data: {'id': id},
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
return _parseSingle(response.data);
|
||||
Future<StockTransaction> submit(int id, {String? note}) async {
|
||||
return _postTransition(id: id, action: 'submit', note: note);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<StockTransaction> complete(int id) async {
|
||||
final response = await _api.post<Map<String, dynamic>>(
|
||||
'$_basePath/$id/complete',
|
||||
data: {'id': id},
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
return _parseSingle(response.data);
|
||||
Future<StockTransaction> complete(int id, {String? note}) async {
|
||||
return _postTransition(id: id, action: 'complete', note: note);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<StockTransaction> approve(int id) async {
|
||||
final response = await _api.post<Map<String, dynamic>>(
|
||||
'$_basePath/$id/approve',
|
||||
data: {'id': id},
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
return _parseSingle(response.data);
|
||||
Future<StockTransaction> approve(int id, {String? note}) async {
|
||||
return _postTransition(id: id, action: 'approve', note: note);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<StockTransaction> reject(int id) async {
|
||||
final response = await _api.post<Map<String, dynamic>>(
|
||||
'$_basePath/$id/reject',
|
||||
data: {'id': id},
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
return _parseSingle(response.data);
|
||||
Future<StockTransaction> reject(int id, {String? note}) async {
|
||||
return _postTransition(id: id, action: 'reject', note: note);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<StockTransaction> cancel(int id) async {
|
||||
Future<StockTransaction> cancel(int id, {String? note}) async {
|
||||
return _postTransition(id: id, action: 'cancel', note: note);
|
||||
}
|
||||
|
||||
Future<StockTransaction> _postTransition({
|
||||
required int id,
|
||||
required String action,
|
||||
String? note,
|
||||
}) async {
|
||||
final payload = <String, dynamic>{'id': id};
|
||||
final trimmedNote = note?.trim();
|
||||
if (trimmedNote != null && trimmedNote.isNotEmpty) {
|
||||
payload['note'] = trimmedNote;
|
||||
}
|
||||
final response = await _api.post<Map<String, dynamic>>(
|
||||
'$_basePath/$id/cancel',
|
||||
data: {'id': id},
|
||||
'$_basePath/$id/$action',
|
||||
data: payload,
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
return _parseSingle(response.data);
|
||||
|
||||
@@ -29,19 +29,29 @@ abstract class StockTransactionRepository {
|
||||
Future<StockTransaction> restore(int id);
|
||||
|
||||
/// 재고 트랜잭션을 상신(submit)한다.
|
||||
Future<StockTransaction> submit(int id);
|
||||
///
|
||||
/// [note]는 상태 전이 사유를 서버에 전달할 때 사용된다.
|
||||
Future<StockTransaction> submit(int id, {String? note});
|
||||
|
||||
/// 재고 트랜잭션을 완료 처리한다.
|
||||
Future<StockTransaction> complete(int id);
|
||||
///
|
||||
/// [note]는 상태 전이 사유를 서버에 전달할 때 사용된다.
|
||||
Future<StockTransaction> complete(int id, {String? note});
|
||||
|
||||
/// 재고 트랜잭션을 승인 처리한다.
|
||||
Future<StockTransaction> approve(int id);
|
||||
///
|
||||
/// [note]는 상태 전이 사유를 서버에 전달할 때 사용된다.
|
||||
Future<StockTransaction> approve(int id, {String? note});
|
||||
|
||||
/// 재고 트랜잭션을 반려 처리한다.
|
||||
Future<StockTransaction> reject(int id);
|
||||
///
|
||||
/// [note]는 상태 전이 사유를 서버에 전달할 때 사용된다.
|
||||
Future<StockTransaction> reject(int id, {String? note});
|
||||
|
||||
/// 재고 트랜잭션을 취소 처리한다.
|
||||
Future<StockTransaction> cancel(int id);
|
||||
///
|
||||
/// [note]는 상태 전이 사유를 서버에 전달할 때 사용된다.
|
||||
Future<StockTransaction> cancel(int id, {String? note});
|
||||
}
|
||||
|
||||
/// 재고 트랜잭션 라인 저장소 인터페이스.
|
||||
|
||||
Reference in New Issue
Block a user