fixed orderprocessing to be more efficent, and i now use BigDecimal to cal money
This commit is contained in:
@@ -7,5 +7,6 @@
|
|||||||
<classpathentry kind="lib" path="lib/jcalendar-1.3.3.jar"/>
|
<classpathentry kind="lib" path="lib/jcalendar-1.3.3.jar"/>
|
||||||
<classpathentry kind="lib" path="lib/ostermillerutils_1_07_00.jar"/>
|
<classpathentry kind="lib" path="lib/ostermillerutils_1_07_00.jar"/>
|
||||||
<classpathentry kind="lib" path="lib/mysql-connector-java-5.1.14-bin.jar"/>
|
<classpathentry kind="lib" path="lib/mysql-connector-java-5.1.14-bin.jar"/>
|
||||||
|
<classpathentry kind="lib" path="lib/sqlitejdbc-v056.jar"/>
|
||||||
<classpathentry kind="output" path="bin"/>
|
<classpathentry kind="output" path="bin"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import java.io.FileInputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.net.SocketException;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.DriverManager;
|
import java.sql.DriverManager;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
@@ -38,11 +39,11 @@ import com.TwentyCodes.java.OrderProcessor.ProgressListener;
|
|||||||
public class OrderDB {
|
public class OrderDB {
|
||||||
|
|
||||||
private final String CREATE_TABLE = "CREATE TABLE Orders (id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, item blob);";
|
private final String CREATE_TABLE = "CREATE TABLE Orders (id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, item blob);";
|
||||||
|
private final String CREATE_TABLE_LOCAL = "CREATE TABLE Orders (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, 'item' blob);";
|
||||||
private final String INSERT = "insert into Orders (item) values (?);";
|
private final String INSERT = "insert into Orders (item) values (?);";
|
||||||
private ProgressListener mListener;
|
private ProgressListener mListener;
|
||||||
private PreparedStatement prep;
|
private PreparedStatement prep;
|
||||||
// private String dbLocation;
|
private String dbLocation;
|
||||||
private boolean isLoadingFile = false;
|
private boolean isLoadingFile = false;
|
||||||
private ArrayList<Order> list;
|
private ArrayList<Order> list;
|
||||||
|
|
||||||
@@ -51,18 +52,27 @@ public class OrderDB {
|
|||||||
* @author ricky barrette
|
* @author ricky barrette
|
||||||
* @throws ClassNotFoundException
|
* @throws ClassNotFoundException
|
||||||
* @throws SQLException
|
* @throws SQLException
|
||||||
|
* @throws SocketException
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public OrderDB() throws ClassNotFoundException, SQLException {
|
public OrderDB() throws ClassNotFoundException, SQLException, SocketException {
|
||||||
// dbLocation = System.getProperty("user.home") +"/.TwentyCodesOrders";
|
if(Main.USE_REMOTE_DB) {
|
||||||
// Class.forName("org.sqlite.JDBC");
|
Class.forName("com.mysql.jdbc.Driver");
|
||||||
Class.forName("com.mysql.jdbc.Driver");
|
}
|
||||||
|
else {
|
||||||
|
Class.forName("org.sqlite.JDBC");
|
||||||
|
dbLocation = System.getProperty("user.home") +"/.TwentyCodesOrders";
|
||||||
|
}
|
||||||
|
|
||||||
Connection conn = getConnection();
|
Connection conn = getConnection();
|
||||||
Statement stat = conn.createStatement();
|
Statement stat = conn.createStatement();
|
||||||
|
|
||||||
// stat.executeUpdate("drop table if exists Orders;");
|
// stat.executeUpdate("drop table if exists Orders;");
|
||||||
try {
|
try {
|
||||||
stat.executeUpdate(CREATE_TABLE);
|
if(Main.USE_REMOTE_DB)
|
||||||
|
stat.executeUpdate(CREATE_TABLE);
|
||||||
|
else
|
||||||
|
stat.executeUpdate(CREATE_TABLE_LOCAL);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
//most likely the table already exist
|
//most likely the table already exist
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -150,9 +160,11 @@ public class OrderDB {
|
|||||||
* @throws SQLException
|
* @throws SQLException
|
||||||
* @author ricky barrette
|
* @author ricky barrette
|
||||||
*/
|
*/
|
||||||
private Connection getConnection() throws SQLException{
|
private Connection getConnection() throws SQLException, SocketException{
|
||||||
// return DriverManager.getConnection("jdbc:sqlite:"+ dbLocation+".db");
|
if(Main.USE_REMOTE_DB)
|
||||||
return DriverManager.getConnection("jdbc:mysql://tcdevsvn1/Orders");
|
return DriverManager.getConnection("jdbc:mysql://tcdevsvn1/Orders");
|
||||||
|
else
|
||||||
|
return DriverManager.getConnection("jdbc:sqlite:"+ dbLocation+".db");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -160,8 +172,9 @@ public class OrderDB {
|
|||||||
* @return number orders in the database
|
* @return number orders in the database
|
||||||
* @throws SQLException
|
* @throws SQLException
|
||||||
* @author ricky barrette
|
* @author ricky barrette
|
||||||
|
* @throws SocketException
|
||||||
*/
|
*/
|
||||||
public int getCount() throws SQLException{
|
public int getCount() throws SQLException, SocketException{
|
||||||
int count = -1;
|
int count = -1;
|
||||||
Connection conn = getConnection();
|
Connection conn = getConnection();
|
||||||
ResultSet rs = conn.createStatement().executeQuery("select COUNT(*) from Orders;");
|
ResultSet rs = conn.createStatement().executeQuery("select COUNT(*) from Orders;");
|
||||||
@@ -270,19 +283,20 @@ public class OrderDB {
|
|||||||
int row = 1;
|
int row = 1;
|
||||||
int count = getCount();
|
int count = getCount();
|
||||||
|
|
||||||
Connection conn = getConnection();
|
// Connection conn = getConnection();
|
||||||
Statement stat = conn.createStatement();
|
// Statement stat = conn.createStatement();
|
||||||
ResultSet rs = stat.executeQuery("select * from Orders;");
|
// ResultSet rs = stat.executeQuery("select * from Orders;");
|
||||||
ArrayList<Order> list = new ArrayList<Order>();
|
ArrayList<Order> list = new ArrayList<Order>();
|
||||||
String[] parts = text.split(", ");
|
String[] parts = text.split(", ");
|
||||||
ByteArrayInputStream bais;
|
// ByteArrayInputStream bais;
|
||||||
ObjectInputStream ins;
|
// ObjectInputStream ins;
|
||||||
while (rs.next()) {
|
// while (rs.next()) {
|
||||||
|
for(Order order : getAllOrders()){
|
||||||
if(mListener != null && ! isLoadingFile)
|
if(mListener != null && ! isLoadingFile)
|
||||||
mListener.onProgressUpdate("Searching for: "+text, false, row++, count);
|
mListener.onProgressUpdate("Searching for: "+text, false, row++, count);
|
||||||
bais = new ByteArrayInputStream(rs.getBytes("item"));
|
// bais = new ByteArrayInputStream(rs.getBytes("item"));
|
||||||
ins = new ObjectInputStream(bais);
|
// ins = new ObjectInputStream(bais);
|
||||||
Order order = (Order) ins.readObject();
|
// Order order = (Order) ins.readObject();
|
||||||
|
|
||||||
for (int i = 0; i < parts.length; i++) {
|
for (int i = 0; i < parts.length; i++) {
|
||||||
if (isExclusive) {
|
if (isExclusive) {
|
||||||
@@ -295,11 +309,11 @@ public class OrderDB {
|
|||||||
|| order.getCustomerContry().toLowerCase(Locale.ENGLISH).contains(parts[i].toLowerCase(Locale.ENGLISH)))
|
|| order.getCustomerContry().toLowerCase(Locale.ENGLISH).contains(parts[i].toLowerCase(Locale.ENGLISH)))
|
||||||
list.add(order);
|
list.add(order);
|
||||||
}
|
}
|
||||||
ins.close();
|
// ins.close();
|
||||||
bais.close();
|
// bais.close();
|
||||||
}
|
}
|
||||||
rs.close();
|
// rs.close();
|
||||||
conn.close();
|
// conn.close();
|
||||||
|
|
||||||
Collections.sort(list);
|
Collections.sort(list);
|
||||||
return list;
|
return list;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ public class Main {
|
|||||||
|
|
||||||
|
|
||||||
public static final boolean DEBUG = true;
|
public static final boolean DEBUG = true;
|
||||||
|
public static final boolean USE_REMOTE_DB = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* called when the application first starts
|
* called when the application first starts
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import java.awt.event.ActionEvent;
|
|||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.SocketException;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
@@ -40,6 +41,7 @@ public class MainWindow extends JFrame implements ActionListener, ProgressListen
|
|||||||
private String mCurrentFile;
|
private String mCurrentFile;
|
||||||
private UncaughtExceptionHandler mExceptionReport = new UncaughtExceptionHandler(this.getClass());
|
private UncaughtExceptionHandler mExceptionReport = new UncaughtExceptionHandler(this.getClass());
|
||||||
private JLabel orderCountLabel;
|
private JLabel orderCountLabel;
|
||||||
|
private JLabel mWarningLabel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new MainWindow
|
* Creates a new MainWindow
|
||||||
@@ -89,6 +91,9 @@ public class MainWindow extends JFrame implements ActionListener, ProgressListen
|
|||||||
mLoadFileButton = new JButton("Load File", new ImageIcon(getClass().getResource("/database_add.png")));
|
mLoadFileButton = new JButton("Load File", new ImageIcon(getClass().getResource("/database_add.png")));
|
||||||
mLoadFileButton.addActionListener(this);
|
mLoadFileButton.addActionListener(this);
|
||||||
|
|
||||||
|
|
||||||
|
mWarningLabel = new JLabel("");
|
||||||
|
|
||||||
//order count labels
|
//order count labels
|
||||||
try {
|
try {
|
||||||
panel.add(new JLabel("Total Orders in the database:"));
|
panel.add(new JLabel("Total Orders in the database:"));
|
||||||
@@ -96,10 +101,21 @@ public class MainWindow extends JFrame implements ActionListener, ProgressListen
|
|||||||
panel.add(orderCountLabel);
|
panel.add(orderCountLabel);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
mWarningLabel.setText(e.getMessage());
|
||||||
|
mWarningLabel.setVisible(true);
|
||||||
|
} catch (SocketException e) {
|
||||||
|
mWarningLabel.setText(e.getMessage());
|
||||||
|
mWarningLabel.setVisible(true);
|
||||||
|
} catch (NullPointerException e){
|
||||||
|
mWarningLabel.setText("Database not avilable");
|
||||||
|
mWarningLabel.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
panel.add(mLoadFileButton);
|
panel.add(mLoadFileButton);
|
||||||
|
|
||||||
|
panel.add(mWarningLabel);
|
||||||
|
|
||||||
|
|
||||||
//progress bar
|
//progress bar
|
||||||
mProgressBar = new JProgressBar();
|
mProgressBar = new JProgressBar();
|
||||||
mProgressBar.setStringPainted(true);
|
mProgressBar.setStringPainted(true);
|
||||||
@@ -172,6 +188,8 @@ public class MainWindow extends JFrame implements ActionListener, ProgressListen
|
|||||||
orderCountLabel.setText(db.getCount()+"");
|
orderCountLabel.setText(db.getCount()+"");
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
} catch (SocketException e) {
|
||||||
|
mWarningLabel.setText(e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
package com.TwentyCodes.java.OrderProcessor.UI;
|
package com.TwentyCodes.java.OrderProcessor.UI;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
@@ -19,7 +20,6 @@ import javax.swing.text.StyledDocument;
|
|||||||
|
|
||||||
import com.TwentyCodes.java.OrderProcessor.Order;
|
import com.TwentyCodes.java.OrderProcessor.Order;
|
||||||
import com.TwentyCodes.java.OrderProcessor.ProgressListener;
|
import com.TwentyCodes.java.OrderProcessor.ProgressListener;
|
||||||
import com.TwentyCodes.java.OrderProcessor.Status;
|
|
||||||
import com.TwentyCodes.java.OrderProcessor.TextStyle;
|
import com.TwentyCodes.java.OrderProcessor.TextStyle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,36 +50,53 @@ public class OrderPane extends JScrollPane {
|
|||||||
* @author ricky barrette
|
* @author ricky barrette
|
||||||
*/
|
*/
|
||||||
public void displayOrders(ArrayList<Order> list) {
|
public void displayOrders(ArrayList<Order> list) {
|
||||||
|
ArrayList<String> styles = new ArrayList<String>();
|
||||||
mOutput.setText(null);
|
mOutput.setText(null);
|
||||||
float possible = 0;
|
BigDecimal possible = new BigDecimal(0);
|
||||||
float actual = 0;
|
BigDecimal actual = new BigDecimal(0);
|
||||||
int total = 0;
|
int total = 0;
|
||||||
for (int i = 0; i < list.size(); i++) {
|
for (Order item : list) {
|
||||||
if(mListener != null)
|
if(mListener != null)
|
||||||
mListener.onProgressUpdate("Processing Order: "+ i +" of "+ list.size(), false, i, list.size());
|
mListener.onProgressUpdate("Processing Order: "+ (total+1) +" of "+ list.size(), false, total, list.size());
|
||||||
switch (list.get(i).getFinancialStatus()) {
|
|
||||||
case CANCELLED:
|
switch (item.getFulfillmentStatus()) {
|
||||||
updateTextPane(list.get(i).toString(), TextStyle.RED);
|
case WILL_NOT_DELIVER:
|
||||||
break;
|
styles.add(TextStyle.RED.toString());
|
||||||
case CANCELLED_BY_GOOGLE:
|
|
||||||
updateTextPane(list.get(i).toString(), TextStyle.RED);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
updateTextPane(list.get(i).toString(), TextStyle.REGULAR);
|
styles.add(TextStyle.REGULAR.toString());
|
||||||
|
total++;
|
||||||
}
|
}
|
||||||
|
|
||||||
possible = possible + list.get(i).getOrderAmount();
|
possible = possible.add(new BigDecimal(item.getOrderAmount()));
|
||||||
actual = actual + list.get(i).getAmountCharged();
|
actual = actual.add(new BigDecimal(item.getAmountCharged()));
|
||||||
if (list.get(i).getFulfillmentStatus() == Status.DELIVERED)
|
|
||||||
total++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTextPane("Possible sales: $" + possible, TextStyle.BOLD);
|
updateTextPane(list, styles);
|
||||||
updateTextPane("Actual sales: $" + actual, TextStyle.BOLD);
|
updateTextPane("\nPossible sales: $" + possible.setScale(2, BigDecimal.ROUND_DOWN) + "\nActual sales: $" + actual.setScale(2, BigDecimal.ROUND_DOWN) + "\nPossible sold:" + list.size() + "\nTotal sold: " + total, TextStyle.BOLD);
|
||||||
updateTextPane("Possible sold:" + list.size(), TextStyle.BOLD);
|
|
||||||
updateTextPane("Total sold: " + total, TextStyle.BOLD);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* addeds the orders to the text pane
|
||||||
|
* @param list of orders
|
||||||
|
* @param styles for the orders
|
||||||
|
* @author ricky barrette
|
||||||
|
*/
|
||||||
|
private void updateTextPane(ArrayList<Order> list, ArrayList<String> styles) {
|
||||||
|
StyledDocument doc = mOutput.getStyledDocument();
|
||||||
|
addStylesToDocument(doc);
|
||||||
|
// Load the text pane with styled text.
|
||||||
|
try {
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
doc.insertString(doc.getLength(), "\n"+list.get(i).toString(),
|
||||||
|
doc.getStyle(styles.get(i)));
|
||||||
|
}
|
||||||
|
} catch (BadLocationException ble) {
|
||||||
|
System.err.println("Couldn't insert initial text into text pane.");
|
||||||
|
}
|
||||||
|
mOutput.setDocument(doc);
|
||||||
|
}
|
||||||
|
|
||||||
public String getText(){
|
public String getText(){
|
||||||
return mOutput.getText();
|
return mOutput.getText();
|
||||||
}
|
}
|
||||||
@@ -132,6 +149,7 @@ public class OrderPane extends JScrollPane {
|
|||||||
|
|
||||||
s = doc.addStyle("RED", regular);
|
s = doc.addStyle("RED", regular);
|
||||||
StyleConstants.setForeground(s, Color.RED);
|
StyleConstants.setForeground(s, Color.RED);
|
||||||
|
StyleConstants.setBold(s, true);
|
||||||
|
|
||||||
s = doc.addStyle("GREEN", regular);
|
s = doc.addStyle("GREEN", regular);
|
||||||
StyleConstants.setForeground(s, Color.GREEN);
|
StyleConstants.setForeground(s, Color.GREEN);
|
||||||
@@ -146,4 +164,13 @@ public class OrderPane extends JScrollPane {
|
|||||||
public void setOnProgressListerner(ProgressListener listener) {
|
public void setOnProgressListerner(ProgressListener listener) {
|
||||||
mListener = listener;
|
mListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sets text of the order pane
|
||||||
|
* @param message
|
||||||
|
* @author ricky barrette
|
||||||
|
*/
|
||||||
|
public void setText(String message) {
|
||||||
|
mOutput.setText(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -14,6 +14,7 @@ import java.awt.event.MouseListener;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.SocketException;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
@@ -78,7 +79,7 @@ public class SearchPanel extends JPanel implements ActionListener, ProgressListe
|
|||||||
add(panel, BorderLayout.NORTH);
|
add(panel, BorderLayout.NORTH);
|
||||||
|
|
||||||
//search string
|
//search string
|
||||||
panel.add(new JLabel("Enter a Product name, Customer Name, or Google Order Number:"));
|
panel.add(new JLabel("Search:"));
|
||||||
mTextField = new JTextField();
|
mTextField = new JTextField();
|
||||||
mTextField.setColumns(20);
|
mTextField.setColumns(20);
|
||||||
mTextField.addActionListener(this);
|
mTextField.addActionListener(this);
|
||||||
@@ -140,14 +141,19 @@ public class SearchPanel extends JPanel implements ActionListener, ProgressListe
|
|||||||
if(e.getSource() == mExportButton)
|
if(e.getSource() == mExportButton)
|
||||||
exportToCSV();
|
exportToCSV();
|
||||||
else
|
else
|
||||||
if(! mStartDateField.getText().isEmpty() && !mEndDateField.getText().isEmpty())
|
if(! mStartDateField.getText().isEmpty() && !mEndDateField.getText().isEmpty()){
|
||||||
|
mProgressBar.setIndeterminate(true);
|
||||||
try {
|
try {
|
||||||
preformSearch(mTextField.getText(), new Date(mStartDateField.getText()), new Date(mEndDateField.getText()));
|
preformSearch(mTextField.getText(), new Date(mStartDateField.getText()), new Date(mEndDateField.getText()));
|
||||||
} catch (InvalidDateFormatException e1) {
|
} catch (InvalidDateFormatException e1) {
|
||||||
e1.printStackTrace();
|
e1.printStackTrace();
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
preformSearch(mTextField.getText(), null, null);
|
else{
|
||||||
|
mProgressBar.setIndeterminate(true);
|
||||||
|
preformSearch(mTextField.getText(), null, null);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -273,6 +279,8 @@ public class SearchPanel extends JPanel implements ActionListener, ProgressListe
|
|||||||
mProgressBar.setValue(progress + MainWindow.db.getCount());
|
mProgressBar.setValue(progress + MainWindow.db.getCount());
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
} catch (SocketException e) {
|
||||||
|
mOrderPanel.setText(e.getMessage());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mProgressBar.setValue(progress);
|
mProgressBar.setValue(progress);
|
||||||
|
|||||||
@@ -49,7 +49,12 @@ public class ShowAllPanel extends JPanel implements ActionListener, ProgressList
|
|||||||
add(ok, BorderLayout.NORTH);
|
add(ok, BorderLayout.NORTH);
|
||||||
mProgressBar.setString("Loading from Database");
|
mProgressBar.setString("Loading from Database");
|
||||||
mProgressBar.setStringPainted(true);
|
mProgressBar.setStringPainted(true);
|
||||||
MainWindow.db.setOnProgressListerner(this);
|
try {
|
||||||
|
MainWindow.db.setOnProgressListerner(this);
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
mOutput.setText("Database not initialized");
|
||||||
|
}
|
||||||
frame.pack();
|
frame.pack();
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user