mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-07-04 08:21:40 +00:00
Compare commits
7 commits
60c5e24c94
...
753ecee849
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
753ecee849 | ||
|
|
8f6ed74d93 | ||
|
|
17f11c02f3 | ||
|
|
a070ebb2ce | ||
|
|
44bb132de1 | ||
|
|
95f3fc66c2 | ||
|
|
74e5102943 |
8 changed files with 90 additions and 53 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
2.9.0
|
2.9.1
|
||||||
|
|
|
||||||
|
|
@ -240,7 +240,8 @@ class ProjectController extends AbstractController
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect fields and get suggestions
|
// Detect fields and get suggestions
|
||||||
$detected_fields = $BOMImporter->detectFields($file_content);
|
$detected_delimiter = $BOMImporter->detectDelimiter($file_content);
|
||||||
|
$detected_fields = $BOMImporter->detectFields($file_content, $detected_delimiter);
|
||||||
$suggested_mapping = $BOMImporter->getSuggestedFieldMapping($detected_fields);
|
$suggested_mapping = $BOMImporter->getSuggestedFieldMapping($detected_fields);
|
||||||
|
|
||||||
// Create mapping of original field names to sanitized field names for template
|
// Create mapping of original field names to sanitized field names for template
|
||||||
|
|
@ -257,7 +258,7 @@ class ProjectController extends AbstractController
|
||||||
$builder->add('delimiter', ChoiceType::class, [
|
$builder->add('delimiter', ChoiceType::class, [
|
||||||
'label' => 'project.bom_import.delimiter',
|
'label' => 'project.bom_import.delimiter',
|
||||||
'required' => true,
|
'required' => true,
|
||||||
'data' => ',',
|
'data' => $detected_delimiter,
|
||||||
'choices' => [
|
'choices' => [
|
||||||
'project.bom_import.delimiter.comma' => ',',
|
'project.bom_import.delimiter.comma' => ',',
|
||||||
'project.bom_import.delimiter.semicolon' => ';',
|
'project.bom_import.delimiter.semicolon' => ';',
|
||||||
|
|
|
||||||
|
|
@ -721,26 +721,36 @@ class BOMImporter
|
||||||
return $mapped;
|
return $mapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to detect the separator used in the CSV data by analyzing the first line and counting occurrences of common delimiters.
|
||||||
|
* @param string $data
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function detectDelimiter(string $data): string
|
||||||
|
{
|
||||||
|
$delimiters = [',', ';', "\t"];
|
||||||
|
$lines = explode("\n", $data, 2);
|
||||||
|
$header_line = $lines[0] ?? '';
|
||||||
|
$delimiter_counts = [];
|
||||||
|
foreach ($delimiters as $delim) {
|
||||||
|
$delimiter_counts[$delim] = substr_count($header_line, $delim);
|
||||||
|
}
|
||||||
|
// Choose the delimiter with the highest count, default to comma if all are zero
|
||||||
|
$max_count = max($delimiter_counts);
|
||||||
|
$delimiter = array_search($max_count, $delimiter_counts, true);
|
||||||
|
if ($max_count === 0 || $delimiter === false) {
|
||||||
|
$delimiter = ',';
|
||||||
|
}
|
||||||
|
return $delimiter;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detect available fields in CSV data for field mapping UI
|
* Detect available fields in CSV data for field mapping UI
|
||||||
*/
|
*/
|
||||||
public function detectFields(string $data, ?string $delimiter = null): array
|
public function detectFields(string $data, ?string $delimiter = null): array
|
||||||
{
|
{
|
||||||
if ($delimiter === null) {
|
if ($delimiter === null) {
|
||||||
// Detect delimiter by counting occurrences in the first row (header)
|
$delimiter = $this->detectDelimiter($data);
|
||||||
$delimiters = [',', ';', "\t"];
|
|
||||||
$lines = explode("\n", $data, 2);
|
|
||||||
$header_line = $lines[0] ?? '';
|
|
||||||
$delimiter_counts = [];
|
|
||||||
foreach ($delimiters as $delim) {
|
|
||||||
$delimiter_counts[$delim] = substr_count($header_line, $delim);
|
|
||||||
}
|
|
||||||
// Choose the delimiter with the highest count, default to comma if all are zero
|
|
||||||
$max_count = max($delimiter_counts);
|
|
||||||
$delimiter = array_search($max_count, $delimiter_counts, true);
|
|
||||||
if ($max_count === 0 || $delimiter === false) {
|
|
||||||
$delimiter = ',';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Handle potential BOM (Byte Order Mark) at the beginning
|
// Handle potential BOM (Byte Order Mark) at the beginning
|
||||||
$data = preg_replace('/^\xEF\xBB\xBF/', '', $data);
|
$data = preg_replace('/^\xEF\xBB\xBF/', '', $data);
|
||||||
|
|
|
||||||
|
|
@ -47,17 +47,17 @@
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ detail.price | format_money(detail.currency) }} / {{ detail.PriceRelatedQuantity | format_amount(part.partUnit) }}
|
{{ detail.price | format_money(detail.currency) }} / {{ detail.PriceRelatedQuantity | format_amount(part.partUnit) }}
|
||||||
{% set tmp = pricedetail_helper.convertMoneyToCurrency(detail.price, detail.currency) %}
|
{% set tmp = pricedetail_helper.convertMoneyToCurrency(detail.price, detail.currency, app.user.currency ?? null) %}
|
||||||
{% if detail.currency != (app.user.currency ?? null) and tmp is not null and tmp.GreaterThan(0) %}
|
{% if detail.currency != (app.user.currency ?? null) and tmp is not null and tmp.GreaterThan(0) %}
|
||||||
<span class="text-muted">({{ pricedetail_helper.convertMoneyToCurrency(detail.price, detail.currency, app.user.currency ?? null) | format_money(app.user.currency ?? null) }})</span>
|
<span class="text-muted">({{ tmp | format_money(app.user.currency ?? null) }})</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<small class="text-muted">{{- helper.vat_text(detail.includesVAT) -}}</small>
|
<small class="text-muted">{{- helper.vat_text(detail.includesVAT) -}}</small>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ detail.PricePerUnit | format_money(detail.currency) }}
|
{{ detail.PricePerUnit | format_money(detail.currency) }}
|
||||||
{% set tmp = pricedetail_helper.convertMoneyToCurrency(detail.PricePerUnit, detail.currency) %}
|
{% set tmp = pricedetail_helper.convertMoneyToCurrency(detail.PricePerUnit, detail.currency, app.user.currency ?? null) %}
|
||||||
{% if detail.currency != (app.user.currency ?? null) and tmp is not null and tmp.GreaterThan(0) %}
|
{% if detail.currency != (app.user.currency ?? null) and tmp is not null and tmp.GreaterThan(0) %}
|
||||||
<span class="text-muted">({{ pricedetail_helper.convertMoneyToCurrency(detail.PricePerUnit, detail.currency, app.user.currency ?? null) | format_money(app.user.currency ?? null) }})</span>
|
<span class="text-muted">({{ tmp | format_money(app.user.currency ?? null) }})</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<small class="text-muted">{{- helper.vat_text(detail.includesVAT) -}}</small>
|
<small class="text-muted">{{- helper.vat_text(detail.includesVAT) -}}</small>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
||||||
|
|
@ -48,51 +48,59 @@
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans %}project.bom_import.field_mapping.csv_field{% endtrans %}</th>
|
<th>{% trans %}project.bom_import.field_mapping.csv_field{% endtrans %}</th>
|
||||||
<th>{% trans %}project.bom_import.field_mapping.maps_to{% endtrans %}</th>
|
<th>{% trans %}project.bom_import.field_mapping.maps_to{% endtrans %}</th>
|
||||||
<th>{% trans %}project.bom_import.field_mapping.suggestion{% endtrans %}</th>
|
<th>{% trans %}project.bom_import.field_mapping.suggestion{% endtrans %}</th>
|
||||||
<th>{% trans %}project.bom_import.field_mapping.priority{% endtrans %}</th>
|
<th>{% trans %}project.bom_import.field_mapping.priority{% endtrans %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for field in detected_fields %}
|
{% for field in detected_fields %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<code>{{ field }}</code>
|
<code>{{ field }}</code>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
{# TODO: This is more a workaround than a proper fix. Ideally the controller should be fixed in a way, that we get the correct fields here #}
|
||||||
|
{% if field_name_mapping[field] is defined %}
|
||||||
|
{% set field_name = field_name_mapping[field] %}
|
||||||
{{ form_widget(form['mapping_' ~ field_name_mapping[field]], {
|
{{ form_widget(form['mapping_' ~ field_name_mapping[field]], {
|
||||||
'attr': {
|
'attr': {
|
||||||
'class': 'form-select field-mapping-select',
|
'class': 'form-select field-mapping-select',
|
||||||
'data-field': field
|
'data-field': field
|
||||||
}
|
}
|
||||||
}) }}
|
}) }}
|
||||||
</td>
|
{% else %}
|
||||||
<td>
|
<b class="text-danger">
|
||||||
{% if suggested_mapping[field] is defined %}
|
{% trans %}project.bom_import.field_mapping.error.check_delimiter{% endtrans %}
|
||||||
<span class="badge bg-success">
|
</b>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if suggested_mapping[field] is defined %}
|
||||||
|
<span class="badge bg-success">
|
||||||
<i class="fa-solid fa-magic fa-fw"></i>
|
<i class="fa-solid fa-magic fa-fw"></i>
|
||||||
{{ suggested_mapping[field] }}
|
{{ suggested_mapping[field] }}
|
||||||
</span>
|
</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="text-muted">
|
<span class="text-muted">
|
||||||
<i class="fa-solid fa-question fa-fw"></i>
|
<i class="fa-solid fa-question fa-fw"></i>
|
||||||
{% trans %}project.bom_import.field_mapping.no_suggestion{% endtrans %}
|
{% trans %}project.bom_import.field_mapping.no_suggestion{% endtrans %}
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="number"
|
<input type="number"
|
||||||
class="form-control form-control-sm priority-input"
|
class="form-control form-control-sm priority-input"
|
||||||
min="1"
|
min="1"
|
||||||
value="10"
|
value="10"
|
||||||
style="width: 80px;"
|
style="width: 80px;"
|
||||||
data-field="{{ field }}"
|
data-field="{{ field }}"
|
||||||
title="{% trans %}project.bom_import.field_mapping.priority_help{% endtrans %}">
|
title="{% trans %}project.bom_import.field_mapping.priority_help{% endtrans %}">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -12945,5 +12945,11 @@ Buerklin-API-Authentication-Server:
|
||||||
<target>[Part_lot] aus Barcode erstellt: Bitte überprüfen Sie, ob die Daten korrekt und gewünscht sind.</target>
|
<target>[Part_lot] aus Barcode erstellt: Bitte überprüfen Sie, ob die Daten korrekt und gewünscht sind.</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
|
<unit id="F8pQuL9" name="project.bom_import.field_mapping.error.check_delimiter">
|
||||||
|
<segment state="translated">
|
||||||
|
<source>project.bom_import.field_mapping.error.check_delimiter</source>
|
||||||
|
<target>Zuordnungsfehler: Bitte prüfen Sie, ob Sie das richtige Trennzeichen ausgewählt haben!</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
||||||
|
|
|
||||||
|
|
@ -12947,5 +12947,11 @@ Buerklin-API Authentication server:
|
||||||
<target>[Part_lot] created from barcode: Please check if the data is correct and desired.</target>
|
<target>[Part_lot] created from barcode: Please check if the data is correct and desired.</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
|
<unit id="F8pQuL9" name="project.bom_import.field_mapping.error.check_delimiter">
|
||||||
|
<segment state="translated">
|
||||||
|
<source>project.bom_import.field_mapping.error.check_delimiter</source>
|
||||||
|
<target>Mapping error: Check if you have selected the right delimiter!</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
||||||
|
|
|
||||||
|
|
@ -247,5 +247,11 @@
|
||||||
<target>该类型在此语言下已存在翻译定义!</target>
|
<target>该类型在此语言下已存在翻译定义!</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
|
<unit id="zT_j_oQ" name="validator.invalid_gtin">
|
||||||
|
<segment state="translated">
|
||||||
|
<source>validator.invalid_gtin</source>
|
||||||
|
<target>无效的GTIN / EAN 码。</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue