diff --git a/assets/controllers/pages/statistics_assembly_controller.js b/assets/controllers/pages/statistics_assembly_controller.js
new file mode 100644
index 00000000..b20ef3b3
--- /dev/null
+++ b/assets/controllers/pages/statistics_assembly_controller.js
@@ -0,0 +1,45 @@
+import { Controller } from '@hotwired/stimulus';
+
+export default class extends Controller {
+ static values = {
+ url: String,
+ confirmMsg: String,
+ successMsg: String,
+ errorMsg: String
+ }
+
+ static targets = ["count"]
+
+ async cleanup(event) {
+ event.preventDefault();
+
+ if (!confirm(this.confirmMsgValue)) {
+ return;
+ }
+
+ try {
+ const response = await fetch(this.urlValue, {
+ method: 'POST',
+ headers: {
+ 'X-Requested-With': 'XMLHttpRequest'
+ }
+ });
+
+ if (response.ok) {
+ const data = await response.json();
+ alert(this.successMsgValue.replace('%count%', data.count));
+ // Update the count displayed in the UI
+ if (this.hasCountTarget) {
+ this.countTarget.innerText = '0';
+ }
+ // Reload page to reflect changes if needed, or just let the user see 0
+ window.location.reload();
+ } else {
+ alert(this.errorMsgValue);
+ }
+ } catch (error) {
+ console.error('Cleanup failed:', error);
+ alert(this.errorMsgValue);
+ }
+ }
+}
diff --git a/src/Controller/StatisticsController.php b/src/Controller/StatisticsController.php
index 67c29781..e3b378f1 100644
--- a/src/Controller/StatisticsController.php
+++ b/src/Controller/StatisticsController.php
@@ -42,7 +42,10 @@ declare(strict_types=1);
namespace App\Controller;
use App\Services\Tools\StatisticsHelper;
+use App\Entity\AssemblySystem\AssemblyBOMEntry;
+use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
@@ -57,4 +60,33 @@ class StatisticsController extends AbstractController
'helper' => $helper,
]);
}
+
+ #[Route(path: '/statistics/cleanup-assembly-bom-entries', name: 'statistics_cleanup_assembly_bom_entries', methods: ['POST'])]
+ public function cleanupAssemblyBOMEntries(EntityManagerInterface $em): JsonResponse
+ {
+ $this->denyAccessUnlessGranted('@tools.statistics');
+
+ $qb = $em->createQueryBuilder();
+ $qb->select('be', 'IDENTITY(be.part) AS part_id')
+ ->from(AssemblyBOMEntry::class, 'be')
+ ->leftJoin('be.part', 'p')
+ ->where('be.part IS NOT NULL')
+ ->andWhere('p.id IS NULL');
+
+ $results = $qb->getQuery()->getResult();
+ $count = count($results);
+
+ foreach ($results as $result) {
+ /** @var AssemblyBOMEntry $entry */
+ $entry = $result[0];
+ $part_id = $result['part_id'] ?? 'unknown';
+
+ $entry->setPart(null);
+ $entry->setName(sprintf('part-id=%s not found', $part_id));
+ }
+
+ $em->flush();
+
+ return new JsonResponse(['success' => true, 'count' => $count]);
+ }
}
diff --git a/src/Services/Tools/StatisticsHelper.php b/src/Services/Tools/StatisticsHelper.php
index 00bb05c9..a3da3fde 100644
--- a/src/Services/Tools/StatisticsHelper.php
+++ b/src/Services/Tools/StatisticsHelper.php
@@ -41,8 +41,10 @@ declare(strict_types=1);
namespace App\Services\Tools;
+use App\Entity\AssemblySystem\AssemblyBOMEntry;
use App\Entity\Attachments\Attachment;
use App\Entity\Attachments\AttachmentType;
+use App\Entity\AssemblySystem\Assembly;
use App\Entity\ProjectSystem\Project;
use App\Entity\Parts\Category;
use App\Entity\Parts\Footprint;
@@ -79,6 +81,14 @@ class StatisticsHelper
return $this->part_repo->count([]);
}
+ /**
+ * Returns the count of distinct projects.
+ */
+ public function getDistinctProjectsCount(): int
+ {
+ return $this->em->getRepository(Project::class)->count([]);
+ }
+
/**
* Returns the summed instocked over all parts (only parts without a measurement unit).
*
@@ -116,6 +126,7 @@ class StatisticsHelper
'storelocation' => StorageLocation::class,
'supplier' => Supplier::class,
'currency' => Currency::class,
+ 'assembly' => Assembly::class,
];
if (!isset($arr[$type])) {
@@ -164,4 +175,19 @@ class StatisticsHelper
{
return $this->attachment_repo->getUserUploadedAttachments();
}
+
+ /**
+ * Returns the count of BOM entries which point to a non-existent part ID.
+ */
+ public function getInvalidPartBOMEntriesCount(): int
+ {
+ $qb = $this->em->createQueryBuilder();
+ $qb->select('COUNT(be.id)')
+ ->from(AssemblyBOMEntry::class, 'be')
+ ->leftJoin('be.part', 'p')
+ ->where('be.part IS NOT NULL')
+ ->andWhere('p.id IS NULL');
+
+ return (int) $qb->getQuery()->getSingleScalarResult();
+ }
}
diff --git a/templates/tools/statistics/statistics.html.twig b/templates/tools/statistics/statistics.html.twig
index 1f488439..ed817cbe 100644
--- a/templates/tools/statistics/statistics.html.twig
+++ b/templates/tools/statistics/statistics.html.twig
@@ -14,6 +14,16 @@
{% trans %}statistics.parts{% endtrans %}
+
+
+ {% trans %}statistics.assemblies{% endtrans %}
+
+
+
+
+ {% trans %}statistics.projects{% endtrans %}
+
+
{% trans %}statistics.data_structures{% endtrans %}
@@ -52,6 +62,58 @@
+
+
+
+
+ | {% trans %}statistics.property{% endtrans %} |
+ {% trans %}statistics.value{% endtrans %} |
+
+
+
+
+ | {% trans %}statistics.distinct_assemblies_count{% endtrans %} |
+ {{ helper.dataStructuresCount("assembly") }} |
+
+
+ | {% trans %}statistics.invalid_part_bom_entries_count{% endtrans %} |
+
+ {{ helper.invalidPartBOMEntriesCount }}
+ {% if helper.invalidPartBOMEntriesCount > 0 %}
+
+ {% endif %}
+ |
+
+
+
+
+
+
+
+
+
+ | {% trans %}statistics.property{% endtrans %} |
+ {% trans %}statistics.value{% endtrans %} |
+
+
+
+
+ | {% trans %}statistics.distinct_projects_count{% endtrans %} |
+ {{ helper.distinctProjectsCount }} |
+
+
+
+
+
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/translations/messages.cs.xlf b/translations/messages.cs.xlf
index a413fe07..9dff3038 100644
--- a/translations/messages.cs.xlf
+++ b/translations/messages.cs.xlf
@@ -1845,6 +1845,48 @@ Související prvky budou přesunuty nahoru.
Projekty
+
+
+ statistics.cleanup_assembly_bom_entries.error
+ Při čištění došlo k chybě.
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.success
+ %count% položek bylo úspěšně vyčištěno.
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.confirm
+ Chcete vyčistit všechny neplatné položky kusovníku? Odkaz na díl bude odstraněn a název nastaven na "part-id=id not found".
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.button
+ Vyčistit
+
+
+
+
+ statistics.invalid_part_bom_entries_count
+ Neplatné položky kusovníku (díl nenalezen)
+
+
+
+
+ statistics.distinct_projects_count
+ Počet [[Project]]
+
+
+
+
+ statistics.distinct_assemblies_count
+ Počet [[Assembly]]
+
+
statistics.assemblies
diff --git a/translations/messages.da.xlf b/translations/messages.da.xlf
index 07577158..a125c32f 100644
--- a/translations/messages.da.xlf
+++ b/translations/messages.da.xlf
@@ -1803,6 +1803,48 @@ Underelementer vil blive flyttet opad.
Projekter
+
+
+ statistics.cleanup_assembly_bom_entries.error
+ Der opstod en fejl under oprydningen.
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.success
+ %count% poster blev ryddet op.
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.confirm
+ Vil du rydde op i alle ugyldige stykliste-poster? Forbindelsen til delen vil blive slettet, og navnet sat til "part-id=id not found".
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.button
+ Ryd op
+
+
+
+
+ statistics.invalid_part_bom_entries_count
+ Ugyldige stykliste-poster (del ikke fundet)
+
+
+
+
+ statistics.distinct_projects_count
+ Antal [[Project]]
+
+
+
+
+ statistics.distinct_assemblies_count
+ Antal [[Assembly]]
+
+
statistics.assemblies
diff --git a/translations/messages.de.xlf b/translations/messages.de.xlf
index 2e64896c..5c7986dc 100644
--- a/translations/messages.de.xlf
+++ b/translations/messages.de.xlf
@@ -1808,6 +1808,48 @@ Subelemente werden beim Löschen nach oben verschoben.
Projekte
+
+
+ statistics.distinct_assemblies_count
+ Anzahl [[Assembly]]
+
+
+
+
+ statistics.distinct_projects_count
+ Anzahl [[Project]]
+
+
+
+
+ statistics.invalid_part_bom_entries_count
+ Ungültige Stücklisteneinträge (Teil nicht gefunden)
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.button
+ Bereinigen
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.confirm
+ Möchten Sie alle ungültigen Stücklisteneinträge bereinigen? Die Verknüpfung zum Teil wird gelöscht und der Name auf "part-id=id not found" gesetzt.
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.success
+ %count% Einträge wurden erfolgreich bereinigt.
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.error
+ Bei der Bereinigung ist ein Fehler aufgetreten.
+
+
new
diff --git a/translations/messages.el.xlf b/translations/messages.el.xlf
index 6959f737..9995d942 100644
--- a/translations/messages.el.xlf
+++ b/translations/messages.el.xlf
@@ -1883,5 +1883,47 @@
Έργα
+
+
+ statistics.cleanup_assembly_bom_entries.error
+ Παρουσιάστηκε σφάλμα κατά την εκκαθάριση.
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.success
+ %count% εγγραφές εκκαθαρίστηκαν με επιτυχία.
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.confirm
+ Θέλετε να εκκαθαρίσετε όλες τις μη έγκυρες εγγραφές BOM; Ο σύνδεσμος προς το εξάρτημα θα διαγραφεί και το όνομα θα οριστεί σε "part-id=id not found".
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.button
+ Εκκαθάριση
+
+
+
+
+ statistics.invalid_part_bom_entries_count
+ Μη έγκυρες εγγραφές BOM (το εξάρτημα δεν βρέθηκε)
+
+
+
+
+ statistics.distinct_projects_count
+ Αριθμός [[Project]]
+
+
+
+
+ statistics.distinct_assemblies_count
+ Αριθμός [[Assembly]]
+
+
diff --git a/translations/messages.en.xlf b/translations/messages.en.xlf
index 07290f58..47896b4e 100644
--- a/translations/messages.en.xlf
+++ b/translations/messages.en.xlf
@@ -1809,6 +1809,48 @@ Sub elements will be moved upwards.
Projects
+
+
+ statistics.distinct_assemblies_count
+ Number of [[Assembly]]
+
+
+
+
+ statistics.distinct_projects_count
+ Number of [[Project]]
+
+
+
+
+ statistics.invalid_part_bom_entries_count
+ Invalid BOM entries (part not found)
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.button
+ Cleanup
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.confirm
+ Do you want to cleanup all invalid BOM entries? The link to the part will be removed and the name set to "part-id=id not found".
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.success
+ %count% entries were successfully cleaned up.
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.error
+ An error occurred during cleanup.
+
+
new
diff --git a/translations/messages.es.xlf b/translations/messages.es.xlf
index 5e19a148..b03886bb 100644
--- a/translations/messages.es.xlf
+++ b/translations/messages.es.xlf
@@ -1845,6 +1845,48 @@ Subelementos serán desplazados hacia arriba.
Proyectos
+
+
+ statistics.cleanup_assembly_bom_entries.error
+ Ocurrió un error durante la limpieza.
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.success
+ %count% entradas se limpiaron con éxito.
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.confirm
+ ¿Desea limpiar todas las entradas de lista de materiales no válidas? El enlace a la pieza se eliminará y el nombre se establecerá en "part-id=id not found".
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.button
+ Limpiar
+
+
+
+
+ statistics.invalid_part_bom_entries_count
+ Entradas de lista de materiales no válidas (pieza no encontrada)
+
+
+
+
+ statistics.distinct_projects_count
+ Número de [[Project]]
+
+
+
+
+ statistics.distinct_assemblies_count
+ Número de [[Assembly]]
+
+
statistics.assemblies
diff --git a/translations/messages.fr.xlf b/translations/messages.fr.xlf
index 6fbb667c..63159e5a 100644
--- a/translations/messages.fr.xlf
+++ b/translations/messages.fr.xlf
@@ -1828,6 +1828,48 @@ Show/Hide sidebar
Projets
+
+
+ statistics.cleanup_assembly_bom_entries.error
+ Une erreur est survenue lors du nettoyage.
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.success
+ %count% entrées ont été nettoyées avec succès.
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.confirm
+ Voulez-vous nettoyer toutes les entrées de nomenclature non valides ? Le lien vers la pièce sera supprimé et le nom sera défini sur "part-id=id not found".
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.button
+ Nettoyer
+
+
+
+
+ statistics.invalid_part_bom_entries_count
+ Entrées de nomenclature non valides (pièce non trouvée)
+
+
+
+
+ statistics.distinct_projects_count
+ Nombre de [[Project]]
+
+
+
+
+ statistics.distinct_assemblies_count
+ Nombre de [[Assembly]]
+
+
statistics.assemblies
diff --git a/translations/messages.hu.xlf b/translations/messages.hu.xlf
index 53e16b95..b754bb4a 100644
--- a/translations/messages.hu.xlf
+++ b/translations/messages.hu.xlf
@@ -1756,6 +1756,48 @@
Projektek
+
+
+ statistics.cleanup_assembly_bom_entries.error
+ Hiba történt a tisztítás során.
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.success
+ %count% tétel sikeresen törölve.
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.confirm
+ Szeretné törölni az összes érvénytelen darabjegyzék tételt? Az alkatrészre mutató hivatkozás törlődik, a név pedig "part-id=id not found" lesz.
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.button
+ Tisztítás
+
+
+
+
+ statistics.invalid_part_bom_entries_count
+ Érvénytelen darabjegyzék tételek (alkatrész nem található)
+
+
+
+
+ statistics.distinct_projects_count
+ [[Project]] száma
+
+
+
+
+ statistics.distinct_assemblies_count
+ [[Assembly]] száma
+
+
statistics.assemblies
diff --git a/translations/messages.it.xlf b/translations/messages.it.xlf
index 8b80bdbe..b2de4468 100644
--- a/translations/messages.it.xlf
+++ b/translations/messages.it.xlf
@@ -1845,6 +1845,48 @@ I sub elementi saranno spostati verso l'alto.
Progetti
+
+
+ statistics.cleanup_assembly_bom_entries.error
+ Si è verificato un errore durante la pulizia.
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.success
+ %count% voci sono state pulite con successo.
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.confirm
+ Vuoi pulire tutte le voci della distinta base non valide? Il collegamento alla parte verrà rimosso e il nome impostato su "part-id=id not found".
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.button
+ Pulisci
+
+
+
+
+ statistics.invalid_part_bom_entries_count
+ Voci della distinta base non valide (parte non trovata)
+
+
+
+
+ statistics.distinct_projects_count
+ Numero di [[Project]]
+
+
+
+
+ statistics.distinct_assemblies_count
+ Numero di [[Assembly]]
+
+
statistics.assemblies
diff --git a/translations/messages.ja.xlf b/translations/messages.ja.xlf
index e75953e4..bd7119ab 100644
--- a/translations/messages.ja.xlf
+++ b/translations/messages.ja.xlf
@@ -1828,6 +1828,48 @@
プロジェクト
+
+
+ statistics.cleanup_assembly_bom_entries.error
+ クリーンアップ中にエラーが発生しました。
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.success
+ %count% 個のエントリが正常にクリーンアップされました。
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.confirm
+ すべての無効な部品表エントリをクリーンアップしますか?部品へのリンクは削除され、名前は "part-id=id not found" に設定されます。
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.button
+ クリーンアップ
+
+
+
+
+ statistics.invalid_part_bom_entries_count
+ 無効な部品表エントリ (部品が見つかりません)
+
+
+
+
+ statistics.distinct_projects_count
+ [[Project]]の数
+
+
+
+
+ statistics.distinct_assemblies_count
+ [[Assembly]]の数
+
+
statistics.assemblies
diff --git a/translations/messages.nl.xlf b/translations/messages.nl.xlf
index b5b076e2..cf022030 100644
--- a/translations/messages.nl.xlf
+++ b/translations/messages.nl.xlf
@@ -1560,5 +1560,47 @@
Projecten
+
+
+ statistics.cleanup_assembly_bom_entries.error
+ Er is een fout opgetreden tijdens het opschonen.
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.success
+ %count% regels zijn succesvol opgeschoond.
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.confirm
+ Wilt u alle ongeldige stuklijstregels opschonen? De koppeling naar het onderdeel wordt verwijderd en de naam wordt ingesteld op "part-id=id not found".
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.button
+ Opschonen
+
+
+
+
+ statistics.invalid_part_bom_entries_count
+ Ongeldige stuklijstregels (onderdeel niet gevonden)
+
+
+
+
+ statistics.distinct_projects_count
+ Aantal [[Project]]
+
+
+
+
+ statistics.distinct_assemblies_count
+ Aantal [[Assembly]]
+
+
diff --git a/translations/messages.pl.xlf b/translations/messages.pl.xlf
index 7c0095e5..f253417e 100644
--- a/translations/messages.pl.xlf
+++ b/translations/messages.pl.xlf
@@ -1842,6 +1842,48 @@ Po usunięciu pod elementy zostaną przeniesione na górę.
Projekty
+
+
+ statistics.cleanup_assembly_bom_entries.error
+ Wystąpił błąd podczas czyszczenia.
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.success
+ Pomyślnie wyczyszczono %count% wpisów.
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.confirm
+ Czy chcesz wyczyścić wszystkie nieprawidłowe wpisy w zestawieniu? Link do części zostanie usunięty, a nazwa ustawiona na "part-id=id not found".
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.button
+ Wyczyść
+
+
+
+
+ statistics.invalid_part_bom_entries_count
+ Nieprawidłowe wpisy w zestawieniu (nie znaleziono części)
+
+
+
+
+ statistics.distinct_projects_count
+ Liczba [[Project]]
+
+
+
+
+ statistics.distinct_assemblies_count
+ Liczba [[Assembly]]
+
+
statistics.assemblies
diff --git a/translations/messages.ru.xlf b/translations/messages.ru.xlf
index 3c43a00c..bf0ec4f5 100644
--- a/translations/messages.ru.xlf
+++ b/translations/messages.ru.xlf
@@ -1846,6 +1846,48 @@
Проекты
+
+
+ statistics.cleanup_assembly_bom_entries.error
+ Произошла ошибка при очистке.
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.success
+ %count% записей успешно очищено.
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.confirm
+ Вы хотите очистить все неверные записи в спецификации? Ссылка на деталь будет удалена, а имя установлено в "part-id=id not found".
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.button
+ Очистить
+
+
+
+
+ statistics.invalid_part_bom_entries_count
+ Недопустимые записи в спецификации (деталь не найдена)
+
+
+
+
+ statistics.distinct_projects_count
+ Количество [[Project]]
+
+
+
+
+ statistics.distinct_assemblies_count
+ Количество [[Assembly]]
+
+
statistics.assemblies
diff --git a/translations/messages.zh.xlf b/translations/messages.zh.xlf
index 2d5eab25..6b319f8b 100644
--- a/translations/messages.zh.xlf
+++ b/translations/messages.zh.xlf
@@ -1845,6 +1845,48 @@
项目
+
+
+ statistics.cleanup_assembly_bom_entries.error
+ 清理过程中发生错误。
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.success
+ 成功清理了 %count% 条条目。
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.confirm
+ 您想清理所有无效的物料清单条目吗?部件链接将被删除,名称将设置为 "part-id=id not found"。
+
+
+
+
+ statistics.cleanup_assembly_bom_entries.button
+ 清理
+
+
+
+
+ statistics.invalid_part_bom_entries_count
+ 无效的物料清单条目(找不到部件)
+
+
+
+
+ statistics.distinct_projects_count
+ [[Project]] 数量
+
+
+
+
+ statistics.distinct_assemblies_count
+ [[Assembly]] 数量
+
+
statistics.assemblies