fix(receipt-import): surface create-product errors and harden response parsing
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import '../../../core/api/api_exception.dart';
|
||||||
import '../../../core/api/api_paths.dart';
|
import '../../../core/api/api_paths.dart';
|
||||||
import '../../../core/api/api_providers.dart';
|
import '../../../core/api/api_providers.dart';
|
||||||
import '../../../core/ui/category_then_product_picker.dart';
|
import '../../../core/ui/category_then_product_picker.dart';
|
||||||
@@ -370,7 +371,16 @@ class _EditDialogState extends State<_EditDialog> {
|
|||||||
_newProductNameCtrl.text.trim(),
|
_newProductNameCtrl.text.trim(),
|
||||||
_newCategoryId!,
|
_newCategoryId!,
|
||||||
);
|
);
|
||||||
if (newProduct == null || !mounted) return;
|
if (newProduct == null || !mounted) {
|
||||||
|
if (mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(
|
||||||
|
content: Text('Kunde inte skapa produkten. Försök igen.'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!_localProducts.any((p) => p.id == newProduct.id)) {
|
if (!_localProducts.any((p) => p.id == newProduct.id)) {
|
||||||
_localProducts = [..._localProducts, newProduct];
|
_localProducts = [..._localProducts, newProduct];
|
||||||
}
|
}
|
||||||
@@ -379,6 +389,28 @@ class _EditDialogState extends State<_EditDialog> {
|
|||||||
_productCategoryId = _newCategoryId;
|
_productCategoryId = _newCategoryId;
|
||||||
_productCategoryPath = _newCategoryPath;
|
_productCategoryPath = _newCategoryPath;
|
||||||
_productCategorySource = _newCategorySource ?? CategorySelectionSource.manual;
|
_productCategorySource = _newCategorySource ?? CategorySelectionSource.manual;
|
||||||
|
} on ApiException catch (e) {
|
||||||
|
if (mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(
|
||||||
|
e.message.trim().isEmpty
|
||||||
|
? 'Kunde inte skapa produkten. Försök igen.'
|
||||||
|
: e.message,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} catch (_) {
|
||||||
|
if (mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(
|
||||||
|
content: Text('Kunde inte skapa produkten. Försök igen.'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
} finally {
|
} finally {
|
||||||
if (mounted) setState(() => _isCreatingProduct = false);
|
if (mounted) setState(() => _isCreatingProduct = false);
|
||||||
}
|
}
|
||||||
@@ -997,27 +1029,44 @@ class _ReceiptImportTabState extends ConsumerState<ReceiptImportTab> {
|
|||||||
categoryTree: _categoryTree,
|
categoryTree: _categoryTree,
|
||||||
initialEntryMode: initialEntryMode,
|
initialEntryMode: initialEntryMode,
|
||||||
onCreate: (name, categoryId) async {
|
onCreate: (name, categoryId) async {
|
||||||
try {
|
final token = await ref.read(authStateProvider.future);
|
||||||
final token = await ref.read(authStateProvider.future);
|
final api = ref.read(apiClientProvider);
|
||||||
final api = ref.read(apiClientProvider);
|
final raw = await api.postJson(
|
||||||
final data = await api.postJson(
|
ProductApiPaths.createPrivate,
|
||||||
ProductApiPaths.createPrivate,
|
body: {
|
||||||
body: {
|
'name': name.trim(),
|
||||||
'name': name.trim(),
|
'categoryId': categoryId,
|
||||||
'categoryId': categoryId,
|
},
|
||||||
},
|
token: token,
|
||||||
token: token,
|
);
|
||||||
) as Map<String, dynamic>;
|
|
||||||
final newProduct = (
|
if (raw is! Map<String, dynamic>) {
|
||||||
id: data['id'] as int,
|
throw Exception('Ogiltigt API-svar vid produktskapande.');
|
||||||
name: (data['canonicalName'] ?? data['name']) as String,
|
|
||||||
categoryId: categoryId,
|
|
||||||
);
|
|
||||||
if (mounted) setState(() => _products = [..._products, newProduct]);
|
|
||||||
return newProduct;
|
|
||||||
} catch (_) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final idRaw = raw['id'];
|
||||||
|
if (idRaw is! num) {
|
||||||
|
throw Exception('API-svar saknar giltigt produkt-id.');
|
||||||
|
}
|
||||||
|
|
||||||
|
final displayNameRaw = raw['canonicalName'] ?? raw['name'];
|
||||||
|
final displayName = displayNameRaw?.toString().trim();
|
||||||
|
if (displayName == null || displayName.isEmpty) {
|
||||||
|
throw Exception('API-svar saknar produktnamn.');
|
||||||
|
}
|
||||||
|
|
||||||
|
final returnedCategoryId = raw['categoryId'] is num
|
||||||
|
? (raw['categoryId'] as num).toInt()
|
||||||
|
: categoryId;
|
||||||
|
|
||||||
|
final newProduct = (
|
||||||
|
id: idRaw.toInt(),
|
||||||
|
name: displayName,
|
||||||
|
categoryId: returnedCategoryId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (mounted) setState(() => _products = [..._products, newProduct]);
|
||||||
|
return newProduct;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user