From b034d3c5b3c896a2714520698728ca0f28ac35f3 Mon Sep 17 00:00:00 2001 From: ispyisail Date: Sun, 13 Sep 2026 02:00:50 +1200 Subject: [PATCH] Include slave and terminal elements in the bill of materials A slave and a terminal are both routinely separately orderable hardware. A circuit breaker can carry ten or twenty auxiliary blocks, each with its own order code, and a terminal block is a purchased part in its own right. Neither was reaching the bill of materials. Decided in discussion #847: @IBSYSLevi -- "I would not expect that a defined piece of hardware is excluded from BOM when not specifically defined as so" -- with use cases from @jozi332 covering Siemens breakers with ten to twenty auxiliary blocks and PLC cards carrying per-channel data. Two filters had to change, which is easy to miss: BomExport::defaultQuery() and, upstream of it, the WHERE clause of element_nomenclature_view itself. Changing only the query does nothing for slaves, because the view had already removed them. Terminals were already in the view, so they appeared as soon as the query allowed them -- which made a half-finished change look like it had worked. Measured on examples/industrial.qet, which holds 96 terminals and 41 slaves: 258 rows before, 354 with terminals, 395 with both. A slave given a manufacturer and part number now appears in the export; previously it could not, at any setting. Nothing that should stay out of a bill of materials is newly included. The folio report arrows and the conductor definition are still excluded because they are not hardware, and anything else -- a relay's own auxiliary contact, which is not orderable separately -- is kept out with exclude_from_bom, which the view already honours and which #721 and #765 made settable on the symbol itself. tst_smart_device is updated rather than weakened. @enesgursoy6110 wrote it in #830 to prove the filter works, inserting rows designated "Must not be exported"; the slave and terminal rows now carry real designations and are asserted present, and a folio report arrow takes over as the negative case, so the test still proves filtering happens -- at the boundary we now want. Co-Authored-By: Claude Opus 5 --- sources/bomexport.cpp | 21 ++++++++++++++++++++- sources/dataBase/projectdatabase.cpp | 9 ++++++++- tests/qttest/tst_smart_device.cpp | 14 ++++++++++++-- 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/sources/bomexport.cpp b/sources/bomexport.cpp index ef5581016..a84bb51b2 100644 --- a/sources/bomexport.cpp +++ b/sources/bomexport.cpp @@ -59,8 +59,27 @@ QStringList BomExport::defaultColumns() QString BomExport::defaultQuery() { + //Slaves and terminals are included because both are routinely + //separately orderable hardware. A circuit breaker can carry ten or + //twenty auxiliary blocks, each with its own order code, and a + //terminal block is a purchased part in its own right. Neither shares + //a line with its master: the query is ungrouped, one row per element, + //so each appears as the distinct item it is. + // + //Anything that should not be ordered is kept out by setting + //exclude_from_bom on the element, which the view already honours -- + //a relay's own auxiliary contact, say. + // + //Thumbnails are deliberately left out for now even though ten of + //them in the shipped examples carry manufacturer and reference data + //(the assembly-plan mounting-plate symbols), because that has not + //been asked for and is a separate question. The folio report arrows + //and the conductor definition stay out because they are not hardware. + // + //See discussion #847. return QStringLiteral("SELECT %1 FROM element_nomenclature_view " - "WHERE ( element_type = 'simple' OR element_type = 'master') " + "WHERE element_type IN " + "('simple', 'master', 'slave', 'terminal') " "ORDER BY diagram_position, position, label") .arg(defaultColumns().join(QStringLiteral(", "))); } diff --git a/sources/dataBase/projectdatabase.cpp b/sources/dataBase/projectdatabase.cpp index df686fe12..bd0f0069d 100644 --- a/sources/dataBase/projectdatabase.cpp +++ b/sources/dataBase/projectdatabase.cpp @@ -711,7 +711,14 @@ void projectDataBase::createElementNomenclatureView() //the table's. Kept identical to the mask populateElementTable() //used to apply, so what this view returns does not change -- //a slave element (a relay contact) is still not a line item. - " AND e.type IN ('simple', 'terminal', 'master', 'thumbnail')"); + //Slave is here because an auxiliary contact block is + //separately orderable hardware with its own part + //number, even though it shares its master's BMK. + //Anything that should not be ordered -- a relay's + //own auxiliary contact, say -- is kept out by + //exclude_from_bom above, not by its base type. + //See discussion #847. + " AND e.type IN ('simple', 'terminal', 'master', 'slave', 'thumbnail')"); QSqlQuery query(m_data_base); if (!query.exec(create_view)) { diff --git a/tests/qttest/tst_smart_device.cpp b/tests/qttest/tst_smart_device.cpp index b054a6eba..ced7ecbdc 100644 --- a/tests/qttest/tst_smart_device.cpp +++ b/tests/qttest/tst_smart_device.cpp @@ -92,9 +92,17 @@ void SmartDeviceTest::defaultQueryAndCsv() QStringLiteral("日本電機"))); QVERIFY(add(QStringLiteral("-K1"), QStringLiteral("master"), 1, 2, QStringLiteral("Müller"))); + //A slave and a terminal are both separately orderable hardware -- an + //auxiliary contact block has its own order code, and so does a + //terminal block -- so both belong in the bill of materials. See + //discussion #847. Anything that should not be ordered is kept out by + //exclude_from_bom rather than by its base type. QVERIFY(add(QStringLiteral("-K1.1"), QStringLiteral("slave"), 1, 3, - QStringLiteral("Must not be exported"))); + QStringLiteral("Aux contact block"))); QVERIFY(add(QStringLiteral("X1"), QStringLiteral("terminal"), 1, 4, + QStringLiteral("Terminal block"))); + //Still filtered out: a folio report arrow is not hardware. + QVERIFY(add(QStringLiteral(">1"), QStringLiteral("next_report"), 1, 5, QStringLiteral("Must not be exported"))); QSqlQuery query(db); @@ -103,11 +111,13 @@ void SmartDeviceTest::defaultQueryAndCsv() int rows = 0; const auto csv = BomExport::toCsv( query, BomExport::defaultColumns(), true, &rows); - QCOMPARE(rows, 2); + QCOMPARE(rows, 4); QVERIFY(csv.startsWith("\xEF\xBB\xBF\"label\";\"designation\";")); QVERIFY(csv.contains(QStringLiteral("Müller").toUtf8())); QVERIFY(csv.contains(QStringLiteral("日本電機").toUtf8())); QVERIFY(csv.contains("\"Quoted \"\"note\"\"\nnext line\"")); + QVERIFY(csv.contains("Aux contact block")); + QVERIFY(csv.contains("Terminal block")); QVERIFY(!csv.contains("Must not be exported")); QVERIFY(csv.indexOf("-K1") < csv.indexOf("-K2")); }