Alan Colon 7 лет назад
Родитель
Сommit
b4cf837a86

+ 11 - 4
app/app.js

@@ -4,7 +4,7 @@ const routes = require('./routes')
 
 
 const app = angular.module('app', ['material-framework'])
 const app = angular.module('app', ['material-framework'])
 // TODO: Register app specific services
 // TODO: Register app specific services
-app.config(($routeProvider, $mdThemingProvider) => {
+app.config(($routeProvider, $mdThemingProvider, $mdColorsProvider) => {
   // TODO: App Title
   // TODO: App Title
   window.title = 'Special Dispatch'
   window.title = 'Special Dispatch'
 
 
@@ -12,10 +12,17 @@ app.config(($routeProvider, $mdThemingProvider) => {
   const palettes = ['red', 'pink', 'purple', 'deep-purple', 'indigo', 'blue', 'light-blue', 'cyan', 'teal', 'green', 'light-green', 'lime', 'yellow', 'amber', 'orange', 'deep-orange', 'brown', 'grey', 'blue-grey']
   const palettes = ['red', 'pink', 'purple', 'deep-purple', 'indigo', 'blue', 'light-blue', 'cyan', 'teal', 'green', 'light-green', 'lime', 'yellow', 'amber', 'orange', 'deep-orange', 'brown', 'grey', 'blue-grey']
   const randomPalette = () => palettes[Math.floor(Math.random() * palettes.length)]
   const randomPalette = () => palettes[Math.floor(Math.random() * palettes.length)]
   $mdThemingProvider.theme('default')
   $mdThemingProvider.theme('default')
-    .primaryPalette('blue-grey')
-    .accentPalette('indigo')
-    .warnPalette('red')
+     .primaryPalette('blue-grey')
+     .accentPalette('indigo')
+     .warnPalette('red')
 
 
+  $mdThemingProvider.theme('dark', 'default')
+    .dark()
+  
+  $mdThemingProvider.theme('nav', 'default')
+    .backgroundPalette('blue-grey', {'hue-1': '700'})
+    .dark()
+  
   // TODO: App Routes
   // TODO: App Routes
   routes($routeProvider)
   routes($routeProvider)
   //  $routeProvider.when('/test', {template: '<app-test-page />'})
   //  $routeProvider.when('/test', {template: '<app-test-page />'})

+ 1 - 1
app/components/dashboard-page.js

@@ -2,7 +2,7 @@ const app = require('../app')
 
 
 app.component('appDashboardPage', {
 app.component('appDashboardPage', {
   template: html`
   template: html`
-    <app-user-area>
+    <app-user-area title-text="Dashboard">
       <md-grid-list
       <md-grid-list
         md-cols="1" md-cols-sm="2" md-cols-md="3" md-cols-gt-md="6"
         md-cols="1" md-cols-sm="2" md-cols-md="3" md-cols-gt-md="6"
         md-row-height-gt-md="1:1" md-row-height="4:3"
         md-row-height-gt-md="1:1" md-row-height="4:3"

+ 1 - 2
app/components/labor-entry-page.js

@@ -4,13 +4,12 @@ const { editIcon } = require('../assets')
 
 
 app.component('appLaborEntryPage', {
 app.component('appLaborEntryPage', {
   template: html`
   template: html`
-  <app-user-area>
+  <app-user-area title-text="{{ctrl.locationKey}} Labor Entry">
     <app-breadcrumb links="[
     <app-breadcrumb links="[
       { text: 'Home', link: '/dashboard' },
       { text: 'Home', link: '/dashboard' },
       { text: ctrl.locationKey + ' Labor', link: '/labor/' + ctrl.locationKey },
       { text: ctrl.locationKey + ' Labor', link: '/labor/' + ctrl.locationKey },
       { text: ctrl.startDate.format('L') + ' - ' + ctrl.endDate.format('L'), link: '/labor/' + ctrl.locationKey + '/' + ctrl.startDate.format('YYYY-MM-DD') }
       { text: ctrl.startDate.format('L') + ' - ' + ctrl.endDate.format('L'), link: '/labor/' + ctrl.locationKey + '/' + ctrl.startDate.format('YYYY-MM-DD') }
     ]"></app-breadcrumb>
     ]"></app-breadcrumb>
-    <h1>Labor Entry</h1>
     <p>For week of {{::ctrl.startDate.format('LL')}} to {{::ctrl.endDate.format('LL')}}</p>
     <p>For week of {{::ctrl.startDate.format('LL')}} to {{::ctrl.endDate.format('LL')}}</p>
     <form name="form" ng-submit="ctrl.submit()">
     <form name="form" ng-submit="ctrl.submit()">
       <table md-table md-progress="ctrl.promise">
       <table md-table md-progress="ctrl.promise">

+ 1 - 3
app/components/labor-page.js

@@ -3,13 +3,11 @@ const { editIcon } = require('../assets')
 
 
 app.component('appLaborPage', {
 app.component('appLaborPage', {
   template: html`
   template: html`
-    <app-user-area>
+    <app-user-area title-text="{{ctrl.locationKey}} Labor">
       <app-breadcrumb links="[
       <app-breadcrumb links="[
         { text: 'Home', link: '/dashboard' },
         { text: 'Home', link: '/dashboard' },
         { text: ctrl.locationKey + ' Labor', link: '/labor/' + ctrl.locationKey }
         { text: ctrl.locationKey + ' Labor', link: '/labor/' + ctrl.locationKey }
       ]"></app-breadcrumb>
       ]"></app-breadcrumb>
-
-      <h1>Labor</h1>
       <table md-table ng-model="ctrl.selected" md-progress="ctrl.promise">
       <table md-table ng-model="ctrl.selected" md-progress="ctrl.promise">
         <thead md-head>
         <thead md-head>
           <tr md-row>
           <tr md-row>

+ 1 - 2
app/components/services-entry-page.js

@@ -4,13 +4,12 @@ const { editIcon } = require('../assets')
 
 
 app.component('appServicesEntryPage', {
 app.component('appServicesEntryPage', {
   template: html`
   template: html`
-  <app-user-area>
+  <app-user-area title-text="{{ctrl.locationKey}} Service Entry">
     <app-breadcrumb links="[
     <app-breadcrumb links="[
       { text: 'Home', link: '/dashboard' },
       { text: 'Home', link: '/dashboard' },
       { text: ctrl.locationKey + ' Services', link: '/services/' + ctrl.locationKey },
       { text: ctrl.locationKey + ' Services', link: '/services/' + ctrl.locationKey },
       { text: ctrl.startDate.format('L') + ' - ' + ctrl.endDate.format('L'), link: '/services/' + ctrl.locationKey + '/' + ctrl.startDate.format('YYYY-MM-DD') }
       { text: ctrl.startDate.format('L') + ' - ' + ctrl.endDate.format('L'), link: '/services/' + ctrl.locationKey + '/' + ctrl.startDate.format('YYYY-MM-DD') }
     ]"></app-breadcrumb>
     ]"></app-breadcrumb>
-    <h1>Service Entry</h1>
     <p>For week of {{::ctrl.startDate.format('LL')}} to {{::ctrl.endDate.format('LL')}}</p>
     <p>For week of {{::ctrl.startDate.format('LL')}} to {{::ctrl.endDate.format('LL')}}</p>
     <form name="form" ng-submit="ctrl.submit()">
     <form name="form" ng-submit="ctrl.submit()">
       <table md-table md-progress="ctrl.promise">
       <table md-table md-progress="ctrl.promise">

+ 1 - 3
app/components/services-page.js

@@ -3,13 +3,11 @@ const { editIcon } = require('../assets')
 
 
 app.component('appServicesPage', {
 app.component('appServicesPage', {
   template: html`
   template: html`
-    <app-user-area>
+    <app-user-area title-text="{{ctrl.locationKey}} Services">
       <app-breadcrumb links="[
       <app-breadcrumb links="[
         { text: 'Home', link: '/dashboard' },
         { text: 'Home', link: '/dashboard' },
         { text: ctrl.locationKey + ' Services', link: '/services/' + ctrl.locationKey }
         { text: ctrl.locationKey + ' Services', link: '/services/' + ctrl.locationKey }
       ]"></app-breadcrumb>
       ]"></app-breadcrumb>
-
-      <h1>Services</h1>
       <table md-table ng-model="ctrl.selected" md-progress="ctrl.promise">
       <table md-table ng-model="ctrl.selected" md-progress="ctrl.promise">
         <thead md-head>
         <thead md-head>
           <tr md-row>
           <tr md-row>

+ 6 - 4
app/components/staff-member-pages.js

@@ -4,10 +4,12 @@ pages({
   camelName: 'staffMember',
   camelName: 'staffMember',
   apiPrefix: '/api/staff-members/:location',
   apiPrefix: '/api/staff-members/:location',
   appPrefix: '/staff-members/:location',
   appPrefix: '/staff-members/:location',
-  pascalName: 'StaffMember',
-  pascalPlural: 'StaffMembers',
-  titleName: '{{ctrl.$routeParams.location == "all" ? "All" : ctrl.$routeParams.location}} Staff Member',
-  titlePlural: '{{ctrl.$routeParams.location == "all" ? "All" : ctrl.$routeParams.location}} Staff Members',
+  titles: {
+    details: ctrl => ctrl.isNew
+    ? `New ${ctrl.$routeParams.location == 'all' ? '' : ctrl.$routeParams.location} Staff Member`
+    : `${ctrl.$routeParams.location == 'all' ? '' : ctrl.$routeParams.location} Staff Member Details`,
+    list: ctrl => `${ctrl.$routeParams.location == 'all' ? 'All' : ctrl.$routeParams.location} Staff Members`
+  },
   columns: [
   columns: [
     { camelName: 'name', row: 1 },
     { camelName: 'name', row: 1 },
     { camelName: 'title', row: 2 },
     { camelName: 'title', row: 2 },

+ 40 - 35
app/components/user-area-nav.js

@@ -4,38 +4,40 @@ const { dashboardIcon, staffMemberIcon, staffingAgencyIcon, laborIcon, retailerI
 
 
 app.component('appUserAreaNav', {
 app.component('appUserAreaNav', {
   template: html`
   template: html`
-    <h3>
-      Intelligence
-    </h3>
+    <div ng-if="$ctrl.api.claims.METRICS_VIEW">
+      <h3>
+        Intelligence
+      </h3>
 
 
-    <md-menu-item>
-      <md-button ng-href="/dashboard">
-        <md-icon md-svg-icon="${dashboardIcon}"></md-icon>
-        Dashboard
-      </md-button>
-    </md-menu-item>
+      <md-menu-item ng-if="$ctrl.api.claims.METRICS_VIEW">
+        <md-button ng-href="/dashboard">
+          <md-icon md-svg-icon="${dashboardIcon}"></md-icon>
+          Dashboard
+        </md-button>
+      </md-menu-item>
+    </div>
 
 
-    <div ng-repeat="location in ctrl.locations">
+    <div ng-repeat="location in $ctrl.locations">
       <h3>{{::location.name}}</h3>
       <h3>{{::location.name}}</h3>
-      <md-menu-item>
+      <md-menu-item ng-if="$ctrl.api.claims.LABOR_VIEW">
         <md-button ng-href="/labor/{{::location.key}}">
         <md-button ng-href="/labor/{{::location.key}}">
           <md-icon md-svg-icon="${laborIcon}"></md-icon>
           <md-icon md-svg-icon="${laborIcon}"></md-icon>
           {{::location.key}} Labor
           {{::location.key}} Labor
         </md-button>
         </md-button>
       </md-menu-item>
       </md-menu-item>
-      <md-menu-item>
+      <md-menu-item ng-if="$ctrl.api.claims.SERVICES_VIEW">
         <md-button ng-href="/services/{{::location.key}}">
         <md-button ng-href="/services/{{::location.key}}">
           <md-icon md-svg-icon="${serviceIcon}"></md-icon>
           <md-icon md-svg-icon="${serviceIcon}"></md-icon>
           {{::location.key}} Services
           {{::location.key}} Services
         </md-button>
         </md-button>
       </md-menu-item>
       </md-menu-item>
-      <md-menu-item>
+      <md-menu-item ng-if="$ctrl.api.claims.STAFF_MEMBER_READ">
         <md-button ng-href="/staff-members/{{::location.key}}">
         <md-button ng-href="/staff-members/{{::location.key}}">
           <md-icon md-svg-icon="${staffMemberIcon}"></md-icon>
           <md-icon md-svg-icon="${staffMemberIcon}"></md-icon>
           {{::location.key}} Staff Members
           {{::location.key}} Staff Members
         </md-button>
         </md-button>
       </md-menu-item>
       </md-menu-item>
-      <md-menu-item>
+      <md-menu-item ng-if="$ctrl.api.claims.RETAILER_READ">
         <md-button ng-href="/retailers/{{::location.key}}">
         <md-button ng-href="/retailers/{{::location.key}}">
           <md-icon md-svg-icon="${retailerIcon}"></md-icon>
           <md-icon md-svg-icon="${retailerIcon}"></md-icon>
           {{::location.key}} Clients
           {{::location.key}} Clients
@@ -43,31 +45,34 @@ app.component('appUserAreaNav', {
       </md-menu-item>
       </md-menu-item>
     </div>
     </div>
 
 
-    <h3>All</h3>
-    <md-menu-item>
-      <md-button ng-href="/staff-members/all">
-        <md-icon md-svg-icon="${staffMemberIcon}"></md-icon>
-        Staff Members
-      </md-button>
-    </md-menu-item>
-    <md-menu-item>
-      <md-button ng-href="/staffing-agencies">
-        <md-icon md-svg-icon="${staffingAgencyIcon}"></md-icon>
-        Staffing Agencies
-      </md-button>
-    </md-menu-item>
-    <md-menu-item>
-      <md-button ng-href="/retailers/all">
-        <md-icon md-svg-icon="${retailerIcon}"></md-icon>
-        Clients
-      </md-button>
-    </md-menu-item>
+    <div ng-if="$ctrl.api.claims.LOCATION_ALL_ACCESS">
+      <h3>All</h3>
+      <md-menu-item ng-if="$ctrl.api.claims.STAFF_MEMBER_READ">
+        <md-button ng-href="/staff-members/all">
+          <md-icon md-svg-icon="${staffMemberIcon}"></md-icon>
+          Staff Members
+        </md-button>
+      </md-menu-item>
+      <md-menu-item ng-if="$ctrl.api.claims.STAFFING_AGENCY_READ">
+        <md-button ng-href="/staffing-agencies">
+          <md-icon md-svg-icon="${staffingAgencyIcon}"></md-icon>
+          Staffing Agencies
+        </md-button>
+      </md-menu-item>
+      <md-menu-item ng-if="$ctrl.api.claims.RETAILER_READ">
+        <md-button ng-href="/retailers/all">
+          <md-icon md-svg-icon="${retailerIcon}"></md-icon>
+          Clients
+        </md-button>
+      </md-menu-item>
+    </div>
     
     
   `,
   `,
-  controllerAs: 'ctrl',
   controller: function(api) {
   controller: function(api) {
-    window.api = api.locations().then(locations => {
+    this.api = api
+    api.locations().then(locations => {
       this.locations = _.sortBy(locations, 'key')
       this.locations = _.sortBy(locations, 'key')
+        .filter(({key}) => api.claims[`LOCATION_${key}_ACCESS`])
     })
     })
   }
   }
 })
 })

+ 1 - 0
app/index.html

@@ -4,6 +4,7 @@
   <base href="/" />
   <base href="/" />
   <title>Special Dispatch</title>
   <title>Special Dispatch</title>
   <script type="text/javascript" src="index.js"></script>
   <script type="text/javascript" src="index.js"></script>
+  <meta name="viewport" content="width=device-width, initial-scale=1">
 </head>
 </head>
   <body flex layout="row"><ng-view></ng-view></body>  
   <body flex layout="row"><ng-view></ng-view></body>  
 </html>
 </html>

+ 6 - 4
auto-crud/retailer.js

@@ -5,10 +5,12 @@ register({
   iconAsset: 'userIcon',
   iconAsset: 'userIcon',
   appPrefix: '/retailers/:location',
   appPrefix: '/retailers/:location',
   apiPrefix: '/api/retailers/:location',
   apiPrefix: '/api/retailers/:location',
-  pascalName: 'Retailer',
-  pascalPlural: 'Retailers',
-  titleName: '{{ctrl.$routeParams.location == "all" ? "All" : ctrl.$routeParams.location}} Client',
-  titlePlural: '{{ctrl.$routeParams.location == "all" ? "All" : ctrl.$routeParams.location}} Clients',
+  titles: {
+    details: ctrl => ctrl.isNew
+    ? `New ${ctrl.$routeParams.location == 'all' ? '' : ctrl.$routeParams.location} Client`
+    : `${ctrl.$routeParams.location == 'all' ? '' : ctrl.$routeParams.location} Client Details`,
+    list: ctrl => `${ctrl.$routeParams.location == 'all' ? 'All' : ctrl.$routeParams.location} Clients`
+  },
   showNav: false,
   showNav: false,
   schema: {
   schema: {
     id: {
     id: {

+ 9 - 1
lib/controllers/statistics.js

@@ -1,6 +1,11 @@
+const { Location } = require('../database')
 const sequelize = require('../database/sequelize')
 const sequelize = require('../database/sequelize')
 
 
 const get = async (req, res) => {
 const get = async (req, res) => {
+  const locationIds = (await Location.findAll())
+    .filter(loc => req.claims.LOCATION_ALL_ACCESS || req.claims[`LOCATION_${loc.key}_ACCESS`])
+    .map(loc => loc.id)
+
   const [results, metadata] = await sequelize.query(`
   const [results, metadata] = await sequelize.query(`
     SELECT
     SELECT
       loc.key,
       loc.key,
@@ -12,8 +17,11 @@ const get = async (req, res) => {
     FROM workdays wd
     FROM workdays wd
     JOIN locations loc on wd.locationId = loc.id
     JOIN locations loc on wd.locationId = loc.id
     LEFT JOIN services svc on svc.workdayId = wd.id
     LEFT JOIN services svc on svc.workdayId = wd.id
+    WHERE loc.id IN(:locationIds)
     GROUP BY loc.key, wd.date
     GROUP BY loc.key, wd.date
-  `)
+  `, {
+    replacements: { locationIds }
+  })
   res.status(200).send(results)
   res.status(200).send(results)
 
 
 }
 }

+ 9 - 0
lib/database/index.js

@@ -1,6 +1,8 @@
 // TODO: App Specific Models
 // TODO: App Specific Models
 const {database} = require('@alancnet/material-framework/server')
 const {database} = require('@alancnet/material-framework/server')
 const { Location, Retailer } = database
 const { Location, Retailer } = database
+const initialize = require('./initialize')
+
 // const Location = require('./location')
 // const Location = require('./location')
 const Workday = require('./workday')
 const Workday = require('./workday')
 const Service = require('./service')
 const Service = require('./service')
@@ -27,8 +29,15 @@ const RetailerLocation = Retailer.belongsTo(Location)
 // const StaffingAgencyStaffMember = StaffingAgency.hasMany(StaffMember)
 // const StaffingAgencyStaffMember = StaffingAgency.hasMany(StaffMember)
 // const StaffMemberStaffingAgency = StaffMember.belongsTo(StaffingAgency)
 // const StaffMemberStaffingAgency = StaffMember.belongsTo(StaffingAgency)
 
 
+const originalInit = database.init
+const init = async () => {
+  await originalInit()
+  await initialize.init(database)
+}
+
 
 
 module.exports = Object.assign(database, {
 module.exports = Object.assign(database, {
+  init,
   Location,
   Location,
   Workday,
   Workday,
   Service,
   Service,

+ 186 - 0
lib/database/initialize.js

@@ -0,0 +1,186 @@
+const { controllers: C } = require('@alancnet/material-framework/server')
+const { register } = C.auth.permissions
+const locations = [
+  {
+    name: 'Las Vegas',
+    key: 'LAS'
+  },
+  {
+    name: 'Los Angeles',
+    key: 'LAX'
+  },
+  {
+    name: 'Phoenix',
+    key: 'PHX'
+  },
+  {
+    name: 'San Francisco',
+    key: 'SFO'
+  }
+]
+
+const categories = [
+  {
+    name: 'Warehouse',
+    key: 'WAREHOUSE'
+  },
+  {
+    name: 'Admin',
+    key: 'ADMIN'
+  },
+  {
+    name: 'Delivery',
+    key: 'DELIVERY'
+  },
+  {
+    name: 'Ops',
+    key: 'OPS'
+  }
+]
+
+const roles = [
+  {
+    name: 'Management',
+    key: 'MANAGER',
+    permissions: [
+      'LABOR_CATEGORY_CREATE',
+      'LABOR_CATEGORY_DELETE',
+      'LABOR_CATEGORY_READ',
+      'LABOR_CATEGORY_UNDELETE',
+      'LABOR_CATEGORY_UPDATE',
+      'LABOR_ENTRY',
+      'LABOR_VIEW',
+      'LOCATION_ALL_ACCESS',
+      'LOCATION_CREATE',
+      'LOCATION_DELETE',
+      'LOCATION_READ',
+      'LOCATION_UNDELETE',
+      'LOCATION_UPDATE',
+      'METRICS_VIEW',
+      'RETAILER_CREATE',
+      'RETAILER_DELETE',
+      'RETAILER_READ',
+      'RETAILER_UNDELETE',
+      'RETAILER_UPDATE',
+      'ROLE_CREATE',
+      'ROLE_DELETE',
+      'ROLE_READ',
+      'ROLE_UNDELETE',
+      'ROLE_UPDATE',
+      'SERVICES_ENTRY',
+      'SERVICES_VIEW',
+      'STAFFING_AGENCY_CREATE',
+      'STAFFING_AGENCY_DELETE',
+      'STAFFING_AGENCY_READ',
+      'STAFFING_AGENCY_UNDELETE',
+      'STAFFING_AGENCY_UPDATE',
+      'STAFF_MEMBER_CREATE',
+      'STAFF_MEMBER_DELETE',
+      'STAFF_MEMBER_READ',
+      'STAFF_MEMBER_UNDELETE',
+      'STAFF_MEMBER_UPDATE',
+      'USER_CREATE',
+      'USER_DELETE',
+      'USER_READ',
+      'USER_UNDELETE',
+      'USER_UPDATE',
+      'VIEW_INCOME_ADMIN',
+      'VIEW_INCOME_DELIVERY',
+      'VIEW_INCOME_OPS',
+      'VIEW_INCOME_WAREHOUSE',
+      'VIEW_LOCATION_LAS',
+      'VIEW_LOCATION_LAX',
+      'VIEW_LOCATION_PHX',
+      'VIEW_LOCATION_SFO'
+    ].join(',')
+  },
+  {
+    name: 'Terminal Manager',
+    key: 'TERMINAL_MANAGER',
+    permissions: [
+      'LABOR_CATEGORY_READ',
+      'LOCATION_READ',
+      'METRICS_VIEW',
+      'RETAILER_CREATE',
+      'RETAILER_DELETE',
+      'RETAILER_READ',
+      'RETAILER_UNDELETE',
+      'RETAILER_UPDATE',
+      'STAFFING_AGENCY_READ',
+      'STAFF_MEMBER_CREATE',
+      'STAFF_MEMBER_DELETE',
+      'STAFF_MEMBER_READ',
+      'STAFF_MEMBER_UNDELETE',
+      'STAFF_MEMBER_UPDATE'
+    ]
+      .concat(categories.map(x => x.key).filter(key => key !== 'ADMIN').map(key => `INCOME_${key}_VIEW`))
+      .join(',')
+  },
+  {
+    name: 'Accounting',
+    key: 'ACCOUNTING',
+    permissions: [
+      'LABOR_CATEGORY_READ',
+      'LOCATION_READ',
+      'RETAILER_READ',
+      'STAFFING_AGENCY_CREATE',
+      'STAFFING_AGENCY_DELETE',
+      'STAFFING_AGENCY_READ',
+      'STAFFING_AGENCY_UNDELETE',
+      'STAFFING_AGENCY_UPDATE',
+      'STAFF_MEMBER_CREATE',
+      'STAFF_MEMBER_DELETE',
+      'STAFF_MEMBER_READ',
+      'STAFF_MEMBER_UNDELETE',
+      'STAFF_MEMBER_UPDATE'
+    ]
+      .concat(categories.map(x => x.key).filter(key => key !== 'ADMIN').map(key => `INCOME_${key}_VIEW`))
+      .join(',')
+  },
+  {
+    name: 'Standard User',
+    key: 'USER',
+  }
+].concat(locations.map(loc => ({
+  name: `${loc.name}`,
+  key: `${loc.key}`,
+  permissions: `LOCATION_${loc.key}_ACCESS`
+})))
+
+
+
+const initializeLocations = async db => {
+
+  for (let location of locations) {
+    const record = await db.upsert(db.Location, location)
+    register(`LOCATION_${location.key}_ACCESS`, `Access ${location.name}.`)
+    console.log(`Upserted Location ${location.name}: ${JSON.stringify(record)}`) 
+  }
+}
+
+const initializeRoles = async db => {
+  for (let role of roles) {
+    const record = await db.upsert(db.Role, role)
+    console.log(`Upserted Role ${role.name}: ${JSON.stringify(record)}`)
+  }
+}
+
+const initializeLaborCategories = async db => {
+  for (let category of categories) {
+    const record = await db.upsert(db.LaborCategory, category)
+    register(`INCOME_${category.key}_VIEW`, `View ${category.name} Staff income.`)
+    console.log(`Upserted Labor Category ${category.name}: ${JSON.stringify(record)}`) 
+  }
+}
+
+const init = async (db) => {
+  await initializeLocations(db)
+  await initializeRoles(db)
+  await initializeLaborCategories(db)
+}
+
+module.exports = {
+  initializeRoles,
+  initializeLaborCategories,
+  init
+}

+ 11 - 0
lib/permissions.js

@@ -0,0 +1,11 @@
+const { controllers: C } = require('@alancnet/material-framework/server')
+const { register } = C.auth.permissions
+
+register('LOCATION_ALL_ACCESS', 'Access records for entire organization. Does not grant access to location-specific views. ')
+register('METRICS_VIEW', 'View metrics / statistics.')
+register('LABOR_VIEW', 'View labor / hours worked.')
+register('LABOR_ENTRY', 'Enter labor / hours worked.')
+register('SERVICES_VIEW', 'View cartons scanned / delivered.')
+register('SERVICES_ENTRY', 'Enter cartons scanned / delivered.')
+
+// register('VIEW_{{LaborCategory.key}}_INCOME', 'View {{LaborCategory.name}} labor rates')

+ 1 - 1
lib/routes.js

@@ -2,7 +2,7 @@ const C = require('./controllers')
 const { routes:crudRoutes } = require('@alancnet/material-framework/lib/crud')
 const { routes:crudRoutes } = require('@alancnet/material-framework/lib/crud')
 
 
 module.exports = app => {
 module.exports = app => {
-  app.get('/api/statistics', C.statistics.get)
+  app.get('/api/statistics', C.auth.verify('METRICS_VIEW'), C.statistics.get)
   crudRoutes({ app, apiPrefix: '/api/staff-members/:location', controller: C.staffMember, camelName: 'staffMember' })
   crudRoutes({ app, apiPrefix: '/api/staff-members/:location', controller: C.staffMember, camelName: 'staffMember' })
  // crudRoutes({ app, controller: C.staffMember, camelName: 'staffMember' })
  // crudRoutes({ app, controller: C.staffMember, camelName: 'staffMember' })
   crudRoutes({ app, controller: C.staffingAgency, camelName: 'staffingAgency' })
   crudRoutes({ app, controller: C.staffingAgency, camelName: 'staffingAgency' })

+ 1 - 0
lib/server.js

@@ -1,4 +1,5 @@
 require('../auto-crud')
 require('../auto-crud')
+require('./permissions')
 const { app } = require('@alancnet/material-framework/server')
 const { app } = require('@alancnet/material-framework/server')
 const routes = require('./routes')
 const routes = require('./routes')
 routes(app)
 routes(app)