Add very basic mobile app support for Theo
much more to come yet, but should be enough to launch a demo
This commit is contained in:
parent
774221420c
commit
8c738d3ee8
3
mobile/.browserslistrc
Normal file
3
mobile/.browserslistrc
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
> 1%
|
||||||
|
last 2 versions
|
||||||
|
not dead
|
29
mobile/.gitignore
vendored
Normal file
29
mobile/.gitignore
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
/dist
|
||||||
|
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|
||||||
|
|
||||||
|
# custom entries follow
|
||||||
|
|
||||||
|
vue.config.js
|
||||||
|
src/appsettings.js
|
19
mobile/README.md
Normal file
19
mobile/README.md
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# mobile
|
||||||
|
|
||||||
|
## Project setup
|
||||||
|
```
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compiles and hot-reloads for development
|
||||||
|
```
|
||||||
|
npm run serve
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compiles and minifies for production
|
||||||
|
```
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Customize configuration
|
||||||
|
See [Configuration Reference](https://cli.vuejs.org/config/).
|
5
mobile/babel.config.js
Normal file
5
mobile/babel.config.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
'@vue/cli-plugin-babel/preset'
|
||||||
|
]
|
||||||
|
}
|
11346
mobile/package-lock.json
generated
Normal file
11346
mobile/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
26
mobile/package.json
Normal file
26
mobile/package.json
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"name": "theo-mobile",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"serve": "vue-cli-service serve",
|
||||||
|
"build": "vue-cli-service build"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"buefy": "^0.9.4",
|
||||||
|
"byjove": "^0.1.9",
|
||||||
|
"core-js": "^3.6.5",
|
||||||
|
"js-cookie": "^2.2.1",
|
||||||
|
"vue": "^2.6.11",
|
||||||
|
"vue-resource": "^1.5.1",
|
||||||
|
"vue-router": "^3.2.0",
|
||||||
|
"vuex": "^3.4.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vue/cli-plugin-babel": "~4.5.0",
|
||||||
|
"@vue/cli-plugin-router": "~4.5.0",
|
||||||
|
"@vue/cli-plugin-vuex": "~4.5.0",
|
||||||
|
"@vue/cli-service": "~4.5.0",
|
||||||
|
"vue-template-compiler": "^2.6.11"
|
||||||
|
}
|
||||||
|
}
|
BIN
mobile/public/favicon.ico
Normal file
BIN
mobile/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
18
mobile/public/index.html
Normal file
18
mobile/public/index.html
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
|
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.2/css/all.css">
|
||||||
|
<title>Theo Mobile</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<strong>We're sorry but Theo Mobile doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||||
|
</noscript>
|
||||||
|
<div id="app"></div>
|
||||||
|
<!-- built files will be auto injected -->
|
||||||
|
</body>
|
||||||
|
</html>
|
122
mobile/src/App.vue
Normal file
122
mobile/src/App.vue
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
<template>
|
||||||
|
<div id="app">
|
||||||
|
<byjove-app :appsettings="appsettings">
|
||||||
|
<app-nav></app-nav>
|
||||||
|
<template v-slot:footer>
|
||||||
|
<router-link to="/about">{{ appsettings.appTitle}} {{ appsettings.version }}</router-link>
|
||||||
|
<div>
|
||||||
|
<br />
|
||||||
|
<a href="/">View Desktop Site</a>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</byjove-app>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {ByjoveApp} from 'byjove'
|
||||||
|
import AppNav from './components/AppNav.vue'
|
||||||
|
import appsettings from './appsettings'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'app',
|
||||||
|
components: {
|
||||||
|
ByjoveApp,
|
||||||
|
AppNav,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
appsettings: appsettings,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
/******************************
|
||||||
|
* main app layout
|
||||||
|
******************************/
|
||||||
|
|
||||||
|
#app {
|
||||||
|
font-family: 'Avenir', Helvetica, Arial, sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
color: #2c3e50;
|
||||||
|
}
|
||||||
|
.main-container {
|
||||||
|
display: flex;
|
||||||
|
min-height: 100vh;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.page-content {
|
||||||
|
flex: 1;
|
||||||
|
padding: 0.5rem 1rem 0.5rem 0.5rem;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************
|
||||||
|
* general stuff
|
||||||
|
******************************/
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************
|
||||||
|
* root-user
|
||||||
|
******************************/
|
||||||
|
|
||||||
|
[role=menuitem].root-user {
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************
|
||||||
|
* model-index
|
||||||
|
******************************/
|
||||||
|
|
||||||
|
.model-index .menu {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-index .menu-list li {
|
||||||
|
border-bottom: 1px solid #4a4a4a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-index .menu-list li:last-child {
|
||||||
|
border: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************
|
||||||
|
* model-crud
|
||||||
|
******************************/
|
||||||
|
|
||||||
|
.model-crud .menu {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-crud .menu-list li {
|
||||||
|
border-bottom: 1px solid #4a4a4a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-crud .menu-list li:last-child {
|
||||||
|
border: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
14
mobile/src/appsettings.js.dist
Normal file
14
mobile/src/appsettings.js.dist
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// -*- mode: js; -*-
|
||||||
|
|
||||||
|
import packageData from '../package.json'
|
||||||
|
|
||||||
|
var appsettings = {
|
||||||
|
systemTitle: "Theo",
|
||||||
|
appTitle: "Theo-Mobile",
|
||||||
|
version: packageData.version,
|
||||||
|
logo: '/tailbone/img/home_logo.png',
|
||||||
|
production: false,
|
||||||
|
watermark: 'url("/tailbone/img/testing.png")',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default appsettings;
|
BIN
mobile/src/assets/logo.png
Normal file
BIN
mobile/src/assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
40
mobile/src/components/AppNav.vue
Normal file
40
mobile/src/components/AppNav.vue
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<template>
|
||||||
|
<byjove-menu :appsettings="appsettings">
|
||||||
|
|
||||||
|
<b-dropdown-item aria-role="menuitem" has-link>
|
||||||
|
<router-link to="/">Home</router-link>
|
||||||
|
</b-dropdown-item>
|
||||||
|
|
||||||
|
<b-dropdown-item v-if="$hasPerm('ordering.list')"
|
||||||
|
aria-role="menuitem"
|
||||||
|
has-link>
|
||||||
|
<router-link to="/ordering/">Ordering</router-link>
|
||||||
|
</b-dropdown-item>
|
||||||
|
|
||||||
|
</byjove-menu>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {ByjoveMenu} from 'byjove'
|
||||||
|
import appsettings from '../appsettings'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AppNav',
|
||||||
|
components: {
|
||||||
|
ByjoveMenu,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
appsettings: appsettings,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
user: function() {
|
||||||
|
return this.$store.state.user
|
||||||
|
},
|
||||||
|
user_is_root: function() {
|
||||||
|
return this.$store.state.user_is_root
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
34
mobile/src/main.js
Normal file
34
mobile/src/main.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import Vue from 'vue'
|
||||||
|
import App from './App.vue'
|
||||||
|
import router from './router'
|
||||||
|
import store from './store'
|
||||||
|
import vueResource from 'vue-resource'
|
||||||
|
import Cookie from 'js-cookie'
|
||||||
|
import Buefy from 'buefy'
|
||||||
|
import 'buefy/dist/buefy.css'
|
||||||
|
import {ByjovePlugin} from 'byjove'
|
||||||
|
|
||||||
|
Vue.config.productionTip = false
|
||||||
|
|
||||||
|
Vue.use(vueResource)
|
||||||
|
|
||||||
|
// the backend API will set a cookie for XSRF-TOKEN, which we will submit
|
||||||
|
// *back* to the backend API whenever we call it from then on. we set this up
|
||||||
|
// globally so none of our API calls actually have to mess with it
|
||||||
|
Vue.http.interceptors.push((request, next) => {
|
||||||
|
request.headers.set('X-XSRF-TOKEN', Cookie.get('XSRF-TOKEN'))
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
|
Vue.use(Buefy, {
|
||||||
|
// use FontAwesome icon pack
|
||||||
|
defaultIconPack: 'fas',
|
||||||
|
})
|
||||||
|
|
||||||
|
Vue.use(ByjovePlugin)
|
||||||
|
|
||||||
|
new Vue({
|
||||||
|
router,
|
||||||
|
store,
|
||||||
|
render: h => h(App)
|
||||||
|
}).$mount('#app')
|
80
mobile/src/router/index.js
Normal file
80
mobile/src/router/index.js
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
import Vue from 'vue'
|
||||||
|
import VueRouter from 'vue-router'
|
||||||
|
import Home from '../views/Home.vue'
|
||||||
|
import Login from '../views/Login.vue'
|
||||||
|
import {OrderingBatches, OrderingBatch, OrderingBatchRow, OrderingBatchWorksheet} from '../views/ordering'
|
||||||
|
|
||||||
|
Vue.use(VueRouter)
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
name: 'Home',
|
||||||
|
component: Home
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/login',
|
||||||
|
name: 'login',
|
||||||
|
component: Login
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/about',
|
||||||
|
name: 'About',
|
||||||
|
// route level code-splitting
|
||||||
|
// this generates a separate chunk (about.[hash].js) for this route
|
||||||
|
// which is lazy-loaded when the route is visited.
|
||||||
|
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
|
||||||
|
},
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
// Ordering
|
||||||
|
//////////////////////////////
|
||||||
|
{
|
||||||
|
path: '/ordering/',
|
||||||
|
name: 'ordering',
|
||||||
|
component: OrderingBatches,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/ordering/new',
|
||||||
|
name: 'ordering.new',
|
||||||
|
component: OrderingBatch,
|
||||||
|
props: {mode: 'creating'},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/ordering/:uuid',
|
||||||
|
name: 'ordering.view',
|
||||||
|
component: OrderingBatch,
|
||||||
|
props: {mode: 'viewing'},
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// path: '/ordering/:uuid/edit',
|
||||||
|
// name: 'ordering.edit',
|
||||||
|
// component: OrderingBatch,
|
||||||
|
// props: {mode: 'editing'},
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
path: '/ordering/rows/:uuid',
|
||||||
|
name: 'ordering.rows.view',
|
||||||
|
component: OrderingBatchRow,
|
||||||
|
props: {mode: 'viewing'},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/ordering/rows/:uuid/edit',
|
||||||
|
name: 'ordering.rows.edit',
|
||||||
|
component: OrderingBatchRow,
|
||||||
|
props: {mode: 'editing'},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/ordering/:uuid/worksheet',
|
||||||
|
name: 'ordering.worksheet',
|
||||||
|
component: OrderingBatchWorksheet,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const router = new VueRouter({
|
||||||
|
mode: 'history',
|
||||||
|
base: process.env.BASE_URL,
|
||||||
|
routes
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
7
mobile/src/store/index.js
Normal file
7
mobile/src/store/index.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import Vue from 'vue'
|
||||||
|
import Vuex from 'vuex'
|
||||||
|
import {ByjoveStoreConfig} from 'byjove'
|
||||||
|
|
||||||
|
Vue.use(Vuex)
|
||||||
|
|
||||||
|
export default new Vuex.Store(ByjoveStoreConfig)
|
5
mobile/src/views/About.vue
Normal file
5
mobile/src/views/About.vue
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<template>
|
||||||
|
<div class="about">
|
||||||
|
<h1>This is an about page</h1>
|
||||||
|
</div>
|
||||||
|
</template>
|
29
mobile/src/views/Home.vue
Normal file
29
mobile/src/views/Home.vue
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<template>
|
||||||
|
<div class="home-page">
|
||||||
|
<byjove-logo :appsettings="appsettings"></byjove-logo>
|
||||||
|
<h2>Welcome to {{ appsettings.appTitle }}</h2>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import appsettings from '@/appsettings'
|
||||||
|
import {ByjoveLogo} from 'byjove'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'home',
|
||||||
|
components: {
|
||||||
|
ByjoveLogo,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
appsettings: appsettings,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.home-page {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
92
mobile/src/views/Login.vue
Normal file
92
mobile/src/views/Login.vue
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
<template>
|
||||||
|
<div class="login">
|
||||||
|
|
||||||
|
<byjove-logo :appsettings="appsettings"></byjove-logo>
|
||||||
|
|
||||||
|
<div v-if="loginError" style="background-color: red;">
|
||||||
|
{{ loginError }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form>
|
||||||
|
<div>
|
||||||
|
<label for="username">Username</label>
|
||||||
|
<input v-model="username" />
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div>
|
||||||
|
<label for="password">Password</label>
|
||||||
|
<input v-model="password" type="password" />
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div>
|
||||||
|
<button type="button" v-on:click="attemptLogin">Login</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import appsettings from '@/appsettings'
|
||||||
|
import {ByjoveLogo} from 'byjove'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Login',
|
||||||
|
components: {
|
||||||
|
ByjoveLogo,
|
||||||
|
},
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
appsettings: appsettings,
|
||||||
|
username: null,
|
||||||
|
password: null,
|
||||||
|
loginError: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeCreate: function() {
|
||||||
|
this.checkUser()
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'$store.state.user': 'checkUser',
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
checkUser() {
|
||||||
|
// send logged-in users to "home" instead
|
||||||
|
if (this.$store.state.user) {
|
||||||
|
this.$router.push('/')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
attemptLogin: function() {
|
||||||
|
this.loginError = null;
|
||||||
|
var creds = {
|
||||||
|
username: this.username,
|
||||||
|
password: this.password
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$http.post('/api/login', creds).then(response => {
|
||||||
|
if (response.data.error) {
|
||||||
|
this.loginError = response.data.error;
|
||||||
|
} else {
|
||||||
|
// let byjove do login proper
|
||||||
|
this.$loginUser(response.data.user, response.data.permissions)
|
||||||
|
// after user logs in, show home page
|
||||||
|
this.$router.push('/')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.login {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
max-height: 200px;
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
</style>
|
256
mobile/src/views/ordering/OrderingBatch.vue
Normal file
256
mobile/src/views/ordering/OrderingBatch.vue
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
<template>
|
||||||
|
<byjove-model-crud model-name="PurchaseBatch"
|
||||||
|
model-index-title="Ordering"
|
||||||
|
model-title="Ordering Batch"
|
||||||
|
model-title-plural="Ordering Batches"
|
||||||
|
model-path-prefix="/ordering"
|
||||||
|
model-permission-prefix="ordering"
|
||||||
|
row-path-prefix="/ordering/rows"
|
||||||
|
:mode="mode"
|
||||||
|
@refresh="record => { batch = record }"
|
||||||
|
api-index-url="/api/ordering-batches"
|
||||||
|
api-object-url="/api/ordering-batch/"
|
||||||
|
:header-label-renderer="renderHeaderLabel"
|
||||||
|
:allow-edit="false"
|
||||||
|
has-rows
|
||||||
|
api-rows-url="/api/ordering-batch-rows"
|
||||||
|
:row-label-renderer="renderRowLabel"
|
||||||
|
:row-route-getter="getRowRoute"
|
||||||
|
save-button-text="Make Ordering Batch"
|
||||||
|
@save="save">
|
||||||
|
|
||||||
|
<div v-if="mode == 'creating'">
|
||||||
|
|
||||||
|
<b-field label="Vendor"
|
||||||
|
:type="{'is-danger': !batch.vendor_uuid}">
|
||||||
|
<byjove-autocomplete v-model="batch.vendor_uuid"
|
||||||
|
service-url="/api/vendors/autocomplete">
|
||||||
|
</byjove-autocomplete>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
</div> <!-- creating -->
|
||||||
|
|
||||||
|
<div v-if="mode == 'viewing'">
|
||||||
|
|
||||||
|
<b-field label="Vendor">
|
||||||
|
<span>
|
||||||
|
{{ batch.vendor_display }}
|
||||||
|
</span>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="Date Ordered"
|
||||||
|
v-if="mode != 'creating' && batch.executed">
|
||||||
|
<span>
|
||||||
|
{{ batch.date_ordered }}
|
||||||
|
</span>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="Total">
|
||||||
|
<span>
|
||||||
|
{{ batch.po_total_calculated_display }}
|
||||||
|
</span>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="Created"
|
||||||
|
v-if="mode != 'creating' && batch.executed">
|
||||||
|
<span>
|
||||||
|
{{ batch.created }}
|
||||||
|
</span>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="Created By"
|
||||||
|
v-if="mode != 'creating' && batch.executed">
|
||||||
|
<span>
|
||||||
|
{{ batch.created_by_display }}
|
||||||
|
</span>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<br v-if="!batch.executed" />
|
||||||
|
|
||||||
|
<div v-if="batch.executed">
|
||||||
|
|
||||||
|
<b-field label="Executed">
|
||||||
|
<span>
|
||||||
|
{{ batch.executed }}
|
||||||
|
</span>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="Executed By">
|
||||||
|
<span>
|
||||||
|
{{ batch.executed_by_display }}
|
||||||
|
</span>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
</div> <!-- executed -->
|
||||||
|
|
||||||
|
</div> <!-- viewing -->
|
||||||
|
|
||||||
|
<template slot="quick-entry">
|
||||||
|
<div v-if="mode == 'viewing' && !batch.executed && !batch.complete">
|
||||||
|
<b-input v-model="quickEntry"
|
||||||
|
placeholder="Enter UPC"
|
||||||
|
icon="search"
|
||||||
|
@keypress.native="quickKey">
|
||||||
|
</b-input>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template slot="footer"
|
||||||
|
v-if="mode == 'viewing' && !batch.executed">
|
||||||
|
<br />
|
||||||
|
<div class="buttons is-centered">
|
||||||
|
<b-button v-if="!batch.complete"
|
||||||
|
type="is-primary"
|
||||||
|
@click="editWorksheet()">
|
||||||
|
Edit as Worksheet
|
||||||
|
</b-button>
|
||||||
|
<b-button v-if="!batch.complete"
|
||||||
|
type="is-primary"
|
||||||
|
@click="markOrderingComplete()">
|
||||||
|
Ordering is Complete!
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</byjove-model-crud>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {ByjoveModelCrud, ByjoveAutocomplete} from 'byjove'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'OrderingBatch',
|
||||||
|
props: {
|
||||||
|
mode: String,
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
ByjoveModelCrud,
|
||||||
|
ByjoveAutocomplete,
|
||||||
|
},
|
||||||
|
data: function() {
|
||||||
|
return {
|
||||||
|
batch: {},
|
||||||
|
quickEntry: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
window.addEventListener('keypress', this.globalKey)
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
window.removeEventListener('keypress', this.globalKey)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
renderHeaderLabel(batch) {
|
||||||
|
return batch.id_str
|
||||||
|
},
|
||||||
|
|
||||||
|
renderRowLabel(row) {
|
||||||
|
return `(${row.cases_ordered_display} / ${row.units_ordered_display}) ${row.full_description}`
|
||||||
|
},
|
||||||
|
|
||||||
|
getRowRoute(row) {
|
||||||
|
return `/ordering/rows/${row.uuid}/edit`
|
||||||
|
},
|
||||||
|
|
||||||
|
save(url) {
|
||||||
|
if (url === undefined) {
|
||||||
|
url = '/api/ordering-batches'
|
||||||
|
}
|
||||||
|
let params = {
|
||||||
|
vendor_uuid: this.batch.vendor_uuid,
|
||||||
|
}
|
||||||
|
this.$http.post(url, params).then(response => {
|
||||||
|
if (response.data.data) {
|
||||||
|
// navigate to new ordering batch
|
||||||
|
this.$router.push('/ordering/' + response.data.data.uuid)
|
||||||
|
} else {
|
||||||
|
this.$buefy.toast.open({
|
||||||
|
message: response.data.error || "Failed to save batch!",
|
||||||
|
type: 'is-danger',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, response => {
|
||||||
|
this.$buefy.toast.open({
|
||||||
|
message: "Failed to save batch!",
|
||||||
|
type: 'is-danger',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
globalKey(event) {
|
||||||
|
if (event.target.tagName == 'BODY') {
|
||||||
|
|
||||||
|
// mimic keyboard wedge
|
||||||
|
if (event.charCode >= 48 && event.charCode <= 57) { // numeric (qwerty)
|
||||||
|
this.$nextTick(function() {
|
||||||
|
this.quickEntry += event.key
|
||||||
|
})
|
||||||
|
|
||||||
|
} else if (event.keyCode == 13) { // enter
|
||||||
|
this.submitQuickEntry()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
quickKey(event) {
|
||||||
|
if (event.keyCode == 13) { // enter
|
||||||
|
this.submitQuickEntry()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
submitQuickEntry() {
|
||||||
|
if (!this.quickEntry) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let params = {
|
||||||
|
batch_uuid: this.batch.uuid,
|
||||||
|
quick_entry: this.quickEntry,
|
||||||
|
}
|
||||||
|
let url = '/api/ordering-batch-rows/quick-entry'
|
||||||
|
this.$http.post(url, params).then(response => {
|
||||||
|
if (response.data.data) {
|
||||||
|
// let user edit row immediately
|
||||||
|
this.$router.push(`/ordering/rows/${response.data.data.uuid}/edit`)
|
||||||
|
} else {
|
||||||
|
this.$buefy.toast.open({
|
||||||
|
message: response.data.error || "Failed to post quick entry!",
|
||||||
|
type: 'is-danger',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, response => {
|
||||||
|
this.$buefy.toast.open({
|
||||||
|
message: "Failed to post quick entry!",
|
||||||
|
type: 'is-danger',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
editWorksheet() {
|
||||||
|
this.$router.push(`/ordering/${this.batch.uuid}/worksheet`)
|
||||||
|
},
|
||||||
|
|
||||||
|
markOrderingComplete() {
|
||||||
|
this.$http.post(`/api/ordering-batch/${this.batch.uuid}/mark-complete`).then(response => {
|
||||||
|
if (response.data.data) {
|
||||||
|
this.batch = response.data.data
|
||||||
|
this.$router.push('/ordering')
|
||||||
|
} else {
|
||||||
|
this.$buefy.toast.open({
|
||||||
|
message: response.data.error || "Failed to mark ordering complete!",
|
||||||
|
type: 'is-danger',
|
||||||
|
position: 'is-bottom',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, response => {
|
||||||
|
this.$buefy.toast.open({
|
||||||
|
message: "Failed to mark ordering complete!",
|
||||||
|
type: 'is-danger',
|
||||||
|
position: 'is-bottom',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
127
mobile/src/views/ordering/OrderingBatchRow.vue
Normal file
127
mobile/src/views/ordering/OrderingBatchRow.vue
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
<template>
|
||||||
|
<byjove-model-crud model-name="OrderingBatchRow"
|
||||||
|
model-index-title="Ordering"
|
||||||
|
:mode="mode"
|
||||||
|
:header-label-renderer="renderHeaderLabel"
|
||||||
|
:parent-header-label-renderer="renderParentHeaderLabel"
|
||||||
|
api-object-url="/api/ordering-batch-row/"
|
||||||
|
model-path-prefix="/ordering"
|
||||||
|
row-path-prefix="/ordering/rows"
|
||||||
|
@refresh="record => { row = record }"
|
||||||
|
is-row
|
||||||
|
@save="save">
|
||||||
|
|
||||||
|
<b-field label="UPC">
|
||||||
|
<span>
|
||||||
|
{{ row.upc_pretty }}
|
||||||
|
</span>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="Description">
|
||||||
|
<span>
|
||||||
|
{{ row.description }}
|
||||||
|
</span>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="Cases Ordered">
|
||||||
|
<b-input v-if="mode == 'editing'"
|
||||||
|
v-model="row.cases_ordered"
|
||||||
|
type="number"
|
||||||
|
ref="casesOrdered"
|
||||||
|
@keypress.native="handleEnter">
|
||||||
|
</b-input>
|
||||||
|
<span v-if="mode == 'viewing'">
|
||||||
|
{{ row.cases_ordered_display }}
|
||||||
|
</span>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="Units Ordered">
|
||||||
|
<b-input v-if="mode == 'editing'"
|
||||||
|
v-model="row.units_ordered"
|
||||||
|
type="number"
|
||||||
|
ref="unitsOrdered"
|
||||||
|
@keypress.native="handleEnter">
|
||||||
|
</b-input>
|
||||||
|
<span v-if="mode == 'viewing'">
|
||||||
|
{{ row.units_ordered_display }}
|
||||||
|
</span>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="PO Unit Cost">
|
||||||
|
<span>
|
||||||
|
{{ row.po_unit_cost_display }}
|
||||||
|
</span>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="Total">
|
||||||
|
<span>
|
||||||
|
{{ row.po_total_calculated_display }}
|
||||||
|
</span>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="Status">
|
||||||
|
<span>
|
||||||
|
{{ row.status_display }}
|
||||||
|
</span>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
</byjove-model-crud>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {ByjoveModelCrud} from 'byjove'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'OrderingBatchRow',
|
||||||
|
props: {
|
||||||
|
mode: String,
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
ByjoveModelCrud,
|
||||||
|
},
|
||||||
|
data: function() {
|
||||||
|
return {
|
||||||
|
row: {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// TODO: pretty sure this is too aggressive
|
||||||
|
updated() {
|
||||||
|
if (this.mode == 'editing') {
|
||||||
|
this.$nextTick(function() {
|
||||||
|
this.$refs.casesOrdered.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
renderParentHeaderLabel(row) {
|
||||||
|
return row.batch_id_str
|
||||||
|
},
|
||||||
|
|
||||||
|
renderHeaderLabel(row) {
|
||||||
|
return row.upc_pretty
|
||||||
|
},
|
||||||
|
|
||||||
|
handleEnter(event) {
|
||||||
|
if (event.keyCode == 13) { // enter
|
||||||
|
this.save()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
save(url) {
|
||||||
|
// we only update the order quantities
|
||||||
|
let params = {
|
||||||
|
cases_ordered: parseInt(this.row.cases_ordered),
|
||||||
|
units_ordered: parseInt(this.row.units_ordered),
|
||||||
|
}
|
||||||
|
if (!url) {
|
||||||
|
url = `/api/ordering-batch-row/${this.row.uuid}`
|
||||||
|
}
|
||||||
|
this.$http.post(url, params).then(response => {
|
||||||
|
// go back to the purchase order
|
||||||
|
this.$router.push('/ordering/' + response.data.data.batch_uuid)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
182
mobile/src/views/ordering/OrderingBatchWorksheet.vue
Normal file
182
mobile/src/views/ordering/OrderingBatchWorksheet.vue
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<router-link to="/ordering">Ordering</router-link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<router-link :to="`/ordering/${batch.uuid}`">
|
||||||
|
{{ batch.id_str }}
|
||||||
|
</router-link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Worksheet
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<b-field label="Vendor">
|
||||||
|
<span>
|
||||||
|
{{ batch.vendor_display }}
|
||||||
|
</span>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="Total">
|
||||||
|
<span>
|
||||||
|
{{ batch.po_total_calculated_display }}
|
||||||
|
</span>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<div v-if="sortedDepartments">
|
||||||
|
|
||||||
|
<div v-for="dept in sortedDepartments"
|
||||||
|
:key="dept.uuid">
|
||||||
|
<h2>
|
||||||
|
Dept. {{ dept.number }} ({{ dept.name }})
|
||||||
|
</h2>
|
||||||
|
<div v-for="subdept in dept.subdepartments"
|
||||||
|
:key="subdept.uuid">
|
||||||
|
<h3>
|
||||||
|
Sub. {{ subdept.number }} ({{ subdept.name }})
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<b-table :data="subdept.costs">
|
||||||
|
<template slot-scope="props">
|
||||||
|
|
||||||
|
<b-table-column field="upc_pretty" label="UPC" sortable>
|
||||||
|
{{ props.row.upc_pretty }}
|
||||||
|
</b-table-column>
|
||||||
|
|
||||||
|
<b-table-column field="brand_name" label="Brand" sortable>
|
||||||
|
{{ props.row.brand_name }}
|
||||||
|
</b-table-column>
|
||||||
|
|
||||||
|
<b-table-column field="description" label="Description" sortable>
|
||||||
|
{{ props.row.description }} {{ props.row.size }}
|
||||||
|
</b-table-column>
|
||||||
|
|
||||||
|
<b-table-column field="case_size" label="Case" numeric sortable>
|
||||||
|
{{ props.row.case_size }} {{ props.row.uom_display }}
|
||||||
|
</b-table-column>
|
||||||
|
|
||||||
|
<b-table-column field="vendor_item_code" label="Vend. Code" sortable>
|
||||||
|
{{ props.row.vendor_item_code }}
|
||||||
|
</b-table-column>
|
||||||
|
|
||||||
|
<b-table-column field="preferred" label="Pref." sortable>
|
||||||
|
{{ props.row.preferred ? "X" : "" }}
|
||||||
|
</b-table-column>
|
||||||
|
|
||||||
|
<b-table-column field="unit_cost" label="Unit Cost" numeric sortable>
|
||||||
|
{{ props.row.unit_cost_display }}
|
||||||
|
</b-table-column>
|
||||||
|
|
||||||
|
<b-table-column field="units_ordered" label="Cases / Units">
|
||||||
|
<b-field grouped>
|
||||||
|
<b-input v-model="props.row.cases_ordered"
|
||||||
|
type="number"
|
||||||
|
size="is-small">
|
||||||
|
</b-input>
|
||||||
|
<b-input v-model="props.row.units_ordered"
|
||||||
|
type="number"
|
||||||
|
size="is-small">
|
||||||
|
</b-input>
|
||||||
|
</b-field>
|
||||||
|
</b-table-column>
|
||||||
|
|
||||||
|
<b-table-column field="po_total" label="PO Total" numeric sortable>
|
||||||
|
{{ props.row.po_total_display }}
|
||||||
|
</b-table-column>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
</b-table>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
</div> <!-- subdept -->
|
||||||
|
</div> <!-- dept -->
|
||||||
|
</div> <!-- if sortedDepartments -->
|
||||||
|
|
||||||
|
<b-loading :active="!sortedDepartments"></b-loading>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'OrderingBatchWorksheet',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
inited: false,
|
||||||
|
batch: {},
|
||||||
|
sortedDepartments: null,
|
||||||
|
purchaseHistory: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created: function() {
|
||||||
|
this.init()
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'$store.state.session_established': 'init',
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
init() {
|
||||||
|
// only need to init once
|
||||||
|
if (this.inited) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// cannot init until session is established
|
||||||
|
if (!this.$store.state.session_established) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// is someone logged in?
|
||||||
|
if (this.$store.state.user) {
|
||||||
|
|
||||||
|
// go ahead and fetch data, but only if user has permission
|
||||||
|
if (this.$hasPerm('ordering.worksheet')) {
|
||||||
|
this.fetchData()
|
||||||
|
} else { // send others back to "home" page
|
||||||
|
this.router.push('/')
|
||||||
|
}
|
||||||
|
|
||||||
|
} else { // send logged-out users to "login"
|
||||||
|
this.$router.push('/login')
|
||||||
|
}
|
||||||
|
|
||||||
|
// we did it!
|
||||||
|
this.inited = true
|
||||||
|
},
|
||||||
|
|
||||||
|
fetchData(uuid) {
|
||||||
|
if (uuid === undefined) {
|
||||||
|
uuid = this.$route.params.uuid
|
||||||
|
}
|
||||||
|
this.$http.get(`/api/ordering-batch/${uuid}`).then(response => {
|
||||||
|
this.batch = response.data.purchasebatch
|
||||||
|
this.fetchWorksheetData(uuid)
|
||||||
|
}, response => {
|
||||||
|
this.$buefy.toast.open({
|
||||||
|
message: "Failed to fetch batch data!",
|
||||||
|
type: 'is-danger',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
fetchWorksheetData(uuid) {
|
||||||
|
this.$http.get(`/api/ordering-batch/${uuid}/worksheet`).then(response => {
|
||||||
|
this.sortedDepartments = response.data.sorted_departments
|
||||||
|
this.purchaseHistory = response.data.history
|
||||||
|
}, response => {
|
||||||
|
this.$buefy.toast.open({
|
||||||
|
message: "Failed to fetch worksheet data!",
|
||||||
|
type: 'is-danger',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
40
mobile/src/views/ordering/OrderingBatches.vue
Normal file
40
mobile/src/views/ordering/OrderingBatches.vue
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<template>
|
||||||
|
<byjove-model-index model-name="PurchaseBatch"
|
||||||
|
model-index-title="Ordering"
|
||||||
|
model-title="Ordering Batch"
|
||||||
|
model-title-plural="Ordering Batches"
|
||||||
|
model-path-prefix="/ordering"
|
||||||
|
model-permission-prefix="ordering"
|
||||||
|
api-index-url="/api/ordering-batches"
|
||||||
|
:api-index-sort="{field: 'id', dir: 'desc'}"
|
||||||
|
:api-index-filters="filters"
|
||||||
|
:label-renderer="renderLabel">
|
||||||
|
</byjove-model-index>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {ByjoveModelIndex} from 'byjove'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'OrderingBatches',
|
||||||
|
components: {
|
||||||
|
ByjoveModelIndex,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
filters: [
|
||||||
|
{field: 'executed', op: 'is_null'},
|
||||||
|
{or: [
|
||||||
|
{field: 'complete', op: 'is_null'},
|
||||||
|
{field: 'complete', op: 'eq', value: false},
|
||||||
|
]},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
renderLabel(batch) {
|
||||||
|
return `(${batch.id_str}) ${batch.vendor_display}`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
11
mobile/src/views/ordering/index.js
Normal file
11
mobile/src/views/ordering/index.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import OrderingBatches from './OrderingBatches'
|
||||||
|
import OrderingBatch from './OrderingBatch'
|
||||||
|
import OrderingBatchRow from './OrderingBatchRow'
|
||||||
|
import OrderingBatchWorksheet from './OrderingBatchWorksheet'
|
||||||
|
|
||||||
|
export {
|
||||||
|
OrderingBatches,
|
||||||
|
OrderingBatch,
|
||||||
|
OrderingBatchRow,
|
||||||
|
OrderingBatchWorksheet,
|
||||||
|
}
|
11
mobile/vue.config.js.dist
Normal file
11
mobile/vue.config.js.dist
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
// -*- mode: js; -*-
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
publicPath: '/m/',
|
||||||
|
devServer: {
|
||||||
|
host: '127.0.0.1',
|
||||||
|
port: 6647,
|
||||||
|
public: 'theo-demo.example.com',
|
||||||
|
// clientLogLevel: 'debug',
|
||||||
|
}
|
||||||
|
}
|
3
setup.py
3
setup.py
|
@ -2,7 +2,7 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# Rattail -- Retail Software Framework
|
# Rattail -- Retail Software Framework
|
||||||
# Copyright © 2010-2020 Lance Edgar
|
# Copyright © 2010-2021 Lance Edgar
|
||||||
#
|
#
|
||||||
# This file is part of Rattail.
|
# This file is part of Rattail.
|
||||||
#
|
#
|
||||||
|
@ -163,6 +163,7 @@ setup(
|
||||||
|
|
||||||
'paste.app_factory': [
|
'paste.app_factory': [
|
||||||
'main = theo.web.app:main',
|
'main = theo.web.app:main',
|
||||||
|
'webapi = theo.web.webapi:main',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
38
theo/web/api/__init__.py
Normal file
38
theo/web/api/__init__.py
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
# -*- coding: utf-8; -*-
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# Rattail -- Retail Software Framework
|
||||||
|
# Copyright © 2010-2021 Lance Edgar
|
||||||
|
#
|
||||||
|
# This file is part of Rattail.
|
||||||
|
#
|
||||||
|
# Rattail is free software: you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU General Public License as published by the Free Software
|
||||||
|
# Foundation, either version 3 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
# details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along with
|
||||||
|
# Rattail. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
"""
|
||||||
|
Theo Web API
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def includeme(config):
|
||||||
|
|
||||||
|
# core
|
||||||
|
config.include('tailbone.api.common')
|
||||||
|
config.include('tailbone.api.auth')
|
||||||
|
|
||||||
|
# models
|
||||||
|
config.include('tailbone.api.vendors')
|
||||||
|
|
||||||
|
# batches
|
||||||
|
config.include('tailbone.api.batch.ordering')
|
41
theo/web/webapi.py
Normal file
41
theo/web/webapi.py
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
# -*- coding: utf-8; -*-
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# Rattail -- Retail Software Framework
|
||||||
|
# Copyright © 2010-2021 Lance Edgar
|
||||||
|
#
|
||||||
|
# This file is part of Rattail.
|
||||||
|
#
|
||||||
|
# Rattail is free software: you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU General Public License as published by the Free Software
|
||||||
|
# Foundation, either version 3 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
# details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along with
|
||||||
|
# Rattail. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
"""
|
||||||
|
Theo web API app
|
||||||
|
"""
|
||||||
|
|
||||||
|
from tailbone import webapi as base
|
||||||
|
|
||||||
|
|
||||||
|
def main(global_config, **settings):
|
||||||
|
"""
|
||||||
|
This function returns a Pyramid WSGI application.
|
||||||
|
"""
|
||||||
|
rattail_config = base.make_rattail_config(settings)
|
||||||
|
pyramid_config = base.make_pyramid_config(settings)
|
||||||
|
|
||||||
|
# bring in some Theo / Tailbone
|
||||||
|
pyramid_config.include('theo.web.subscribers')
|
||||||
|
pyramid_config.include('theo.web.api')
|
||||||
|
|
||||||
|
return pyramid_config.make_wsgi_app()
|
Loading…
Reference in a new issue