From da2cdd5b23104a1e87af4371e26d598ff979560c Mon Sep 17 00:00:00 2001 From: seb Date: Sun, 18 Oct 2026 14:20:35 +0200 Subject: [PATCH] u --- src/store/articlehistory/record.js | 1226 +++++++--------------------- 1 file changed, 304 insertions(+), 922 deletions(-) diff --git a/src/store/articlehistory/record.js b/src/store/articlehistory/record.js index 8df7952..51d53cd 100644 --- a/src/store/articlehistory/record.js +++ b/src/store/articlehistory/record.js @@ -1,334 +1,209 @@ +/** + * ============================================================================ + * ARTICLE HISTORY LOGGER + * ============================================================================ + * Recorded History Event Types (`meta.type`): + * + * 1. Orders & Inventory Ledger + * - `order` : Customer order placement (`recordOrderHistory`) + * - `acceptance` : Incoming supplier delivery without warehouse (`recordAcceptanceHistory`) + * - `shipment` : Outgoing customer shipment (`recordShipmentHistory`) + * - `warehouse_in` : Inbound warehouse movement (`recordWarehouseHistory`) + * - `warehouse_out` : Outbound warehouse movement (`recordWarehouseHistory`) + * - `delivery` : Legacy incoming delivery with warehouse (`recordDeliveryHistory`) + * - `movement` : Legacy generic warehouse movement (`recordDeliveryHistory`) + * - `stock_adjustment` : Manual stock correction (`recordStockAdjustmentHistory`) + * + * 2. Core Article Lifecycle & Master Data + * - `article_created` : Initial article creation (`recordArticleCreatedHistory`) + * - `article_imported` : Article created via import (`recordArticleImportedHistory`) + * - `article_update` : General field updates (`recordArticleUpdateHistory`) + * - `price_change` : Price tier / net / tax modifications (`recordPriceChangeHistory`) + * - `category_change` : Category assignment changes (`recordCategoryChangeHistory`) + * - `brand_change` : Brand name updates (`recordBrandChangeHistory`) + * - `ean_change` : EAN list updates (`recordEanChangeHistory`) + * - `weight_change` : Article weight changes (`recordWeightChangeHistory`) + * - `track_stock_change` : Stock tracking toggle (`recordTrackStockChangeHistory`) + * + * 3. Variants & Parent/Child Relationships + * - `parent_converted` : Article converted to parent (`recordParentConvertedHistory`) + * - `parent_deactivated` : Parent mode disabled (`recordParentDeactivatedHistory`) + * - `variant_added_to_parent` : Variant linked on parent record (`recordVariantAddedOnParentHistory`) + * - `child_created_under_parent` : New variant created under parent (`recordChildAssignedToParentHistory`) + * - `child_assigned_to_parent` : Existing article assigned as variant (`recordChildAssignedToParentHistory`) + * - `child_unlinked` : Child unlinked from parent - child log (`recordChildUnlinkedFromParentHistory`) + * - `variant_separated` : Variant separated from parent - parent log (`recordChildUnlinkedFromParentHistory`) + * - `variant_deleted` : Variant deleted from parent (`recordChildDeletedFromParentHistory`) + * + * 4. Suppliers + * - `supplier_add` : Supplier linked to article (`recordSupplierAddHistory`) + * - `supplier_update` : Supplier details modified (`recordSupplierUpdateHistory`) + * - `supplier_delete` : Supplier unlinked from article (`recordSupplierDeleteHistory`) + * + * 5. Properties, Attributes & Bills of Materials (BOM) + * - `property_change` : Property key/value changes (`recordPropertyChangeHistory`) + * - `property_delete` : Single property removed (`recordPropertyDeleteHistory`) + * - `attribute_change` : Variant/article attribute changes (`recordAttributeChangeHistory`) + * - `articlelist_change` : Stückliste / component BOM set (`recordArticleListChangeHistory`) + * - `optionslist_change` : Optionsliste set (`recordOptionsListChangeHistory`) + * + * 6. Media & Images + * - `image_add` : Image uploaded/linked (`recordImageAddHistory`) + * - `image_delete` : Image removed (`recordImageDeleteHistory`) + * - `image_order` : Image gallery reordered (`recordImageOrderHistory`) + * ============================================================================ + */ + import { INSERT as MSSQL_INSERT } from '../../providers/mssql/queries/articlehistory.js'; import { INSERT as SQLITE_INSERT } from '../../providers/sqlite3/queries/articlehistory.js'; -/** - * Formats a numeric currency amount in German language style: "€ 1.000,99" - * - * @param {number|string} amount - The currency amount. - * @returns {string} The formatted currency string. - */ +// #region Constants & Internal Helpers +// MARK: - Constants & Internal Helpers + +export const ARTICLE_HISTORY_TYPES = Object.freeze({ + ORDER: 'order', ACCEPTANCE: 'acceptance', SHIPMENT: 'shipment', + WAREHOUSE_IN: 'warehouse_in', WAREHOUSE_OUT: 'warehouse_out', + DELIVERY: 'delivery', MOVEMENT: 'movement', STOCK_ADJUSTMENT: 'stock_adjustment', + PRICE_CHANGE: 'price_change', CATEGORY_CHANGE: 'category_change', + BRAND_CHANGE: 'brand_change', EAN_CHANGE: 'ean_change', + WEIGHT_CHANGE: 'weight_change', TRACK_STOCK_CHANGE: 'track_stock_change', + ARTICLE_UPDATE: 'article_update', ARTICLE_CREATED: 'article_created', ARTICLE_IMPORTED: 'article_imported', + PARENT_CONVERTED: 'parent_converted', PARENT_DEACTIVATED: 'parent_deactivated', + VARIANT_ADDED_TO_PARENT: 'variant_added_to_parent', + CHILD_CREATED_UNDER_PARENT: 'child_created_under_parent', CHILD_ASSIGNED_TO_PARENT: 'child_assigned_to_parent', + CHILD_UNLINKED: 'child_unlinked', VARIANT_SEPARATED: 'variant_separated', VARIANT_DELETED: 'variant_deleted', + SUPPLIER_ADD: 'supplier_add', SUPPLIER_UPDATE: 'supplier_update', SUPPLIER_DELETE: 'supplier_delete', + PROPERTY_CHANGE: 'property_change', PROPERTY_DELETE: 'property_delete', ATTRIBUTE_CHANGE: 'attribute_change', + ARTICLELIST_CHANGE: 'articlelist_change', OPTIONSLIST_CHANGE: 'optionslist_change', + IMAGE_ADD: 'image_add', IMAGE_DELETE: 'image_delete', IMAGE_ORDER: 'image_order', +}); + +const DE_CURRENCY_FMT = new Intl.NumberFormat('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); +const isFiniteNum = (val) => val != null && Number.isFinite(Number(val)); +const toNumOrNull = (val) => (val != null ? Number(val) : null); +const round2 = (val) => Math.round(val * 100) / 100; +const fmtSuffix = (details) => (details.length ? ` (${details.join(', ')})` : ''); + +/** Formats a numeric currency amount in German language style: "€ 1.000,99" */ export function formatCurrencyDe(amount) { - if (amount == null || !Number.isFinite(Number(amount))) return ''; - const num = Number(amount); - const formatted = new Intl.NumberFormat('de-DE', { - minimumFractionDigits: 2, - maximumFractionDigits: 2, - }).format(num); - return `€ ${formatted}`; + return isFiniteNum(amount) ? `€ ${DE_CURRENCY_FMT.format(Number(amount))}` : ''; } -/** - * Calculates unit gross price given net, rebate, and taxrate. - * - * @param {number|string} net - Unit net price. - * @param {number|string} [rebate=0] - Rebate percentage. - * @param {number|string} [taxrate=0] - Tax rate percentage. - * @returns {number} The calculated gross price. - */ +/** Calculates unit gross price given net, rebate, and taxrate. */ export function calculateGross(net, rebate = 0, taxrate = 0) { - const n = Number(net || 0); - const r = Number(rebate || 0); - const t = Number(taxrate || 0); - return n * (1 - r / 100) * (1 + t / 100); + return Number(net || 0) * (1 - Number(rebate || 0) / 100) * (1 + Number(taxrate || 0) / 100); } -/** - * Core function for recording an article history entry. - * Serializes meta into a JSON string if provided as an object. - * - * @param {object} runner - The database runner. - * @param {object} params - Insertion parameters. - * @param {number} params.articleId - The ID of the article. - * @param {number|null} [params.companyuserId] - The ID of the acting company user. - * @param {string} params.text - The human-readable history text. - * @param {object|string|null} [params.meta] - Structured meta object or JSON string. - * @param {string} [params.type] - Optional fallback type if meta is not an object. - * @returns {Promise} Query result. - */ -export async function recordArticleHistory(runner, { - articleId, - companyuserId, - date, - text, - meta, - type, -}) { - if (!articleId) { - throw new Error('articleId required for article history'); - } +// #endregion + +// #region Core Persistence +// MARK: - Core Persistence + +/** Core function for recording an article history entry. Serializes meta into a JSON string if provided as an object. */ +export async function recordArticleHistory(runner, { articleId, companyuserId, date, text, meta, type }) { + if (!articleId) throw new Error('articleId required for article history'); const cleanText = typeof text === 'string' ? text.trim() : ''; - if (!cleanText) { - throw new Error('text required for article history'); - } + if (!cleanText) throw new Error('text required for article history'); - const isSqlite = runner?.dialect === 'sqlite3'; - const insertSql = isSqlite ? SQLITE_INSERT : MSSQL_INSERT; - - let metaObj = meta; - if (metaObj === undefined || metaObj === null) { - metaObj = type ? { type } : null; - } else if (typeof metaObj === 'object' && type && !metaObj.type) { - metaObj = { type, ...metaObj }; - } - - let serializedMeta = null; - if (metaObj !== undefined && metaObj !== null) { - serializedMeta = typeof metaObj === 'string' ? metaObj : JSON.stringify(metaObj); - } + let metaObj = meta ?? (type ? { type } : null); + if (metaObj && typeof metaObj === 'object' && type && !metaObj.type) metaObj = { type, ...metaObj }; + const serializedMeta = metaObj == null ? null : (typeof metaObj === 'string' ? metaObj : JSON.stringify(metaObj)); let parsedDate = null; if (date != null) { - parsedDate = date instanceof Date ? date : new Date(date); - if (Number.isNaN(parsedDate.getTime())) { - parsedDate = null; - } + const d = date instanceof Date ? date : new Date(date); + if (!Number.isNaN(d.getTime())) parsedDate = d; } - return runner.query(insertSql, { - articleId, - companyuserId: companyuserId ?? null, - date: parsedDate, - text: cleanText, - meta: serializedMeta, - }); + const insertSql = runner?.dialect === 'sqlite3' ? SQLITE_INSERT : MSSQL_INSERT; + return runner.query(insertSql, { articleId, companyuserId: companyuserId ?? null, date: parsedDate, text: cleanText, meta: serializedMeta }); } -/** - * Records an order entry in article history, including German currency formatted gross price. - * Format: "Auftrag {number} (Kunde: "{customerName}", Menge: {amount}, € 1.000,99)" - */ -export async function recordOrderHistory(runner, { - articleId, - companyuserId, - date, - orderId, - number, - customerName, - amount, - net, - rebate, - taxrate, -}) { +// #endregion + +// #region Orders, Deliveries & Warehouse Ledger +// MARK: - Orders, Deliveries & Warehouse Ledger + +/** Records an order entry: "Auftrag {number} (Kunde: "{customerName}", Menge: {amount}, € 1.000,99)" */ +export async function recordOrderHistory(runner, { articleId, companyuserId, date, orderId, number, customerName, amount, net, rebate, taxrate }) { const details = []; - if (customerName) { - details.push(`Kunde: "${customerName}"`); - } - if (amount != null && Number.isFinite(Number(amount))) { - details.push(`Menge: ${amount}`); - } + if (customerName) details.push(`Kunde: "${customerName}"`); + if (isFiniteNum(amount)) details.push(`Menge: ${amount}`); + let gross = null; - if (net != null && Number.isFinite(Number(net))) { + if (isFiniteNum(net)) { gross = calculateGross(net, rebate, taxrate); const formattedGross = formatCurrencyDe(gross); - if (formattedGross) { - details.push(formattedGross); - } + if (formattedGross) details.push(formattedGross); } - const suffix = details.length ? ` (${details.join(', ')})` : ''; - const text = `Auftrag ${number}${suffix}`; const meta = { - type: 'order', - ...(orderId != null ? { orderId } : {}), - orderNumber: number, - customerName: customerName ?? null, - amount: amount != null ? Number(amount) : null, - net: net != null ? Number(net) : null, - rebate: rebate != null ? Number(rebate) : 0, - taxrate: taxrate != null ? Number(taxrate) : 0, - gross: gross != null ? Math.round(gross * 100) / 100 : null, + type: 'order', ...(orderId != null ? { orderId } : {}), orderNumber: number, customerName: customerName ?? null, + amount: toNumOrNull(amount), net: toNumOrNull(net), rebate: rebate != null ? Number(rebate) : 0, + taxrate: taxrate != null ? Number(taxrate) : 0, gross: gross != null ? round2(gross) : null, }; - - return recordArticleHistory(runner, { - articleId, - companyuserId, - date, - text, - meta, - }); + return recordArticleHistory(runner, { articleId, companyuserId, date, text: `Auftrag ${number}${fmtSuffix(details)}`, meta }); } -/** - * Records an incoming delivery acceptance ledger entry in article history. - * Format: "Lieferung {manifestNumber} (Lieferant: "{supplierName}", EK brutto: {ekBrutto}, Menge: {amount})" - */ -export async function recordAcceptanceHistory(runner, { - articleId, - companyuserId, - date, - manifestNumber, - supplierName, - net, - rebate, - taxrate, - amount, - transactionText, -}) { +/** Records incoming delivery acceptance: "Lieferung {manifestNumber} (Lieferant: "{supplierName}", EK brutto: {ekBrutto}, Menge: {amount})" */ +export async function recordAcceptanceHistory(runner, { articleId, companyuserId, date, manifestNumber, supplierName, net, rebate, taxrate, amount, transactionText }) { const details = []; - if (supplierName) { - details.push(`Lieferant: "${supplierName}"`); - } + if (supplierName) details.push(`Lieferant: "${supplierName}"`); + let ekBrutto = null; - if (net != null && Number.isFinite(Number(net))) { - const netNum = Number(net); - const rebateNum = Number(rebate ?? 0); - const taxrateNum = Number(taxrate ?? 0); - const netAfterRebate = netNum * (1 - rebateNum / 100); - const brutto = netAfterRebate * (1 + taxrateNum / 100); - ekBrutto = (Math.round(brutto * 100) / 100).toFixed(2); + if (isFiniteNum(net)) { + ekBrutto = round2(calculateGross(net, rebate, taxrate)).toFixed(2); details.push(`EK brutto: ${ekBrutto}`); } - if (amount != null && Number.isFinite(Number(amount))) { - details.push(`Menge: ${amount}`); - } - const suffix = details.length ? ` (${details.join(', ')})` : ''; + if (isFiniteNum(amount)) details.push(`Menge: ${amount}`); + const base = manifestNumber ? `Lieferung ${manifestNumber}` : (transactionText?.trim() || 'Lieferung'); - const historyText = `${base}${suffix}`; - const meta = { - type: 'acceptance', - manifestNumber: manifestNumber ?? null, - deliveryNumber: manifestNumber ?? null, - supplierName: supplierName ?? null, - amount: amount != null ? Number(amount) : null, - net: net != null ? Number(net) : null, - rebate: rebate != null ? Number(rebate) : 0, - taxrate: taxrate != null ? Number(taxrate) : 0, - ekBrutto: ekBrutto != null ? Number(ekBrutto) : null, - transactionText: transactionText ?? null, + type: 'acceptance', manifestNumber: manifestNumber ?? null, deliveryNumber: manifestNumber ?? null, + supplierName: supplierName ?? null, amount: toNumOrNull(amount), net: toNumOrNull(net), + rebate: rebate != null ? Number(rebate) : 0, taxrate: taxrate != null ? Number(taxrate) : 0, + ekBrutto: toNumOrNull(ekBrutto), transactionText: transactionText ?? null, }; - - return recordArticleHistory(runner, { - articleId, - companyuserId, - date, - text: historyText, - meta, - }); + return recordArticleHistory(runner, { articleId, companyuserId, date, text: `${base}${fmtSuffix(details)}`, meta }); } -/** - * Records an outgoing shipment ledger entry in article history. - * Format: "Lieferschein {manifestNumber} (Kunde: "{customerName}", Menge: {amount})" - */ -export async function recordShipmentHistory(runner, { - articleId, - companyuserId, - date, - manifestNumber, - customerName, - amount, - transactionText, -}) { +/** Records outgoing shipment: "Lieferschein {manifestNumber} (Kunde: "{customerName}", Menge: {amount})" */ +export async function recordShipmentHistory(runner, { articleId, companyuserId, date, manifestNumber, customerName, amount, transactionText }) { const details = []; - if (customerName) { - details.push(`Kunde: "${customerName}"`); - } - if (amount != null && Number.isFinite(Number(amount))) { - details.push(`Menge: ${amount}`); - } - const suffix = details.length ? ` (${details.join(', ')})` : ''; + if (customerName) details.push(`Kunde: "${customerName}"`); + if (isFiniteNum(amount)) details.push(`Menge: ${amount}`); + const base = manifestNumber ? `Lieferschein ${manifestNumber}` : (transactionText?.trim() || 'Lieferschein'); - const historyText = `${base}${suffix}`; - const meta = { - type: 'shipment', - manifestNumber: manifestNumber ?? null, - shipmentNumber: manifestNumber ?? null, - customerName: customerName ?? null, - amount: amount != null ? Number(amount) : null, - transactionText: transactionText ?? null, + type: 'shipment', manifestNumber: manifestNumber ?? null, shipmentNumber: manifestNumber ?? null, + customerName: customerName ?? null, amount: toNumOrNull(amount), transactionText: transactionText ?? null, }; - - return recordArticleHistory(runner, { - articleId, - companyuserId, - date, - text: historyText, - meta, - }); + return recordArticleHistory(runner, { articleId, companyuserId, date, text: `${base}${fmtSuffix(details)}`, meta }); } -/** - * Records a warehouse inventory ledger entry in article history. - * Format: - * Inbound: "Lieferung {manifestNumber} (Lager: "{warehouseName}", Menge: {amount})" - * Outbound: "Lieferschein {manifestNumber} (Lager: "{warehouseName}", Menge: {amount})" - */ -export async function recordWarehouseHistory(runner, { - articleId, - companyuserId, - date, - manifestNumber, - manifestType, - warehouseName, - amount, - transactionText, -}) { +/** Records warehouse inventory ledger entry (Inbound "Lieferung" / Outbound "Lieferschein"). */ +export async function recordWarehouseHistory(runner, { articleId, companyuserId, date, manifestNumber, manifestType, warehouseName, amount, transactionText }) { const isShipment = manifestType === 'shipment' || (amount != null && Number(amount) < 0); const prefix = isShipment ? 'Lieferschein' : 'Lieferung'; const displayAmount = amount != null ? Math.abs(Number(amount)) : null; const details = []; - if (warehouseName) { - details.push(`Lager: "${warehouseName}"`); - } - if (displayAmount != null && Number.isFinite(displayAmount)) { - details.push(`Menge: ${displayAmount}`); - } - const suffix = details.length ? ` (${details.join(', ')})` : ''; + if (warehouseName) details.push(`Lager: "${warehouseName}"`); + if (displayAmount != null && Number.isFinite(displayAmount)) details.push(`Menge: ${displayAmount}`); + const base = manifestNumber ? `${prefix} ${manifestNumber}` : (transactionText?.trim() || prefix); - const historyText = `${base}${suffix}`; - const meta = { - type: isShipment ? 'warehouse_out' : 'warehouse_in', - manifestNumber: manifestNumber ?? null, - deliveryNumber: !isShipment ? (manifestNumber ?? null) : null, - shipmentNumber: isShipment ? (manifestNumber ?? null) : null, - warehouseName: warehouseName ?? null, - amount: amount != null ? Number(amount) : null, - transactionText: transactionText ?? null, + type: isShipment ? 'warehouse_out' : 'warehouse_in', manifestNumber: manifestNumber ?? null, + deliveryNumber: !isShipment ? (manifestNumber ?? null) : null, shipmentNumber: isShipment ? (manifestNumber ?? null) : null, + warehouseName: warehouseName ?? null, amount: toNumOrNull(amount), transactionText: transactionText ?? null, }; - - return recordArticleHistory(runner, { - articleId, - companyuserId, - date, - text: historyText, - meta, - }); + return recordArticleHistory(runner, { articleId, companyuserId, date, text: `${base}${fmtSuffix(details)}`, meta }); } -/** - * Backward-compatible helper that records delivery / warehouse ledger entry in article history. - */ -export async function recordDeliveryHistory(runner, { - articleId, - companyuserId, - date, - manifestNumber, - supplierName, - warehouseName, - net, - rebate, - taxrate, - amount, - transactionText, - isDelivery = true, -}) { +/** Backward-compatible helper that records delivery / warehouse ledger entry in article history. */ +export async function recordDeliveryHistory(runner, { articleId, companyuserId, date, manifestNumber, supplierName, warehouseName, net, rebate, taxrate, amount, transactionText, isDelivery = true }) { if (isDelivery && !warehouseName) { - return recordAcceptanceHistory(runner, { - articleId, - companyuserId, - date, - manifestNumber, - supplierName, - net, - rebate, - taxrate, - amount, - transactionText, - }); + return recordAcceptanceHistory(runner, { articleId, companyuserId, date, manifestNumber, supplierName, net, rebate, taxrate, amount, transactionText }); } let historyText = transactionText; @@ -336,336 +211,123 @@ export async function recordDeliveryHistory(runner, { if (isDelivery) { const details = []; - if (supplierName) { - details.push(`Lieferant: "${supplierName}"`); - } - if (warehouseName) { - details.push(`Lager: "${warehouseName}"`); - } - if (net != null && Number.isFinite(Number(net))) { - const netNum = Number(net); - const rebateNum = Number(rebate ?? 0); - const taxrateNum = Number(taxrate ?? 0); - const netAfterRebate = netNum * (1 - rebateNum / 100); - const brutto = netAfterRebate * (1 + taxrateNum / 100); - ekBrutto = (Math.round(brutto * 100) / 100).toFixed(2); + if (supplierName) details.push(`Lieferant: "${supplierName}"`); + if (warehouseName) details.push(`Lager: "${warehouseName}"`); + if (isFiniteNum(net)) { + ekBrutto = round2(calculateGross(net, rebate, taxrate)).toFixed(2); details.push(`EK brutto: ${ekBrutto}`); } - if (amount != null && Number.isFinite(Number(amount))) { - details.push(`Menge: ${amount}`); - } - const suffix = details.length ? ` (${details.join(', ')})` : ''; + if (isFiniteNum(amount)) details.push(`Menge: ${amount}`); const base = manifestNumber ? `Lieferung ${manifestNumber}` : (transactionText?.trim() || 'Lieferung'); - historyText = `${base}${suffix}`; + historyText = `${base}${fmtSuffix(details)}`; } else if (warehouseName && transactionText && !transactionText.includes(warehouseName)) { historyText = `${transactionText} (Lager: "${warehouseName}")`; } const meta = { - type: isDelivery ? 'delivery' : 'movement', - manifestNumber: manifestNumber ?? null, - deliveryNumber: manifestNumber ?? null, - supplierName: supplierName ?? null, - warehouseName: warehouseName ?? null, - amount: amount != null ? Number(amount) : null, - net: net != null ? Number(net) : null, - rebate: rebate != null ? Number(rebate) : 0, - taxrate: taxrate != null ? Number(taxrate) : 0, - ekBrutto: ekBrutto != null ? Number(ekBrutto) : null, - transactionText: transactionText ?? null, + type: isDelivery ? 'delivery' : 'movement', manifestNumber: manifestNumber ?? null, deliveryNumber: manifestNumber ?? null, + supplierName: supplierName ?? null, warehouseName: warehouseName ?? null, amount: toNumOrNull(amount), + net: toNumOrNull(net), rebate: rebate != null ? Number(rebate) : 0, taxrate: taxrate != null ? Number(taxrate) : 0, + ekBrutto: toNumOrNull(ekBrutto), transactionText: transactionText ?? null, }; - - return recordArticleHistory(runner, { - articleId, - companyuserId, - date, - text: historyText, - meta, - }); + return recordArticleHistory(runner, { articleId, companyuserId, date, text: historyText, meta }); } -/** - * Records a stock adjustment in article history. - */ -export async function recordStockAdjustmentHistory(runner, { - articleId, - companyuserId, - date, - amount, - reason, -}) { - const sign = amount > 0 ? '+' : ''; - const text = `Bestandskorrektur ${sign}${amount} (${reason})`; - const meta = { - type: 'stock_adjustment', - amount, - reason, - }; - return recordArticleHistory(runner, { - articleId, - companyuserId, - date, - text, - meta, - }); +/** Records a stock adjustment in article history. */ +export async function recordStockAdjustmentHistory(runner, { articleId, companyuserId, date, amount, reason }) { + const text = `Bestandskorrektur ${amount > 0 ? '+' : ''}${amount} (${reason})`; + return recordArticleHistory(runner, { articleId, companyuserId, date, text, meta: { type: 'stock_adjustment', amount, reason } }); } -/** - * Records price changes in article history. - */ -export async function recordPriceChangeHistory(runner, { - articleId, - companyuserId, - date, - oldPrices, - newPrices, -}) { +// #endregion + +// #region Article Master Data & Field Changes +// MARK: - Article Master Data & Field Changes + +/** Records price changes in article history. */ +export async function recordPriceChangeHistory(runner, { articleId, companyuserId, date, oldPrices, newPrices }) { const fmtPrice = (p) => `${p.net ?? p.price}@${p.taxRate ?? p.tax ?? 0}%`; const oldStr = (oldPrices || []).map(fmtPrice).join(', '); const newStr = (newPrices || []).map(fmtPrice).join(', '); if (oldStr === newStr) return; const text = `Preise geändert von [${oldStr}] auf [${newStr}]`; - const meta = { - type: 'price_change', - oldPrices, - newPrices, - }; - return recordArticleHistory(runner, { - articleId, - companyuserId, - date, - text, - meta, - }); + return recordArticleHistory(runner, { articleId, companyuserId, date, text, meta: { type: 'price_change', oldPrices, newPrices } }); } -/** - * Records category assignment changes in article history. - */ -export async function recordCategoryChangeHistory(runner, { - articleId, - companyuserId, - date, - oldCategoryIds, - newCategoryIds, -}) { - const oldStr = [...(oldCategoryIds || [])].sort((a, b) => a - b).join(', '); - const newStr = [...(newCategoryIds || [])].sort((a, b) => a - b).join(', '); +/** Records category assignment changes in article history. */ +export async function recordCategoryChangeHistory(runner, { articleId, companyuserId, date, oldCategoryIds, newCategoryIds }) { + const numSort = (a, b) => a - b; + const oldStr = [...(oldCategoryIds || [])].sort(numSort).join(', '); + const newStr = [...(newCategoryIds || [])].sort(numSort).join(', '); if (oldStr === newStr) return; const text = `Kategorien geändert von [${oldStr}] auf [${newStr}]`; - const meta = { - type: 'category_change', - oldCategoryIds, - newCategoryIds, - }; - return recordArticleHistory(runner, { - articleId, - companyuserId, - date, - text, - meta, - }); + return recordArticleHistory(runner, { articleId, companyuserId, date, text, meta: { type: 'category_change', oldCategoryIds, newCategoryIds } }); } -/** - * Records brand changes in article history. - */ -export async function recordBrandChangeHistory(runner, { - articleId, - companyuserId, - date, - oldBrandName, - newBrandName, -}) { +/** Records brand changes in article history. */ +export async function recordBrandChangeHistory(runner, { articleId, companyuserId, date, oldBrandName, newBrandName }) { if (oldBrandName === newBrandName) return; - const text = `Marke geändert von "${oldBrandName ?? ''}" auf "${newBrandName ?? ''}"`; - const meta = { - type: 'brand_change', - oldBrandName: oldBrandName ?? null, - newBrandName: newBrandName ?? null, - }; - return recordArticleHistory(runner, { - articleId, - companyuserId, - date, - text, - meta, - }); + return recordArticleHistory(runner, { articleId, companyuserId, date, text, meta: { type: 'brand_change', oldBrandName: oldBrandName ?? null, newBrandName: newBrandName ?? null } }); } -/** - * Records EAN changes in article history. - */ -export async function recordEanChangeHistory(runner, { - articleId, - companyuserId, - date, - oldEans, - newEans, -}) { +/** Records EAN changes in article history. */ +export async function recordEanChangeHistory(runner, { articleId, companyuserId, date, oldEans, newEans }) { const oldStr = (oldEans || []).join(', '); const newStr = [...(newEans || [])].sort().join(', '); if (oldStr === newStr) return; const text = `EANs geändert von "${oldStr}" auf "${newStr}"`; - const meta = { - type: 'ean_change', - oldEans, - newEans, - }; - return recordArticleHistory(runner, { - articleId, - companyuserId, - date, - text, - meta, - }); + return recordArticleHistory(runner, { articleId, companyuserId, date, text, meta: { type: 'ean_change', oldEans, newEans } }); } -/** - * Records weight changes in article history. - */ -export async function recordWeightChangeHistory(runner, { - articleId, - companyuserId, - date, - oldWeight, - newWeight, -}) { - if (Number(oldWeight) === Number(newWeight)) return; +/** Records weight changes in article history. */ +export async function recordWeightChangeHistory(runner, { articleId, companyuserId, date, oldWeight, newWeight }) { + const oldNum = Number(oldWeight), newNum = Number(newWeight); + if (oldNum === newNum) return; const text = `Gewicht geändert von "${oldWeight}" auf "${newWeight}"`; - const meta = { - type: 'weight_change', - oldWeight: Number(oldWeight), - newWeight: Number(newWeight), - }; - return recordArticleHistory(runner, { - articleId, - companyuserId, - date, - text, - meta, - }); + return recordArticleHistory(runner, { articleId, companyuserId, date, text, meta: { type: 'weight_change', oldWeight: oldNum, newWeight: newNum } }); } -/** - * Records track_stock changes in article history. - */ -export async function recordTrackStockChangeHistory(runner, { - articleId, - companyuserId, - date, - oldTrack, - newTrack, -}) { - if (Boolean(oldTrack) === Boolean(newTrack)) return; +/** Records track_stock changes in article history. */ +export async function recordTrackStockChangeHistory(runner, { articleId, companyuserId, date, oldTrack, newTrack }) { + const oldBool = Boolean(oldTrack), newBool = Boolean(newTrack); + if (oldBool === newBool) return; - const text = `Bestandsführung geändert von "${oldTrack ? 'Ja' : 'Nein'}" auf "${newTrack ? 'Ja' : 'Nein'}"`; - const meta = { - type: 'track_stock_change', - oldTrackStock: Boolean(oldTrack), - newTrackStock: Boolean(newTrack), - }; - return recordArticleHistory(runner, { - articleId, - companyuserId, - date, - text, - meta, - }); + const text = `Bestandsführung geändert von "${oldBool ? 'Ja' : 'Nein'}" auf "${newBool ? 'Ja' : 'Nein'}"`; + return recordArticleHistory(runner, { articleId, companyuserId, date, text, meta: { type: 'track_stock_change', oldTrackStock: oldBool, newTrackStock: newBool } }); } -/** - * Records general article field updates (name, unit, description, track_stock). - */ -export async function recordArticleUpdateHistory(runner, { - articleId, - companyuserId, - date, - historyLines, - changes, -}) { - if (!historyLines || !historyLines.length) return; - - const text = historyLines.join('\n'); - const meta = { - type: 'article_update', - changes: changes ?? historyLines, - }; - return recordArticleHistory(runner, { - articleId, - companyuserId, - date, - text, - meta, - }); +/** Records general article field updates (name, unit, description, track_stock). */ +export async function recordArticleUpdateHistory(runner, { articleId, companyuserId, date, historyLines, changes }) { + if (!historyLines?.length) return; + return recordArticleHistory(runner, { articleId, companyuserId, date, text: historyLines.join('\n'), meta: { type: 'article_update', changes: changes ?? historyLines } }); } -/** - * Records initial article creation. - */ -export async function recordArticleCreatedHistory(runner, { - articleId, - companyuserId, - date, - name, -}) { - const text = `Artikel "${name}" angelegt`; - const meta = { - type: 'article_created', - name, - }; - return recordArticleHistory(runner, { - articleId, - companyuserId, - date, - text, - meta, - }); +/** Records initial article creation. */ +export async function recordArticleCreatedHistory(runner, { articleId, companyuserId, date, name }) { + return recordArticleHistory(runner, { articleId, companyuserId, date, text: `Artikel "${name}" angelegt`, meta: { type: 'article_created', name } }); } -/** - * Records article import. - */ -export async function recordArticleImportedHistory(runner, { - articleId, - companyuserId, - date, - name, -}) { - const text = `Artikel "${name}" importiert`; - const meta = { - type: 'article_imported', - name, - }; - return recordArticleHistory(runner, { - articleId, - companyuserId, - date, - text, - meta, - }); +/** Records article import. */ +export async function recordArticleImportedHistory(runner, { articleId, companyuserId, date, name }) { + return recordArticleHistory(runner, { articleId, companyuserId, date, text: `Artikel "${name}" importiert`, meta: { type: 'article_imported', name } }); } -/** - * Records conversion of an article to parent article. - */ -export async function recordParentConvertedHistory(runner, { - articleId, - companyuserId, - attributeName, - existingPrices, -}) { - const historyLines = [ - attributeName - ? `Artikel zu Vaterartikel konvertiert (Varianten-Attribut: "${attributeName}")` - : 'Artikel zu Vaterartikel konvertiert', - ]; - if (existingPrices && existingPrices.length > 0) { +// #endregion + +// #region Parent & Variant Management +// MARK: - Parent & Variant Management + +/** Records conversion of an article to parent article. */ +export async function recordParentConvertedHistory(runner, { articleId, companyuserId, attributeName, existingPrices }) { + const historyLines = [attributeName ? `Artikel zu Vaterartikel konvertiert (Varianten-Attribut: "${attributeName}")` : 'Artikel zu Vaterartikel konvertiert']; + const deletedPricesCount = existingPrices?.length || 0; + + if (deletedPricesCount > 0) { const priceParts = existingPrices.map((p) => { let str = `${p.net} €`; if (p.taxRate != null) str += ` (${p.taxRate}% MwSt.)`; @@ -673,423 +335,143 @@ export async function recordParentConvertedHistory(runner, { if (p.customerGroupName) str += ` [${p.customerGroupName}]`; return str; }); - const label = existingPrices.length === 1 ? 'Bisheriger Preis gelöscht' : 'Bisherige Preise gelöscht'; - historyLines.push(`${label}: ${priceParts.join(', ')}`); + historyLines.push(`${deletedPricesCount === 1 ? 'Bisheriger Preis gelöscht' : 'Bisherige Preise gelöscht'}: ${priceParts.join(', ')}`); } - const text = historyLines.join('\n'); - const meta = { - type: 'parent_converted', - attributeName: attributeName ?? null, - deletedPricesCount: existingPrices ? existingPrices.length : 0, - }; - return recordArticleHistory(runner, { - articleId, - companyuserId, - text, - meta, - }); + + const meta = { type: 'parent_converted', attributeName: attributeName ?? null, deletedPricesCount }; + return recordArticleHistory(runner, { articleId, companyuserId, text: historyLines.join('\n'), meta }); } -/** - * Records deactivation of parent article mode. - */ -export async function recordParentDeactivatedHistory(runner, { - articleId, - companyuserId, -}) { - const text = 'Vaterartikel-Modus deaktiviert'; - const meta = { - type: 'parent_deactivated', - }; - return recordArticleHistory(runner, { - articleId, - companyuserId, - text, - meta, - }); +/** Records deactivation of parent article mode. */ +export async function recordParentDeactivatedHistory(runner, { articleId, companyuserId }) { + return recordArticleHistory(runner, { articleId, companyuserId, text: 'Vaterartikel-Modus deaktiviert', meta: { type: 'parent_deactivated' } }); } -/** - * Records adding a variant onto a parent article. - */ -export async function recordVariantAddedOnParentHistory(runner, { - parentId, - companyuserId, - childName, - childSku, -}) { +/** Records adding a variant onto a parent article. */ +export async function recordVariantAddedOnParentHistory(runner, { parentId, companyuserId, childName, childSku }) { const text = `Variante "${childName}" (${childSku}) hinzugefügt`; - const meta = { - type: 'variant_added_to_parent', - childName, - childSku, - }; - return recordArticleHistory(runner, { - articleId: parentId, - companyuserId, - text, - meta, - }); + return recordArticleHistory(runner, { articleId: parentId, companyuserId, text, meta: { type: 'variant_added_to_parent', childName, childSku } }); } -/** - * Records assigning a child article to a parent article. - */ -export async function recordChildAssignedToParentHistory(runner, { - childId, - companyuserId, - childName, - childSku, - parentSku, - created = false, -}) { +/** Records assigning a child article to a parent article. */ +export async function recordChildAssignedToParentHistory(runner, { childId, companyuserId, childName, childSku, parentSku, created = false }) { const text = created ? `Artikel "${childName}" (${childSku}) als Variante von "${parentSku}" angelegt` - : (childName - ? `Artikel "${childName}" (${childSku}) als Variante von "${parentSku}" zugewiesen` - : `Artikel "${childSku}" als Variante von "${parentSku}" zugewiesen`); - const meta = { - type: created ? 'child_created_under_parent' : 'child_assigned_to_parent', - childName: childName ?? null, - childSku, - parentSku, - }; - return recordArticleHistory(runner, { - articleId: childId, - companyuserId, - text, - meta, + : (childName ? `Artikel "${childName}" (${childSku}) als Variante von "${parentSku}" zugewiesen` : `Artikel "${childSku}" als Variante von "${parentSku}" zugewiesen`); + const meta = { type: created ? 'child_created_under_parent' : 'child_assigned_to_parent', childName: childName ?? null, childSku, parentSku }; + return recordArticleHistory(runner, { articleId: childId, companyuserId, text, meta }); +} + +/** Records unlinking a child article from a parent article on both child and parent. */ +export async function recordChildUnlinkedFromParentHistory(runner, { childId, parentId, companyuserId, childSku, childName, parentSku }) { + await recordArticleHistory(runner, { + articleId: childId, companyuserId, text: `Artikel "${childSku}" von Vaterartikel "${parentSku}" getrennt`, + meta: { type: 'child_unlinked', childSku, parentSku }, + }); + await recordArticleHistory(runner, { + articleId: parentId, companyuserId, text: `Variante "${childName || childSku}" (${childSku}) getrennt`, + meta: { type: 'variant_separated', childSku, childName: childName ?? null }, }); } -/** - * Records unlinking a child article from a parent article on both child and parent. - */ -export async function recordChildUnlinkedFromParentHistory(runner, { - childId, - parentId, - companyuserId, - childSku, - childName, - parentSku, -}) { - await recordArticleHistory(runner, { - articleId: childId, - companyuserId, - text: `Artikel "${childSku}" von Vaterartikel "${parentSku}" getrennt`, - meta: { - type: 'child_unlinked', - childSku, - parentSku, - }, - }); - - await recordArticleHistory(runner, { - articleId: parentId, - companyuserId, - text: `Variante "${childName || childSku}" (${childSku}) getrennt`, - meta: { - type: 'variant_separated', - childSku, - childName: childName ?? null, - }, - }); -} - -/** - * Records deleting a variant child article from a parent. - */ -export async function recordChildDeletedFromParentHistory(runner, { - parentId, - companyuserId, - childName, - childSku, -}) { +/** Records deleting a variant child article from a parent. */ +export async function recordChildDeletedFromParentHistory(runner, { parentId, companyuserId, childName, childSku }) { const text = `Variante "${childName || childSku}" (${childSku}) gelöscht`; - const meta = { - type: 'variant_deleted', - childName: childName ?? null, - childSku, - }; - return recordArticleHistory(runner, { - articleId: parentId, - companyuserId, - text, - meta, - }); + return recordArticleHistory(runner, { articleId: parentId, companyuserId, text, meta: { type: 'variant_deleted', childName: childName ?? null, childSku } }); } -/** - * Records adding a supplier to an article. - */ -export async function recordSupplierAddHistory(runner, { - articleId, - companyuserId, - supplierName, - supplierSku, - netPrice, -}) { +// #endregion + +// #region Suppliers +// MARK: - Suppliers + +/** Records adding a supplier to an article. */ +export async function recordSupplierAddHistory(runner, { articleId, companyuserId, supplierName, supplierSku, netPrice }) { const details = []; if (supplierSku) details.push(`Artikelnr: "${supplierSku}"`); - if (netPrice !== null && netPrice !== undefined) details.push(`EK: ${netPrice}`); - const suffix = details.length ? ` (${details.join(', ')})` : ''; - const text = `Lieferant "${supplierName}" hinzugefügt${suffix}`; - const meta = { - type: 'supplier_add', - supplierName, - supplierSku: supplierSku ?? null, - netPrice: netPrice ?? null, - }; - return recordArticleHistory(runner, { - articleId, - companyuserId, - text, - meta, - }); + if (netPrice != null) details.push(`EK: ${netPrice}`); + + const text = `Lieferant "${supplierName}" hinzugefügt${fmtSuffix(details)}`; + const meta = { type: 'supplier_add', supplierName, supplierSku: supplierSku ?? null, netPrice: netPrice ?? null }; + return recordArticleHistory(runner, { articleId, companyuserId, text, meta }); } -/** - * Records updating supplier details on an article. - */ -export async function recordSupplierUpdateHistory(runner, { - articleId, - companyuserId, - historyLines, - supplierName, -}) { - if (!historyLines || !historyLines.length) return; - const text = historyLines.join('\n'); - const meta = { - type: 'supplier_update', - supplierName, - changes: historyLines, - }; - return recordArticleHistory(runner, { - articleId, - companyuserId, - text, - meta, - }); +/** Records updating supplier details on an article. */ +export async function recordSupplierUpdateHistory(runner, { articleId, companyuserId, historyLines, supplierName }) { + if (!historyLines?.length) return; + return recordArticleHistory(runner, { articleId, companyuserId, text: historyLines.join('\n'), meta: { type: 'supplier_update', supplierName, changes: historyLines } }); } -/** - * Records deleting a supplier from an article. - */ -export async function recordSupplierDeleteHistory(runner, { - articleId, - companyuserId, - supplierName, - supplierSku, -}) { - const suffix = supplierSku ? ` (Artikelnr: "${supplierSku}")` : ''; - const text = `Lieferant "${supplierName}" entfernt${suffix}`; - const meta = { - type: 'supplier_delete', - supplierName, - supplierSku: supplierSku ?? null, - }; - return recordArticleHistory(runner, { - articleId, - companyuserId, - text, - meta, - }); +/** Records deleting a supplier from an article. */ +export async function recordSupplierDeleteHistory(runner, { articleId, companyuserId, supplierName, supplierSku }) { + const text = `Lieferant "${supplierName}" entfernt${supplierSku ? ` (Artikelnr: "${supplierSku}")` : ''}`; + return recordArticleHistory(runner, { articleId, companyuserId, text, meta: { type: 'supplier_delete', supplierName, supplierSku: supplierSku ?? null } }); } -/** - * Records property changes on an article. - */ -export async function recordPropertyChangeHistory(runner, { - articleId, - companyuserId, - oldProps, - newProps, -}) { +// #endregion + +// #region Properties, Attributes & Lists (BOM / Options) +// MARK: - Properties, Attributes & Lists + +/** Records property changes on an article. */ +export async function recordPropertyChangeHistory(runner, { articleId, companyuserId, oldProps, newProps }) { const fmtProp = (p) => `${p.name}=${p.value}`; const oldStr = (oldProps || []).map(fmtProp).join(', '); const newStr = (newProps || []).map(fmtProp).join(', '); if (oldStr === newStr) return; const text = `Eigenschaften geändert von [${oldStr}] auf [${newStr}]`; - const meta = { - type: 'property_change', - oldProps, - newProps, - }; - return recordArticleHistory(runner, { - articleId, - companyuserId, - text, - meta, - }); + return recordArticleHistory(runner, { articleId, companyuserId, text, meta: { type: 'property_change', oldProps, newProps } }); } -/** - * Records property deletion from an article. - */ -export async function recordPropertyDeleteHistory(runner, { - articleId, - companyuserId, - name, - value, -}) { - const text = `Eigenschaft "${name}" (${value}) gelöscht`; - const meta = { - type: 'property_delete', - name, - value, - }; - return recordArticleHistory(runner, { - articleId, - companyuserId, - text, - meta, - }); +/** Records property deletion from an article. */ +export async function recordPropertyDeleteHistory(runner, { articleId, companyuserId, name, value }) { + return recordArticleHistory(runner, { articleId, companyuserId, text: `Eigenschaft "${name}" (${value}) gelöscht`, meta: { type: 'property_delete', name, value } }); } -/** - * Records attribute value changes on an article. - */ -export async function recordAttributeChangeHistory(runner, { - articleId, - companyuserId, - oldAttrs, - newAttrs, -}) { +/** Records attribute value changes on an article. */ +export async function recordAttributeChangeHistory(runner, { articleId, companyuserId, oldAttrs, newAttrs }) { const fmtAttr = (a) => `${a.attributeName}: ${a.value}`; const oldStr = (oldAttrs || []).map(fmtAttr).join(', '); const newStr = (newAttrs || []).map(fmtAttr).join(', '); if (oldStr === newStr) return; const text = `Attributwerte geändert von [${oldStr}] auf [${newStr}]`; - const meta = { - type: 'attribute_change', - oldAttrs, - newAttrs, - }; - return recordArticleHistory(runner, { - articleId, - companyuserId, - text, - meta, - }); + return recordArticleHistory(runner, { articleId, companyuserId, text, meta: { type: 'attribute_change', oldAttrs, newAttrs } }); } -/** - * Records Stückliste (component items) assignment on an article. - */ -export async function recordArticleListChangeHistory(runner, { - articleId, - companyuserId, - items, -}) { +/** Records Stückliste (component items) assignment on an article. */ +export async function recordArticleListChangeHistory(runner, { articleId, companyuserId, items }) { const newNames = (items || []).map((r) => `${r.count}x ${r.name || r.sku}`); - const text = `Stückliste gesetzt: [${newNames.join(', ')}]`; - const meta = { - type: 'articlelist_change', - items, - }; - return recordArticleHistory(runner, { - articleId, - companyuserId, - text, - meta, - }); + return recordArticleHistory(runner, { articleId, companyuserId, text: `Stückliste gesetzt: [${newNames.join(', ')}]`, meta: { type: 'articlelist_change', items } }); } -/** - * Records Optionsliste assignment on an article. - */ -export async function recordOptionsListChangeHistory(runner, { - articleId, - companyuserId, - options, -}) { +/** Records Optionsliste assignment on an article. */ +export async function recordOptionsListChangeHistory(runner, { articleId, companyuserId, options }) { const newNames = (options || []).map((r) => r.name || r.sku); - const text = `Optionsliste gesetzt: [${newNames.join(', ')}]`; - const meta = { - type: 'optionslist_change', - options, - }; - return recordArticleHistory(runner, { - articleId, - companyuserId, - text, - meta, - }); + return recordArticleHistory(runner, { articleId, companyuserId, text: `Optionsliste gesetzt: [${newNames.join(', ')}]`, meta: { type: 'optionslist_change', options } }); } -/** - * Records image addition to an article. - */ -export async function recordImageAddHistory(runner, { - articleId, - companyuserId, - date, - fullHash, - sort, -}) { - const text = typeof sort === 'number' - ? `Bild hinzugefügt (Position ${sort + 1})` - : 'Bild hinzugefügt'; - const meta = { - type: 'image_add', - fullHash, - sort, - }; - return recordArticleHistory(runner, { - articleId, - companyuserId, - date, - text, - meta, - }); +// #endregion + +// #region Images & Media +// MARK: - Images & Media + +/** Records image addition to an article. */ +export async function recordImageAddHistory(runner, { articleId, companyuserId, date, fullHash, sort }) { + const text = typeof sort === 'number' ? `Bild hinzugefügt (Position ${sort + 1})` : 'Bild hinzugefügt'; + return recordArticleHistory(runner, { articleId, companyuserId, date, text, meta: { type: 'image_add', fullHash, sort } }); } -/** - * Records image deletion from an article. - */ -export async function recordImageDeleteHistory(runner, { - articleId, - companyuserId, - date, - fullHash, -}) { - const text = 'Bild gelöscht'; - const meta = { - type: 'image_delete', - fullHash, - }; - return recordArticleHistory(runner, { - articleId, - companyuserId, - date, - text, - meta, - }); +/** Records image deletion from an article. */ +export async function recordImageDeleteHistory(runner, { articleId, companyuserId, date, fullHash }) { + return recordArticleHistory(runner, { articleId, companyuserId, date, text: 'Bild gelöscht', meta: { type: 'image_delete', fullHash } }); } -/** - * Records image reordering for an article. - */ -export async function recordImageOrderHistory(runner, { - articleId, - companyuserId, - date, - fullHashes, -}) { +/** Records image reordering for an article. */ +export async function recordImageOrderHistory(runner, { articleId, companyuserId, date, fullHashes }) { const count = Array.isArray(fullHashes) ? fullHashes.length : 0; - const text = `Bildreihenfolge geändert (${count} Bilder)`; - const meta = { - type: 'image_order', - fullHashes, - }; - return recordArticleHistory(runner, { - articleId, - companyuserId, - date, - text, - meta, - }); + return recordArticleHistory(runner, { articleId, companyuserId, date, text: `Bildreihenfolge geändert (${count} Bilder)`, meta: { type: 'image_order', fullHashes } }); } +// #endregion \ No newline at end of file