Add barcode scanner integration, identifier lookup, and enhanced field mapping for label creation
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
const ISO_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
|
||||
|
||||
function normalizedText(value) {
|
||||
return typeof value === 'string' ? value.trim() : '';
|
||||
}
|
||||
|
||||
function nonEmptyText(value) {
|
||||
const text = normalizedText(value);
|
||||
return text ? text : null;
|
||||
}
|
||||
|
||||
function normalizedNumberText(value) {
|
||||
if (typeof value === 'number' && Number.isFinite(value)) {
|
||||
return String(value);
|
||||
}
|
||||
|
||||
if (typeof value === 'string') {
|
||||
const text = value.trim();
|
||||
if (!text) {
|
||||
return null;
|
||||
}
|
||||
const parsed = Number(text);
|
||||
if (Number.isFinite(parsed)) {
|
||||
return String(parsed);
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
function parseIsoDate(isoDate) {
|
||||
if (!ISO_DATE_PATTERN.test(String(isoDate || ''))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const [year, month, day] = String(isoDate).split('-').map(Number);
|
||||
const parsed = new Date(year, month - 1, day);
|
||||
|
||||
if (
|
||||
parsed.getFullYear() !== year
|
||||
|| parsed.getMonth() !== month - 1
|
||||
|| parsed.getDate() !== day
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function addDaysToIsoDate(isoDate, days) {
|
||||
const parsed = parseIsoDate(isoDate);
|
||||
if (!parsed) {
|
||||
return '';
|
||||
}
|
||||
|
||||
parsed.setDate(parsed.getDate() + days);
|
||||
const year = parsed.getFullYear();
|
||||
const month = String(parsed.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(parsed.getDate()).padStart(2, '0');
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
function diffIsoDays(fromIsoDate, toIsoDate) {
|
||||
const fromDate = parseIsoDate(fromIsoDate);
|
||||
const toDate = parseIsoDate(toIsoDate);
|
||||
if (!fromDate || !toDate) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const millisecondsPerDay = 24 * 60 * 60 * 1000;
|
||||
const diff = Math.round((toDate - fromDate) / millisecondsPerDay);
|
||||
return diff >= 0 ? diff : null;
|
||||
}
|
||||
|
||||
function nonNegativeDays(value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const parsed = Number(value);
|
||||
if (!Number.isFinite(parsed) || parsed < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Math.round(parsed);
|
||||
}
|
||||
|
||||
export function deriveLookupExpirationDays(lookupItem) {
|
||||
const explicitDays = nonNegativeDays(lookupItem?.expiration_days);
|
||||
if (explicitDays !== null) {
|
||||
return explicitDays;
|
||||
}
|
||||
|
||||
return diffIsoDays(lookupItem?.date, lookupItem?.expire_date);
|
||||
}
|
||||
|
||||
export function mapLookupItemToForm({
|
||||
form,
|
||||
lookupItem,
|
||||
locations = [],
|
||||
}) {
|
||||
const nextForm = { ...form };
|
||||
let nextLocationSearch = null;
|
||||
let didUpdate = false;
|
||||
|
||||
const setField = (targetField, value) => {
|
||||
if (nextForm[targetField] === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
nextForm[targetField] = value;
|
||||
didUpdate = true;
|
||||
};
|
||||
|
||||
const textValue = (sourceField) => normalizedText(lookupItem?.[sourceField]);
|
||||
const numberValue = (sourceField) => normalizedNumberText(lookupItem?.[sourceField]);
|
||||
|
||||
setField('identifierCode', textValue('identifier_code'));
|
||||
setField('name', textValue('name'));
|
||||
setField('description', textValue('description'));
|
||||
setField('stockType', textValue('stock_type'));
|
||||
setField('level', textValue('level'));
|
||||
setField('quantity', numberValue('quantity_initial'));
|
||||
setField('uom', textValue('uom_symbol'));
|
||||
setField('energy', numberValue('calories'));
|
||||
setField('energyUnit', textValue('calories_unit'));
|
||||
setField('externalSource', textValue('external_source'));
|
||||
setField('externalId', textValue('external_id'));
|
||||
|
||||
setField('search', nextForm.name);
|
||||
|
||||
const expirationDays = deriveLookupExpirationDays(lookupItem);
|
||||
if (expirationDays !== null) {
|
||||
setField('expireDays', String(expirationDays));
|
||||
setField('expirationDate', addDaysToIsoDate(nextForm.productionDate, expirationDays));
|
||||
} else {
|
||||
setField('expireDays', '');
|
||||
setField('expirationDate', '');
|
||||
}
|
||||
|
||||
const canSetLocation = !String(nextForm.locationId || '').trim();
|
||||
const locationUuid = nonEmptyText(lookupItem?.location_initial_uuid_b64);
|
||||
if (canSetLocation && locationUuid) {
|
||||
const matchingLocation = locations.find((entry) => entry.uuid_b64 === locationUuid);
|
||||
if (matchingLocation) {
|
||||
nextForm.locationId = String(matchingLocation.id);
|
||||
nextLocationSearch = matchingLocation.name;
|
||||
didUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
form: nextForm,
|
||||
locationSearch: nextLocationSearch,
|
||||
didUpdate,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user