diff --git a/api/config.js b/api/config.js new file mode 100644 index 0000000..cc6461b --- /dev/null +++ b/api/config.js @@ -0,0 +1,20 @@ +const router = module.exports = require('express-promise-router')() +const parseJSON = require('body-parser').json() +const db = require('../db') +const ensureAdmin = require('./middleware/ensureAdmin') +const validate = require('./middleware/validators') + +router.get('/', ensureAdmin, async (req, res) => { + const config = await db.config.getLatestConfig() + res.json(config) +}) + +router.post('/address', ensureAdmin, parseJSON, validate.addressWithPhone, async (req, res) => { + const {name, street1, street2, city, state, zip, country, phone} = req.body; + + // Create address, update order + const address = await db.address.create(name, street1, street2, city, state, zip, country, phone) + const config = await db.config.setShippingAddress(address.uuid, req.user.uuid) + + res.json(config) +}) \ No newline at end of file diff --git a/api/index.js b/api/index.js index b4c0a39..0e90ab0 100644 --- a/api/index.js +++ b/api/index.js @@ -26,6 +26,7 @@ router.use('/orders/', require('./orders')) router.use('/shipments/', require('./shipments')) router.use('/email/', require('./email')) router.use('/coupons/', require('./coupons')) +router.use('/config/', require('./config')) router.use((req, res, next)=>{ const err = new Error('Not found') diff --git a/api/middleware/validators.js b/api/middleware/validators.js index a5f3fa9..36912e1 100644 --- a/api/middleware/validators.js +++ b/api/middleware/validators.js @@ -46,6 +46,11 @@ validators.address = [ body('zip').isString().isLength({min: 1}).withMessage('Postal code is required') ] +validators.addressWithPhone = [ + ...validators.address, + body('phone').isString().isLength({min: 1}).withMessage('Phone is required') +] + validators.coupon = [ body('code').isString().isLength({min: 3, max: 50}).withMessage('Coupon code is required') ] diff --git a/api/orders.js b/api/orders.js index 5a07e34..2921970 100644 --- a/api/orders.js +++ b/api/orders.js @@ -74,7 +74,7 @@ router.post('/current/address', ensureCart, parseJSON, validate.address, validat const {name, street1, street2, city, state, zip, country} = req.body; // Create address, update order - const address = await db.address.create(name, street1, street2, city, state, zip, country) + const address = await db.address.create(name, street1, street2, city, state, zip, country, null) const order = await db.order.addAddress(currentTransaction, address) res.json(order) diff --git a/components/admin/nav/nav.js b/components/admin/nav/nav.js index 52acca6..aaa7afd 100644 --- a/components/admin/nav/nav.js +++ b/components/admin/nav/nav.js @@ -25,6 +25,7 @@ export default function AdminNav({children}){ Users Shipments Coupons + Config
{children} diff --git a/db/models/address.js b/db/models/address.js index 76f437b..b916bc7 100644 --- a/db/models/address.js +++ b/db/models/address.js @@ -8,9 +8,9 @@ const easypost = new (require('@easypost/api'))(process.env.EASYPOST_API_KEY); const address = module.exports = {} -address.create = async (name, street1, street2, city, state, zip, country) => { +address.create = async (name, street1, street2, city, state, zip, country, phone) => { const epAddress = new easypost.Address({ - name, street1, street2, city, state, zip, country, + name, street1, street2, city, state, zip, country, phone, verify: ['delivery'] }) diff --git a/db/models/config.js b/db/models/config.js index 71f9785..e935d8d 100644 --- a/db/models/config.js +++ b/db/models/config.js @@ -2,6 +2,7 @@ const pg = require('../pg') const joinjs = require('join-js').default; const debug = require('debug')('sos:db:config') const mappings = require('../mappings') +const dbUtil = require('../util') const config = module.exports = {} @@ -15,3 +16,11 @@ config.getTaxRate = async () => { const current = await config.getLatestConfig() return parseFloat(current.default_tax_percent) } + +config.setShippingAddress = async (address_uuid, user_uuid) => + dbUtil.executeFunction({ + name: 'set_shipping_source', + params: [address_uuid, user_uuid], + returnType: 'config', + single: true, + }) \ No newline at end of file diff --git a/db/sql/3-functions.sql b/db/sql/3-functions.sql index 8300aee..3327ed0 100644 --- a/db/sql/3-functions.sql +++ b/db/sql/3-functions.sql @@ -1228,3 +1228,26 @@ begin return query select * from sos.v_user where user_uuid = _user_uuid; end; $function$; + +create or replace function sos.set_shipping_source(_address_uuid uuid, _user_uuid uuid) + returns setof sos.v_config + language plpgsql +as $function$ +declare + _tax_percent numeric(8,6); +begin + select config_default_tax_percent into _tax_percent + from sos.v_config; + + insert into sos.config ( + config_default_tax_percent, + config_shipping_from, + config_updated_by + ) values ( + _tax_percent, + _address_uuid, + _user_uuid + ); + + return query select * from sos.v_config; +end; $function$; \ No newline at end of file diff --git a/pages/admin/config/index.js b/pages/admin/config/index.js new file mode 100644 index 0000000..a9c0fc9 --- /dev/null +++ b/pages/admin/config/index.js @@ -0,0 +1,48 @@ +import router from 'next/router' +import Link from 'next/link' +import ActionBar from '~/components/admin/actionBar' +import {DateTime} from 'luxon' +import Table from '~/components/table' + + +AdminConfig.getInitialProps = async ({ctx}) => { + const {data: config} = await ctx.axios.get('/api/config') + return {config} +} + +export default function AdminConfig({config}) { + return ( + <> + + config.name}, + {name: 'Value', extractor: config => config.value}, + {name: '', extractor: config => + config.href && ( + Edit + ) + } + ]} + rows={[ + {id: 'tax', name: 'UT Tax Percent', value: parseFloat(config.default_tax_percent) + '%'}, + {id: 'address', name: 'Shipping Source Address', value: (() => { + if(!config.shipping_from) + return 'Unset' + + const {name, street1} = config.shipping_from + return `${name}, ${street1}`.slice(0, 20) + '...' + })(), href: '/admin/config/shipping'}, + ]} + /> + {config.modified_by && ( +

+ Config last modified on + {' ' + DateTime.fromISO(config.date_updated).setZone('local').toFormat('LLL dd, yyyy') + ' '} + by {config.modified_by.email} +

+ )} + + + ) +} \ No newline at end of file diff --git a/pages/admin/config/shipping.js b/pages/admin/config/shipping.js new file mode 100644 index 0000000..8b6bff6 --- /dev/null +++ b/pages/admin/config/shipping.js @@ -0,0 +1,32 @@ +import router from 'next/router' + +import ActionBar from '~/components/admin/actionBar' +import {FormController, Input, Button} from '~/components/form' + +ShippingFrom.getInitialProps = async ({ctx: {axios}}) => { + const {data: {shipping_from: address}} = await axios.get(`/api/config`) + return {address} +} + +export default function ShippingFrom({address}) { + const afterSave = () => { + router.push(`/admin/config`) + } + + return ( + <> + + + value.length > 0}/> + value.length > 0}/> + + value.length > 0}/> + value.length > 0}/> + value.length > 0}/> + value.length > 0}/> + value.length > 0}/> + + + + ) +}