Enforce unique product barcodes

This commit is contained in:
beatz174-bit
2025-11-21 19:20:23 +10:00
parent b8b85dbbec
commit 7ee999e67f
4 changed files with 217 additions and 19 deletions
+41
View File
@@ -30,6 +30,7 @@ export const ManageProductsScreen = () => {
const [name, setName] = useState('');
const [category, setCategory] = useState('');
const [barcode, setBarcode] = useState('');
const [barcodeError, setBarcodeError] = useState('');
const [scannerOpen, setScannerOpen] = useState(false);
const [lookupStatus, setLookupStatus] = useState<'idle' | 'loading' | 'found' | 'notfound' | 'offline'>(
'idle',
@@ -66,6 +67,30 @@ export const ManageProductsScreen = () => {
return Array.from(new Set([...categoryNames, ...productCategories]));
}, [categories, products]);
const findBarcodeConflict = useCallback(
(value?: string, productId?: string) =>
value
? products.find((product) => product.barcode === value && product.id !== productId)
: undefined,
[products],
);
const assertUniqueBarcode = useCallback(
async (value?: string, productId?: string) => {
if (!value) return;
const conflict =
findBarcodeConflict(value, productId) ?? (await db.products.where('barcode').equals(value).first());
if (conflict && conflict.id !== productId) {
const error = new Error('This barcode is already assigned to another product.');
error.name = 'DuplicateBarcodeError';
throw error;
}
},
[db.products, findBarcodeConflict],
);
useEffect(() => {
if (categoryOptions.length === 0) return;
if (!categoryOptions.includes(category)) {
@@ -94,10 +119,22 @@ export const ManageProductsScreen = () => {
setLookupStatus('idle');
setExternalProduct(null);
}
setBarcodeError('');
}, [barcode]);
const addProduct = async () => {
if (!name || !category) return;
try {
await assertUniqueBarcode(barcode || undefined);
} catch (error) {
if (error instanceof Error && error.name === 'DuplicateBarcodeError') {
setBarcodeError(error.message);
return;
}
throw error;
}
await db.products.add({
id: uuidv4(),
name,
@@ -121,6 +158,8 @@ export const ManageProductsScreen = () => {
barcode?: string;
},
) => {
await assertUniqueBarcode(updates.barcode, productId);
await db.products.update(productId, {
...updates,
unit_type: DEFAULT_UNIT_TYPE,
@@ -185,6 +224,8 @@ export const ManageProductsScreen = () => {
label="Barcode"
value={barcode}
onChange={(event) => setBarcode(event.target.value)}
error={Boolean(barcodeError)}
helperText={barcodeError || undefined}
InputProps={{
endAdornment: (
<Button onClick={() => setBarcode('')} size="small">