Implement Entries // TODO: Move to packages, add Repos.
This commit is contained in:
		@ -0,0 +1,10 @@
 | 
			
		||||
package org.hso.ecommerce.entities;
 | 
			
		||||
 | 
			
		||||
import javax.persistence.Embeddable;
 | 
			
		||||
 | 
			
		||||
@Embeddable
 | 
			
		||||
public class Address {
 | 
			
		||||
    public String name;
 | 
			
		||||
    public String addressString;
 | 
			
		||||
    public String country = "DE";
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,38 @@
 | 
			
		||||
package org.hso.ecommerce.entities;
 | 
			
		||||
 | 
			
		||||
import javax.persistence.*;
 | 
			
		||||
import javax.validation.constraints.NotNull;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
@Entity
 | 
			
		||||
@Table(name = "articles")
 | 
			
		||||
public class Article {
 | 
			
		||||
 | 
			
		||||
    @Id
 | 
			
		||||
    @GeneratedValue(strategy = GenerationType.IDENTITY)
 | 
			
		||||
    @Basic
 | 
			
		||||
    public long id;
 | 
			
		||||
 | 
			
		||||
    @ManyToOne(optional = false)
 | 
			
		||||
    public ArticleOffer related;
 | 
			
		||||
 | 
			
		||||
    public int shopPricePerUnitNetCent;
 | 
			
		||||
    public int warehouseUnitsPerSlot;
 | 
			
		||||
 | 
			
		||||
    public boolean shouldReorder;
 | 
			
		||||
    public int reorderMaxPrice;
 | 
			
		||||
 | 
			
		||||
    @NotNull
 | 
			
		||||
    public String title;
 | 
			
		||||
 | 
			
		||||
    @NotNull
 | 
			
		||||
    public String description;
 | 
			
		||||
 | 
			
		||||
    @OneToOne(optional = true)
 | 
			
		||||
    public Image image;
 | 
			
		||||
 | 
			
		||||
    @ManyToMany
 | 
			
		||||
    @JoinTable(name = "article_categories_bindings")
 | 
			
		||||
    public Set<Category> categories = new HashSet<>();
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,22 @@
 | 
			
		||||
package org.hso.ecommerce.entities;
 | 
			
		||||
 | 
			
		||||
import javax.persistence.*;
 | 
			
		||||
import javax.validation.constraints.NotNull;
 | 
			
		||||
 | 
			
		||||
@Entity
 | 
			
		||||
@Table(name = "article_offers")
 | 
			
		||||
public class ArticleOffer {
 | 
			
		||||
 | 
			
		||||
    @Id
 | 
			
		||||
    @GeneratedValue(strategy = GenerationType.IDENTITY)
 | 
			
		||||
    @Basic
 | 
			
		||||
    public long id;
 | 
			
		||||
 | 
			
		||||
    @NotNull
 | 
			
		||||
    public String manufacturer;
 | 
			
		||||
 | 
			
		||||
    @NotNull
 | 
			
		||||
    public String articleNumber;
 | 
			
		||||
 | 
			
		||||
    public int vatPercent;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,23 @@
 | 
			
		||||
package org.hso.ecommerce.entities;
 | 
			
		||||
 | 
			
		||||
import javax.persistence.*;
 | 
			
		||||
import javax.validation.constraints.NotNull;
 | 
			
		||||
 | 
			
		||||
@Entity
 | 
			
		||||
@Table(name = "background_jobs")
 | 
			
		||||
public class BackgroundJob {
 | 
			
		||||
 | 
			
		||||
    public final String JOB_DASHBOARD = "Dashboard";
 | 
			
		||||
    public final String JOB_REORDER = "SupplierOrder";
 | 
			
		||||
 | 
			
		||||
    @Id
 | 
			
		||||
    @GeneratedValue(strategy = GenerationType.IDENTITY)
 | 
			
		||||
    @Basic
 | 
			
		||||
    public long id;
 | 
			
		||||
 | 
			
		||||
    @NotNull
 | 
			
		||||
    public String jobName;
 | 
			
		||||
 | 
			
		||||
    @NotNull
 | 
			
		||||
    public java.sql.Timestamp lastExecution;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,25 @@
 | 
			
		||||
package org.hso.ecommerce.entities;
 | 
			
		||||
 | 
			
		||||
import javax.persistence.*;
 | 
			
		||||
 | 
			
		||||
@Entity
 | 
			
		||||
@Table(name = "bookings")
 | 
			
		||||
public class Booking {
 | 
			
		||||
 | 
			
		||||
    @Id
 | 
			
		||||
    @GeneratedValue(strategy = GenerationType.IDENTITY)
 | 
			
		||||
    @Basic
 | 
			
		||||
    public long id;
 | 
			
		||||
 | 
			
		||||
    // always >= 0
 | 
			
		||||
    public int amountCent;
 | 
			
		||||
 | 
			
		||||
    @ManyToOne(optional = true)
 | 
			
		||||
    public BookingAccountEntry source;
 | 
			
		||||
 | 
			
		||||
    @ManyToOne(optional = true)
 | 
			
		||||
    public BookingAccountEntry destination;
 | 
			
		||||
 | 
			
		||||
    @OneToOne(optional = false)
 | 
			
		||||
    public BookingReason reason;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,25 @@
 | 
			
		||||
package org.hso.ecommerce.entities;
 | 
			
		||||
 | 
			
		||||
import javax.persistence.*;
 | 
			
		||||
 | 
			
		||||
@Entity
 | 
			
		||||
@Table(name = "booking_account_entries")
 | 
			
		||||
public class BookingAccountEntry {
 | 
			
		||||
 | 
			
		||||
    @Id
 | 
			
		||||
    @GeneratedValue(strategy = GenerationType.IDENTITY)
 | 
			
		||||
    @Basic
 | 
			
		||||
    public long id;
 | 
			
		||||
 | 
			
		||||
    public int newSumCent;
 | 
			
		||||
 | 
			
		||||
    @ManyToOne(optional = true)
 | 
			
		||||
    public User userAccount;
 | 
			
		||||
 | 
			
		||||
    @ManyToOne(optional = true)
 | 
			
		||||
    public Supplier supplierAccount;
 | 
			
		||||
 | 
			
		||||
    public boolean isMainAccount;
 | 
			
		||||
    public boolean isVATAccount;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,27 @@
 | 
			
		||||
package org.hso.ecommerce.entities;
 | 
			
		||||
 | 
			
		||||
import javax.persistence.*;
 | 
			
		||||
 | 
			
		||||
@Entity
 | 
			
		||||
@Table(name = "booking_reasons")
 | 
			
		||||
public class BookingReason {
 | 
			
		||||
 | 
			
		||||
    @Id
 | 
			
		||||
    @GeneratedValue(strategy = GenerationType.IDENTITY)
 | 
			
		||||
    @Basic
 | 
			
		||||
    public long id;
 | 
			
		||||
 | 
			
		||||
    public boolean isManuel;
 | 
			
		||||
    public boolean isStartBooking;
 | 
			
		||||
 | 
			
		||||
    public String comment;
 | 
			
		||||
 | 
			
		||||
    @ManyToOne(optional = true)
 | 
			
		||||
    public CustomerOrder customerOrder;
 | 
			
		||||
 | 
			
		||||
    @ManyToOne(optional = true)
 | 
			
		||||
    public CustomerPayment customerPayment;
 | 
			
		||||
 | 
			
		||||
    @ManyToOne(optional = true)
 | 
			
		||||
    public SupplierOrder supplierOrder;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,23 @@
 | 
			
		||||
package org.hso.ecommerce.entities;
 | 
			
		||||
 | 
			
		||||
import javax.persistence.*;
 | 
			
		||||
import javax.validation.constraints.NotNull;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
@Entity
 | 
			
		||||
@Table(name = "categories")
 | 
			
		||||
public class Category {
 | 
			
		||||
 | 
			
		||||
    @Id
 | 
			
		||||
    @GeneratedValue(strategy = GenerationType.IDENTITY)
 | 
			
		||||
    @Basic
 | 
			
		||||
    public long id;
 | 
			
		||||
 | 
			
		||||
    @NotNull
 | 
			
		||||
    @Column(unique = true)
 | 
			
		||||
    public String name;
 | 
			
		||||
 | 
			
		||||
    @ManyToMany(mappedBy = "categories")
 | 
			
		||||
    public Set<Article> articles = new HashSet<>();
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,44 @@
 | 
			
		||||
package org.hso.ecommerce.entities;
 | 
			
		||||
 | 
			
		||||
import javax.persistence.*;
 | 
			
		||||
import javax.validation.constraints.NotNull;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@Entity
 | 
			
		||||
@Table(name = "customer_orders")
 | 
			
		||||
public class CustomerOrder {
 | 
			
		||||
 | 
			
		||||
    @Id
 | 
			
		||||
    @GeneratedValue(strategy = GenerationType.IDENTITY)
 | 
			
		||||
    @Basic
 | 
			
		||||
    public long id;
 | 
			
		||||
 | 
			
		||||
    @ManyToOne(optional = false)
 | 
			
		||||
    public User customer;
 | 
			
		||||
 | 
			
		||||
    @Embedded
 | 
			
		||||
    public Address destination;
 | 
			
		||||
 | 
			
		||||
    @OneToMany(
 | 
			
		||||
            targetEntity = CustomerOrderPosition.class,
 | 
			
		||||
            mappedBy = "order"
 | 
			
		||||
    )
 | 
			
		||||
    public List<CustomerOrderPosition> positions = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
    @NotNull
 | 
			
		||||
    public java.sql.Timestamp created;
 | 
			
		||||
 | 
			
		||||
    @NotNull
 | 
			
		||||
    public String trackingId;
 | 
			
		||||
 | 
			
		||||
    @Column(nullable = true)
 | 
			
		||||
    public java.sql.Timestamp inDeliverySince;
 | 
			
		||||
 | 
			
		||||
    @Column(nullable = true)
 | 
			
		||||
    public java.sql.Timestamp deliveredAt;
 | 
			
		||||
 | 
			
		||||
    public int totalNetCent;
 | 
			
		||||
    public int totalGrossCent;
 | 
			
		||||
    public int totalVatCent;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,22 @@
 | 
			
		||||
package org.hso.ecommerce.entities;
 | 
			
		||||
 | 
			
		||||
import javax.persistence.*;
 | 
			
		||||
 | 
			
		||||
@Entity
 | 
			
		||||
@Table(name = "customer_order_positions")
 | 
			
		||||
public class CustomerOrderPosition {
 | 
			
		||||
 | 
			
		||||
    @Id
 | 
			
		||||
    @GeneratedValue(strategy = GenerationType.IDENTITY)
 | 
			
		||||
    @Basic
 | 
			
		||||
    public long id;
 | 
			
		||||
 | 
			
		||||
    @ManyToOne(optional = false)
 | 
			
		||||
    public CustomerOrder order;
 | 
			
		||||
 | 
			
		||||
    @ManyToOne(optional = false)
 | 
			
		||||
    public Article article;
 | 
			
		||||
 | 
			
		||||
    public int pricePerUnit;
 | 
			
		||||
    public int quantity;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,18 @@
 | 
			
		||||
package org.hso.ecommerce.entities;
 | 
			
		||||
 | 
			
		||||
import javax.persistence.*;
 | 
			
		||||
 | 
			
		||||
@Entity
 | 
			
		||||
@Table(name = "customer_payments")
 | 
			
		||||
public class CustomerPayment {
 | 
			
		||||
 | 
			
		||||
    @Id
 | 
			
		||||
    @GeneratedValue(strategy = GenerationType.IDENTITY)
 | 
			
		||||
    @Basic
 | 
			
		||||
    public long id;
 | 
			
		||||
 | 
			
		||||
    public int amountCent;
 | 
			
		||||
 | 
			
		||||
    @Embedded
 | 
			
		||||
    public PaymentMethod payment;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,25 @@
 | 
			
		||||
package org.hso.ecommerce.entities;
 | 
			
		||||
 | 
			
		||||
import javax.persistence.*;
 | 
			
		||||
import javax.validation.constraints.NotNull;
 | 
			
		||||
 | 
			
		||||
@Entity
 | 
			
		||||
@Table(name = "dashboard_summaries")
 | 
			
		||||
public class DashboardSummary {
 | 
			
		||||
 | 
			
		||||
    @Id
 | 
			
		||||
    @GeneratedValue(strategy = GenerationType.IDENTITY)
 | 
			
		||||
    @Basic
 | 
			
		||||
    public long id;
 | 
			
		||||
 | 
			
		||||
    @NotNull
 | 
			
		||||
    public java.sql.Date created;
 | 
			
		||||
 | 
			
		||||
    public int todaysCustomers;
 | 
			
		||||
    public int todaysCustomersOrders;
 | 
			
		||||
    public int todaysSuppliersOrders;
 | 
			
		||||
    public int todaysItemsSold;
 | 
			
		||||
    public int todaysSalesCent;
 | 
			
		||||
    public int totalWarehouseCapacity;
 | 
			
		||||
    public int currentWarehouseCapacity;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,17 @@
 | 
			
		||||
package org.hso.ecommerce.entities;
 | 
			
		||||
 | 
			
		||||
import javax.persistence.*;
 | 
			
		||||
 | 
			
		||||
@Entity
 | 
			
		||||
@Table(name = "images")
 | 
			
		||||
public class Image {
 | 
			
		||||
 | 
			
		||||
    @Id
 | 
			
		||||
    @GeneratedValue(strategy = GenerationType.IDENTITY)
 | 
			
		||||
    @Basic
 | 
			
		||||
    public long id;
 | 
			
		||||
 | 
			
		||||
    @Lob
 | 
			
		||||
    @Column(name = "data", columnDefinition = "BLOB", nullable = false)
 | 
			
		||||
    private byte[] data;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,10 @@
 | 
			
		||||
package org.hso.ecommerce.entities;
 | 
			
		||||
 | 
			
		||||
import javax.persistence.Embeddable;
 | 
			
		||||
import javax.validation.constraints.NotNull;
 | 
			
		||||
 | 
			
		||||
@Embeddable
 | 
			
		||||
public class PaymentMethod {
 | 
			
		||||
    @NotNull
 | 
			
		||||
    public String creditCardNumber;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,23 @@
 | 
			
		||||
package org.hso.ecommerce.entities;
 | 
			
		||||
 | 
			
		||||
import javax.persistence.*;
 | 
			
		||||
import javax.validation.constraints.NotNull;
 | 
			
		||||
 | 
			
		||||
@Entity
 | 
			
		||||
@Table(name = "suppliers")
 | 
			
		||||
public class Supplier {
 | 
			
		||||
 | 
			
		||||
    @Id
 | 
			
		||||
    @GeneratedValue(strategy = GenerationType.IDENTITY)
 | 
			
		||||
    @Basic
 | 
			
		||||
    public long id;
 | 
			
		||||
 | 
			
		||||
    @Column(unique = true)
 | 
			
		||||
    public String uuid;
 | 
			
		||||
 | 
			
		||||
    @NotNull
 | 
			
		||||
    public String name;
 | 
			
		||||
 | 
			
		||||
    @NotNull
 | 
			
		||||
    public String apiUrl;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,37 @@
 | 
			
		||||
package org.hso.ecommerce.entities;
 | 
			
		||||
 | 
			
		||||
import javax.persistence.*;
 | 
			
		||||
import javax.validation.constraints.NotNull;
 | 
			
		||||
import java.sql.Timestamp;
 | 
			
		||||
 | 
			
		||||
@Entity
 | 
			
		||||
@Table(name = "supplier_orders")
 | 
			
		||||
public class SupplierOrder {
 | 
			
		||||
 | 
			
		||||
    @Id
 | 
			
		||||
    @GeneratedValue(strategy = GenerationType.IDENTITY)
 | 
			
		||||
    @Basic
 | 
			
		||||
    public long id;
 | 
			
		||||
 | 
			
		||||
    @NotNull
 | 
			
		||||
    public java.sql.Timestamp created;
 | 
			
		||||
 | 
			
		||||
    @ManyToOne(optional = false)
 | 
			
		||||
    public Supplier supplier;
 | 
			
		||||
 | 
			
		||||
    @ManyToOne(optional = false)
 | 
			
		||||
    public ArticleOffer ordered;
 | 
			
		||||
 | 
			
		||||
    public int numberOfUnits;
 | 
			
		||||
    public int pricePerUnitNetCent;
 | 
			
		||||
 | 
			
		||||
    // Includes discounts
 | 
			
		||||
    public int totalPriceNet;
 | 
			
		||||
 | 
			
		||||
    @Column(nullable = true)
 | 
			
		||||
    public Timestamp delivered;
 | 
			
		||||
 | 
			
		||||
    public boolean wasDelivered() {
 | 
			
		||||
        return delivered != null;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -3,6 +3,7 @@ package org.hso.ecommerce.entities;
 | 
			
		||||
import org.springframework.security.crypto.bcrypt.BCrypt;
 | 
			
		||||
 | 
			
		||||
import javax.persistence.*;
 | 
			
		||||
import javax.validation.constraints.NotNull;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@Entity
 | 
			
		||||
@ -13,16 +14,24 @@ public class User {
 | 
			
		||||
    @GeneratedValue(strategy = GenerationType.IDENTITY)
 | 
			
		||||
    @Basic
 | 
			
		||||
    public long id;
 | 
			
		||||
 | 
			
		||||
    @NotNull
 | 
			
		||||
    public java.sql.Timestamp created;
 | 
			
		||||
 | 
			
		||||
    @NotNull
 | 
			
		||||
    @Column(unique = true)
 | 
			
		||||
    public String email;
 | 
			
		||||
 | 
			
		||||
    public String passwordHash;
 | 
			
		||||
 | 
			
		||||
    public boolean isActive;
 | 
			
		||||
    public boolean isEmployee;
 | 
			
		||||
    public boolean getsAds;
 | 
			
		||||
    public boolean isB2B;
 | 
			
		||||
 | 
			
		||||
    @Embedded
 | 
			
		||||
    private Address defaultDeliveryAddress;
 | 
			
		||||
 | 
			
		||||
    @Embedded
 | 
			
		||||
    private PaymentMethod defaultPayment;
 | 
			
		||||
 | 
			
		||||
    public long getId() {
 | 
			
		||||
        return id;
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,27 @@
 | 
			
		||||
package org.hso.ecommerce.entities;
 | 
			
		||||
 | 
			
		||||
import javax.persistence.*;
 | 
			
		||||
import javax.validation.constraints.NotNull;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@Entity
 | 
			
		||||
@Table(name = "warehouse_bookings")
 | 
			
		||||
public class WarehouseBooking {
 | 
			
		||||
 | 
			
		||||
    @Id
 | 
			
		||||
    @GeneratedValue(strategy = GenerationType.IDENTITY)
 | 
			
		||||
    @Basic
 | 
			
		||||
    public long id;
 | 
			
		||||
 | 
			
		||||
    @NotNull
 | 
			
		||||
    public java.sql.Timestamp created;
 | 
			
		||||
 | 
			
		||||
    public boolean isInProgress;
 | 
			
		||||
    public boolean isDone;
 | 
			
		||||
 | 
			
		||||
    @OneToMany(
 | 
			
		||||
            mappedBy = "booking"
 | 
			
		||||
    )
 | 
			
		||||
    public List<WarehouseBookingPosition> positions = new ArrayList<>();
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,30 @@
 | 
			
		||||
package org.hso.ecommerce.entities;
 | 
			
		||||
 | 
			
		||||
import javax.persistence.*;
 | 
			
		||||
 | 
			
		||||
// TODO Unify with $$$ Bookings + WarehouseBookingEntry.
 | 
			
		||||
 | 
			
		||||
@Entity
 | 
			
		||||
@Table(name = "warehouse_booking_positions")
 | 
			
		||||
public class WarehouseBookingPosition {
 | 
			
		||||
 | 
			
		||||
    @Id
 | 
			
		||||
    @GeneratedValue(strategy = GenerationType.IDENTITY)
 | 
			
		||||
    @Basic
 | 
			
		||||
    public long id;
 | 
			
		||||
 | 
			
		||||
    @ManyToOne(optional = false)
 | 
			
		||||
    public WarehouseBooking booking;
 | 
			
		||||
 | 
			
		||||
    @ManyToOne(optional = false)
 | 
			
		||||
    public Article article;
 | 
			
		||||
 | 
			
		||||
    public int amount; // positive or negative
 | 
			
		||||
 | 
			
		||||
    @ManyToOne(optional = true)
 | 
			
		||||
    public WarehouseBookingPositionSlotEntry source;
 | 
			
		||||
 | 
			
		||||
    @ManyToOne(optional = true)
 | 
			
		||||
    public WarehouseBookingPositionSlotEntry destination;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,21 @@
 | 
			
		||||
package org.hso.ecommerce.entities;
 | 
			
		||||
 | 
			
		||||
import javax.persistence.*;
 | 
			
		||||
 | 
			
		||||
@Entity
 | 
			
		||||
@Table(name = "warehouse_booking_position_entries")
 | 
			
		||||
public class WarehouseBookingPositionSlotEntry {
 | 
			
		||||
 | 
			
		||||
    @Id
 | 
			
		||||
    @GeneratedValue(strategy = GenerationType.IDENTITY)
 | 
			
		||||
    @Basic
 | 
			
		||||
    public long id;
 | 
			
		||||
 | 
			
		||||
    @ManyToOne
 | 
			
		||||
    public Article article;
 | 
			
		||||
 | 
			
		||||
    public int newSumArticles;
 | 
			
		||||
    public int newSumWarehousePosition;
 | 
			
		||||
 | 
			
		||||
    public int slot;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,23 @@
 | 
			
		||||
package org.hso.ecommerce.entities;
 | 
			
		||||
 | 
			
		||||
import javax.persistence.*;
 | 
			
		||||
 | 
			
		||||
@Entity
 | 
			
		||||
@Table(name = "warehouse_booking_reasons")
 | 
			
		||||
public class WarehouseBookingReason {
 | 
			
		||||
 | 
			
		||||
    @Id
 | 
			
		||||
    @GeneratedValue(strategy = GenerationType.IDENTITY)
 | 
			
		||||
    @Basic
 | 
			
		||||
    public long id;
 | 
			
		||||
 | 
			
		||||
    public String comment;
 | 
			
		||||
 | 
			
		||||
    @ManyToOne(optional = true)
 | 
			
		||||
    public SupplierOrder causeSupplierOrder;
 | 
			
		||||
 | 
			
		||||
    @ManyToOne(optional = true)
 | 
			
		||||
    public CustomerOrder customerOrder;
 | 
			
		||||
 | 
			
		||||
    public boolean isManuel;
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user