diff --git a/src/store/article/addChild.js b/src/store/article/addChild.js index 9f463f4..f625e0e 100644 --- a/src/store/article/addChild.js +++ b/src/store/article/addChild.js @@ -147,6 +147,21 @@ export default function addChild(provider) { }); const childId = childRes.rows[0]?.id ?? childRes.insertId; + // Copy existing attribute values from father (ancestor attributes that father has) + const fatherAttrsRes = await runner.query( + 'SELECT attributevalue_id, sort FROM articleattributevalue WHERE article_id = @fatherId', + { fatherId }, + ); + for (const faRow of fatherAttrsRes.rows) { + if (faRow.attributevalue_id !== finalValueId) { + await runner.query(articleAttrValQueries.INSERT_ROW, { + articleId: childId, + attributeValueId: faRow.attributevalue_id, + sort: faRow.sort, + }); + } + } + // Link articleattributevalue await runner.query(articleAttrValQueries.INSERT_ROW, { articleId: childId, diff --git a/src/store/article/toggleFather.js b/src/store/article/toggleFather.js index c4da910..d467311 100644 --- a/src/store/article/toggleFather.js +++ b/src/store/article/toggleFather.js @@ -41,13 +41,27 @@ export default function toggleFather(provider) { throw new Error('attributeNameId is required to become a father article'); } - // Verify article is not a child of another article const currentRes = await runner.query( 'SELECT fatherarticle_id FROM article WHERE id = @articleId', { articleId }, ); - if (currentRes.rows[0]?.fatherarticle_id != null) { - throw new Error('A child article cannot become a father'); + + // If article is a child, verify attribute is not used by father or any father up the chain + let currentFatherId = currentRes.rows[0]?.fatherarticle_id; + const visited = new Set(); + while (currentFatherId != null && !visited.has(currentFatherId)) { + visited.add(currentFatherId); + const fRes = await runner.query( + 'SELECT a.fatherarticle_id, a.fatherattributename_id, an.name AS fatherAttributeName FROM article a LEFT JOIN attributename an ON an.id = a.fatherattributename_id WHERE a.id = @currentFatherId', + { currentFatherId }, + ); + const fRow = fRes.rows[0]; + if (!fRow) break; + if (fRow.fatherattributename_id != null && Number(fRow.fatherattributename_id) === Number(targetAttrId)) { + const attrName = fRow.fatherAttributeName || 'ancestor'; + throw new Error(`Attribute "${attrName}" is already used by an ancestor father article`); + } + currentFatherId = fRow.fatherarticle_id; } // Verify attribute exists and belongs to company @@ -59,6 +73,17 @@ export default function toggleFather(provider) { throw new Error('Attribute name not found'); } + // Verify article does not have an attribute value for this attribute + const existingValRes = await runner.query( + `SELECT aav.id FROM articleattributevalue aav + INNER JOIN attributevalue av ON av.id = aav.attributevalue_id + WHERE aav.article_id = @articleId AND av.attribute_id = @attributeId`, + { articleId, attributeId: Number(targetAttrId) }, + ); + if (existingValRes.rows.length > 0) { + throw new Error('A father article cannot have attribute values for its variant attribute'); + } + await runner.query(queries.TOGGLE_FATHER, { articleId, isFather: 1, diff --git a/src/store/articleattributevalue/setByArticleSku.js b/src/store/articleattributevalue/setByArticleSku.js index db35987..9ab535f 100644 --- a/src/store/articleattributevalue/setByArticleSku.js +++ b/src/store/articleattributevalue/setByArticleSku.js @@ -79,6 +79,42 @@ export default function setByArticleSku(provider) { }); } + // Check father / child constraints + const artRes = await runner.query( + 'SELECT id, is_father, fatherarticle_id, fatherattributename_id FROM article WHERE id = @articleId', + { articleId }, + ); + const article = artRes.rows[0]; + + if (article?.is_father && article.fatherattributename_id) { + const hasFatherAttr = validated.some((v) => v.attributeId === article.fatherattributename_id); + if (hasFatherAttr) { + throw new Error('A father article cannot have attribute values for its variant attribute'); + } + } + + if (article?.fatherarticle_id != null) { + let currentFatherId = article.fatherarticle_id; + const visited = new Set(); + while (currentFatherId != null && !visited.has(currentFatherId)) { + visited.add(currentFatherId); + const fatherRes = await runner.query( + 'SELECT a.fatherarticle_id, a.fatherattributename_id, an.name AS fatherAttributeName FROM article a LEFT JOIN attributename an ON an.id = a.fatherattributename_id WHERE a.id = @currentFatherId', + { currentFatherId }, + ); + const fRow = fatherRes.rows[0]; + if (!fRow) break; + if (fRow.fatherattributename_id != null) { + const hasFatherAttr = validated.some((v) => v.attributeId === fRow.fatherattributename_id); + if (!hasFatherAttr) { + const fatherAttrName = fRow.fatherAttributeName || 'ancestor variant attribute'; + throw new Error(`Child article must have an attribute value for the father attribute "${fatherAttrName}"`); + } + } + currentFatherId = fRow.fatherarticle_id; + } + } + await runner.query(deleteSql, { articleId }); const inserted = [];