diff --git a/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Abstract.php b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Abstract.php
new file mode 100644
index 0000000..08c687c
--- /dev/null
+++ b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Abstract.php
@@ -0,0 +1,153 @@
+
+ * @copyright Copyright (C) 2019
+ * @license MIT License
+ */
+abstract class Divante_VueStorefrontBridge_Helper_Mapper_Abstract extends Mage_Core_Helper_Abstract
+{
+ /**
+ * Name to address custom mappers via config.xml
+ * @var string $_mapperIdentifier
+ */
+ protected $_mapperIdentifier = '';
+
+ /**
+ * Get and process Dto from entity
+ *
+ * @param array $initialDto
+ * @param bool $callMapperExtensions
+ *
+ * @return array
+ */
+ final public function filterDto($initialDto, $callMapperExtensions = true)
+ {
+ $dto = $this->customDtoFiltering($initialDto);
+
+ foreach ($dto as $key => $value) {
+ $isCustom = false;
+
+ if (in_array($key, $this->getBlacklist())) {
+ unset($dto[$key]);
+ continue;
+ }
+
+ if (in_array($key, $this->getAttributesToCastInt())) {
+ $dto[$key] = $dto[$key] !== null ? intval($dto[$key]) : null;
+ $isCustom = true;
+ }
+
+ if (in_array($key, $this->getAttributesToCastBool())) {
+ $dto[$key] = $dto[$key] !== null ? boolval($dto[$key]) : null;
+ $isCustom = true;
+ }
+
+ if (in_array($key, $this->getAttributesToCastStr())) {
+ $dto[$key] = $dto[$key] !== null ? strval($dto[$key]) : null;
+ $isCustom = true;
+ }
+
+ if (in_array($key, $this->getAttributesToCastFloat())) {
+ $dto[$key] = $dto[$key] !== null ? floatval($dto[$key]) : null;
+ $isCustom = true;
+ }
+
+ if (!$isCustom) {
+ // If beginning by "use", "has", ... , then must be a boolean (can be overriden using cast array)
+ if (preg_match('#^(use|has|is|enable|disable)_.*$#', $key)) {
+ $dto[$key] = $dto[$key] !== null ? boolval($value) : null;
+ }
+
+ // If ending by "id", then must be an integer (can be overriden using cast array)
+ if (preg_match('#^.*_?id$#', $key)) {
+ $dto[$key] = $dto[$key] !== null ? intval($value) : null;
+ }
+ }
+ }
+
+ if ($callMapperExtensions) {
+ $this->callMapperExtensions($dto);
+ }
+
+ return $dto;
+ }
+
+ /**
+ * Allow to extend the mappers using config.xml
+ *
+ * @param array $dto
+ * @return array
+ */
+ protected function callMapperExtensions(&$dto)
+ {
+ /** @var Divante_VueStorefrontBridge_Model_Config_Mapper $model */
+ $model = Mage::getModel('vsbridge/config_mapper');
+ return $model->applyExtendedMapping($this->_mapperIdentifier, $dto);
+ }
+
+ /**
+ * Apply custom dto filtering
+ *
+ * @param array $dto
+ *
+ * @return array
+ */
+ protected function customDtoFiltering($dto)
+ {
+ return $dto;
+ }
+
+ /**
+ * Get Dto attribute blacklist
+ *
+ * @return array
+ */
+ protected function getBlacklist()
+ {
+ return [];
+ }
+
+ /**
+ * Get attribute list to cast to integer
+ *
+ * @return array
+ */
+ protected function getAttributesToCastInt()
+ {
+ return [];
+ }
+
+ /**
+ * Get attribute list to cast to boolean
+ *
+ * @return array
+ */
+ protected function getAttributesToCastBool()
+ {
+ return [];
+ }
+
+ /**
+ * Get attribute list to cast to string
+ *
+ * @return array
+ */
+ protected function getAttributesToCastStr()
+ {
+ return [];
+ }
+
+ /**
+ * Get attribute list to cast to float
+ *
+ * @return array
+ */
+ protected function getAttributesToCastFloat()
+ {
+ return [];
+ }
+}
diff --git a/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Address.php b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Address.php
new file mode 100644
index 0000000..2a63454
--- /dev/null
+++ b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Address.php
@@ -0,0 +1,52 @@
+
+ * @copyright Copyright (C) 2019
+ * @license MIT License
+ */
+class Divante_VueStorefrontBridge_Helper_Mapper_Address extends Divante_VueStorefrontBridge_Helper_Mapper_Abstract
+{
+ /**
+ * Name to address custom mappers via config.xml
+ * @var string $_mapperIdentifier
+ */
+ protected $_mapperIdentifier = 'address';
+
+ /**
+ * @inheritdoc
+ */
+ protected function customDtoFiltering($dto)
+ {
+ if (!is_array($dto['street'])) {
+ $dto['street'] = explode("\n", $dto['street']);
+ }
+
+ return $dto;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getAttributesToCastStr()
+ {
+ return [
+ 'country_id'
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getAttributesToCastBool()
+ {
+ return [
+ 'default_billing',
+ 'default_shipping'
+ ];
+ }
+}
diff --git a/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Category.php b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Category.php
new file mode 100644
index 0000000..3177806
--- /dev/null
+++ b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Category.php
@@ -0,0 +1,52 @@
+
+ * @copyright Copyright (C) 2019
+ * @license MIT License
+ */
+class Divante_VueStorefrontBridge_Helper_Mapper_Category extends Divante_VueStorefrontBridge_Helper_Mapper_Abstract
+{
+ /**
+ * Name to address custom mappers via config.xml
+ * @var string $_mapperIdentifier
+ */
+ protected $_mapperIdentifier = 'category';
+
+ /**
+ * @inheritdoc
+ */
+ protected function getBlacklist()
+ {
+ return [
+ 'entity_id',
+ 'path'
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getAttributesToCastInt()
+ {
+ return [
+ 'position',
+ 'level',
+ 'children_count'
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getAttributesToCastBool()
+ {
+ return [
+ 'include_in_menu'
+ ];
+ }
+}
diff --git a/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Customer.php b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Customer.php
new file mode 100644
index 0000000..66c3afc
--- /dev/null
+++ b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Customer.php
@@ -0,0 +1,45 @@
+
+ * @copyright Copyright (C) 2019
+ * @license MIT License
+ */
+class Divante_VueStorefrontBridge_Helper_Mapper_Customer extends Divante_VueStorefrontBridge_Helper_Mapper_Abstract
+{
+ /**
+ * Name to address custom mappers via config.xml
+ * @var string $_mapperIdentifier
+ */
+ protected $_mapperIdentifier = 'customer';
+
+ /**
+ * @inheritdoc
+ */
+ protected function getBlacklist()
+ {
+ return [
+ 'password',
+ 'password_hash',
+ 'password_confirmation',
+ 'password_created_at',
+ 'confirmation',
+ 'entity_type_id'
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getAttributesToCastInt()
+ {
+ return [
+ 'default_billing',
+ 'default_shipping'
+ ];
+ }
+}
diff --git a/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Order.php b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Order.php
new file mode 100644
index 0000000..1c755a2
--- /dev/null
+++ b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Order.php
@@ -0,0 +1,191 @@
+
+ * @copyright Copyright (C) 2019
+ * @license MIT License
+ */
+class Divante_VueStorefrontBridge_Helper_Mapper_Order extends Divante_VueStorefrontBridge_Helper_Mapper_Abstract
+{
+ /**
+ * Name to address custom mappers via config.xml
+ * @var string $_mapperIdentifier
+ */
+ protected $_mapperIdentifier = 'order';
+
+ /**
+ * @inheritdoc
+ */
+ protected function getAttributesToCastInt()
+ {
+ return [
+ 'total_item_count'
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getAttributesToCastBool()
+ {
+ return [
+ 'customer_is_guest',
+ 'email_sent',
+ 'paypal_ipn_customer_notified'
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getAttributesToCastFloat()
+ {
+ return [
+ 'base_discount_amount',
+ 'base_discount_canceled',
+ 'base_discount_invoiced',
+ 'base_discount_refunded',
+ 'base_grand_total',
+ 'base_shipping_amount',
+ 'base_shipping_canceled',
+ 'base_shipping_invoiced',
+ 'base_shipping_refunded',
+ 'base_shipping_tax_amount',
+ 'base_shipping_tax_refunded',
+ 'base_subtotal',
+ 'base_subtotal_canceled',
+ 'base_subtotal_invoiced',
+ 'base_subtotal_refunded',
+ 'base_tax_amount',
+ 'base_tax_canceled',
+ 'base_tax_invoiced',
+ 'base_tax_refunded',
+ 'base_to_global_rate',
+ 'base_to_order_rate',
+ 'base_total_canceled',
+ 'base_total_invoiced',
+ 'base_total_invoiced_cost',
+ 'base_total_offline_refunded',
+ 'base_total_online_refunded',
+ 'base_total_paid',
+ 'base_total_qty_ordered',
+ 'base_total_refunded',
+ 'discount_amount',
+ 'discount_canceled',
+ 'discount_invoiced',
+ 'discount_refunded',
+ 'grand_total',
+ 'shipping_amount',
+ 'shipping_canceled',
+ 'shipping_invoiced',
+ 'shipping_refunded',
+ 'shipping_tax_amount',
+ 'shipping_tax_refunded',
+ 'store_to_base_rate',
+ 'store_to_order_rate',
+ 'subtotal',
+ 'subtotal_canceled',
+ 'subtotal_invoiced',
+ 'subtotal_refunded',
+ 'tax_amount',
+ 'tax_canceled',
+ 'tax_invoiced',
+ 'tax_refunded',
+ 'total_canceled',
+ 'total_invoiced',
+ 'total_offline_refunded',
+ 'total_online_refunded',
+ 'total_paid',
+ 'total_qty_ordered',
+ 'total_refunded',
+ // "can_ship_partially": null,
+ // "can_ship_partially_item": null,
+ 'adjustment_negative',
+ 'adjustment_positive',
+ 'base_adjustment_negative',
+ 'base_adjustment_positive',
+ 'base_shipping_discount_amount',
+ 'base_subtotal_incl_tax',
+ 'base_total_due',
+ 'payment_authorization_amount',
+ 'shipping_discount_amount',
+ 'subtotal_incl_tax',
+ 'total_due',
+ 'weight',
+ 'hidden_tax_amount',
+ 'base_hidden_tax_amount',
+ 'shipping_hidden_tax_amount',
+ 'base_shipping_hidden_tax_amnt',
+ 'base_shipping_hidden_tax_amount',
+ 'hidden_tax_invoiced',
+ 'base_hidden_tax_invoiced',
+ 'hidden_tax_refunded',
+ 'base_hidden_tax_refunded',
+ 'shipping_incl_tax',
+ 'base_shipping_incl_tax',
+ 'base_customer_balance_amount',
+ 'customer_balance_amount',
+ 'base_customer_balance_invoiced',
+ 'customer_balance_invoiced',
+ 'base_customer_balance_refunded',
+ 'customer_balance_refunded',
+ 'bs_customer_bal_total_refunded',
+ 'customer_bal_total_refunded',
+ 'base_gift_cards_amount',
+ 'gift_cards_amount',
+ 'base_gift_cards_invoiced',
+ 'gift_cards_invoiced',
+ 'base_gift_cards_refunded',
+ 'gift_cards_refunded',
+ 'gw_base_price',
+ 'gw_price',
+ 'gw_items_base_price',
+ 'gw_items_price',
+ 'gw_card_base_price',
+ 'gw_card_price',
+ 'gw_base_tax_amount',
+ 'gw_tax_amount',
+ 'gw_items_base_tax_amount',
+ 'gw_items_tax_amount',
+ 'gw_card_base_tax_amount',
+ 'gw_card_tax_amount',
+ 'gw_base_price_invoiced',
+ 'gw_price_invoiced',
+ 'gw_items_base_price_invoiced',
+ 'gw_items_price_invoiced',
+ 'gw_card_base_price_invoiced',
+ 'gw_card_price_invoiced',
+ 'gw_base_tax_amount_invoiced',
+ 'gw_tax_amount_invoiced',
+ 'gw_items_base_tax_invoiced',
+ 'gw_items_tax_invoiced',
+ 'gw_card_base_tax_invoiced',
+ 'gw_card_tax_invoiced',
+ 'gw_base_price_refunded',
+ 'gw_price_refunded',
+ 'gw_items_base_price_refunded',
+ 'gw_items_price_refunded',
+ 'gw_card_base_price_refunded',
+ 'gw_card_price_refunded',
+ 'gw_base_tax_amount_refunded',
+ 'gw_tax_amount_refunded',
+ 'gw_items_base_tax_refunded',
+ 'gw_items_tax_refunded',
+ 'gw_card_base_tax_refunded',
+ 'gw_card_tax_refunded',
+ 'reward_points_balance',
+ 'base_reward_currency_amount',
+ 'reward_currency_amount',
+ 'base_rwrd_crrncy_amt_invoiced',
+ 'rwrd_currency_amount_invoiced',
+ 'base_rwrd_crrncy_amnt_refnded',
+ 'rwrd_crrncy_amnt_refunded',
+ 'reward_points_balance_refund',
+ 'reward_points_balance_refunded'
+ ];
+ }
+}
diff --git a/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/OrderItem.php b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/OrderItem.php
new file mode 100644
index 0000000..e0b9739
--- /dev/null
+++ b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/OrderItem.php
@@ -0,0 +1,92 @@
+
+ * @copyright Copyright (C) 2019
+ * @license MIT License
+ */
+class Divante_VueStorefrontBridge_Helper_Mapper_OrderItem extends Divante_VueStorefrontBridge_Helper_Mapper_Abstract
+{
+ /**
+ * Name to address custom mappers via config.xml
+ * @var string $_mapperIdentifier
+ */
+ protected $_mapperIdentifier = 'order_item';
+
+ /**
+ * @inheritdoc
+ */
+ protected function getAttributesToCastFloat()
+ {
+ return [
+ 'weight',
+ 'base_cost',
+ 'price',
+ 'base_price',
+ 'original_price',
+ 'base_original_price',
+ 'tax_percent',
+ 'tax_amount',
+ 'base_tax_amount',
+ 'tax_invoiced',
+ 'base_tax_invoiced',
+ 'discount_percent',
+ 'discount_amount',
+ 'base_discount_amount',
+ 'discount_invoiced',
+ 'base_discount_invoiced',
+ 'amount_refunded',
+ 'base_amount_refunded',
+ 'row_total',
+ 'base_row_total',
+ 'row_invoiced',
+ 'base_row_invoiced',
+ 'row_weight',
+ 'base_tax_before_discount',
+ 'tax_before_discount',
+ 'price_incl_tax',
+ 'base_price_incl_tax',
+ 'row_total_incl_tax',
+ 'base_row_total_incl_tax',
+ 'hidden_tax_amount',
+ 'base_hidden_tax_amount',
+ 'hidden_tax_invoiced',
+ 'base_hidden_tax_invoiced',
+ 'hidden_tax_refunded',
+ 'base_hidden_tax_refunded',
+ 'tax_canceled',
+ 'hidden_tax_canceled',
+ 'tax_refunded',
+ 'base_tax_refunded',
+ 'discount_refunded',
+ 'base_discount_refunded',
+ 'base_weee_tax_applied_amount',
+ 'base_weee_tax_applied_row_amnt',
+ 'base_weee_tax_applied_row_amount',
+ 'weee_tax_applied_amount',
+ 'weee_tax_applied_row_amount',
+ 'weee_tax_applied',
+ 'weee_tax_disposition',
+ 'weee_tax_row_disposition',
+ 'base_weee_tax_disposition',
+ 'base_weee_tax_row_disposition',
+ 'gw_base_price',
+ 'gw_price',
+ 'gw_base_tax_amount',
+ 'gw_tax_amount',
+ 'gw_base_price_invoiced',
+ 'gw_price_invoiced',
+ 'gw_base_tax_amount_invoiced',
+ 'gw_tax_amount_invoiced',
+ 'gw_base_price_refunded',
+ 'gw_price_refunded',
+ 'gw_base_tax_amount_refunded',
+ 'gw_tax_amount_refunded',
+ 'qty_returned'
+ ];
+ }
+}
diff --git a/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Payment.php b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Payment.php
new file mode 100644
index 0000000..956381a
--- /dev/null
+++ b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Payment.php
@@ -0,0 +1,19 @@
+
+ * @copyright Copyright (C) 2019
+ * @license MIT License
+ */
+class Divante_VueStorefrontBridge_Helper_Mapper_Payment extends Divante_VueStorefrontBridge_Helper_Mapper_Abstract
+{
+ /**
+ * Name to address custom mappers via config.xml
+ * @var string $_mapperIdentifier
+ */
+ protected $_mapperIdentifier = 'payment';
+}
diff --git a/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Product.php b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Product.php
new file mode 100644
index 0000000..5b36d34
--- /dev/null
+++ b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Product.php
@@ -0,0 +1,19 @@
+
+ * @copyright Copyright (C) 2019
+ * @license MIT License
+ */
+class Divante_VueStorefrontBridge_Helper_Mapper_Product extends Divante_VueStorefrontBridge_Helper_Mapper_Abstract
+{
+ /**
+ * Name to address custom mappers via config.xml
+ * @var string $_mapperIdentifier
+ */
+ protected $_mapperIdentifier = 'product';
+}
diff --git a/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/ProductAttribute.php b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/ProductAttribute.php
new file mode 100644
index 0000000..e7b11ae
--- /dev/null
+++ b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/ProductAttribute.php
@@ -0,0 +1,41 @@
+
+ * @copyright Copyright (C) 2019
+ * @license MIT License
+ */
+class Divante_VueStorefrontBridge_Helper_Mapper_ProductAttribute extends Divante_VueStorefrontBridge_Helper_Mapper_Abstract
+{
+ /**
+ * Name to address custom mappers via config.xml
+ * @var string $_mapperIdentifier
+ */
+ protected $_mapperIdentifier = 'product_attribute';
+
+ /**
+ * @inheritdoc
+ */
+ protected function getAttributesToCastInt()
+ {
+ return [
+ 'default_value',
+ 'position'
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getAttributesToCastBool()
+ {
+ return [
+ 'used_in_product_listing',
+ 'used_for_sort_by'
+ ];
+ }
+}
diff --git a/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/ProductChild.php b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/ProductChild.php
new file mode 100644
index 0000000..5b71949
--- /dev/null
+++ b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/ProductChild.php
@@ -0,0 +1,46 @@
+
+ * @copyright Copyright (C) 2019
+ * @license MIT License
+ */
+class Divante_VueStorefrontBridge_Helper_Mapper_ProductChild extends Divante_VueStorefrontBridge_Helper_Mapper_Abstract
+{
+ /**
+ * Name to address custom mappers via config.xml
+ * @var string $_mapperIdentifier
+ */
+ protected $_mapperIdentifier = 'product_child';
+
+ /**
+ * @inheritdoc
+ */
+ protected function getBlacklist()
+ {
+ return [
+ 'entity_id',
+ 'id',
+ 'type_id',
+ 'updated_at',
+ 'created_at',
+ 'stock_item',
+ 'short_description',
+ 'page_layout',
+ 'news_from_date',
+ 'news_to_date',
+ 'meta_description',
+ 'meta_keyword',
+ 'meta_title',
+ 'description',
+ 'attribute_set_id',
+ 'entity_type_id',
+ 'has_options',
+ 'required_options'
+ ];
+ }
+}
diff --git a/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/QuoteItem.php b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/QuoteItem.php
new file mode 100644
index 0000000..8b6108d
--- /dev/null
+++ b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/QuoteItem.php
@@ -0,0 +1,29 @@
+
+ * @copyright Copyright (C) 2019
+ * @license MIT License
+ */
+class Divante_VueStorefrontBridge_Helper_Mapper_QuoteItem extends Divante_VueStorefrontBridge_Helper_Mapper_Abstract
+{
+ /**
+ * Name to address custom mappers via config.xml
+ * @var string $_mapperIdentifier
+ */
+ protected $_mapperIdentifier = 'quote_item';
+
+ /**
+ * @inheritdoc
+ */
+ protected function getAttributesToCastStr()
+ {
+ return [
+ 'quote_id'
+ ];
+ }
+}
diff --git a/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Stock.php b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Stock.php
new file mode 100644
index 0000000..6137da5
--- /dev/null
+++ b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Stock.php
@@ -0,0 +1,30 @@
+
+ * @copyright Copyright (C) 2019
+ * @license MIT License
+ */
+class Divante_VueStorefrontBridge_Helper_Mapper_Stock extends Divante_VueStorefrontBridge_Helper_Mapper_Abstract
+{
+ /**
+ * Name to address custom mappers via config.xml
+ * @var string $_mapperIdentifier
+ */
+ protected $_mapperIdentifier = 'stock';
+
+ /**
+ * @inheritdoc
+ */
+ protected function getAttributesToCastBool()
+ {
+ return [
+ 'stock_status_changed_auto',
+ 'stock_status_changed_automatically'
+ ];
+ }
+}
diff --git a/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Taxrule.php b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Taxrule.php
new file mode 100644
index 0000000..13f4ca2
--- /dev/null
+++ b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/Taxrule.php
@@ -0,0 +1,43 @@
+
+ * @copyright Copyright (C) 2019
+ * @license MIT License
+ */
+class Divante_VueStorefrontBridge_Helper_Mapper_Taxrule extends Divante_VueStorefrontBridge_Helper_Mapper_Abstract
+{
+ /**
+ * Name to address custom mappers via config.xml
+ * @var string $_mapperIdentifier
+ */
+ protected $_mapperIdentifier = 'taxrule';
+
+ /**
+ * @inheritdoc
+ */
+ protected function getBlacklist()
+ {
+ return [
+ 'tax_calculation_rule_id',
+ 'tax_rates',
+ 'product_tax_classes',
+ 'customer_tax_classes'
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getAttributesToCastInt()
+ {
+ return [
+ 'position',
+ 'priority'
+ ];
+ }
+}
diff --git a/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/TaxruleRate.php b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/TaxruleRate.php
new file mode 100644
index 0000000..ace4eb0
--- /dev/null
+++ b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/TaxruleRate.php
@@ -0,0 +1,29 @@
+
+ * @copyright Copyright (C) 2019
+ * @license MIT License
+ */
+class Divante_VueStorefrontBridge_Helper_Mapper_TaxruleRate extends Divante_VueStorefrontBridge_Helper_Mapper_Abstract
+{
+ /**
+ * Name to address custom mappers via config.xml
+ * @var string $_mapperIdentifier
+ */
+ protected $_mapperIdentifier = 'taxrule_rate';
+
+ /**
+ * @inheritdoc
+ */
+ protected function getBlacklist()
+ {
+ return [
+ 'tax_calculation_rate_id'
+ ];
+ }
+}
diff --git a/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/WhishListItem.php b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/WhishListItem.php
new file mode 100644
index 0000000..7720ab6
--- /dev/null
+++ b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Helper/Mapper/WhishListItem.php
@@ -0,0 +1,19 @@
+
+ * @copyright Copyright (C) 2019
+ * @license MIT License
+ */
+class Divante_VueStorefrontBridge_Helper_Mapper_WhishListItem extends Divante_VueStorefrontBridge_Helper_Mapper_Abstract
+{
+ /**
+ * Name to address custom mappers via config.xml
+ * @var string $_mapperIdentifier
+ */
+ protected $_mapperIdentifier = 'whishlist_item';
+}
diff --git a/magento1-module/app/code/local/Divante/VueStorefrontBridge/Model/Config/Mapper.php b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Model/Config/Mapper.php
new file mode 100644
index 0000000..c220df6
--- /dev/null
+++ b/magento1-module/app/code/local/Divante/VueStorefrontBridge/Model/Config/Mapper.php
@@ -0,0 +1,53 @@
+getNode(self::MAPPER_CONFIG_ROOT_NODE)->asArray();
+ foreach ($mappingConfigTypes as $mappingIdentifier => $mapper) {
+ $this->config[$mappingIdentifier] = $mapper['mapper'];
+ }
+
+ return $this->config;
+ }
+
+ /**
+ * @param string $type
+ * @param array $initialDto
+ * @return array
+ */
+ public function applyExtendedMapping($type, &$initialDto)
+ {
+ if ($this->config[$type] && !empty($this->config[$type])) {
+ foreach ($this->config[$type] as $name => $className) {
+ $model = Mage::getModel($className);
+ if (!$model || !$model instanceof Divante_VueStorefrontBridge_Helper_Mapper_Abstract) {
+ $error = 'Class "%s" is not an instance on "Divante_VueStorefrontBridge_Helper_Mapper_Abstract".';
+ Mage::log(sprintf($error, $className), 'system.log', true);
+ continue;
+ }
+
+ $initialDto = $model->filterDto($initialDto, false);
+ }
+ }
+
+ return $initialDto;
+ }
+}
diff --git a/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/AbstractController.php b/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/AbstractController.php
index b7cca5b..4c620dd 100644
--- a/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/AbstractController.php
+++ b/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/AbstractController.php
@@ -224,27 +224,7 @@ protected function _processParams(Mage_Core_Controller_Request_Http $request)
return $paramsDTO;
}
- /**
- * Filters parameters map removing blacklisted
- *
- * @param array $dtoToFilter
- * @param array|null $blackList
- *
- * @return mixed
- */
- protected function _filterDTO(array $dtoToFilter, array $blackList = null)
- {
- foreach ($dtoToFilter as $key => $val) {
- if ($blackList && in_array($key, $blackList)) {
- unset ($dtoToFilter[$key]);
- } else {
- if (strstr($key, 'is_') || strstr($key, 'has_')) {
- $dtoToFilter[$key] = boolval($val);
- }
- }
- }
- return $dtoToFilter;
- }
+
/**
* Sends back code and result of performed operation
*
@@ -258,8 +238,7 @@ protected function _result($code, $result)
[
'code' => $code,
'result' => $result,
- ],
- JSON_NUMERIC_CHECK
+ ]
)
)
->setHttpResponseCode($code)
diff --git a/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/AttributesController.php b/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/AttributesController.php
index c8dc043..2fbacea 100644
--- a/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/AttributesController.php
+++ b/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/AttributesController.php
@@ -50,7 +50,8 @@ public function indexAction()
/** @var Mage_Catalog_Model_Resource_Eav_Attribute $productAttr */
$attribute = Mage::getSingleton('eav/config')
->getAttribute(Mage_Catalog_Model_Product::ENTITY, $productAttr->getAttributeCode());
- $options = [];
+
+ $options = [];
if ($attribute->usesSource()) {
$options = $attribute->getSource()->getAllOptions(false);
}
@@ -61,11 +62,11 @@ public function indexAction()
continue;
} // exception - this attribute has string typed values; this is not acceptable by VS
- $productAttrDTO['id'] = intval($productAttr->getAttributeId());
+ $productAttrDTO['id'] = $productAttr->getAttributeId();
$productAttrDTO['options'] = $options;
$productAttrDTO['default_value'] = (int)$productAttrDTO['default_value'];
- $productAttrDTO = $this->_filterDTO($productAttrDTO);
- $attrList[] = $productAttrDTO;
+
+ $attrList[] = Mage::helper('vsbridge_mapper/productAttribute')->filterDto($productAttrDTO);
}
$this->_result(200, $attrList);
}
diff --git a/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/CartController.php b/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/CartController.php
index 0766d4b..13283b8 100644
--- a/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/CartController.php
+++ b/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/CartController.php
@@ -112,7 +112,10 @@ public function pullAction()
$items = [];
foreach ($quoteObj->getAllVisibleItems() as $item) {
- $items[] = $this->cartModel->getItemAsArray($item);
+ $quoteItemDTO = $this->cartModel->getItemAsArray($item);
+ $quoteItemDTO = Mage::helper('vsbridge_mapper/quoteItem')->filterDto($quoteItemDTO);
+
+ $items[] = $quoteItemDTO;
}
return $this->_result(200, $items);
diff --git a/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/CategoriesController.php b/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/CategoriesController.php
index 20f04b6..268d6b9 100644
--- a/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/CategoriesController.php
+++ b/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/CategoriesController.php
@@ -36,23 +36,6 @@ public function indexAction()
}
}
- /**
- * Prepares category entity data
- *
- * @param Mage_Catalog_Model_Category $category
- *
- * @return mixed
- */
- protected function _prepareDTO(Mage_Catalog_Model_Category $category)
- {
- $categoryDTO = $category->getData();
- $categoryDTO['id'] = intval($categoryDTO['entity_id']);
- unset($categoryDTO['entity_id']);
- unset($categoryDTO['path']);
-
- return $categoryDTO;
- }
-
/**
* Processes category data
*
@@ -63,16 +46,16 @@ protected function _prepareDTO(Mage_Catalog_Model_Category $category)
*/
protected function _processCategory(Mage_Catalog_Model_Category $category, $level = 0)
{
- $childCats = $category->getChildrenCategories();
- $catDTO = $this->_prepareDTO($category);
+ $catDTO = $category->getData();
+ $catDTO['id'] = $catDTO['entity_id'];
$catDTO['children_data'] = [];
- foreach ($childCats as $childCategory) {
+
+ foreach ($category->getChildrenCategories() as $childCategory) {
$catDTO['children_data'][] = $this->_processCategory($childCategory, $level + 1);
}
+
// $catDTO['level'] = $level;
$catDTO['children_count'] = count($catDTO['children_data']);
- $catDTO = $this->_filterDTO($catDTO);
-
- return $catDTO;
+ return Mage::helper('vsbridge_mapper/category')->filterDto($catDTO);
}
}
diff --git a/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/ProductsController.php b/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/ProductsController.php
index 1c8e005..b1c86d0 100644
--- a/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/ProductsController.php
+++ b/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/ProductsController.php
@@ -17,26 +17,6 @@ public function indexAction()
{
if ($this->_authorizeAdminUser($this->getRequest())) {
$params = $this->_processParams($this->getRequest());
- $confChildBlacklist = [
- 'entity_id',
- 'id',
- 'type_id',
- 'updated_at',
- 'created_at',
- 'stock_item',
- 'short_description',
- 'page_layout',
- 'news_from_date',
- 'news_to_date',
- 'meta_description',
- 'meta_keyword',
- 'meta_title',
- 'description',
- 'attribute_set_id',
- 'entity_type_id',
- 'has_options',
- 'required_options',
- ];
$result = [];
$productCollection = Mage::getModel('catalog/product')
@@ -91,7 +71,7 @@ public function indexAction()
$productDTO[$productAttribute['attribute_code'] . '_options'] = $availableOptions;
}
- $childDTO = $this->_filterDTO($childDTO, $confChildBlacklist);
+ $childDTO = Mage::helper('vsbridge_mapper/productChild')->filterDto($childDTO);
$productDTO['configurable_children'][] = $childDTO;
}
}
@@ -101,14 +81,17 @@ public function indexAction()
$productDTO['category_ids'] = [];
foreach ($cats as $category_id) {
$cat = Mage::getModel('catalog/category')->load($category_id);
- $productDTO['category'][] = [
+ $categoryDTO = [
'category_id' => $cat->getId(),
'name' => $cat->getName()
];
+ $categoryDTO = Mage::helper('vsbridge_mapper/category')->filterDto($categoryDTO);
+
+ $productDTO['category'][] = $categoryDTO;
$productDTO['category_ids'][] = $category_id;
}
- $productDTO = $this->_filterDTO($productDTO);
+ $productDTO = Mage::helper('vsbridge_mapper/product')->filterDto($productDTO);
$result[] = $productDTO;
}
diff --git a/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/StockController.php b/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/StockController.php
index a333005..1242c2d 100644
--- a/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/StockController.php
+++ b/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/StockController.php
@@ -34,9 +34,10 @@ public function checkAction()
$product_id = Mage::getModel('catalog/product')->getIdBySku($sku);
$product = Mage::getModel('catalog/product')->load($product_id);
$stock = $product->getStockItem();
+
$stockDTO = $stock->getData();
- $stockDTO['is_in_stock'] = boolval($stockDTO['is_in_stock']);
$stockDTO['notify_stock_qty'] = $stock->getNotifyStockQty();
+ $stockDTO = Mage::helper('vsbridge_mapper/stock')->filterDto($stockDTO);
return $this->_result(200, $stockDTO);
} catch (Exception $err) {
diff --git a/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/TaxrulesController.php b/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/TaxrulesController.php
index 0195863..77755a1 100644
--- a/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/TaxrulesController.php
+++ b/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/TaxrulesController.php
@@ -32,23 +32,24 @@ public function indexAction()
$taxRules = array();
if ($collection->getSize()) {
foreach ($collection as $rule) {
- $taxRuleDTO = $rule->getData();
- $taxRuleDTO['id'] = intval($taxRuleDTO['tax_calculation_rule_id']);
- unset($taxRuleDTO['tax_calculation_rule_id']);
+ $taxRuleDTO = $rule->getData();
+
+ $taxRuleDTO['id'] = $taxRuleDTO['tax_calculation_rule_id'];
$taxRuleDTO['tax_rates_ids'] = $taxRuleDTO['tax_rates'];
- unset($taxRuleDTO['tax_rates']);
$taxRuleDTO['product_tax_class_ids'] = $taxRuleDTO['product_tax_classes'];
- unset($taxRuleDTO['product_tax_classes']);
$taxRuleDTO['customer_tax_class_ids'] = $taxRuleDTO['customer_tax_classes'];
- unset($taxRuleDTO['customer_tax_classes']);
$taxRuleDTO['rates'] = [];
+
foreach ($taxRuleDTO['tax_rates_ids'] as $rateId) {
$rate->load($rateId);
$rateDTO = $rate->getData();
- $rateDTO['id'] = intval($rateDTO['tax_calculation_rate_id']);
- unset($rateDTO['tax_calculation_rate_id']);
+ $rateDTO['id'] = $rateDTO['tax_calculation_rate_id'];
+ $rateDTO = Mage::helper('vsbridge_mapper/taxruleRate')->filterDto($rateDTO);
+
$taxRuleDTO['rates'][] = $rateDTO;
}
+
+ $taxRuleDTO = Mage::helper('vsbridge_mapper/taxrule')->filterDto($taxRuleDTO);
$taxRules[] = $taxRuleDTO;
}
}
diff --git a/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/UserController.php b/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/UserController.php
index a271a6d..f7e817c 100644
--- a/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/UserController.php
+++ b/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/UserController.php
@@ -303,9 +303,10 @@ public function orderHistoryAction()
foreach ($orderCollection as $order) {
$orderDTO = $order->getData();
$orderDTO['id'] = $orderDTO['entity_id'];
- $orderDTO['items'] = [];
- foreach($order->getAllVisibleItems() as $item) {
+ // Order items
+ $orderDTO['items'] = [];
+ foreach ($order->getAllVisibleItems() as $item) {
$itemDTO = $item->getData();
$itemDTO['id'] = $itemDTO['item_id'];
$itemDTO['thumbnail'] = null;
@@ -320,26 +321,30 @@ public function orderHistoryAction()
$itemDTO['thumbnail'] = $image;
}
+ $itemDTO = Mage::helper('vsbridge_mapper/orderItem')->filterDto($itemDTO);
$orderDTO['items'][] = $itemDTO;
}
+ // Order tax
$orderDTO['discount_tax_compensation_amount'] = $orderDTO['hidden_tax_amount'];
- $payment = $order->getPayment();
- $orderDTO['payment'] = $payment->toArray();
- $orderDTO['payment']['additional_information'][0] = $payment->getMethodInstance()->getTitle();
+ // Order payment
+ $paymentDTO = $order->getPayment()->toArray();
+ $paymentDTO['additional_information'][0] = $order->getPayment()->getMethodInstance()->getTitle();
+ $paymentDTO = Mage::helper('vsbridge_mapper/payment')->filterDto($paymentDTO);
+ $orderDTO['payment'] = $paymentDTO;
- //TODO explode street by linebreak with mapper when mapper merged
- $shippingAddress = $order->getShippingAddress()->getData();
- $shippingAddress['street'] = explode("\n", $shippingAddress['street']);
- $orderDTO['extension_attributes']['shipping_assignments'][0]['shipping']['address'] = $shippingAddress;
+ // Order addresses
+ $shippingAddressDTO = $order->getShippingAddress()->getData();
+ $shippingAddressDTO = Mage::helper('vsbridge_mapper/address')->filterDto($shippingAddressDTO);
+ $orderDTO['extension_attributes']['shipping_assignments'][0]['shipping']['address'] = $shippingAddressDTO;
- //TODO explode street by linebreak with mapper when mapper merged
- $billingAddress = $order->getBillingAddress()->getData();
- $billingAddress['street'] = explode("\n", $billingAddress['street']);
- $orderDTO['billing_address'] = $billingAddress; //TODO explode street by linebreak when mapper merged
+ $billingAddressDTO = $order->getBillingAddress()->getData();
+ $billingAddressDTO = Mage::helper('vsbridge_mapper/address')->filterDto($billingAddressDTO);
+ $orderDTO['billing_address'] = $billingAddressDTO;
- $ordersDTO[] = $orderDTO;
+ // Order
+ $ordersDTO[] = Mage::helper('vsbridge_mapper/order')->filterDto($orderDTO);
}
return $this->_result(200, array('items' => $ordersDTO));
@@ -372,27 +377,30 @@ public function createAction()
$store = Mage::app()->getStore();
$customer = Mage::getModel("customer/customer");
- $customer ->setWebsiteId($websiteId)
+ $customer->setWebsiteId($websiteId)
->setStore($store)
->setFirstname($request->customer->firstname)
->setLastname($request->customer->lastname)
->setEmail($request->customer->email)
->setPassword($request->password);
- try{
+ try {
$customer->save();
- $filteredCustomerData = $this->_filterDTO($customer->getData(), array('password', 'password_hash', 'password_confirmation', 'confirmation', 'entity_type_id'));
- return $this->_result(200, $filteredCustomerData); // TODO: add support for 'Refresh-token'
+ $customerDTO = $customer->getData();
+ $customerDTO = Mage::helper('vsbridge_mapper/customer')->filterDto($customerDTO);
+
+ return $this->_result(200, $customerDTO); // TODO: add support for 'Refresh-token'
} catch (Exception $e) {
return $this->_result(500, $e->getMessage());
}
}
- public function meAction(){
+ public function meAction()
+ {
$customer = $this->_currentCustomer($this->getRequest());
- if(!$customer) {
+ if (!$customer) {
return $this->_result(500, 'User is not authroized to access self');
} else {
$updatedShippingId = 0;
@@ -401,11 +409,10 @@ public function meAction(){
try {
if ($this->_checkHttpMethod(array('POST'))) { // modify user data
$request = _object_to_array($this->_getJsonBody());
- if(!$request['customer']) {
+ if (!$request['customer']) {
return $this->_result(500, 'No customer data provided!');
}
- //die(print_r($customer->getData(), true));
$updatedCustomer = $request['customer'];
$updatedCustomer['entity_id'] = $customer->getId();
@@ -420,15 +427,16 @@ public function meAction(){
$customer->save();
if ($updatedCustomer['addresses']) {
- foreach($updatedCustomer['addresses'] as $updatedAdress) {
+ foreach ($updatedCustomer['addresses'] as $updatedAdress) {
$updatedAdress['region'] = $updatedAdress['region']['region'];
- if($updatedAdress['default_billing']) {
+ if ($updatedAdress['default_billing']) {
$bAddressId = $customer->getDefaultBilling();
$bAddress = Mage::getModel('customer/address');
- if($bAddressId)
+ if ($bAddressId) {
$bAddress->load($bAddressId);
+ }
$updatedAdress['parent_id'] = $customer->getId();
@@ -436,12 +444,13 @@ public function meAction(){
$bAddress->setData($updatedAdress)->setIsDefaultBilling(1)->save();
$updatedBillingId = $bAddress->getId();
}
- if($updatedAdress['default_shipping']) {
+ if ($updatedAdress['default_shipping']) {
$sAddressId = $customer->getDefaultShipping();
$sAddress = Mage::getModel('customer/address');
- if($sAddressId)
+ if ($sAddressId) {
$sAddress->load($sAddressId);
+ }
$updatedAdress['parent_id'] = $customer->getId();
$sAddress->setData($updatedAdress)->setIsDefaultShipping(1)->save();
@@ -495,20 +504,24 @@ public function meAction(){
// its customer default billing address
$addressDTO['default_billing'] = true;
+ $addressDTO = Mage::helper('vsbridge_mapper/address')->filterDto($addressDTO);
+
$customerDTO['default_billing'] = $address->getId();
$customerDTO['addresses'][] = $addressDTO;
- } else if($defaultShipping == $address->getId()|| $address->getId() == $updatedShippingId) {
+ } elseif ($defaultShipping == $address->getId()|| $address->getId() == $updatedShippingId) {
// its customer default shipping address
$addressDTO['default_shipping'] = true;
+ $addressDTO = Mage::helper('vsbridge_mapper/address')->filterDto($addressDTO);
+
$customerDTO['default_shipping'] = $address->getId();
$customerDTO['addresses'][] = $addressDTO;
}
}
$customerDTO['id'] = $customerDTO['entity_id'];
+ $customerDTO = Mage::helper('vsbridge_mapper/customer')->filterDto($customerDTO);
- $filteredCustomerData = $this->_filterDTO($customerDTO, array('password', 'password_hash', 'password_confirmation', 'confirmation', 'entity_type_id'));
- return $this->_result(200, $filteredCustomerData);
+ return $this->_result(200, $customerDTO);
} catch (Exception $err) {
return $this->_result(500, $err->getMessage());
}
diff --git a/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/WishlistController.php b/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/WishlistController.php
index 1defd6b..ddb30a2 100644
--- a/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/WishlistController.php
+++ b/magento1-module/app/code/local/Divante/VueStorefrontBridge/controllers/WishlistController.php
@@ -226,7 +226,7 @@ private function prepareWishListItem(Mage_Wishlist_Model_Item $item)
'wishlist_item_id' => $item->getId(),
];
- return $wishListDTO;
+ return Mage::helper('vsbridge_mapper/whishListItem')->filterDto($wishListDTO);
}
/**
diff --git a/magento1-module/app/code/local/Divante/VueStorefrontBridge/etc/config.xml b/magento1-module/app/code/local/Divante/VueStorefrontBridge/etc/config.xml
index 5fbb7bb..5b3b459 100644
--- a/magento1-module/app/code/local/Divante/VueStorefrontBridge/etc/config.xml
+++ b/magento1-module/app/code/local/Divante/VueStorefrontBridge/etc/config.xml
@@ -26,7 +26,22 @@
Divante_VueStorefrontBridge_Helper
+
+ Divante_VueStorefrontBridge_Helper_Mapper
+
+
+
+
+
+
+
+