refactor customer and order ref
This commit is contained in:
@@ -129,7 +129,7 @@ class Customer extends BaseCustomer implements UserInterface
|
|||||||
|
|
||||||
protected function generateRef()
|
protected function generateRef()
|
||||||
{
|
{
|
||||||
return uniqid(substr($this->getLastname(), 0, (strlen($this->getLastname()) >= 3) ? 3 : strlen($this->getLastname())), true);
|
return sprintf('CUS%s', str_pad($this->getId(), 12, 0, STR_PAD_LEFT));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -164,7 +164,7 @@ class Customer extends BaseCustomer implements UserInterface
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
public function setRef($ref)
|
public function setRef($ref)
|
||||||
{
|
{
|
||||||
if (null === $ref && null === $this->ref) {
|
if (null === $ref && null === $this->ref) {
|
||||||
@@ -174,7 +174,7 @@ class Customer extends BaseCustomer implements UserInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
public function setEmail($email, $force = false)
|
public function setEmail($email, $force = false)
|
||||||
{
|
{
|
||||||
@@ -273,10 +273,6 @@ class Customer extends BaseCustomer implements UserInterface
|
|||||||
// Set the serial number (for auto-login)
|
// Set the serial number (for auto-login)
|
||||||
$this->setRememberMeSerial(uniqid());
|
$this->setRememberMeSerial(uniqid());
|
||||||
|
|
||||||
if (null === $this->ref) {
|
|
||||||
$this->setRef($this->generateRef());
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->dispatchEvent(TheliaEvents::BEFORE_CREATECUSTOMER, new CustomerEvent($this));
|
$this->dispatchEvent(TheliaEvents::BEFORE_CREATECUSTOMER, new CustomerEvent($this));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -287,6 +283,11 @@ class Customer extends BaseCustomer implements UserInterface
|
|||||||
*/
|
*/
|
||||||
public function postInsert(ConnectionInterface $con = null)
|
public function postInsert(ConnectionInterface $con = null)
|
||||||
{
|
{
|
||||||
|
if (null === $this->getRef()) {
|
||||||
|
$this->setRef($this->generateRef())
|
||||||
|
->save($con);
|
||||||
|
}
|
||||||
|
|
||||||
$this->dispatchEvent(TheliaEvents::AFTER_CREATECUSTOMER, new CustomerEvent($this));
|
$this->dispatchEvent(TheliaEvents::AFTER_CREATECUSTOMER, new CustomerEvent($this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ class CustomerTableMap extends TableMap
|
|||||||
$this->setUseIdGenerator(true);
|
$this->setUseIdGenerator(true);
|
||||||
// columns
|
// columns
|
||||||
$this->addPrimaryKey('ID', 'Id', 'INTEGER', true, null, null);
|
$this->addPrimaryKey('ID', 'Id', 'INTEGER', true, null, null);
|
||||||
$this->addColumn('REF', 'Ref', 'VARCHAR', true, 50, null);
|
$this->addColumn('REF', 'Ref', 'VARCHAR', false, 50, null);
|
||||||
$this->addForeignKey('TITLE_ID', 'TitleId', 'INTEGER', 'customer_title', 'ID', true, null, null);
|
$this->addForeignKey('TITLE_ID', 'TitleId', 'INTEGER', 'customer_title', 'ID', true, null, null);
|
||||||
$this->addColumn('FIRSTNAME', 'Firstname', 'VARCHAR', true, 255, null);
|
$this->addColumn('FIRSTNAME', 'Firstname', 'VARCHAR', true, 255, null);
|
||||||
$this->addColumn('LASTNAME', 'Lastname', 'VARCHAR', true, 255, null);
|
$this->addColumn('LASTNAME', 'Lastname', 'VARCHAR', true, 255, null);
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ class OrderTableMap extends TableMap
|
|||||||
$this->setUseIdGenerator(true);
|
$this->setUseIdGenerator(true);
|
||||||
// columns
|
// columns
|
||||||
$this->addPrimaryKey('ID', 'Id', 'INTEGER', true, null, null);
|
$this->addPrimaryKey('ID', 'Id', 'INTEGER', true, null, null);
|
||||||
$this->addColumn('REF', 'Ref', 'VARCHAR', true, 45, null);
|
$this->addColumn('REF', 'Ref', 'VARCHAR', false, 45, null);
|
||||||
$this->addForeignKey('CUSTOMER_ID', 'CustomerId', 'INTEGER', 'customer', 'ID', true, null, null);
|
$this->addForeignKey('CUSTOMER_ID', 'CustomerId', 'INTEGER', 'customer', 'ID', true, null, null);
|
||||||
$this->addForeignKey('INVOICE_ORDER_ADDRESS_ID', 'InvoiceOrderAddressId', 'INTEGER', 'order_address', 'ID', true, null, null);
|
$this->addForeignKey('INVOICE_ORDER_ADDRESS_ID', 'InvoiceOrderAddressId', 'INTEGER', 'order_address', 'ID', true, null, null);
|
||||||
$this->addForeignKey('DELIVERY_ORDER_ADDRESS_ID', 'DeliveryOrderAddressId', 'INTEGER', 'order_address', 'ID', true, null, null);
|
$this->addForeignKey('DELIVERY_ORDER_ADDRESS_ID', 'DeliveryOrderAddressId', 'INTEGER', 'order_address', 'ID', true, null, null);
|
||||||
|
|||||||
@@ -23,8 +23,6 @@ class Order extends BaseOrder
|
|||||||
*/
|
*/
|
||||||
public function preInsert(ConnectionInterface $con = null)
|
public function preInsert(ConnectionInterface $con = null)
|
||||||
{
|
{
|
||||||
$this->setRef($this->generateRef());
|
|
||||||
|
|
||||||
$this->dispatchEvent(TheliaEvents::ORDER_BEFORE_CREATE, new OrderEvent($this));
|
$this->dispatchEvent(TheliaEvents::ORDER_BEFORE_CREATE, new OrderEvent($this));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -35,6 +33,8 @@ class Order extends BaseOrder
|
|||||||
*/
|
*/
|
||||||
public function postInsert(ConnectionInterface $con = null)
|
public function postInsert(ConnectionInterface $con = null)
|
||||||
{
|
{
|
||||||
|
$this->setRef($this->generateRef())
|
||||||
|
->save($con);
|
||||||
$this->dispatchEvent(TheliaEvents::ORDER_AFTER_CREATE, new OrderEvent($this));
|
$this->dispatchEvent(TheliaEvents::ORDER_AFTER_CREATE, new OrderEvent($this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,9 +49,7 @@ class Order extends BaseOrder
|
|||||||
|
|
||||||
public function generateRef()
|
public function generateRef()
|
||||||
{
|
{
|
||||||
/* order addresses are unique */
|
return sprintf('ORD%s', str_pad($this->getId(), 12, 0, STR_PAD_LEFT));
|
||||||
|
|
||||||
return uniqid('ORD', true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -109,9 +109,9 @@
|
|||||||
<index name="idx_country_area_id">
|
<index name="idx_country_area_id">
|
||||||
<index-column name="area_id" />
|
<index-column name="area_id" />
|
||||||
</index>
|
</index>
|
||||||
<index name="idx_country_by_default">
|
<index name="idx_country_by_default">
|
||||||
<index-column name="by_default" />
|
<index-column name="by_default" />
|
||||||
</index>
|
</index>
|
||||||
<behavior name="timestampable" />
|
<behavior name="timestampable" />
|
||||||
<behavior name="i18n">
|
<behavior name="i18n">
|
||||||
<parameter name="i18n_columns" value="title, description, chapo, postscriptum" />
|
<parameter name="i18n_columns" value="title, description, chapo, postscriptum" />
|
||||||
@@ -161,11 +161,11 @@
|
|||||||
<index name="idx_tax_rule_country_country_id">
|
<index name="idx_tax_rule_country_country_id">
|
||||||
<index-column name="country_id" />
|
<index-column name="country_id" />
|
||||||
</index>
|
</index>
|
||||||
<index name="idx_tax_rule_country_tax_rule_id_country_id_position">
|
<index name="idx_tax_rule_country_tax_rule_id_country_id_position">
|
||||||
<index-column name="tax_rule_id"/>
|
<index-column name="tax_rule_id"/>
|
||||||
<index-column name="country_id"/>
|
<index-column name="country_id"/>
|
||||||
<index-column name="position"/>
|
<index-column name="position"/>
|
||||||
</index>
|
</index>
|
||||||
<behavior name="timestampable" />
|
<behavior name="timestampable" />
|
||||||
</table>
|
</table>
|
||||||
<table name="feature" namespace="Thelia\Model">
|
<table name="feature" namespace="Thelia\Model">
|
||||||
@@ -225,11 +225,11 @@
|
|||||||
<index name="idx_feature_prod_feature_av_id">
|
<index name="idx_feature_prod_feature_av_id">
|
||||||
<index-column name="feature_av_id" />
|
<index-column name="feature_av_id" />
|
||||||
</index>
|
</index>
|
||||||
<index name="idx_feature_product_product_id_feature_id_position">
|
<index name="idx_feature_product_product_id_feature_id_position">
|
||||||
<index-column name="product_id"/>
|
<index-column name="product_id"/>
|
||||||
<index-column name="feature_id"/>
|
<index-column name="feature_id"/>
|
||||||
<index-column name="position"/>
|
<index-column name="position"/>
|
||||||
</index>
|
</index>
|
||||||
<behavior name="timestampable" />
|
<behavior name="timestampable" />
|
||||||
</table>
|
</table>
|
||||||
<table isCrossRef="true" name="feature_template" namespace="Thelia\Model">
|
<table isCrossRef="true" name="feature_template" namespace="Thelia\Model">
|
||||||
@@ -249,10 +249,10 @@
|
|||||||
<index name="fk_feature_template_idx">
|
<index name="fk_feature_template_idx">
|
||||||
<index-column name="template_id" />
|
<index-column name="template_id" />
|
||||||
</index>
|
</index>
|
||||||
<index name="idx_feature_template_template_id_position">
|
<index name="idx_feature_template_template_id_position">
|
||||||
<index-column name="template_id"/>
|
<index-column name="template_id"/>
|
||||||
<index-column name="position"/>
|
<index-column name="position"/>
|
||||||
</index>
|
</index>
|
||||||
<behavior name="timestampable" />
|
<behavior name="timestampable" />
|
||||||
</table>
|
</table>
|
||||||
<table name="attribute" namespace="Thelia\Model">
|
<table name="attribute" namespace="Thelia\Model">
|
||||||
@@ -326,11 +326,11 @@
|
|||||||
<index name="idx_product_sale_element_product_id">
|
<index name="idx_product_sale_element_product_id">
|
||||||
<index-column name="product_id" />
|
<index-column name="product_id" />
|
||||||
</index>
|
</index>
|
||||||
<index name="idx_product_elements_product_id_promo_is_default">
|
<index name="idx_product_elements_product_id_promo_is_default">
|
||||||
<index-column name="product_id"/>
|
<index-column name="product_id"/>
|
||||||
<index-column name="promo"/>
|
<index-column name="promo"/>
|
||||||
<index-column name="is_default"/>
|
<index-column name="is_default"/>
|
||||||
</index>
|
</index>
|
||||||
<index name="ref">
|
<index name="ref">
|
||||||
<index-column name="ref" />
|
<index-column name="ref" />
|
||||||
</index>
|
</index>
|
||||||
@@ -375,7 +375,7 @@
|
|||||||
</table>
|
</table>
|
||||||
<table name="customer" namespace="Thelia\Model">
|
<table name="customer" namespace="Thelia\Model">
|
||||||
<column autoIncrement="true" name="id" primaryKey="true" required="true" type="INTEGER" />
|
<column autoIncrement="true" name="id" primaryKey="true" required="true" type="INTEGER" />
|
||||||
<column name="ref" required="true" size="50" type="VARCHAR" />
|
<column name="ref" size="50" type="VARCHAR" />
|
||||||
<column name="title_id" required="true" type="INTEGER" />
|
<column name="title_id" required="true" type="INTEGER" />
|
||||||
<column name="firstname" required="true" size="255" type="VARCHAR" />
|
<column name="firstname" required="true" size="255" type="VARCHAR" />
|
||||||
<column name="lastname" required="true" size="255" type="VARCHAR" />
|
<column name="lastname" required="true" size="255" type="VARCHAR" />
|
||||||
@@ -461,9 +461,9 @@
|
|||||||
<column name="decimals" size="45" type="VARCHAR" />
|
<column name="decimals" size="45" type="VARCHAR" />
|
||||||
<column name="by_default" type="TINYINT" />
|
<column name="by_default" type="TINYINT" />
|
||||||
<column name="position" type="INTEGER" />
|
<column name="position" type="INTEGER" />
|
||||||
<index name="idx_lang_by_default">
|
<index name="idx_lang_by_default">
|
||||||
<index-column name="by_default"/>
|
<index-column name="by_default"/>
|
||||||
</index>
|
</index>
|
||||||
<behavior name="timestampable" />
|
<behavior name="timestampable" />
|
||||||
</table>
|
</table>
|
||||||
<table name="folder" namespace="Thelia\Model">
|
<table name="folder" namespace="Thelia\Model">
|
||||||
@@ -522,10 +522,10 @@
|
|||||||
<index name="idx_product_image_product_id">
|
<index name="idx_product_image_product_id">
|
||||||
<index-column name="product_id" />
|
<index-column name="product_id" />
|
||||||
</index>
|
</index>
|
||||||
<index name="idx_product_image_product_id_position">
|
<index name="idx_product_image_product_id_position">
|
||||||
<index-column name="product_id"/>
|
<index-column name="product_id"/>
|
||||||
<index-column name="position"/>
|
<index-column name="position"/>
|
||||||
</index>
|
</index>
|
||||||
<behavior name="timestampable" />
|
<behavior name="timestampable" />
|
||||||
<behavior name="i18n">
|
<behavior name="i18n">
|
||||||
<parameter name="i18n_columns" value="title, description, chapo, postscriptum" />
|
<parameter name="i18n_columns" value="title, description, chapo, postscriptum" />
|
||||||
@@ -553,7 +553,7 @@
|
|||||||
</table>
|
</table>
|
||||||
<table name="order" namespace="Thelia\Model">
|
<table name="order" namespace="Thelia\Model">
|
||||||
<column autoIncrement="true" name="id" primaryKey="true" required="true" type="INTEGER" />
|
<column autoIncrement="true" name="id" primaryKey="true" required="true" type="INTEGER" />
|
||||||
<column name="ref" required="true" size="45" type="VARCHAR" />
|
<column name="ref" size="45" type="VARCHAR" />
|
||||||
<column name="customer_id" required="true" type="INTEGER" />
|
<column name="customer_id" required="true" type="INTEGER" />
|
||||||
<column name="invoice_order_address_id" required="true" type="INTEGER" />
|
<column name="invoice_order_address_id" required="true" type="INTEGER" />
|
||||||
<column name="delivery_order_address_id" required="true" type="INTEGER" />
|
<column name="delivery_order_address_id" required="true" type="INTEGER" />
|
||||||
@@ -630,12 +630,12 @@
|
|||||||
<column name="rate" type="FLOAT" />
|
<column name="rate" type="FLOAT" />
|
||||||
<column name="position" type="INTEGER" />
|
<column name="position" type="INTEGER" />
|
||||||
<column name="by_default" type="TINYINT" />
|
<column name="by_default" type="TINYINT" />
|
||||||
<index name="idx_currency_by_default">
|
<index name="idx_currency_by_default">
|
||||||
<index-column name="by_default"/>
|
<index-column name="by_default"/>
|
||||||
</index>
|
</index>
|
||||||
<index name="idx_currency_code">
|
<index name="idx_currency_code">
|
||||||
<index-column name="code"/>
|
<index-column name="code"/>
|
||||||
</index>
|
</index>
|
||||||
<behavior name="timestampable" />
|
<behavior name="timestampable" />
|
||||||
<behavior name="i18n">
|
<behavior name="i18n">
|
||||||
<parameter name="i18n_columns" value="name" />
|
<parameter name="i18n_columns" value="name" />
|
||||||
@@ -1079,10 +1079,10 @@
|
|||||||
<index name="idx_category_image_category_id">
|
<index name="idx_category_image_category_id">
|
||||||
<index-column name="category_id" />
|
<index-column name="category_id" />
|
||||||
</index>
|
</index>
|
||||||
<index name="idx_category_image_category_id_position">
|
<index name="idx_category_image_category_id_position">
|
||||||
<index-column name="category_id"/>
|
<index-column name="category_id"/>
|
||||||
<index-column name="position"/>
|
<index-column name="position"/>
|
||||||
</index>
|
</index>
|
||||||
<behavior name="timestampable" />
|
<behavior name="timestampable" />
|
||||||
<behavior name="i18n">
|
<behavior name="i18n">
|
||||||
<parameter name="i18n_columns" value="title, description, chapo, postscriptum" />
|
<parameter name="i18n_columns" value="title, description, chapo, postscriptum" />
|
||||||
@@ -1103,10 +1103,10 @@
|
|||||||
<index name="idx_folder_image_folder_id">
|
<index name="idx_folder_image_folder_id">
|
||||||
<index-column name="folder_id" />
|
<index-column name="folder_id" />
|
||||||
</index>
|
</index>
|
||||||
<index name="idx_folder_image_folder_id_position">
|
<index name="idx_folder_image_folder_id_position">
|
||||||
<index-column name="folder_id"/>
|
<index-column name="folder_id"/>
|
||||||
<index-column name="position"/>
|
<index-column name="position"/>
|
||||||
</index>
|
</index>
|
||||||
<behavior name="timestampable" />
|
<behavior name="timestampable" />
|
||||||
<behavior name="i18n">
|
<behavior name="i18n">
|
||||||
<parameter name="i18n_columns" value="title, description, chapo, postscriptum" />
|
<parameter name="i18n_columns" value="title, description, chapo, postscriptum" />
|
||||||
@@ -1127,10 +1127,10 @@
|
|||||||
<index name="idx_content_image_content_id">
|
<index name="idx_content_image_content_id">
|
||||||
<index-column name="content_id" />
|
<index-column name="content_id" />
|
||||||
</index>
|
</index>
|
||||||
<index name="idx_content_image_content_id_position">
|
<index name="idx_content_image_content_id_position">
|
||||||
<index-column name="content_id"/>
|
<index-column name="content_id"/>
|
||||||
<index-column name="position"/>
|
<index-column name="position"/>
|
||||||
</index>
|
</index>
|
||||||
<behavior name="timestampable" />
|
<behavior name="timestampable" />
|
||||||
<behavior name="i18n">
|
<behavior name="i18n">
|
||||||
<parameter name="i18n_columns" value="title, description, chapo, postscriptum" />
|
<parameter name="i18n_columns" value="title, description, chapo, postscriptum" />
|
||||||
@@ -1250,16 +1250,16 @@
|
|||||||
<index name="idx_rewriting_url_redirected">
|
<index name="idx_rewriting_url_redirected">
|
||||||
<index-column name="redirected" />
|
<index-column name="redirected" />
|
||||||
</index>
|
</index>
|
||||||
<index name="idx_rewriting_url_view_updated_at">
|
<index name="idx_rewriting_url_view_updated_at">
|
||||||
<index-column name="view"/>
|
<index-column name="view"/>
|
||||||
<index-column name="updated_at"/>
|
<index-column name="updated_at"/>
|
||||||
</index>
|
</index>
|
||||||
<index name="idx_rewriting_url_view_id_view_view_locale_updated_at">
|
<index name="idx_rewriting_url_view_id_view_view_locale_updated_at">
|
||||||
<index-column name="view_id"/>
|
<index-column name="view_id"/>
|
||||||
<index-column name="view"/>
|
<index-column name="view"/>
|
||||||
<index-column name="view_locale"/>
|
<index-column name="view_locale"/>
|
||||||
<index-column name="updated_at"/>
|
<index-column name="updated_at"/>
|
||||||
</index>
|
</index>
|
||||||
<behavior name="timestampable" />
|
<behavior name="timestampable" />
|
||||||
</table>
|
</table>
|
||||||
<table name="rewriting_argument" namespace="Thelia\Model">
|
<table name="rewriting_argument" namespace="Thelia\Model">
|
||||||
@@ -1297,10 +1297,10 @@
|
|||||||
<index name="idx_module_image_module_id">
|
<index name="idx_module_image_module_id">
|
||||||
<index-column name="module_id" />
|
<index-column name="module_id" />
|
||||||
</index>
|
</index>
|
||||||
<index name="idx_module_image_module_id_position">
|
<index name="idx_module_image_module_id_position">
|
||||||
<index-column name="module_id"/>
|
<index-column name="module_id"/>
|
||||||
<index-column name="position"/>
|
<index-column name="position"/>
|
||||||
</index>
|
</index>
|
||||||
<behavior name="timestampable" />
|
<behavior name="timestampable" />
|
||||||
<behavior name="i18n">
|
<behavior name="i18n">
|
||||||
<parameter name="i18n_columns" value="title, description, chapo, postscriptum" />
|
<parameter name="i18n_columns" value="title, description, chapo, postscriptum" />
|
||||||
|
|||||||
Submodule local/modules/Klikandpay updated: f356c95726...a72c066e74
@@ -441,7 +441,7 @@ DROP TABLE IF EXISTS `customer`;
|
|||||||
CREATE TABLE `customer`
|
CREATE TABLE `customer`
|
||||||
(
|
(
|
||||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
`ref` VARCHAR(50) NOT NULL,
|
`ref` VARCHAR(50),
|
||||||
`title_id` INTEGER NOT NULL,
|
`title_id` INTEGER NOT NULL,
|
||||||
`firstname` VARCHAR(255) NOT NULL,
|
`firstname` VARCHAR(255) NOT NULL,
|
||||||
`lastname` VARCHAR(255) NOT NULL,
|
`lastname` VARCHAR(255) NOT NULL,
|
||||||
@@ -651,7 +651,7 @@ DROP TABLE IF EXISTS `order`;
|
|||||||
CREATE TABLE `order`
|
CREATE TABLE `order`
|
||||||
(
|
(
|
||||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
`ref` VARCHAR(45) NOT NULL,
|
`ref` VARCHAR(45),
|
||||||
`customer_id` INTEGER NOT NULL,
|
`customer_id` INTEGER NOT NULL,
|
||||||
`invoice_order_address_id` INTEGER NOT NULL,
|
`invoice_order_address_id` INTEGER NOT NULL,
|
||||||
`delivery_order_address_id` INTEGER NOT NULL,
|
`delivery_order_address_id` INTEGER NOT NULL,
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ ALTER TABLE `feature_template` ADD INDEX `idx_feature_template_template_id_posit
|
|||||||
|
|
||||||
ALTER TABLE `currency` ADD INDEX `idx_currency_code` (`code`);
|
ALTER TABLE `currency` ADD INDEX `idx_currency_code` (`code`);
|
||||||
|
|
||||||
|
ALTER TABLE `customer` CHANGE `ref` `ref` VARCHAR( 50 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL ;
|
||||||
|
|
||||||
|
ALTER TABLE `order` CHANGE `ref` `ref` VARCHAR( 45 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL ;
|
||||||
|
|
||||||
SELECT @max := MAX(`id`) FROM `resource`;
|
SELECT @max := MAX(`id`) FROM `resource`;
|
||||||
SET @max := @max+1;
|
SET @max := @max+1;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user