added Store Batch Analysis Report

Merge branch 'PR43' into develop
pull/43/MERGE
13621160019@163.com 2021-05-19 14:59:51 +08:00
commit 465f60768b
6 changed files with 763 additions and 0 deletions

View File

@ -64,6 +64,7 @@ from reports import storeenergyitem
from reports import storeload
from reports import storesaving
from reports import storestatistics
from reports import storebatch
from reports import tenantbill
from reports import tenantcost
from reports import tenantenergycategory
@ -578,6 +579,8 @@ api.add_route('/reports/spacesaving',
spacesaving.Reporting())
api.add_route('/reports/spacestatistics',
spacestatistics.Reporting())
api.add_route('/reports/storebatch',
storebatch.Reporting())
api.add_route('/reports/storecost',
storecost.Reporting())
api.add_route('/reports/storeenergycategory',

View File

@ -0,0 +1,188 @@
import base64
import uuid
import os
from openpyxl.styles import PatternFill, Border, Side, Alignment, Protection, Font
from openpyxl.drawing.image import Image
from openpyxl import Workbook
####################################################################################################################
# PROCEDURES
# Step 1: Validate the report data
# Step 2: Generate excelexporters file
# Step 3: Encode the excelexporters file to Base64
####################################################################################################################
def export(result, space_name, reporting_start_datetime_local, reporting_end_datetime_local):
####################################################################################################################
# Step 1: Validate the report data
####################################################################################################################
if result is None:
return None
####################################################################################################################
# Step 2: Generate excel file from the report data
####################################################################################################################
filename = generate_excel(result,
space_name,
reporting_start_datetime_local,
reporting_end_datetime_local)
####################################################################################################################
# Step 3: Encode the excel file to Base64
####################################################################################################################
try:
with open(filename, 'rb') as binary_file:
binary_file_data = binary_file.read()
except IOError as ex:
pass
# Base64 encode the bytes
base64_encoded_data = base64.b64encode(binary_file_data)
# get the Base64 encoded data using human-readable characters.
base64_message = base64_encoded_data.decode('utf-8')
# delete the file from server
try:
os.remove(filename)
except NotImplementedError as ex:
pass
return base64_message
def generate_excel(report, space_name, reporting_start_datetime_local, reporting_end_datetime_local):
wb = Workbook()
ws = wb.active
# Row height
ws.row_dimensions[1].height = 102
for i in range(2, 5 + 1):
ws.row_dimensions[i].height = 42
for i in range(6, len(report['stores']) + 15):
ws.row_dimensions[i].height = 60
# Col width
ws.column_dimensions['A'].width = 1.5
ws.column_dimensions['B'].width = 25.0
for i in range(ord('C'), ord('L')):
ws.column_dimensions[chr(i)].width = 15.0
# Font
name_font = Font(name='Constantia', size=15, bold=True)
title_font = Font(name='宋体', size=15, bold=True)
data_font = Font(name='Franklin Gothic Book', size=11)
table_fill = PatternFill(fill_type='solid', fgColor='1F497D')
f_border = Border(left=Side(border_style='medium', color='00000000'),
right=Side(border_style='medium', color='00000000'),
bottom=Side(border_style='medium', color='00000000'),
top=Side(border_style='medium', color='00000000')
)
b_border = Border(
bottom=Side(border_style='medium', color='00000000'),
)
b_c_alignment = Alignment(vertical='bottom',
horizontal='center',
text_rotation=0,
wrap_text=True,
shrink_to_fit=False,
indent=0)
c_c_alignment = Alignment(vertical='center',
horizontal='center',
text_rotation=0,
wrap_text=True,
shrink_to_fit=False,
indent=0)
b_r_alignment = Alignment(vertical='bottom',
horizontal='right',
text_rotation=0,
wrap_text=True,
shrink_to_fit=False,
indent=0)
c_r_alignment = Alignment(vertical='bottom',
horizontal='center',
text_rotation=0,
wrap_text=True,
shrink_to_fit=False,
indent=0)
# Img
img = Image("excelexporters/myems.png")
img.width = img.width * 0.85
img.height = img.height * 0.85
ws.add_image(img, 'B1')
# Title
ws.row_dimensions[3].height = 60
ws['B3'].font = name_font
ws['B3'].alignment = b_r_alignment
ws['B3'] = '空间:'
ws['C3'].border = b_border
ws['C3'].alignment = b_c_alignment
ws['C3'].font = name_font
ws['C3'] = space_name
ws['F3'].font = name_font
ws['F3'].alignment = b_r_alignment
ws['F3'] = '日期:'
ws['G3'].border = b_border
ws['G3'].alignment = b_c_alignment
ws['G3'].font = name_font
ws['G3'] = reporting_start_datetime_local + "~" + reporting_end_datetime_local
ws.merge_cells("G3:H3")
# Title
ws['B6'].border = f_border
ws['B6'].font = name_font
ws['B6'].alignment = c_c_alignment
ws['B6'].fill = table_fill
ws['B6'] = '名称'
ws['C6'].border = f_border
ws['C6'].alignment = c_c_alignment
ws['C6'].font = name_font
ws['C6'].fill = table_fill
ws['C6'] = '空间'
ca_len = len(report['energycategories'])
for i in range(0, ca_len):
col = chr(ord('D') + i)
ws[col + '6'].fill = table_fill
ws[col + '6'].font = name_font
ws[col + '6'].alignment = c_c_alignment
ws[col + '6'] = report['energycategories'][i]['name'] + \
" (" + report['energycategories'][i]['unit_of_measure'] + ")"
ws[col + '6'].border = f_border
current_row_number = 7
for i in range(0, len(report['stores'])):
ws['B' + str(current_row_number)].font = title_font
ws['B' + str(current_row_number)].border = f_border
ws['B' + str(current_row_number)].alignment = c_c_alignment
ws['B' + str(current_row_number)] = report['stores'][i]['store_name']
ws['C' + str(current_row_number)].font = title_font
ws['C' + str(current_row_number)].border = f_border
ws['C' + str(current_row_number)].alignment = c_c_alignment
ws['C' + str(current_row_number)] = report['stores'][i]['space_name']
ca_len = len(report['stores'][i]['values'])
for j in range(0, ca_len):
col = chr(ord('D') + j)
ws[col + str(current_row_number)].font = data_font
ws[col + str(current_row_number)].border = f_border
ws[col + str(current_row_number)].alignment = c_c_alignment
ws[col + str(current_row_number)] = round(report['stores'][i]['values'][j], 2)
current_row_number += 1
filename = str(uuid.uuid4()) + '.xlsx'
wb.save(filename)
return filename

View File

@ -0,0 +1,239 @@
import falcon
import simplejson as json
import mysql.connector
import config
from anytree import Node, AnyNode, LevelOrderIter
from datetime import datetime, timedelta, timezone
from decimal import Decimal
import excelexporters.storebatch
class Reporting:
@staticmethod
def __init__():
pass
@staticmethod
def on_options(req, resp):
resp.status = falcon.HTTP_200
####################################################################################################################
# PROCEDURES
# Step 1: valid parameters
# Step 2: build a space tree
# Step 3: query all stores in the space tree
# Step 4: query energy categories
# Step 5: query reporting period energy input
# Step 6: construct the report
####################################################################################################################
@staticmethod
def on_get(req, resp):
print(req.params)
space_id = req.params.get('spaceid')
reporting_period_start_datetime_local = req.params.get('reportingperiodstartdatetime')
reporting_period_end_datetime_local = req.params.get('reportingperiodenddatetime')
################################################################################################################
# Step 1: valid parameters
################################################################################################################
if space_id is None:
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', description='API.INVALID_SPACE_ID')
else:
space_id = str.strip(space_id)
if not space_id.isdigit() or int(space_id) <= 0:
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', description='API.INVALID_SPACE_ID')
else:
space_id = int(space_id)
timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
if config.utc_offset[0] == '-':
timezone_offset = -timezone_offset
if reporting_period_start_datetime_local is None:
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
description="API.INVALID_REPORTING_PERIOD_START_DATETIME")
else:
reporting_period_start_datetime_local = str.strip(reporting_period_start_datetime_local)
try:
reporting_start_datetime_utc = datetime.strptime(reporting_period_start_datetime_local,
'%Y-%m-%dT%H:%M:%S')
except ValueError:
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
description="API.INVALID_REPORTING_PERIOD_START_DATETIME")
reporting_start_datetime_utc = reporting_start_datetime_utc.replace(tzinfo=timezone.utc) - \
timedelta(minutes=timezone_offset)
if reporting_period_end_datetime_local is None:
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
description="API.INVALID_REPORTING_PERIOD_END_DATETIME")
else:
reporting_period_end_datetime_local = str.strip(reporting_period_end_datetime_local)
try:
reporting_end_datetime_utc = datetime.strptime(reporting_period_end_datetime_local,
'%Y-%m-%dT%H:%M:%S')
except ValueError:
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
description="API.INVALID_REPORTING_PERIOD_END_DATETIME")
reporting_end_datetime_utc = reporting_end_datetime_utc.replace(tzinfo=timezone.utc) - \
timedelta(minutes=timezone_offset)
if reporting_start_datetime_utc >= reporting_end_datetime_utc:
raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST',
description='API.INVALID_REPORTING_PERIOD_END_DATETIME')
cnx_system_db = mysql.connector.connect(**config.myems_system_db)
cursor_system_db = cnx_system_db.cursor(dictionary=True)
cursor_system_db.execute(" SELECT name "
" FROM tbl_spaces "
" WHERE id = %s ", (space_id,))
row = cursor_system_db.fetchone()
if row is None:
if cursor_system_db:
cursor_system_db.close()
if cnx_system_db:
cnx_system_db.disconnect()
raise falcon.HTTPError(falcon.HTTP_404, title='API.NOT_FOUND',
description='API.SPACE_NOT_FOUND')
else:
space_name = row['name']
################################################################################################################
# Step 2: build a space tree
################################################################################################################
query = (" SELECT id, name, parent_space_id "
" FROM tbl_spaces "
" ORDER BY id ")
cursor_system_db.execute(query)
rows_spaces = cursor_system_db.fetchall()
node_dict = dict()
if rows_spaces is not None and len(rows_spaces) > 0:
for row in rows_spaces:
parent_node = node_dict[row['parent_space_id']] if row['parent_space_id'] is not None else None
node_dict[row['id']] = AnyNode(id=row['id'], parent=parent_node, name=row['name'])
################################################################################################################
# Step 3: query all stores in the space tree
################################################################################################################
store_dict = dict()
space_dict = dict()
for node in LevelOrderIter(node_dict[space_id]):
space_dict[node.id] = node.name
cursor_system_db.execute(" SELECT store.id, store.name AS store_name, s.name AS space_name, "
" cc.name AS cost_center_name, store.description "
" FROM tbl_spaces s, tbl_spaces_stores ss, tbl_stores store, tbl_cost_centers cc "
" WHERE s.id IN ( " + ', '.join(map(str, space_dict.keys())) + ") "
" AND ss.space_id = s.id AND ss.store_id = store.id "
" AND store.cost_center_id = cc.id ", )
rows_stores = cursor_system_db.fetchall()
if rows_stores is not None and len(rows_stores) > 0:
for row in rows_stores:
store_dict[row['id']] = {"store_name": row['store_name'],
"space_name": row['space_name'],
"cost_center_name": row['cost_center_name'],
"description": row['description'],
"values": list()}
################################################################################################################
# Step 4: query energy categories
################################################################################################################
cnx_energy_db = mysql.connector.connect(**config.myems_energy_db)
cursor_energy_db = cnx_energy_db.cursor()
# query energy categories in reporting period
energy_category_set = set()
cursor_energy_db.execute(" SELECT DISTINCT(energy_category_id) "
" FROM tbl_store_input_category_hourly "
" WHERE start_datetime_utc >= %s AND start_datetime_utc < %s ",
(reporting_start_datetime_utc, reporting_end_datetime_utc))
rows_energy_categories = cursor_energy_db.fetchall()
if rows_energy_categories is not None or len(rows_energy_categories) > 0:
for row_energy_category in rows_energy_categories:
energy_category_set.add(row_energy_category[0])
# query all energy categories
cursor_system_db.execute(" SELECT id, name, unit_of_measure "
" FROM tbl_energy_categories "
" ORDER BY id ", )
rows_energy_categories = cursor_system_db.fetchall()
if rows_energy_categories is None or len(rows_energy_categories) == 0:
if cursor_system_db:
cursor_system_db.close()
if cnx_system_db:
cnx_system_db.disconnect()
if cursor_energy_db:
cursor_energy_db.close()
if cnx_energy_db:
cnx_energy_db.disconnect()
raise falcon.HTTPError(falcon.HTTP_404,
title='API.NOT_FOUND',
description='API.ENERGY_CATEGORY_NOT_FOUND')
energy_category_list = list()
for row_energy_category in rows_energy_categories:
if row_energy_category['id'] in energy_category_set:
energy_category_list.append({"id": row_energy_category['id'],
"name": row_energy_category['name'],
"unit_of_measure": row_energy_category['unit_of_measure']})
################################################################################################################
# Step 5: query reporting period energy input
################################################################################################################
for store_id in store_dict:
cursor_energy_db.execute(" SELECT energy_category_id, SUM(actual_value) "
" FROM tbl_store_input_category_hourly "
" WHERE store_id = %s "
" AND start_datetime_utc >= %s "
" AND start_datetime_utc < %s "
" GROUP BY energy_category_id ",
(store_id,
reporting_start_datetime_utc,
reporting_end_datetime_utc))
rows_store_energy = cursor_energy_db.fetchall()
for energy_category in energy_category_list:
subtotal = Decimal(0.0)
for row_store_energy in rows_store_energy:
if energy_category['id'] == row_store_energy[0]:
subtotal = row_store_energy[1]
break
store_dict[store_id]['values'].append(subtotal)
if cursor_system_db:
cursor_system_db.close()
if cnx_system_db:
cnx_system_db.disconnect()
if cursor_energy_db:
cursor_energy_db.close()
if cnx_energy_db:
cnx_energy_db.disconnect()
################################################################################################################
# Step 6: construct the report
################################################################################################################
store_list = list()
for store_id, store in store_dict.items():
store_list.append({
"id": store_id,
"store_name": store['store_name'],
"space_name": store['space_name'],
"cost_center_name": store['cost_center_name'],
"description": store['description'],
"values": store['values'],
})
result = {'stores': store_list,
'energycategories': energy_category_list}
# export result to Excel file and then encode the file to base64 string
result['excel_bytes_base64'] = excelexporters.storebatch.export(result,
space_name,
reporting_period_start_datetime_local,
reporting_period_end_datetime_local)
resp.body = json.dumps(result)

View File

@ -0,0 +1,330 @@
import React, { Fragment, useEffect, useState } from 'react';
import {
Breadcrumb,
BreadcrumbItem,
Row,
Col,
Card,
CardBody,
Button,
ButtonGroup,
Form,
FormGroup,
Input,
Label,
CustomInput,
Spinner,
} from 'reactstrap';
import Datetime from 'react-datetime';
import moment from 'moment';
import loadable from '@loadable/component';
import Cascader from 'rc-cascader';
import { getCookieValue, createCookie } from '../../../helpers/utils';
import withRedirect from '../../../hoc/withRedirect';
import { withTranslation } from 'react-i18next';
import { toast } from 'react-toastify';
import ButtonIcon from '../../common/ButtonIcon';
import { APIBaseURL } from '../../../config';
const DetailedDataTable = loadable(() => import('../common/DetailedDataTable'));
const StoreBatch = ({ setRedirect, setRedirectUrl, t }) => {
let current_moment = moment();
useEffect(() => {
let is_logged_in = getCookieValue('is_logged_in');
let user_name = getCookieValue('user_name');
let user_display_name = getCookieValue('user_display_name');
let user_uuid = getCookieValue('user_uuid');
let token = getCookieValue('token');
if (is_logged_in === null || !is_logged_in) {
setRedirectUrl(`/authentication/basic/login`);
setRedirect(true);
} else {
//update expires time of cookies
createCookie('is_logged_in', true, 1000 * 60 * 60 * 8);
createCookie('user_name', user_name, 1000 * 60 * 60 * 8);
createCookie('user_display_name', user_display_name, 1000 * 60 * 60 * 8);
createCookie('user_uuid', user_uuid, 1000 * 60 * 60 * 8);
createCookie('token', token, 1000 * 60 * 60 * 8);
}
});
// State
// Query Parameters
const [selectedSpaceName, setSelectedSpaceName] = useState(undefined);
const [selectedSpaceID, setSelectedSpaceID] = useState(undefined);
const [storeList, setStoreList] = useState([]);
const [reportingPeriodBeginsDatetime, setReportingPeriodBeginsDatetime] = useState(current_moment.clone().startOf('month'));
const [reportingPeriodEndsDatetime, setReportingPeriodEndsDatetime] = useState(current_moment);
const [cascaderOptions, setCascaderOptions] = useState(undefined);
// buttons
const [submitButtonDisabled, setSubmitButtonDisabled] = useState(false);
const [spinnerHidden, setSpinnerHidden] = useState(true);
const [exportButtonHidden, setExportButtonHidden] = useState(true);
//Results
const [detailedDataTableColumns, setDetailedDataTableColumns] = useState(
[{dataField: 'name', text: t('Name'), sort: true}, {dataField: 'space', text: t('Space'), sort: true}]);
const [excelBytesBase64, setExcelBytesBase64] = useState(undefined);
useEffect(() => {
let isResponseOK = false;
fetch(APIBaseURL + '/spaces/tree', {
method: 'GET',
headers: {
"Content-type": "application/json",
"User-UUID": getCookieValue('user_uuid'),
"Token": getCookieValue('token')
},
body: null,
}).then(response => {
console.log(response);
if (response.ok) {
isResponseOK = true;
}
return response.json();
}).then(json => {
console.log(json);
if (isResponseOK) {
// rename keys
json = JSON.parse(JSON.stringify([json]).split('"id":').join('"value":').split('"name":').join('"label":'));
setCascaderOptions(json);
// set the default selected space
setSelectedSpaceName([json[0]].map(o => o.label));
setSelectedSpaceID([json[0]].map(o => o.value));
setSubmitButtonDisabled(false);
setSpinnerHidden(true);
} else {
toast.error(json.description);
}
}).catch(err => {
console.log(err);
});
}, []);
const labelClasses = 'ls text-uppercase text-600 font-weight-semi-bold mb-0';
let onSpaceCascaderChange = (value, selectedOptions) => {
setSelectedSpaceName(selectedOptions.map(o => o.label).join('/'));
setSelectedSpaceID(value[value.length - 1]);
setStoreList([]);
setExportButtonHidden(true);
setSubmitButtonDisabled(false);
}
let onReportingPeriodBeginsDatetimeChange = (newDateTime) => {
setReportingPeriodBeginsDatetime(newDateTime);
}
let onReportingPeriodEndsDatetimeChange = (newDateTime) => {
setReportingPeriodEndsDatetime(newDateTime);
}
var getValidReportingPeriodBeginsDatetimes = function (currentDate) {
return currentDate.isBefore(moment(reportingPeriodEndsDatetime, 'MM/DD/YYYY, hh:mm:ss a'));
}
var getValidReportingPeriodEndsDatetimes = function (currentDate) {
return currentDate.isAfter(moment(reportingPeriodBeginsDatetime, 'MM/DD/YYYY, hh:mm:ss a'));
}
// Handler
const handleSubmit = e => {
e.preventDefault();
console.log('handleSubmit');
console.log(selectedSpaceID);
console.log(reportingPeriodBeginsDatetime.format('YYYY-MM-DDTHH:mm:ss'));
console.log(reportingPeriodEndsDatetime.format('YYYY-MM-DDTHH:mm:ss'));
// disable submit button
setSubmitButtonDisabled(true);
// show spinner
setSpinnerHidden(false);
// hide export buttion
setExportButtonHidden(true)
// Reinitialize tables
setStoreList([]);
let isResponseOK = false;
fetch(APIBaseURL + '/reports/storebatch?' +
'spaceid=' + selectedSpaceID +
'&reportingperiodstartdatetime=' + reportingPeriodBeginsDatetime.format('YYYY-MM-DDTHH:mm:ss') +
'&reportingperiodenddatetime=' + reportingPeriodEndsDatetime.format('YYYY-MM-DDTHH:mm:ss'), {
method: 'GET',
headers: {
"Content-type": "application/json",
"User-UUID": getCookieValue('user_uuid'),
"Token": getCookieValue('token')
},
body: null,
}).then(response => {
if (response.ok) {
isResponseOK = true;
};
return response.json();
}).then(json => {
if (isResponseOK) {
console.log(json)
let stores = [];
if (json['stores'].length > 0) {
json['stores'].forEach((currentStore, index) => {
let detailed_value = {};
detailed_value['id'] = currentStore['id'];
detailed_value['name'] = currentStore['store_name'];
detailed_value['space'] = currentStore['space_name'];
detailed_value['costcenter'] = currentStore['cost_center_name'];
currentStore['values'].forEach((currentValue, energyCategoryIndex) => {
detailed_value['a' + energyCategoryIndex] = currentValue.toFixed(2);
});
stores.push(detailed_value);
});
};
setStoreList(stores);
let detailed_column_list = [];
detailed_column_list.push({
dataField: 'name',
text: t('Name'),
sort: true
});
detailed_column_list.push({
dataField: 'space',
text: t('Space'),
sort: true
});
json['energycategories'].forEach((currentValue, index) => {
detailed_column_list.push({
dataField: 'a' + index,
text: currentValue['name'] + ' (' + currentValue['unit_of_measure'] + ')',
sort: true
})
});
setDetailedDataTableColumns(detailed_column_list);
setExcelBytesBase64(json['excel_bytes_base64']);
// enable submit button
setSubmitButtonDisabled(false);
// hide spinner
setSpinnerHidden(true);
// show export buttion
setExportButtonHidden(false);
} else {
toast.error(json.description)
}
}).catch(err => {
console.log(err);
});
};
const handleExport = e => {
e.preventDefault();
const mimeType='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
const fileName = 'storebatch.xlsx'
var fileUrl = "data:" + mimeType + ";base64," + excelBytesBase64;
fetch(fileUrl)
.then(response => response.blob())
.then(blob => {
var link = window.document.createElement("a");
link.href = window.URL.createObjectURL(blob, { type: mimeType });
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
};
return (
<Fragment>
<div>
<Breadcrumb>
<BreadcrumbItem>{t('Store Data')}</BreadcrumbItem><BreadcrumbItem active>{t('Batch Analysis')}</BreadcrumbItem>
</Breadcrumb>
</div>
<Card className="bg-light mb-3">
<CardBody className="p-3">
<Form onSubmit={handleSubmit}>
<Row form>
<Col xs={6} sm={3}>
<FormGroup className="form-group">
<Label className={labelClasses} for="space">
{t('Space')}
</Label>
<br />
<Cascader options={cascaderOptions}
onChange={onSpaceCascaderChange}
changeOnSelect
expandTrigger="hover">
<Input value={selectedSpaceName || ''} readOnly />
</Cascader>
</FormGroup>
</Col>
<Col xs={6} sm={3}>
<FormGroup className="form-group">
<Label className={labelClasses} for="reportingPeriodBeginsDatetime">
{t('Reporting Period Begins')}
</Label>
<Datetime id='reportingPeriodBeginsDatetime'
value={reportingPeriodBeginsDatetime}
onChange={onReportingPeriodBeginsDatetimeChange}
isValidDate={getValidReportingPeriodBeginsDatetimes}
closeOnSelect={true} />
</FormGroup>
</Col>
<Col xs={6} sm={3}>
<FormGroup className="form-group">
<Label className={labelClasses} for="reportingPeriodEndsDatetime">
{t('Reporting Period Ends')}
</Label>
<Datetime id='reportingPeriodEndsDatetime'
value={reportingPeriodEndsDatetime}
onChange={onReportingPeriodEndsDatetimeChange}
isValidDate={getValidReportingPeriodEndsDatetimes}
closeOnSelect={true} />
</FormGroup>
</Col>
<Col xs="auto">
<FormGroup>
<br></br>
<ButtonGroup id="submit">
<Button color="success" disabled={submitButtonDisabled} >{t('Submit')}</Button>
</ButtonGroup>
</FormGroup>
</Col>
<Col xs="auto">
<FormGroup>
<br></br>
<Spinner color="primary" hidden={spinnerHidden} />
</FormGroup>
</Col>
<Col xs="auto">
<br></br>
<ButtonIcon icon="external-link-alt" transform="shrink-3 down-2" color="falcon-default"
hidden={exportButtonHidden}
onClick={handleExport} >
{t('Export')}
</ButtonIcon>
</Col>
</Row>
</Form>
</CardBody>
</Card>
<DetailedDataTable data={storeList} title={t('Detailed Data')} columns={detailedDataTableColumns} pagesize={50} >
</DetailedDataTable>
</Fragment>
);
};
export default withTranslation()(withRedirect(StoreBatch));

View File

@ -150,6 +150,7 @@ import StoreCost from '../components/MyEMS/Store/StoreCost';
import StoreLoad from '../components/MyEMS/Store/StoreLoad';
import StoreStatistics from '../components/MyEMS/Store/StoreStatistics';
import StoreSaving from '../components/MyEMS/Store/StoreSaving';
import StoreBatch from "../components/MyEMS/Store/StoreBatch";
// Shopfloor
import ShopfloorEnergyCategory from '../components/MyEMS/Shopfloor/ShopfloorEnergyCategory';
import ShopfloorEnergyItem from '../components/MyEMS/Shopfloor/ShopfloorEnergyItem';
@ -385,6 +386,7 @@ const MyEMSRoutes = () => (
<Route path="/store/load" exact component={StoreLoad} />
<Route path="/store/statistics" exact component={StoreStatistics} />
<Route path="/store/saving" exact component={StoreSaving} />
<Route path="/store/batch" exact component={StoreBatch} />
{/*Shopfloor*/}
<Route path="/shopfloor/energycategory" exact component={ShopfloorEnergyCategory} />

View File

@ -390,6 +390,7 @@ export const storeRoutes = {
{ to: '/store/load', name: 'Load' },
{ to: '/store/statistics', name: 'Statistics' },
{ to: '/store/saving', name: 'Saving' },
{ to: '/store/batch', name: 'Batch Analysis' },
]
};