shipping complete
This commit is contained in:
@@ -100,7 +100,7 @@ app.get('/add/:id/:sku/:qty', (req, res) => {
|
||||
cart.items = list;
|
||||
cart.total = calcTotal(cart.items);
|
||||
// work out tax @ 20%
|
||||
cart.tax = (cart.total - (cart.total / 1.2)).toFixed(2);
|
||||
cart.tax = (cart.total - (cart.total / 1.2));
|
||||
|
||||
// save the new cart
|
||||
saveCart(req.params.id, cart);
|
||||
@@ -159,6 +159,45 @@ app.get('/update/:id/:sku/:qty', (req, res) => {
|
||||
});
|
||||
});
|
||||
|
||||
// add shipping
|
||||
app.post('/shipping/:id', (req, res) => {
|
||||
var shipping = req.body;
|
||||
if(shipping.distance === undefined || shipping.cost === undefined || shipping.location == undefined) {
|
||||
console.log('bad shipping data', shipping);
|
||||
res.status(400).send('shipping data missing');
|
||||
} else {
|
||||
// get the cart
|
||||
redisClient.get(req.params.id, (err, data) => {
|
||||
if(err) {
|
||||
console.log('ERROR', err);
|
||||
res.status(500).send(err);
|
||||
} else {
|
||||
if(data == null) {
|
||||
console.log('no cart for', req.params.id);
|
||||
res.status(404).send('cart not found');
|
||||
} else {
|
||||
var cart = JSON.parse(data);
|
||||
var item = {
|
||||
qty: 1,
|
||||
sku: 'SHIP',
|
||||
name: 'shipping to ' + shipping.location,
|
||||
price: shipping.cost,
|
||||
subtotal: shipping.cost
|
||||
};
|
||||
cart.items.push(item);
|
||||
cart.total = calcTotal(cart.items);
|
||||
// work out tax @ 20%
|
||||
cart.tax = (cart.total - (cart.total / 1.2));
|
||||
|
||||
// save the updated cart
|
||||
saveCart(req.params.id, cart);
|
||||
res.json(cart);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function mergeList(list, product, qty) {
|
||||
var inlist = false;
|
||||
// loop through looking for sku
|
||||
|
@@ -6,7 +6,8 @@ db.products.insertMany([
|
||||
{sku: 'PB-1', name: 'Positronic Brain', description: 'Highly advanced sentient processing unit', price: 42000, instock: 0, categories: ['components']},
|
||||
{sku: 'SVO-980', name: 'Servo 980Nm', description: 'Servo actuator with 980Nm of torque. Needs 24V 10A supply', price: 50, instock: 32, categories: ['components']},
|
||||
{sku: 'ROB-1', name: 'Robbie', description: 'Large mechanical workhorse, crude but effective', price: 1200, instock: 12, categories: ['complete']},
|
||||
{sku: 'EVE-1', name: 'Eve', description: 'Extraterrestrial Vegetation Evaluator', price: 5000, instock: 10, categories: ['complete']}
|
||||
{sku: 'EVE-1', name: 'Eve', description: 'Extraterrestrial Vegetation Evaluator', price: 5000, instock: 10, categories: ['complete']},
|
||||
{sku: 'HAL-1', name: 'HAL', description: 'Sorry Dave, I cant do that', price: 7500, instock: 2, categories: ['complete']}
|
||||
]);
|
||||
|
||||
// full text index for searching
|
||||
|
@@ -42,6 +42,11 @@
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.8.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.5</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@@ -20,7 +20,7 @@ public class Location {
|
||||
/**
|
||||
* Calculate the distance in km between this location and the given coordinates
|
||||
**/
|
||||
public double getDistance(double targetLatitude, double targetLongitude) {
|
||||
public long getDistance(double targetLatitude, double targetLongitude) {
|
||||
double distance = 0.0;
|
||||
|
||||
double diffLat = Math.pow(this.latitude - targetLatitude, 2);
|
||||
@@ -30,6 +30,6 @@ public class Location {
|
||||
// 1 nautical mile == 1.852km
|
||||
distance = distance * 60.0 / 1.852;
|
||||
|
||||
return Math.round(distance);
|
||||
return (long)(Math.round(distance));
|
||||
}
|
||||
}
|
||||
|
@@ -6,11 +6,20 @@ import spark.Spark;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.params.BasicHttpParams;
|
||||
import org.apache.http.params.HttpConnectionParams;
|
||||
import org.apache.http.params.HttpParams;
|
||||
import org.apache.commons.dbutils.QueryRunner;
|
||||
import org.apache.commons.dbutils.handlers.MapListHandler;
|
||||
import org.apache.commons.dbutils.DbUtils;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.sql.Connection;
|
||||
import java.sql.Statement;
|
||||
import java.sql.ResultSet;
|
||||
@@ -21,6 +30,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Main {
|
||||
private static String CART_URL = "http://cart:8080/shipping/";
|
||||
private static Logger logger = LoggerFactory.getLogger(Main.class);
|
||||
private static ComboPooledDataSource cpds = null;
|
||||
|
||||
@@ -53,8 +63,7 @@ public class Main {
|
||||
Spark.get("/count", (req, res) -> {
|
||||
String data;
|
||||
try {
|
||||
Connection conn = cpds.getConnection();
|
||||
data = queryToJson(conn, "select count(*) as count from cities");
|
||||
data = queryToJson("select count(*) as count from cities");
|
||||
res.header("Content-Type", "application/json");
|
||||
} catch(Exception e) {
|
||||
logger.error("count", e);
|
||||
@@ -68,9 +77,8 @@ public class Main {
|
||||
Spark.get("/codes", (req, res) -> {
|
||||
String data;
|
||||
try {
|
||||
Connection conn = cpds.getConnection();
|
||||
String query = "select code, name from codes order by name asc";
|
||||
data = queryToJson(conn, query);
|
||||
data = queryToJson(query);
|
||||
res.header("Content-Type", "application/json");
|
||||
} catch(Exception e) {
|
||||
logger.error("codes", e);
|
||||
@@ -84,10 +92,9 @@ public class Main {
|
||||
Spark.get("/match/:code/:text", (req, res) -> {
|
||||
String data;
|
||||
try {
|
||||
Connection conn = cpds.getConnection();
|
||||
String query = "select uuid, name from cities where country_code ='" + req.params(":code") + "' and city like '" + req.params(":text") + "%' order by name asc limit 10";
|
||||
logger.info("Query " + query);
|
||||
data = queryToJson(conn, query);
|
||||
data = queryToJson(query);
|
||||
res.header("Content-Type", "application/json");
|
||||
} catch(Exception e) {
|
||||
logger.error("match", e);
|
||||
@@ -107,9 +114,10 @@ public class Main {
|
||||
buffer.append('{');
|
||||
Location location = getLocation(req.params(":uuid"));
|
||||
if(location != null) {
|
||||
long distance = location.getDistance(homeLat, homeLong);
|
||||
// charge 0.05 Euro per km
|
||||
double distance = location.getDistance(homeLat, homeLong);
|
||||
double cost = distance * 0.05;
|
||||
// try to avoid rounding errors
|
||||
double cost = ((double)(distance * 5)) / 100.0;
|
||||
buffer.append(write("distance", distance)).append(',');
|
||||
buffer.append(write("cost", cost));
|
||||
} else {
|
||||
@@ -120,9 +128,18 @@ public class Main {
|
||||
return buffer.toString();
|
||||
});
|
||||
|
||||
Spark.post("/confirm", (req, res) -> {
|
||||
logger.info("confirm " + req.body());
|
||||
return "OK";
|
||||
Spark.post("/confirm/:id", (req, res) -> {
|
||||
logger.info("confirm " + req.params(":id") + " - " + req.body());
|
||||
String cart = addToCart(req.params(":id"), req.body());
|
||||
logger.info("new cart " + cart);
|
||||
|
||||
if(cart.equals("")) {
|
||||
res.status(404);
|
||||
} else {
|
||||
res.header("Content-Type", "application/json");
|
||||
}
|
||||
|
||||
return cart;
|
||||
});
|
||||
|
||||
logger.info("Ready");
|
||||
@@ -133,16 +150,15 @@ public class Main {
|
||||
/**
|
||||
* Query to Json - QED
|
||||
**/
|
||||
private static String queryToJson(Connection connection, String query) {
|
||||
private static String queryToJson(String query) {
|
||||
List<Map<String, Object>> listOfMaps = null;
|
||||
try {
|
||||
QueryRunner queryRunner = new QueryRunner();
|
||||
listOfMaps = queryRunner.query(connection, query, new MapListHandler());
|
||||
QueryRunner queryRunner = new QueryRunner(cpds);
|
||||
listOfMaps = queryRunner.query(query, new MapListHandler());
|
||||
} catch (SQLException se) {
|
||||
throw new RuntimeException("Couldn't query the database.", se);
|
||||
} finally {
|
||||
DbUtils.closeQuietly(connection);
|
||||
}
|
||||
|
||||
return new Gson().toJson(listOfMaps);
|
||||
}
|
||||
|
||||
@@ -180,4 +196,40 @@ public class Main {
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
private static String addToCart(String id, String data) {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
|
||||
DefaultHttpClient httpClient = null;
|
||||
try {
|
||||
// set timeout to 5 secs
|
||||
HttpParams httpParams = new BasicHttpParams();
|
||||
HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
|
||||
|
||||
httpClient = new DefaultHttpClient(httpParams);
|
||||
HttpPost postRequest = new HttpPost(CART_URL + id);
|
||||
StringEntity payload = new StringEntity(data);
|
||||
payload.setContentType("application/json");
|
||||
postRequest.setEntity(payload);
|
||||
HttpResponse res = httpClient.execute(postRequest);
|
||||
|
||||
if(res.getStatusLine().getStatusCode() == 200) {
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(res.getEntity().getContent()));
|
||||
String line;
|
||||
while((line = in.readLine()) != null) {
|
||||
buffer.append(line);
|
||||
}
|
||||
} else {
|
||||
logger.warn("Failed with code: " + res.getStatusLine().getStatusCode());
|
||||
}
|
||||
} catch(Exception e) {
|
||||
logger.error("http client exception", e);
|
||||
} finally {
|
||||
if(httpClient != null) {
|
||||
httpClient.getConnectionManager().shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@
|
||||
<tr ng-repeat="item in data.cart.items">
|
||||
<td><input type="number" size="2" min="0" max="10" ng-model="item.qty" ng-change="change(item.sku, item.qty);"/></td>
|
||||
<td>{{ item.name }}</td>
|
||||
<td class="currency">€{{ item.subtotal }}</td>
|
||||
<td class="currency">€{{ item.subtotal.toFixed(2) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3"> </td>
|
||||
@@ -22,12 +22,12 @@
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Tax</td>
|
||||
<td class="currency">€{{ data.cart.tax }}</td>
|
||||
<td class="currency">€{{ data.cart.tax.toFixed(2) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Total</td>
|
||||
<td class="currency">€{{ data.cart.total }}</td>
|
||||
<td class="currency">€{{ data.cart.total.toFixed(2) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
|
@@ -33,7 +33,7 @@
|
||||
Empty
|
||||
</div>
|
||||
<div ng-if="data.cart.total != 0" class="carttotal">
|
||||
€{{ data.cart.total }}
|
||||
€{{ data.cart.total.toFixed(2) }}
|
||||
</div>
|
||||
<h3>Categories</h3>
|
||||
<ul class="products">
|
||||
|
@@ -32,6 +32,9 @@
|
||||
}).when('/shipping', {
|
||||
templateUrl: 'shipping.html',
|
||||
controller: 'shipform'
|
||||
}).when('/payment', {
|
||||
templateUrl: 'payment.html',
|
||||
controller: 'paymentform'
|
||||
});
|
||||
|
||||
// needed for URL rewrite hash
|
||||
@@ -209,7 +212,7 @@
|
||||
loadCart($scope.data.uniqueid);
|
||||
});
|
||||
|
||||
robotshop.controller('shipform', function($scope, $http, currentUser) {
|
||||
robotshop.controller('shipform', function($scope, $http, $location, currentUser) {
|
||||
$scope.data = {};
|
||||
$scope.data.countries = [];
|
||||
$scope.data.selectedCountry = '';
|
||||
@@ -226,6 +229,7 @@
|
||||
}).then((res) => {
|
||||
console.log('shipping data', res.data);
|
||||
$scope.data.shipping = res.data;
|
||||
$scope.data.shipping.location = $scope.data.selectedCountry.name + ' ' + autoLocation;
|
||||
}).catch((e) => {
|
||||
console.log('ERROR', e);
|
||||
});
|
||||
@@ -234,11 +238,15 @@
|
||||
$scope.confirmShipping = function() {
|
||||
console.log('shipping confirmed');
|
||||
$http({
|
||||
url: '/api/shipping/confirm',
|
||||
url: '/api/shipping/confirm/' + currentUser.uniqueid,
|
||||
method: 'POST',
|
||||
data: $scope.data.shipping
|
||||
}).then((res) => {
|
||||
// go to final confirmation
|
||||
console.log('confirm cart', res.data);
|
||||
// save new cart
|
||||
currentUser.cart = res.data;
|
||||
$location.url('/payment');
|
||||
}).catch((e) => {
|
||||
console.log('ERROR', e);
|
||||
});
|
||||
@@ -251,6 +259,7 @@
|
||||
}
|
||||
$scope.data.selectedLocation = '';
|
||||
$scope.data.disableCalc = true;
|
||||
$scope.data.shipping = '';
|
||||
};
|
||||
|
||||
// auto-complete
|
||||
@@ -290,10 +299,10 @@
|
||||
},
|
||||
onSelect: (e, term, item) => {
|
||||
console.log('select', term, item);
|
||||
console.log('content', item.textContent);
|
||||
console.log('attrib', item.getAttribute('loc-uuid'));
|
||||
uuid = item.getAttribute('loc-uuid');
|
||||
autoLocation = item.getAttribute('data-val');
|
||||
$scope.data.disableCalc = false;
|
||||
$scope.data.shipping = '';
|
||||
// synchronise angular
|
||||
$scope.$apply();
|
||||
}
|
||||
@@ -305,6 +314,17 @@
|
||||
buildauto();
|
||||
});
|
||||
|
||||
robotshop.controller('paymentform', function($scope, $http, currentUser) {
|
||||
$scope.data = {};
|
||||
$scope.data.uniqueid = currentUser.uniqueid;
|
||||
$scope.data.cart = currentUser.cart;
|
||||
|
||||
$scope.pay = function() {
|
||||
};
|
||||
|
||||
console.log('paymentform init');
|
||||
});
|
||||
|
||||
robotshop.controller('loginform', function($scope, $http, currentUser) {
|
||||
$scope.data = {};
|
||||
$scope.data.name = '';
|
||||
|
39
web/static/payment.html
Normal file
39
web/static/payment.html
Normal file
@@ -0,0 +1,39 @@
|
||||
<!-- payment template -->
|
||||
<div>
|
||||
<h3>Review your order</h3>
|
||||
<div ng-if="data.cart.total == 0">
|
||||
Your cart is empty, get shopping
|
||||
</div>
|
||||
<div ng-if="data.cart.total != 0">
|
||||
<table>
|
||||
<tr>
|
||||
<th>QTY</th>
|
||||
<th>Name</th>
|
||||
<th>Sub Total</th>
|
||||
</tr>
|
||||
<tr ng-repeat="item in data.cart.items">
|
||||
<td>{{ item.qty }}</td>
|
||||
<td>{{ item.name }}</td>
|
||||
<td class="currency">€{{ item.subtotal.toFixed(2) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3"> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Tax</td>
|
||||
<td class="currency">€{{ data.cart.tax.toFixed(2) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Total</td>
|
||||
<td class="currency">€{{ data.cart.total.toFixed(2) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
<button ng-click="pay();">Pay with Paypal</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
@@ -12,7 +12,7 @@
|
||||
{{ data.product.description }}
|
||||
</div>
|
||||
<div class="productcart">
|
||||
Price €{{ data.product.price }}
|
||||
Price €{{ data.product.price.toFixed(2) }}
|
||||
<span ng-if="data.product.instock == 0">
|
||||
Out of stock
|
||||
</span>
|
||||
|
@@ -12,8 +12,8 @@
|
||||
</div>
|
||||
<!-- add in shipping distance and cost -->
|
||||
<div ng-if="data.shipping">
|
||||
<div>Distance {{ data.shipping.distance }}</div>
|
||||
<div>Cost €{{ data.shipping.cost }}</div>
|
||||
<div>Distance {{ data.shipping.distance }}km</div>
|
||||
<div>Cost €{{ data.shipping.cost.toFixed(2) }}</div>
|
||||
<div>
|
||||
<button ng-click="confirmShipping();">Confirm</button>
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user