mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-01-13 21:59:34 +00:00
Erweitere Exportfunktion um lesbare BOM-Option (PDF-Ausgabe).
Neue Auswahloption "Lesbarer Export" hinzugefügt, die den Export hierarchischer Baugruppen als PDF ermöglicht.
This commit is contained in:
parent
4251b2ab46
commit
a682b87070
19 changed files with 571 additions and 44 deletions
|
|
@ -24,9 +24,16 @@ namespace App\Helpers\Assemblies;
|
|||
|
||||
use App\Entity\AssemblySystem\Assembly;
|
||||
use App\Entity\Parts\Part;
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Options;
|
||||
use Twig\Environment;
|
||||
|
||||
class AssemblyPartAggregator
|
||||
{
|
||||
public function __construct(private readonly Environment $twig)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggregate the required parts and their total quantities for an assembly.
|
||||
*
|
||||
|
|
@ -80,4 +87,181 @@ class AssemblyPartAggregator
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports a hierarchical Bill of Materials (BOM) for assemblies and parts in a readable format,
|
||||
* including the multiplier for each part and assembly.
|
||||
*
|
||||
* @param Assembly $assembly The root assembly to export.
|
||||
* @param string $indentationSymbol The symbol used for indentation (e.g., ' ').
|
||||
* @param int $initialDepth The starting depth for formatting (default: 0).
|
||||
* @return string Human-readable hierarchical BOM list.
|
||||
*/
|
||||
public function exportReadableHierarchy(Assembly $assembly, string $indentationSymbol = ' ', int $initialDepth = 0): string
|
||||
{
|
||||
// Start building the hierarchy
|
||||
$output = '';
|
||||
$this->processAssemblyHierarchy($assembly, $initialDepth, 1, $indentationSymbol, $output);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function exportReadableHierarchyForPdf(array $assemblyHierarchies): string
|
||||
{
|
||||
$html = $this->twig->render('assemblies/export_bom_pdf.html.twig', [
|
||||
'assemblies' => $assemblyHierarchies,
|
||||
]);
|
||||
|
||||
$options = new Options();
|
||||
$options->set('isHtml5ParserEnabled', true);
|
||||
$options->set('isPhpEnabled', true);
|
||||
|
||||
$dompdf = new Dompdf($options);
|
||||
$dompdf->loadHtml($html);
|
||||
$dompdf->setPaper('A4');
|
||||
$dompdf->render();
|
||||
|
||||
$canvas = $dompdf->getCanvas();
|
||||
$font = $dompdf->getFontMetrics()->getFont('Arial', 'normal');
|
||||
|
||||
return $dompdf->output();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive method to process assemblies and their parts.
|
||||
*
|
||||
* @param Assembly $assembly The current assembly to process.
|
||||
* @param int $depth The current depth in the hierarchy.
|
||||
* @param float $parentMultiplier The multiplier inherited from the parent (default is 1 for root).
|
||||
* @param string $indentationSymbol The symbol used for indentation.
|
||||
* @param string &$output The cumulative output string.
|
||||
*/
|
||||
private function processAssemblyHierarchy(Assembly $assembly, int $depth, float $parentMultiplier, string $indentationSymbol, string &$output): void
|
||||
{
|
||||
// Add the current assembly to the output
|
||||
if ($depth === 0) {
|
||||
$output .= sprintf(
|
||||
"%sAssembly: %s [IPN: %s]\n\n",
|
||||
str_repeat($indentationSymbol, $depth),
|
||||
$assembly->getName(),
|
||||
$assembly->getIpn(),
|
||||
);
|
||||
} else {
|
||||
$output .= sprintf(
|
||||
"%sAssembly: %s [IPN: %s, Multiplier: %.2f]\n\n",
|
||||
str_repeat($indentationSymbol, $depth),
|
||||
$assembly->getName(),
|
||||
$assembly->getIpn(),
|
||||
$parentMultiplier
|
||||
);
|
||||
}
|
||||
|
||||
// Gruppiere BOM-Einträge in Kategorien
|
||||
$parts = [];
|
||||
$referencedAssemblies = [];
|
||||
$others = [];
|
||||
|
||||
foreach ($assembly->getBomEntries() as $bomEntry) {
|
||||
if ($bomEntry->getPart() instanceof Part) {
|
||||
$parts[] = $bomEntry;
|
||||
} elseif ($bomEntry->getReferencedAssembly() instanceof Assembly) {
|
||||
$referencedAssemblies[] = $bomEntry;
|
||||
} else {
|
||||
$others[] = $bomEntry;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($parts)) {
|
||||
// Process each BOM entry for the current assembly
|
||||
foreach ($parts as $bomEntry) {
|
||||
$effectiveQuantity = $bomEntry->getQuantity() * $parentMultiplier;
|
||||
|
||||
$output .= sprintf(
|
||||
"%sPart: %s [IPN: %s, MPNR: %s, Quantity: %.2f%s, EffectiveQuantity: %.2f]\n",
|
||||
str_repeat($indentationSymbol, $depth + 1),
|
||||
$bomEntry->getPart()?->getName(),
|
||||
$bomEntry->getPart()?->getIpn() ?? '-',
|
||||
$bomEntry->getPart()?->getManufacturerProductNumber() ?? '-',
|
||||
$bomEntry->getQuantity(),
|
||||
$parentMultiplier > 1 ? sprintf(", Multiplier: %.2f", $parentMultiplier) : '',
|
||||
$effectiveQuantity,
|
||||
);
|
||||
}
|
||||
|
||||
$output .= "\n";
|
||||
}
|
||||
|
||||
foreach ($referencedAssemblies as $bomEntry) {
|
||||
// Add referenced assembly details
|
||||
$referencedQuantity = $bomEntry->getQuantity() * $parentMultiplier;
|
||||
|
||||
$output .= sprintf(
|
||||
"%sReferenced Assembly: %s [IPN: %s, Quantity: %.2f%s, EffectiveQuantity: %.2f]\n",
|
||||
str_repeat($indentationSymbol, $depth + 1),
|
||||
$bomEntry->getReferencedAssembly()->getName(),
|
||||
$bomEntry->getReferencedAssembly()->getIpn() ?? '-',
|
||||
$bomEntry->getQuantity(),
|
||||
$parentMultiplier > 1 ? sprintf(", Multiplier: %.2f", $parentMultiplier) : '',
|
||||
$referencedQuantity,
|
||||
);
|
||||
|
||||
// Recurse into the referenced assembly
|
||||
$this->processAssemblyHierarchy(
|
||||
$bomEntry->getReferencedAssembly(),
|
||||
$depth + 2, // Increase depth for nested assemblies
|
||||
$referencedQuantity, // Pass the calculated multiplier
|
||||
$indentationSymbol,
|
||||
$output
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($others as $bomEntry) {
|
||||
$output .= sprintf(
|
||||
"%sOther: %s [Quantity: %.2f, Multiplier: %.2f]\n",
|
||||
str_repeat($indentationSymbol, $depth + 1),
|
||||
$bomEntry->getName(),
|
||||
$bomEntry->getQuantity(),
|
||||
$parentMultiplier,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function processAssemblyHierarchyForPdf(Assembly $assembly, int $depth, float $quantity, float $parentMultiplier): array
|
||||
{
|
||||
$result = [
|
||||
'name' => $assembly->getName(),
|
||||
'ipn' => $assembly->getIpn(),
|
||||
'quantity' => $quantity,
|
||||
'multiplier' => $depth === 0 ? null : $parentMultiplier,
|
||||
'parts' => [],
|
||||
'referencedAssemblies' => [],
|
||||
'others' => [],
|
||||
];
|
||||
|
||||
foreach ($assembly->getBomEntries() as $bomEntry) {
|
||||
if ($bomEntry->getPart() instanceof Part) {
|
||||
$result['parts'][] = [
|
||||
'name' => $bomEntry->getPart()->getName(),
|
||||
'ipn' => $bomEntry->getPart()->getIpn(),
|
||||
'quantity' => $bomEntry->getQuantity(),
|
||||
'effectiveQuantity' => $bomEntry->getQuantity() * $parentMultiplier,
|
||||
];
|
||||
} elseif ($bomEntry->getReferencedAssembly() instanceof Assembly) {
|
||||
$result['referencedAssemblies'][] = $this->processAssemblyHierarchyForPdf(
|
||||
$bomEntry->getReferencedAssembly(),
|
||||
$depth + 1,
|
||||
$bomEntry->getQuantity(),
|
||||
$parentMultiplier * $bomEntry->getQuantity()
|
||||
);
|
||||
} else {
|
||||
$result['others'][] = [
|
||||
'name' => $bomEntry->getName(),
|
||||
'quantity' => $bomEntry->getQuantity(),
|
||||
'multiplier' => $parentMultiplier,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,8 +81,9 @@ class EntityExporter
|
|||
$resolver->setDefault('include_children', false);
|
||||
$resolver->setAllowedTypes('include_children', 'bool');
|
||||
|
||||
$resolver->setDefault('readable', false);
|
||||
$resolver->setAllowedTypes('readable', 'bool');
|
||||
$resolver->setDefault('readableSelect', null);
|
||||
$resolver->setAllowedValues('readableSelect', [null, 'readable', 'readable_bom']);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -240,7 +241,7 @@ class EntityExporter
|
|||
$entities = [$entities];
|
||||
}
|
||||
|
||||
if ($request->get('readable', false)) {
|
||||
if ($request->get('readableSelect', false) === 'readable') {
|
||||
// Map entity classes to export functions
|
||||
$entityExportMap = [
|
||||
AttachmentType::class => fn($entities) => $this->exportReadable($entities, AttachmentType::class),
|
||||
|
|
@ -273,6 +274,23 @@ class EntityExporter
|
|||
|
||||
$options['format'] = 'csv';
|
||||
$options['level'] = 'readable';
|
||||
} if ($request->get('readableSelect', false) === 'readable_bom') {
|
||||
$hierarchies = [];
|
||||
|
||||
foreach ($entities as $entity) {
|
||||
if (!$entity instanceof Assembly) {
|
||||
throw new InvalidArgumentException('Only assemblies can be exported in readable BOM format');
|
||||
}
|
||||
|
||||
$hierarchies[] = $this->assemblyPartAggregator->processAssemblyHierarchyForPdf($entity, 0, 1, 1);
|
||||
}
|
||||
|
||||
$pdfContent = $this->assemblyPartAggregator->exportReadableHierarchyForPdf($hierarchies);
|
||||
|
||||
$response = new Response($pdfContent);
|
||||
|
||||
$options['format'] = 'pdf';
|
||||
$options['level'] = 'readable_bom';
|
||||
} else {
|
||||
//Do the serialization with the given options
|
||||
$serialized_data = $this->exportEntities($entities, $options);
|
||||
|
|
@ -294,6 +312,7 @@ class EntityExporter
|
|||
'json' => 'application/json',
|
||||
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'xls' => 'application/vnd.ms-excel',
|
||||
'pdf' => 'application/pdf',
|
||||
default => 'text/plain',
|
||||
};
|
||||
$response->headers->set('Content-Type', $content_type);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue