2026-04-11 02:24:28 +02:00
|
|
|
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('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 locationUuid = nonEmptyText(lookupItem?.location_initial_uuid_b64);
|
2026-04-11 02:33:47 +02:00
|
|
|
if (locationUuid) {
|
2026-04-11 02:24:28 +02:00
|
|
|
const matchingLocation = locations.find((entry) => entry.uuid_b64 === locationUuid);
|
|
|
|
|
if (matchingLocation) {
|
2026-04-11 02:33:47 +02:00
|
|
|
setField('locationId', String(matchingLocation.id));
|
|
|
|
|
if (nextLocationSearch !== matchingLocation.name) {
|
|
|
|
|
nextLocationSearch = matchingLocation.name;
|
|
|
|
|
}
|
2026-04-11 02:24:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
form: nextForm,
|
|
|
|
|
locationSearch: nextLocationSearch,
|
|
|
|
|
didUpdate,
|
|
|
|
|
};
|
|
|
|
|
}
|