Skip to content
Code-Schnipsel Gruppen Projekte
Commit 367d4ebc erstellt von Henning Leutz's avatar Henning Leutz :martial_arts_uniform:
Dateien durchsuchen

feat: multiple prices for different currencies for price fields

Übergeordneter b2d08aff
Keine zugehörigen Branchen gefunden
Keine zugehörigen Tags gefunden
Keine zugehörigen Merge Requests gefunden
Pipeline-Nr. 16534 mit Warnungen bestanden
......@@ -17,23 +17,30 @@
*/
QUI::$Ajax->registerFunction(
'package_quiqqer_products_ajax_products_calcBruttoPrice',
function ($price, $formatted, $productId) {
function ($price, $formatted, $productId, $currency) {
$Currency = QUI\ERP\Defaults::getCurrency();
if (!empty($currency)) {
try {
$Currency = QUI\ERP\Currency\Handler::getCurrency($currency);
} catch (QUI\Exception) {
$Currency = QUI\ERP\Defaults::getCurrency();
}
}
try {
$price = QUI\ERP\Money\Price::validatePrice($price);
$baseFormatted = QUI\ERP\Defaults::getCurrency()->format($price);
$bruttoPrice = Calc::calcBruttoPrice($price, false, $productId);
$nettoPriceFormatted = Calc::calcNettoPrice($bruttoPrice, true, $productId);
$priceResult = Calc::calcBruttoPrice($price, false, $productId);
if ($baseFormatted === $nettoPriceFormatted) {
return Calc::calcBruttoPrice($price, $formatted, $productId);
if ($formatted) {
return $Currency->format($priceResult);
}
return Calc::calcBruttoPrice($price, $formatted, $productId);
return $priceResult;
} catch (Exception $Exception) {
QUI\System\Log::writeException($Exception);
throw new \QUI\ERP\Exception(['quiqqer/products', 'ajax.general_error']);
throw new QUI\ERP\Exception(['quiqqer/products', 'ajax.general_error']);
}
},
['price', 'formatted', 'productId']
['price', 'formatted', 'productId', 'currency']
);
......@@ -26,7 +26,7 @@ function ($productId, $categories, $categoryId, $fields) {
$fields = json_decode($fields, true);
// fields
foreach ($fields as $fieldId => $field) {
foreach ($fields as $fieldId => $fieldValue) {
try {
$fieldId = (int)str_replace('field-', '', $fieldId);
$Field = $Fields->getField($fieldId);
......@@ -44,6 +44,18 @@ function ($productId, $categories, $categoryId, $fields) {
continue;
}
// price options -> like other currencies
if (
$fieldId === QUI\ERP\Products\Handler\Fields::FIELD_PRICE
&& is_string($fieldValue)
&& str_contains($fieldValue, '{')
) {
// price value is json string
$json = json_decode($fieldValue, true);
$fieldValue = $json['value'];
$ProductField->setOption('currencies', $json['currencies']);
}
// pcsg-projects/demo-shop/-/issues/9#note_152341
if (
$Product instanceof VariantParent
......@@ -52,7 +64,7 @@ function ($productId, $categories, $categoryId, $fields) {
$editable = $Product->getAttribute('editableVariantFields');
if (is_array($editable) && in_array($ProductField->getId(), $editable)) {
$ProductField->setValue($field);
$ProductField->setValue($fieldValue);
continue;
}
}
......@@ -62,13 +74,13 @@ function ($productId, $categories, $categoryId, $fields) {
&& ($Product instanceof VariantParent || $Product instanceof VariantChild)
) {
if ($ProductField->getOption('exclude_from_variant_generation')) {
$ProductField->setValue($field);
$ProductField->setValue($fieldValue);
}
continue;
}
$ProductField->setValue($field);
$ProductField->setValue($fieldValue);
} catch (QUI\ERP\Products\Product\Exception $Exception) {
if ($Exception->getCode() === 1002) {
continue;
......@@ -79,7 +91,7 @@ function ($productId, $categories, $categoryId, $fields) {
[
'id' => $Field->getId(),
'title' => $Field->getTitle(),
'data' => $field
'data' => $fieldValue
]
);
......@@ -90,7 +102,7 @@ function ($productId, $categories, $categoryId, $fields) {
[
'id' => $Field->getId(),
'title' => $Field->getTitle(),
'data' => $field
'data' => $fieldValue
]
);
......
......@@ -15,25 +15,27 @@ define('package/quiqqer/products/bin/controls/fields/types/Price', [
], function (QUI, QUIControl, PriceBruttoWindow, QUILocale, QUIAjax) {
"use strict";
var lg = 'quiqqer/products';
const lg = 'quiqqer/products';
return new Class({
Extends: QUIControl,
Type : 'package/quiqqer/products/bin/controls/fields/types/Price',
Type: 'package/quiqqer/products/bin/controls/fields/types/Price',
Binds: [
'$onImport',
'openBruttoInput',
'openBruttoInputForCurrencies',
'$calcBruttoPrice'
],
initialize: function (options) {
this.parent(options);
this.$Button = null;
this.$$BruttoInput = null;
this.$Formatter = null;
this.$Input = null;
this.$Button = null;
this.$Formatter = null;
this.$currencyList = null;
this.$calcTimer = null;
this.$productId = null;
......@@ -47,43 +49,46 @@ define('package/quiqqer/products/bin/controls/fields/types/Price', [
* event : on import
*/
$onImport: function () {
var self = this,
Elm = this.getElm();
const Elm = this.getElm();
Elm.type = 'hidden';
this.$Elm.addClass('field-container-field');
this.$Elm.type = 'text';
this.$Input = new Element('input', {
type: 'text',
'class': 'field-container-field',
value: Elm.value
}).inject(Elm, 'after');
this.getFormatter().then(function (Formatter) {
self.$Elm.placeholder = Formatter.format(1000);
this.getFormatter().then((Formatter) => {
this.$Input.placeholder = Formatter.format(1000);
});
this.$Button = new Element('button', {
'class': 'field-container-item',
html : '<span class="fa fa-calculator"></span>',
title : QUILocale.get('quiqqer/products', 'fields.control.price.brutto'),
styles : {
cursor : 'pointer',
html: '<span class="fa fa-calculator"></span>',
title: QUILocale.get('quiqqer/products', 'fields.control.price.brutto'),
styles: {
cursor: 'pointer',
lineHeight: 30,
textAlign : 'center',
width : 50
textAlign: 'center',
width: 50
},
events : {
events: {
click: this.openBruttoInput
}
}).inject(Elm, 'after');
}).inject(this.$Input, 'after');
this.$BruttoInput = new Element('span', {
'class': 'field-container-item',
html : '<span class="fa fa-spinner fa-spin"></span>',
styles : {
html: '<span class="fa fa-spinner fa-spin"></span>',
styles: {
borderRight: 0,
lineHeight : 30,
maxWidth : 100
lineHeight: 30,
maxWidth: 100
}
}).inject(Elm, 'after');
}).inject(this.$Input, 'after');
if (Elm.getParent('.qui-panel')) {
var Panel = QUI.Controls.getById(Elm.getParent('.qui-panel').get('data-quiid'));
const Panel = QUI.Controls.getById(Elm.getParent('.qui-panel').get('data-quiid'));
if (Panel.getAttribute('productId')) {
this.$productId = Panel.getAttribute('productId');
......@@ -93,19 +98,154 @@ define('package/quiqqer/products/bin/controls/fields/types/Price', [
this.setValue(Elm.value);
Elm.addEvent('change', function () {
self.$calcBruttoPrice();
self.fireEvent('change', [self]);
this.$Input.addEvent('change', () => {
this.$calcBruttoPrice();
this.fireEvent('change', [this]);
});
// load currencies
require(['package/quiqqer/currency/bin/Currency'], (Currencies) => {
Promise.all([
Currencies.getCurrencies(),
this.getFormatter()
]).then((r) => {
const currencies = r[0];
const Formatter = r[1];
if (currencies.length <= 1) {
return;
}
// multiple currencies allowed
const label = this.getElm().getParent('label');
if (!label) {
return;
}
let options = {};
if (
this.$Elm.getAttribute('data-options')
&& typeof this.$Elm.getAttribute('data-options') === 'string'
) {
try {
options = JSON.parse(this.$Elm.getAttribute('data-options'));
} catch (e) {
}
}
if (typeof options.currencies === 'undefined') {
options.currencies = {};
}
this.$currencyList = new Element('div', {
'data-name': 'currency-list',
styles: {
display: 'none',
flexDirection: 'column',
width: '100%'
}
}).inject(label, 'after');
currencies.forEach((currency) => {
const container = new Element('label.field-container', {
html: '' +
'<span class="field-container-item">' + currency.code + '</span>' +
'<input type="text" />' +
'<span class="field-container-item displayNode" style="max-width: 100px; border-right: 0;"></span>' +
'<button class="field-container-item" style="width: 50px; cursor:pointer;">' +
' <span class="fa fa-calculator"></span>' +
'</button>'
}).inject(this.$currencyList);
const displayNode = container.querySelector('.displayNode');
const input = container.querySelector('input');
container.querySelector('button')
.addEventListener('click', this.openBruttoInputForCurrencies);
input.placeholder = Formatter.format(1000);
input.setAttribute('data-currency', currency.code);
input.classList.add('field-container-field');
input.addEventListener('blur', () => {
this.$updateValues();
this.$calcBruttoPriceForCurrency(
input.value,
currency.code,
displayNode
);
});
if (typeof options.currencies[currency.code] !== 'undefined') {
input.value = options.currencies[currency.code];
this.$calcBruttoPriceForCurrency(
input.value,
currency.code,
displayNode
);
}
});
// opener button
const title = label.querySelector('.field-container-item:first-child');
title.style.position = 'relative';
const opener = new Element('span', {
html: '<span class="fa fa-caret-right"></span>',
styles: {
cursor: 'pointer',
position: 'absolute',
right: 0,
top: 0,
textAlign: 'center',
width: 20,
lineHeight: 20,
},
events: {
click: (event) => {
event.stop();
if (this.$currencyList.style.display === 'none') {
this.$currencyList.style.display = 'flex';
opener.innerHTML = '<span class="fa fa-caret-down"></span>';
} else {
this.$currencyList.style.display = 'none';
opener.innerHTML = '<span class="fa fa-caret-right"></span>';
}
}
}
}).inject(title);
});
});
this.$calcBruttoPrice();
},
$updateValues: function () {
const values = {
value: this.$Input.value,
currencies: {}
};
this.$currencyList.querySelectorAll('input').forEach((input) => {
if (input.value !== '') {
values.currencies[input.getAttribute('data-currency')] = input.value;
}
});
this.$Elm.value = JSON.stringify(values);
},
/**
* disable this control
*/
disable: function () {
this.$Elm.disabled = true;
this.$Input.disabled = true;
this.$Button.disabled = true;
this.$Button.setStyle('cursor', 'not-allowed');
},
......@@ -114,7 +254,7 @@ define('package/quiqqer/products/bin/controls/fields/types/Price', [
* enable this control
*/
enable: function () {
this.$Elm.disabled = false;
this.$Input.disabled = false;
this.$Button.disabled = false;
this.$Button.setStyle('cursor', 'pointer');
},
......@@ -136,35 +276,38 @@ define('package/quiqqer/products/bin/controls/fields/types/Price', [
(value === '' || !value || value === 'false')
) {
this.getElm().value = '';
this.$Input.value = '';
this.$calcBruttoPrice();
return;
}
var groupingSeparator = QUILocale.getGroupingSeparator();
var decimalSeparator = QUILocale.getDecimalSeparator();
const groupingSeparator = QUILocale.getGroupingSeparator();
const decimalSeparator = QUILocale.getDecimalSeparator();
var foundGroupSeparator = typeOf(value) === 'string' && value.indexOf(groupingSeparator) >= 0;
var foundDecimalSeparator = typeOf(value) === 'string' && value.indexOf(decimalSeparator) >= 0;
const foundGroupSeparator = typeOf(value) === 'string' && value.indexOf(groupingSeparator) >= 0;
const foundDecimalSeparator = typeOf(value) === 'string' && value.indexOf(decimalSeparator) >= 0;
if ((foundGroupSeparator || foundDecimalSeparator) && !(foundGroupSeparator && !foundDecimalSeparator)) {
this.getElm().value = value;
this.$Input.value = value;
return;
}
this.getFormatter().then(function (Formatter) {
this.getFormatter().then((Formatter) => {
this.getElm().value = Formatter.format(parseFloat(value));
}.bind(this));
this.$Input.value = Formatter.format(parseFloat(value));
});
this.$calcBruttoPrice();
},
/**
* Retuen the field ID
* Returns the field ID
*
* @return {String|Boolean|Number}
*/
getFieldId: function () {
var name = this.getElm().name;
let name = this.getElm().name;
name = name.replace('field-', '');
name = parseInt(name);
......@@ -176,20 +319,19 @@ define('package/quiqqer/products/bin/controls/fields/types/Price', [
* Opens the brutto / gross input
*/
openBruttoInput: function (e) {
var self = this;
e.stop();
e.stopPropagation();
e.preventDefault();
new PriceBruttoWindow({
productId: this.$productId,
events : {
events: {
onOpen: function (Win) {
Win.getContent().set('html', '');
},
onSubmit: function (Win, value) {
self.setValue(value);
self.$calcBruttoPrice();
onSubmit: (Win, value) => {
this.setValue(value);
this.$calcBruttoPrice();
}
}
}).open();
......@@ -207,9 +349,9 @@ define('package/quiqqer/products/bin/controls/fields/types/Price', [
clearTimeout(this.$calcTimer);
}
if (this.$Elm.value === '') {
if (this.$Input.value === '') {
this.$BruttoInput.innerHTML = '---';
this.$BruttoInput.title = QUILocale.get(lg, 'fields.control.price.quantity.title', {
this.$BruttoInput.title = QUILocale.get(lg, 'fields.control.price.quantity.title', {
price: '---'
});
return;
......@@ -217,25 +359,23 @@ define('package/quiqqer/products/bin/controls/fields/types/Price', [
this.$BruttoInput.innerHTML = '<span class="fa fa-spinner fa-spin"></span>';
this.$calcTimer = (function () {
var self = this;
if (!self.$Elm) {
this.$calcTimer = (() => {
if (!this.$Input) {
return;
}
QUIAjax.get('package_quiqqer_products_ajax_products_calcBruttoPrice', function (price) {
self.$BruttoInput.innerHTML = price;
self.$BruttoInput.title = QUILocale.get(lg, 'fields.control.price.quantity.title', {
QUIAjax.get('package_quiqqer_products_ajax_products_calcBruttoPrice', (price) => {
this.$BruttoInput.innerHTML = price;
this.$BruttoInput.title = QUILocale.get(lg, 'fields.control.price.quantity.title', {
price: price
});
}, {
'package': 'quiqqer/products',
price : self.$Elm.value,
price: this.$Input.value,
formatted: 1,
productId: self.$productId
productId: this.$productId
});
}).delay(500, this);
}).delay(500);
},
/**
......@@ -248,14 +388,61 @@ define('package/quiqqer/products/bin/controls/fields/types/Price', [
return Promise.resolve(this.$Formatter);
}
return QUILocale.getSystemLocale().then(function (SystemLocale) {
return QUILocale.getSystemLocale().then((SystemLocale) => {
// admin format
this.$Formatter = SystemLocale.getNumberFormatter({
minimumFractionDigits: 8
});
return this.$Formatter;
}.bind(this));
}
});
},
// region currencies
openBruttoInputForCurrencies: function (e) {
e.stopPropagation();
e.preventDefault();
new PriceBruttoWindow({
productId: this.$productId,
events: {
onSubmit: (Win, value) => {
e.target.getParent('label').querySelector('input').value = value;
}
}
}).open();
},
$calcBruttoPriceForCurrency: function (netPrice, currency, displayNode) {
if (typeof displayNode === 'undefined') {
return;
}
if (netPrice === '') {
displayNode.innerHTML = '---';
displayNode.title = QUILocale.get(lg, 'fields.control.price.quantity.title', {
price: '---'
});
return;
}
displayNode.innerHTML = '<span class="fa fa-spinner fa-spin"></span>';
QUIAjax.get('package_quiqqer_products_ajax_products_calcBruttoPrice', (price) => {
displayNode.innerHTML = price;
displayNode.title = QUILocale.get(lg, 'fields.control.price.quantity.title', {
price: price
});
}, {
'package': 'quiqqer/products',
price: netPrice,
currency: currency,
formatted: 1,
productId: this.$productId
});
},
//endregion
});
});
Dieser Diff ist reduziert.
......@@ -16,7 +16,13 @@
<span class="field-container-item">
{{title}}
</span>
<input type="hidden" name="field-{{id}}" data-qui="{{jsControl}}" value="{{value}}" />
<input
type="hidden"
name="field-{{id}}"
data-qui="{{jsControl}}"
data-options="{{options}}"
value="{{value}}"
/>
</label>
</td>
</tr>
......
......@@ -19,9 +19,9 @@ define('package/quiqqer/products/bin/utils/Products', [
return {
renderDataField: function (field) {
let help = false,
let help = false,
title = QUILocale.get(lg, 'products.field.' + field.id + '.title');
if (QUILocale.exists(lg, 'products.field.' + field.id + '.workingtitle')) {
title = QUILocale.get(lg, 'products.field.' + field.id + '.workingtitle');
}
......@@ -33,12 +33,12 @@ define('package/quiqqer/products/bin/utils/Products', [
}
const FieldElm = new Element('tr', {
'class' : 'field',
html : Mustache.render(templateField, {
'class': 'field',
html: Mustache.render(templateField, {
fieldTitle: title,
fieldHelp : help,
fieldName : 'field-' + field.id,
control : field.jsControl
fieldHelp: help,
fieldName: 'field-' + field.id,
control: field.jsControl
}),
'data-fieldid': field.id
});
......
......@@ -1203,7 +1203,8 @@ public function toProductArray(): array
'isRequired' => $this->isRequired(),
'showInDetails' => $this->showInDetails(),
'type' => $this->getType(),
'search_type' => $this->getSearchType()
'search_type' => $this->getSearchType(),
'options' => $this->getOptions()
];
}
......
......@@ -38,13 +38,6 @@ class Price extends QUI\ERP\Products\Field\Field
*/
protected int | bool $searchDataType = Search::SEARCHDATATYPE_NUMERIC;
/**
* Official currency code (i.e. EUR)
*
* @var string|null
*/
protected ?string $currencyCode = null;
/**
* @return View
*/
......@@ -211,9 +204,9 @@ public function getDefaultSearchType(): string
/**
* Calculates a range with individual steps between a min and a max number
*
* @param float|integer $min
* @param float|integer $max
* @return array - contains values from min to max with calculated steps inbetween
* @param float|int|null $min
* @param float|int|null $max
* @return array - contains values from min to max with calculated steps in between
*/
public function calculateValueRange(null | float | int $min, null | float | int $max): array
{
......
......@@ -244,6 +244,10 @@ public function __construct(int $pid, array $product = [])
$field['value'] = $Field->cleanup($field['value']);
}
if (isset($field['options'])) {
$Field->setOptions($field['options']);
}
try {
$Field->setValue($field['value']);
// } catch (QUI\ERP\Products\Field\ExceptionRequired $Exception) {
......
0% Lade oder .
You are about to add 0 people to the discussion. Proceed with caution.
Bearbeitung dieser Nachricht zuerst beenden!
Bitte registrieren oder zum Kommentieren