+ {% if path is defined and 'assembly' in path %}
+
+
{% trans %}export.readable.label{% endtrans %}
+
+
+
+ {% trans %}export.readable{% endtrans %}
+ {% trans %}export.readable_bom{% endtrans %}
+
+
+
+ {% else %}
+
+
{% trans %}export.readable.label{% endtrans %}
+
+
+
+ {% trans %}export.readable{% endtrans %}
+
+
+
+ {% endif %}
+
{% trans %}export.btn{% endtrans %}
-
\ No newline at end of file
+
diff --git a/templates/admin/assembly_admin.html.twig b/templates/admin/assembly_admin.html.twig
new file mode 100644
index 000000000..2e68a3da9
--- /dev/null
+++ b/templates/admin/assembly_admin.html.twig
@@ -0,0 +1,50 @@
+{% extends "admin/base_admin.html.twig" %}
+
+{# @var entity App\Entity\AssemblySystem\Assembly #}
+
+{% block card_title %}
+ {% set dataSourceName = get_data_source_name('assembly', 'assembly.caption') %}
+ {% set translatedSource = 'assembly.caption'|trans %}
+
{% if dataSourceName != translatedSource %}{{ 'datasource.synonym'|trans({'%name%': translatedSource, '%synonym%': dataSourceName}) }}{% else %}{{ translatedSource }}{% endif %}
+{% endblock %}
+
+{% block edit_title %}
+ {% trans %}assembly.edit{% endtrans %}: {{ entity.name }}
+{% endblock %}
+
+{% block new_title %}
+ {% trans %}assembly.new{% endtrans %}
+{% endblock %}
+
+{% block additional_pills %}
+
{% trans %}assembly_bom_entry.label{% endtrans %}
+{% endblock %}
+
+{% block quick_links %}
+
+{% endblock %}
+
+{% block additional_controls %}
+ {{ form_row(form.description) }}
+ {{ form_row(form.status) }}
+ {{ form_row(form.ipn) }}
+{% endblock %}
+
+{% block additional_panes %}
+
+{% endblock %}
diff --git a/templates/admin/base_admin.html.twig b/templates/admin/base_admin.html.twig
index 51790c3c1..e9fc0fb99 100644
--- a/templates/admin/base_admin.html.twig
+++ b/templates/admin/base_admin.html.twig
@@ -86,7 +86,7 @@
{% trans %}admin.attachments{% endtrans %}
- {% if entity.parameters is defined %}
+ {% if entity.parameters is defined and showParameters == true %}
{% trans %}admin.parameters{% endtrans %}
diff --git a/templates/admin/category_admin.html.twig b/templates/admin/category_admin.html.twig
index 5811640b9..82089a283 100644
--- a/templates/admin/category_admin.html.twig
+++ b/templates/admin/category_admin.html.twig
@@ -1,7 +1,9 @@
{% extends "admin/base_admin.html.twig" %}
{% block card_title %}
-
{% trans %}category.labelp{% endtrans %}
+ {% set dataSourceName = get_data_source_name('category', 'category.labelp') %}
+ {% set translatedSource = 'category.labelp'|trans %}
+
{% if dataSourceName != translatedSource %}{{ 'datasource.synonym'|trans({'%name%': translatedSource, '%synonym%': dataSourceName}) }}{% else %}{{ translatedSource }}{% endif %}
{% endblock %}
{% block additional_pills %}
@@ -31,6 +33,7 @@
{{ form_row(form.partname_regex) }}
{{ form_row(form.partname_hint) }}
+ {{ form_row(form.part_ipn_prefix) }}
{{ form_row(form.default_description) }}
{{ form_row(form.default_comment) }}
diff --git a/templates/admin/footprint_admin.html.twig b/templates/admin/footprint_admin.html.twig
index a2c3e4afd..a6acbe84e 100644
--- a/templates/admin/footprint_admin.html.twig
+++ b/templates/admin/footprint_admin.html.twig
@@ -1,7 +1,9 @@
{% extends "admin/base_admin.html.twig" %}
{% block card_title %}
-
{% trans %}footprint.labelp{% endtrans %}
+ {% set dataSourceName = get_data_source_name('footprint', 'footprint.labelp') %}
+ {% set translatedSource = 'footprint.labelp'|trans %}
+
{% if dataSourceName != translatedSource %}{{ 'datasource.synonym'|trans({'%name%': translatedSource, '%synonym%': dataSourceName}) }}{% else %}{{ translatedSource }}{% endif %}
{% endblock %}
{% block master_picture_block %}
diff --git a/templates/admin/manufacturer_admin.html.twig b/templates/admin/manufacturer_admin.html.twig
index 5db892c04..3ce9a124c 100644
--- a/templates/admin/manufacturer_admin.html.twig
+++ b/templates/admin/manufacturer_admin.html.twig
@@ -1,7 +1,9 @@
{% extends "admin/base_company_admin.html.twig" %}
{% block card_title %}
-
{% trans %}manufacturer.caption{% endtrans %}
+ {% set dataSourceName = get_data_source_name('manufacturer', 'manufacturer.caption') %}
+ {% set translatedSource = 'manufacturer.caption'|trans %}
+
{% if dataSourceName != translatedSource %}{{ 'datasource.synonym'|trans({'%name%': translatedSource, '%synonym%': dataSourceName}) }}{% else %}{{ translatedSource }}{% endif %}
{% endblock %}
{% block edit_title %}
diff --git a/templates/admin/part_custom_state_admin.html.twig b/templates/admin/part_custom_state_admin.html.twig
new file mode 100644
index 000000000..004ceb657
--- /dev/null
+++ b/templates/admin/part_custom_state_admin.html.twig
@@ -0,0 +1,14 @@
+{% extends "admin/base_admin.html.twig" %}
+
+{% block card_title %}
+
{% trans %}part_custom_state.caption{% endtrans %}
+{% endblock %}
+
+{% block edit_title %}
+ {% trans %}part_custom_state.edit{% endtrans %}: {{ entity.name }}
+{% endblock %}
+
+{% block new_title %}
+ {% trans %}part_custom_state.new{% endtrans %}
+{% endblock %}
+
diff --git a/templates/admin/project_admin.html.twig b/templates/admin/project_admin.html.twig
index 1a9950691..401be7cf7 100644
--- a/templates/admin/project_admin.html.twig
+++ b/templates/admin/project_admin.html.twig
@@ -3,7 +3,9 @@
{# @var entity App\Entity\ProjectSystem\Project #}
{% block card_title %}
-
{% trans %}project.caption{% endtrans %}
+ {% set dataSourceName = get_data_source_name('project', 'project.caption') %}
+ {% set translatedSource = 'project.caption'|trans %}
+
{% if dataSourceName != translatedSource %}{{ 'datasource.synonym'|trans({'%name%': translatedSource, '%synonym%': dataSourceName}) }}{% else %}{{ translatedSource }}{% endif %}
{% endblock %}
{% block edit_title %}
@@ -36,7 +38,7 @@
{% if entity.buildPart %}
{{ entity.buildPart.name }}
{% else %}
-
{% trans %}project.edit.associated_build_part.add{% endtrans %}
{% endif %}
{% trans %}project.edit.associated_build.hint{% endtrans %}
diff --git a/templates/admin/storelocation_admin.html.twig b/templates/admin/storelocation_admin.html.twig
index c93339dc1..1e60eeea2 100644
--- a/templates/admin/storelocation_admin.html.twig
+++ b/templates/admin/storelocation_admin.html.twig
@@ -2,7 +2,9 @@
{% import "label_system/dropdown_macro.html.twig" as dropdown %}
{% block card_title %}
-
{% trans %}storelocation.labelp{% endtrans %}
+ {% set dataSourceName = get_data_source_name('storagelocation', 'storelocation.labelp') %}
+ {% set translatedSource = 'storelocation.labelp'|trans %}
+
{% if dataSourceName != translatedSource %}{{ 'datasource.synonym'|trans({'%name%': translatedSource, '%synonym%': dataSourceName}) }}{% else %}{{ translatedSource }}{% endif %}
{% endblock %}
{% block additional_controls %}
diff --git a/templates/admin/supplier_admin.html.twig b/templates/admin/supplier_admin.html.twig
index ce38a5ca4..b5cf7b236 100644
--- a/templates/admin/supplier_admin.html.twig
+++ b/templates/admin/supplier_admin.html.twig
@@ -1,7 +1,9 @@
{% extends "admin/base_company_admin.html.twig" %}
{% block card_title %}
-
{% trans %}supplier.caption{% endtrans %}
+ {% set dataSourceName = get_data_source_name('supplier', 'supplier.caption') %}
+ {% set translatedSource = 'supplier.caption'|trans %}
+
{% if dataSourceName != translatedSource %}{{ 'datasource.synonym'|trans({'%name%': translatedSource, '%synonym%': dataSourceName}) }}{% else %}{{ translatedSource }}{% endif %}
{% endblock %}
{% block additional_panes %}
diff --git a/templates/assemblies/add_parts.html.twig b/templates/assemblies/add_parts.html.twig
new file mode 100644
index 000000000..d8d8e657f
--- /dev/null
+++ b/templates/assemblies/add_parts.html.twig
@@ -0,0 +1,22 @@
+{% extends "main_card.html.twig" %}
+
+{% block title %}{% trans %}assembly.add_parts_to_assembly{% endtrans %}{% endblock %}
+
+{% block card_title %}
+
+ {% trans %}assembly.add_parts_to_assembly{% endtrans %}{% if assembly %}:
{{ assembly.name }} {% endif %}
+{% endblock %}
+
+{% block card_content %}
+
+ {{ form_start(form) }}
+
+ {{ form_row(form.assembly) }}
+ {% form_theme form.bom_entries with ['form/collection_types_layout_assembly.html.twig'] %}
+ {{ form_widget(form.bom_entries) }}
+
+ {{ form_row(form.submit) }}
+
+ {{ form_end(form) }}
+
+{% endblock %}
\ No newline at end of file
diff --git a/templates/assemblies/build/_form.html.twig b/templates/assemblies/build/_form.html.twig
new file mode 100644
index 000000000..97cace564
--- /dev/null
+++ b/templates/assemblies/build/_form.html.twig
@@ -0,0 +1,90 @@
+{% import "helper.twig" as helper %}
+
+{{ form_start(form) }}
+
+
+
+
+
+
+
+
+
+ {% trans %}part.table.name{% endtrans %}
+ {% trans %}assembly.bom.mountnames{% endtrans %}
+ {% trans %}assembly.build.required_qty{% endtrans %}
+
+
+
+ {% for bom_entry in build_request.bomEntries %}
+ {# 1st row basic infos about the BOM entry #}
+
+
+
+
+ {# #}
+
+
+
+ {% if bom_entry.part %}
+ {{ bom_entry.part.name }} {% if bom_entry.name %}({{ bom_entry.name }}){% endif %}
+ {% elseif bom_entry.referencedAssembly %}
+ {{ 'assembly.build.form.referencedAssembly'|trans({'%name%': bom_entry.referencedAssembly.name}) }} {% if bom_entry.name %}({{ bom_entry.name }}){% endif %}
+ {% else %}
+ {{ bom_entry.name }}
+ {% endif %}
+
+
+ {% for tag in bom_entry.mountnames|split(',') %}
+ {{ tag | trim }}
+ {% endfor %}
+
+
+ {{ build_request.neededAmountForBOMEntry(bom_entry) | format_amount(bom_entry.part.partUnit ?? null) }} {% trans %}assembly.builds.needed{% endtrans %}
+ (= {{ number_of_builds }} x {{ bom_entry.quantity | format_amount(bom_entry.part.partUnit ?? null) }})
+
+
+
+
+ {% set lots = build_request.partLotsForBOMEntry(bom_entry) %}
+ {% if lots is not null %}
+ {% for lot in lots %}
+ {# @var lot \App\Entity\Parts\PartLot #}
+
+
+ {% if lot.storageLocation %}
+ {{ helper.structural_entity_link(lot.storageLocation) }}
+ {% endif %}
+ {% if lot.name is not empty %}
+ ({{ lot.name }} )
+ {% endif %}
+
+
+ {{ form_errors(form["lot_"~lot.id]) }}
+ {{ form_widget(form["lot_"~lot.id]) }}
+
+
+ / {{ lot.amount | format_amount(lot.part.partUnit) }} {% trans %}assembly.builds.stocked{% endtrans %}
+
+
+ {% endfor %}
+ {% endif %}
+
+
+ {% endfor %}
+
+
+
+{{ form_row(form.comment) }}
+
+{{ form_row(form.dontCheckQuantity) }}
+
+
+{{ form_row(form.addBuildsToBuildsPart) }}
+{% if form.buildsPartLot is defined %}
+ {{ form_row(form.buildsPartLot) }}
+{% endif %}
+
+{{ form_row(form.submit) }}
+
+{{ form_end(form) }}
\ No newline at end of file
diff --git a/templates/assemblies/build/build.html.twig b/templates/assemblies/build/build.html.twig
new file mode 100644
index 000000000..8f01607cb
--- /dev/null
+++ b/templates/assemblies/build/build.html.twig
@@ -0,0 +1,40 @@
+{% extends "main_card.html.twig" %}
+
+{% block title %}{% trans %}assembly.info.builds.label{% endtrans %}: {{ number_of_builds }}x {{ assembly.name }}{% endblock %}
+
+{% block card_title %}
+
+ {% trans %}assembly.info.builds.label{% endtrans %}:
{{ number_of_builds }}x {{ assembly.name }}
+{% endblock %}
+
+{% block card_content %}
+ {% set can_build = buildHelper.assemblyBuildable(assembly, number_of_builds) %}
+ {% import "components/assemblies.macro.html.twig" as assembly_macros %}
+
+ {% if assembly.status is not empty and assembly.status != "in_production" %}
+
+ {% trans with {"%assembly_status%": ('assembly.status.'~assembly.status)|trans } %}assembly.builds.check_assembly_status{% endtrans %}
+
+ {% endif %}
+
+
+ {% if not can_build %}
+
{% trans %}assembly.builds.build_not_possible{% endtrans %}
+
{% trans with {"%number_of_builds%": number_of_builds} %}assembly.builds.following_bom_entries_miss_instock_n{% endtrans %}
+
+ {% for bom_entry in buildHelper.nonBuildableAssemblyBomEntries(assembly, number_of_builds) %}
+ {{ assembly_macros.assembly_bom_entry_with_missing_instock(bom_entry, number_of_builds) }}
+ {% endfor %}
+
+ {% else %}
+
{% trans %}assembly.builds.build_possible{% endtrans %}
+
{% trans with {"%max_builds%": number_of_builds} %}assembly.builds.number_of_builds_possible{% endtrans %}
+ {% endif %}
+
+
+
{% trans %}assembly.build.help{% endtrans %}
+
+ {% include 'assemblies/build/_form.html.twig' %}
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/templates/assemblies/export_bom_pdf.html.twig b/templates/assemblies/export_bom_pdf.html.twig
new file mode 100644
index 000000000..15bf5d883
--- /dev/null
+++ b/templates/assemblies/export_bom_pdf.html.twig
@@ -0,0 +1,103 @@
+
+
+
+
Assembly Hierarchy
+
+
+
+
+
+
Table of Contents
+
+
+
+ #
+ Assembly Name
+ IPN
+ Section
+
+
+
+ {% for assembly in assemblies %}
+
+ {{ loop.index }}
+ Assembly: {{ assembly.name }}
+ {% if assembly.ipn != '' %}{{ assembly.ipn }}{% else %}-{% endif %}
+ {{ loop.index + 1 }}
+
+ {% endfor %}
+
+
+
+
+
+{% for assembly in assemblies %}
+
+
+
+
+ Name
+ IPN
+ Quantity
+ Multiplier
+ Effective Quantity
+
+
+
+ {% for part in assembly.parts %}
+
+ {{ part.name }}
+ {{ part.ipn }}
+ {{ part.quantity }}
+ {% if assembly.multiplier %}{{ assembly.multiplier }}{% else %}-{% endif %}
+ {{ part.effectiveQuantity }}
+
+ {% endfor %}
+ {% for other in assembly.others %}
+
+ {{ other.name }}
+ {{ other.ipn }}
+ {{ other.quantity }}
+ {{ other.multiplier }}
+ {{ other.effectiveQuantity }}
+
+ {% endfor %}
+ {% for referencedAssembly in assembly.referencedAssemblies %}
+
+ {{ referencedAssembly.name }}
+ {{ referencedAssembly.ipn }}
+ {{ referencedAssembly.quantity }}
+
+ {{ referencedAssembly.quantity }}
+
+ {% endfor %}
+
+
+
+ {% for refAssembly in assembly.referencedAssemblies %}
+ {% include 'assemblies/export_bom_referenced_assembly_pdf.html.twig' with {'assembly': refAssembly} only %}
+ {% endfor %}
+
+ {% if not loop.last %}
+
+ {% endif %}
+
+
+{% endfor %}
+
+
diff --git a/templates/assemblies/export_bom_referenced_assembly_pdf.html.twig b/templates/assemblies/export_bom_referenced_assembly_pdf.html.twig
new file mode 100644
index 000000000..b5a1324d9
--- /dev/null
+++ b/templates/assemblies/export_bom_referenced_assembly_pdf.html.twig
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+ Type
+ Name
+ IPN
+ Quantity
+ Multiplier
+ Effective Quantity
+
+
+
+ {% for part in assembly.parts %}
+
+ Part
+ {{ part.name }}
+ {{ part.ipn }}
+ {{ part.quantity }}
+ {% if assembly.multiplier %}{{ assembly.multiplier }}{% else %}-{% endif %}
+ {{ part.effectiveQuantity }}
+
+ {% endfor %}
+
+ {% for other in assembly.others %}
+
+ Other
+ {{ other.name }}
+ -
+ {{ other.quantity }}
+ {{ other.multiplier }}
+ -
+
+ {% endfor %}
+
+ {% for referencedAssembly in assembly.referencedAssemblies %}
+
+ Referenced assembly
+ {{ referencedAssembly.name }}
+ -
+ {{ referencedAssembly.quantity }}
+
+ {{ referencedAssembly.multiplier }}
+
+ {% endfor %}
+
+
+
+
+ {% for refAssembly in assembly.referencedAssemblies %}
+ {% include 'assemblies/export_bom_referenced_assembly_pdf.html.twig' with {'assembly': refAssembly} only %}
+ {% endfor %}
+
diff --git a/templates/assemblies/import_bom.html.twig b/templates/assemblies/import_bom.html.twig
new file mode 100644
index 000000000..89f504c2f
--- /dev/null
+++ b/templates/assemblies/import_bom.html.twig
@@ -0,0 +1,112 @@
+{% extends "main_card.html.twig" %}
+
+{% block title %}{% trans %}assembly.import_bom{% endtrans %}{% endblock %}
+
+{% block before_card %}
+ {% if validationErrors or importerErrors %}
+
+
{% trans %}parts.import.errors.title{% endtrans %}
+
+ {% if validationErrors %}
+ {% for violation in validationErrors %}
+
+ {{ violation.propertyPath }}:
+ {{ violation.message|trans(violation.parameters, 'validators') }}
+
+ {% endfor %}
+ {% endif %}
+
+ {% if importerErrors %}
+ {% for violation in importerErrors %}
+
+ {{ violation.propertyPath }}:
+ {{ violation.message|trans(violation.parameters, 'validators')|raw }}
+
+ {% endfor %}
+ {% endif %}
+
+
+ {% endif %}
+{% endblock %}
+
+{% block card_title %}
+
+ {% trans %}assembly.import_bom{% endtrans %}{% if assembly %}:
{{ assembly.name }} {% endif %}
+{% endblock %}
+
+{% block card_content %}
+ {{ form(form) }}
+{% endblock %}
+
+{% block additional_content %}
+
+
+
+
+
+
{{ jsonTemplate|json_encode(constant('JSON_PRETTY_PRINT') b-or constant('JSON_UNESCAPED_UNICODE')) }}
+
+ {{ 'assembly.bom_import.template.json.table'|trans|raw }}
+
+
+
+
+
+
+
+ {{ 'assembly.bom_import.template.csv.exptected_columns'|trans }}
+
+
quantity;name;part_id;part_mpnr;part_ipn;part_name;part_description;part_manufacturer_id;part_manufacturer_name;part_category_id;part_category_name
+
+
+ quantity
+ name
+ part_id
+ part_mpnr
+ part_ipn
+ part_name
+ part_description
+ part_manufacturer_id
+ part_manufacturer_name
+ part_category_id
+ part_category_name
+
+
+ {{ 'assembly.bom_import.template.csv.table'|trans|raw }}
+
+
+
+
+
+
+
+ {{ 'assembly.bom_import.template.kicad_pcbnew.exptected_columns'|trans }}
+
Id;Designator;Package;Quantity;Designation;Supplier and ref
+
+
+ Id
+ Designator
+ Package
+ Quantity
+ Designation
+ Supplier and ref
+ Note
+ Footprint
+ Value
+ Footprint
+
+
+ {{ 'assembly.bom_import.template.kicad_pcbnew.exptected_columns.note'|trans|raw }}
+
+ {{ 'assembly.bom_import.template.kicad_pcbnew.table'|trans|raw }}
+
+
+
+
+{% endblock %}
diff --git a/templates/assemblies/info/_attachments_info.html.twig b/templates/assemblies/info/_attachments_info.html.twig
new file mode 100644
index 000000000..747426c3a
--- /dev/null
+++ b/templates/assemblies/info/_attachments_info.html.twig
@@ -0,0 +1,91 @@
+{% import "helper.twig" as helper %}
+
+
+
+
+
+ {% trans %}attachment.name{% endtrans %}
+ {% trans %}attachment.attachment_type{% endtrans %}
+ {% trans %}attachment.file_name{% endtrans %}
+ {% trans %}attachment.file_size{% endtrans %}
+
+
+
+
+
+
+
+ {% for attachment in assembly.attachments %}
+
+
+ {% import "components/attachments.macro.html.twig" as attachments %}
+ {{ attachments.attachment_icon(attachment, attachment_manager) }}
+
+ {{ attachment.name }}
+ {{ attachment.attachmentType.fullPath }}
+
+ {% if attachment.hasInternal() %}
+ {{ attachment.filename }}
+ {% endif %}
+
+
+ {% if not attachment.hasInternal() %}
+
+ {% trans %}attachment.external_only{% endtrans %}
+
+ {% elseif attachment_manager.internalFileExisting(attachment) %}
+
+ {{ attachment_manager.humanFileSize(attachment) }}
+
+ {% else %}
+
+ {% trans %}attachment.file_not_found{% endtrans %}
+
+ {% endif %}
+ {% if attachment.secure %}
+
+ {% trans %}attachment.secure{% endtrans %}
+
+ {% endif %}
+ {% if attachment == assembly.masterPictureAttachment %}
+
+
+ {% trans %}attachment.preview{% endtrans %}
+
+ {% endif %}
+
+
+
+
+ {% endfor %}
+
+
+
+
\ No newline at end of file
diff --git a/templates/assemblies/info/_builds.html.twig b/templates/assemblies/info/_builds.html.twig
new file mode 100644
index 000000000..780c8c609
--- /dev/null
+++ b/templates/assemblies/info/_builds.html.twig
@@ -0,0 +1,40 @@
+{% set can_build = buildHelper.assemblyBuildable(assembly) %}
+
+{% import "components/assemblies.macro.html.twig" as assembly_macros %}
+
+{% if assembly.status is not empty and assembly.status != "in_production" %}
+
+ {% trans with {"%assembly_status%": ('assembly.status.'~assembly.status)|trans } %}assembly.builds.check_assembly_status{% endtrans %}
+
+{% endif %}
+
+
+ {% if not can_build %}
+
{% trans %}assembly.builds.build_not_possible{% endtrans %}
+
{% trans %}assembly.builds.following_bom_entries_miss_instock{% endtrans %}
+
+ {% for bom_entry in buildHelper.nonBuildableAssemblyBomEntries(assembly) %}
+ {{ assembly_macros.assembly_bom_entry_with_missing_instock(bom_entry) }}
+ {% endfor %}
+
+ {% else %}
+
{% trans %}assembly.builds.build_possible{% endtrans %}
+
{% trans with {"%max_builds%": buildHelper.maximumBuildableCount(assembly)} %}assembly.builds.number_of_builds_possible{% endtrans %}
+ {% endif %}
+
+
+
+
+{% if assembly.buildPart %}
+
{% trans %}assembly.builds.no_stocked_builds{% endtrans %}: {{ assembly.buildPart.amountSum }}
+{% endif %}
\ No newline at end of file
diff --git a/templates/assemblies/info/_info.html.twig b/templates/assemblies/info/_info.html.twig
new file mode 100644
index 000000000..97da3f708
--- /dev/null
+++ b/templates/assemblies/info/_info.html.twig
@@ -0,0 +1,72 @@
+{% import "helper.twig" as helper %}
+
+
+
+
+
+ {% if assembly.masterPictureAttachment %}
+
+
+
+ {% else %}
+
+ {% endif %}
+
+
+
{{ assembly.name }}
+ {# You need edit permission to use the edit button #}
+ {% if is_granted('edit', assembly) %}
+
+ {% endif %}
+
+
{{ assembly.description|format_markdown(true) }}
+
+
+
+
+
+
{# Sidebar panel with infos about last creation date, etc. #}
+
+
+ {{ helper.date_user_combination(assembly, true) }}
+
+
+
+ {{ helper.date_user_combination(assembly, false) }}
+
+
+
+
+
+ {{ helper.assemblies_status_to_badge(assembly.status) }}
+
+
+
+
+
+
+ {{ assembly.bomEntries | length }}
+ {% trans %}assembly.info.bom_entries_count{% endtrans %}
+
+
+
+ {% if assembly.children is not empty %}
+
+
+
+
+ {{ assembly.children | length }}
+ {% trans %}assembly.info.sub_assemblies_count{% endtrans %}
+
+
+
+ {% endif %}
+
+
+ {% if assembly.comment is not empty %}
+
+
{% trans %}comment.label{% endtrans %}:
+ {{ assembly.comment|format_markdown }}
+
+ {% endif %}
+
diff --git a/templates/assemblies/info/_info_card.html.twig b/templates/assemblies/info/_info_card.html.twig
new file mode 100644
index 000000000..2d0c535b2
--- /dev/null
+++ b/templates/assemblies/info/_info_card.html.twig
@@ -0,0 +1,118 @@
+{% import "helper.twig" as helper %}
+{% import "label_system/dropdown_macro.html.twig" as dropdown %}
+
+{{ helper.breadcrumb_entity_link(assembly) }}
+
+
+
+
+
+
+ {% if assembly.description is not empty %}
+ {{ assembly.description|format_markdown }}
+ {% endif %}
+
+
+
+
+
+
+
+ {% if assembly.attachments is not empty %}
+
+ {% include "parts/info/_attachments_info.html.twig" with {"part": assembly} %}
+
+ {% endif %}
+
+ {% if assembly.comment is not empty %}
+
+ {% endif %}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/templates/assemblies/info/_part.html.twig b/templates/assemblies/info/_part.html.twig
new file mode 100644
index 000000000..1fa8b90ed
--- /dev/null
+++ b/templates/assemblies/info/_part.html.twig
@@ -0,0 +1,5 @@
+{% import "components/datatables.macro.html.twig" as datatables %}
+
+
+
+{{ datatables.datatable(datatable, 'elements/datatables/datatables', 'assemblies') }}
\ No newline at end of file
diff --git a/templates/assemblies/info/info.html.twig b/templates/assemblies/info/info.html.twig
new file mode 100644
index 000000000..667da909f
--- /dev/null
+++ b/templates/assemblies/info/info.html.twig
@@ -0,0 +1,105 @@
+{% extends "main_card.html.twig" %}
+{% import "helper.twig" as helper %}
+
+{% block title %}
+ {% trans %}assembly.info.title{% endtrans %}: {{ assembly.name }}
+{% endblock %}
+
+{% block before_card %}
+
+{% endblock %}
+
+{% block content %}
+ {{ helper.breadcrumb_entity_link(assembly) }}
+ {{ parent() }}
+{% endblock %}
+
+{% block card_title %}
+ {% if assembly.masterPictureAttachment is not null and attachment_manager.isFileExisting(assembly.masterPictureAttachment) %}
+
+ {% else %}
+ {{ helper.entity_icon(assembly, "me-1") }}
+ {% endif %}
+ {% trans %}assembly.info.title{% endtrans %}:
{{ assembly.name }}
+{% endblock %}
+
+{% block card_content %}
+
+
+
+
+ {% include "assemblies/info/_info.html.twig" %}
+
+
+ {% include "assemblies/info/_part.html.twig" %}
+
+
+ {% include "assemblies/info/_attachments_info.html.twig" with {"assembly": assembly} %}
+
+
+
+{% endblock %}
diff --git a/templates/assemblies/lists/_action_bar.html.twig b/templates/assemblies/lists/_action_bar.html.twig
new file mode 100644
index 000000000..37289812a
--- /dev/null
+++ b/templates/assemblies/lists/_action_bar.html.twig
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/templates/assemblies/lists/_filter.html.twig b/templates/assemblies/lists/_filter.html.twig
new file mode 100644
index 000000000..11be7bc24
--- /dev/null
+++ b/templates/assemblies/lists/_filter.html.twig
@@ -0,0 +1,62 @@
+
\ No newline at end of file
diff --git a/templates/assemblies/lists/all_list.html.twig b/templates/assemblies/lists/all_list.html.twig
new file mode 100644
index 000000000..70d75ad40
--- /dev/null
+++ b/templates/assemblies/lists/all_list.html.twig
@@ -0,0 +1,30 @@
+{% extends "base.html.twig" %}
+
+{% block title %}
+ {% trans %}assembly_list.all.title{% endtrans %}
+{% endblock %}
+
+{% block content %}
+
+
+
+
+ {% include "assemblies/lists/_filter.html.twig" %}
+
+
+ {% include "assemblies/lists/_action_bar.html.twig" with {'url_options': {}} %}
+ {% include "assemblies/lists/data.html.twig" %}
+
+{% endblock %}
diff --git a/templates/assemblies/lists/data.html.twig b/templates/assemblies/lists/data.html.twig
new file mode 100644
index 000000000..69e13e4f5
--- /dev/null
+++ b/templates/assemblies/lists/data.html.twig
@@ -0,0 +1,3 @@
+{% import "components/datatables.macro.html.twig" as datatables %}
+
+{{ datatables.partsDatatableWithForm(datatable) }}
diff --git a/templates/components/assemblies.macro.html.twig b/templates/components/assemblies.macro.html.twig
new file mode 100644
index 000000000..d59005e05
--- /dev/null
+++ b/templates/components/assemblies.macro.html.twig
@@ -0,0 +1,8 @@
+{% macro assembly_bom_entry_with_missing_instock(assembly_bom_entry, number_of_builds = 1) %}
+ {# @var \App\Entity\AssemblySystem\AssemblyBOMEntry assembly_bom_entry #}
+
{{ assembly_bom_entry.part.name }}
+ {% if assembly_bom_entry.name %} ({{ assembly_bom_entry.name }}){% endif %}:
+
{{ assembly_bom_entry.part.amountSum | format_amount(assembly_bom_entry.part.partUnit) }} {% trans %}assembly.builds.stocked{% endtrans %}
+ /
+
{{ (assembly_bom_entry.quantity * number_of_builds) | format_amount(assembly_bom_entry.part.partUnit) }} {% trans %}assembly.builds.needed{% endtrans %}
+{% endmacro %}
\ No newline at end of file
diff --git a/templates/components/tree_macros.html.twig b/templates/components/tree_macros.html.twig
index 366d42fe8..210a00633 100644
--- a/templates/components/tree_macros.html.twig
+++ b/templates/components/tree_macros.html.twig
@@ -1,13 +1,16 @@
{% macro sidebar_dropdown() %}
+ {% set currentLocale = app.request.locale %}
+
{# Format is [mode, route, label, show_condition] #}
{% set data_sources = [
- ['categories', path('tree_category_root'), 'category.labelp', is_granted('@categories.read') and is_granted('@parts.read')],
- ['locations', path('tree_location_root'), 'storelocation.labelp', is_granted('@storelocations.read') and is_granted('@parts.read')],
- ['footprints', path('tree_footprint_root'), 'footprint.labelp', is_granted('@footprints.read') and is_granted('@parts.read')],
- ['manufacturers', path('tree_manufacturer_root'), 'manufacturer.labelp', is_granted('@manufacturers.read') and is_granted('@parts.read')],
- ['suppliers', path('tree_supplier_root'), 'supplier.labelp', is_granted('@suppliers.read') and is_granted('@parts.read')],
- ['projects', path('tree_device_root'), 'project.labelp', is_granted('@projects.read')],
- ['tools', path('tree_tools'), 'tools.label', true],
+ ['categories', path('tree_category_root'), 'category.labelp', is_granted('@categories.read') and is_granted('@parts.read'), 'category'],
+ ['locations', path('tree_location_root'), 'storelocation.labelp', is_granted('@storelocations.read') and is_granted('@parts.read'), 'storagelocation'],
+ ['footprints', path('tree_footprint_root'), 'footprint.labelp', is_granted('@footprints.read') and is_granted('@parts.read'), 'footprint'],
+ ['manufacturers', path('tree_manufacturer_root'), 'manufacturer.labelp', is_granted('@manufacturers.read') and is_granted('@parts.read'), 'manufacturer'],
+ ['suppliers', path('tree_supplier_root'), 'supplier.labelp', is_granted('@suppliers.read') and is_granted('@parts.read'), 'supplier'],
+ ['projects', path('tree_device_root'), 'project.labelp', is_granted('@projects.read'), 'project'],
+ ['assembly', path('tree_assembly_root'), 'assembly.labelp', is_granted('@assemblies.read'), 'assembly'],
+ ['tools', path('tree_tools'), 'tools.label', true, 'tool'],
] %}
@@ -18,9 +21,9 @@
{% for source in data_sources %}
{% if source[3] %} {# show_condition #}
-
{{ source[2] | trans }}
+ >{{ get_data_source_name(source[4], source[2]) }}
{% endif %}
{% endfor %}
{% endmacro %}
@@ -61,4 +64,4 @@
-{% endmacro %}
\ No newline at end of file
+{% endmacro %}
diff --git a/templates/form/collection_types_layout_assembly.html.twig b/templates/form/collection_types_layout_assembly.html.twig
new file mode 100644
index 000000000..6dc6d49a0
--- /dev/null
+++ b/templates/form/collection_types_layout_assembly.html.twig
@@ -0,0 +1,83 @@
+{% block assembly_bom_entry_collection_widget %}
+ {% import 'components/collection_type.macro.html.twig' as collection %}
+
+
+{% endblock %}
+
+{% block assembly_bom_entry_widget %}
+ {% set target_id = 'expand_row-' ~ form.vars.name %}
+
+ {% import 'components/collection_type.macro.html.twig' as collection %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/templates/form/permission_layout.html.twig b/templates/form/permission_layout.html.twig
index 166147b4c..dcceae335 100644
--- a/templates/form/permission_layout.html.twig
+++ b/templates/form/permission_layout.html.twig
@@ -6,12 +6,36 @@
{% endif %}
diff --git a/templates/helper.twig b/templates/helper.twig
index bd1d2aa7a..3ddb4f7fa 100644
--- a/templates/helper.twig
+++ b/templates/helper.twig
@@ -76,6 +76,21 @@
{% endif %}
{% endmacro %}
+{% macro assemblies_status_to_badge(status, class="badge") %}
+ {% if status is not empty %}
+ {% set color = " bg-secondary" %}
+
+ {% if status == "in_production" %}
+ {% set color = " bg-success" %}
+ {% endif %}
+
+
+ {% endif %}
+{% endmacro %}
+
{% macro structural_entity_link(entity, link_type = "list_parts") %}
{# @var entity \App\Entity\Base\StructuralDBElement #}
{% if entity %}
@@ -101,6 +116,7 @@
"category": ["fa-solid fa-tags", "category.label"],
"currency": ["fa-solid fa-coins", "currency.label"],
"device": ["fa-solid fa-archive", "project.label"],
+ "assembly": ["fa-solid fa-list", "assembly.label"],
"footprint": ["fa-solid fa-microchip", "footprint.label"],
"group": ["fa-solid fa-users", "group.label"],
"label_profile": ["fa-solid fa-qrcode", "label_profile.label"],
diff --git a/templates/parts/edit/_advanced.html.twig b/templates/parts/edit/_advanced.html.twig
index 12b546abc..991a36ebb 100644
--- a/templates/parts/edit/_advanced.html.twig
+++ b/templates/parts/edit/_advanced.html.twig
@@ -1,5 +1,16 @@
{{ form_row(form.needsReview) }}
{{ form_row(form.favorite) }}
{{ form_row(form.mass) }}
-{{ form_row(form.ipn) }}
-{{ form_row(form.partUnit) }}
\ No newline at end of file
+
+{{ form_row(form.partUnit) }}
+{{ form_row(form.partCustomState) }}
diff --git a/templates/parts/info/_assemblies.html.twig b/templates/parts/info/_assemblies.html.twig
new file mode 100644
index 000000000..d4996c592
--- /dev/null
+++ b/templates/parts/info/_assemblies.html.twig
@@ -0,0 +1,31 @@
+{% import "components/attachments.macro.html.twig" as attachments %}
+{% import "helper.twig" as helper %}
+
+
\ No newline at end of file
diff --git a/templates/parts/info/show_part_info.html.twig b/templates/parts/info/show_part_info.html.twig
index 96b5e2091..cd7b4ce7a 100644
--- a/templates/parts/info/show_part_info.html.twig
+++ b/templates/parts/info/show_part_info.html.twig
@@ -109,15 +109,20 @@
{% trans %}vendor.partinfo.history{% endtrans %}
- {% if part.projectBomEntries is not empty %}
-