Commit 17ae0da6 authored by Pavel Vinokurov's avatar Pavel Vinokurov
Browse files

CDEK-1308 Added UI for Avon

parent 0b31dcbf
Showing with 1246 additions and 25 deletions
+1246 -25
......@@ -8,6 +8,11 @@ auth.url=http://auth.qa.cdek.ru/authentication/auth
cdek.api.url = http://integration.dev.cdek.ru
cdek.api.order.url = ${cdek.api.url}/new_orders.php
cdek.api.status.url = ${cdek.api.url}/status_report_h.php
cdek.api.info.url = ${cdek.api.url}/info_report.php
# Адрес АПИ платежных поручений
api.payment.url = http://172.16.83.51:8099
api.order.payment.url = ${api.payment.url}/api/paymentOrder/getPayments
#Адрес калькулятора
cdek.api.calc.url = http://dbo.dev.cdek.ru/calculator/getService.php
......@@ -23,10 +28,10 @@ cdek.api.currency.converter.url = http://192.168.1.223:8044/convertToRub/client
#Настройка базы данных
jdbc.driverClassName=org.postgresql.Driver
jdbc.url=jdbc:postgresql://localhost:5432/integration?currentSchema=${jdbc.schema}
jdbc.url=jdbc:postgresql://localhost:5432/avon?currentSchema=${jdbc.schema}
jdbc.username=integration
jdbc.password=integration
jdbc.schema=avon
jdbc.schema=public
catalog.schema=${jdbc.schema}
......@@ -40,7 +45,7 @@ cdek.api.timeout=120
# Настройки для очистки базы
# Кол-во дней за которое храним всю информацию о заказе
cleanup.days=180
cleanup.days=360
# Cron настройка для запуска очистки базы
cleanup.cron=0 2 * * * *
......@@ -64,4 +69,12 @@ cache.fast.expiration.time=5s
cache.slow.expiration.time=1h
#Язык по умолчанию. Используется классом ExceptionBuilder для выбора перевода ошибок
system.default.lang = rus
\ No newline at end of file
system.default.lang = rus
avon.integration.ftp.server=ftp://localhost
avon.integration.ftp.port=234234
avon.integration.ftp.username=234234
avon.integration.ftp.password=234234
avon.integration.ftp.passiveMode=false
\ No newline at end of file
......@@ -123,9 +123,18 @@
</dependency>
<dependency>
<groupId>com.cdek.platform</groupId>
<artifactId>com.cdek.platform.web</artifactId>
<version>${project.parent.version}</version>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
......@@ -152,7 +161,12 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
</dependency>
<dependency>
......@@ -170,7 +184,6 @@
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>4.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.flywaydb.flyway-test-extensions</groupId>
......@@ -182,14 +195,11 @@
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
package com.cdek.integration.avon;
import com.cdek.integration.avon.service.AvonService;
import com.cdek.platform.model.integration.Integration;
import com.cdek.platform.services.IntegrationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Required;
/**
* Точка подключение интеграции Avon.
*/
public class AvonIntegration implements InitializingBean {
private static final Logger LOGGER = LoggerFactory.getLogger(AvonIntegration.class);
protected IntegrationService integrationService;
protected boolean enabled=true;
@Override
public void afterPropertiesSet() throws Exception {
LOGGER.info("Avon Integration startup.....");
integrationService.register(new Integration("avon", "Avon", enabled));
}
@Required
public void setIntegrationService(IntegrationService integrationService) {
this.integrationService = integrationService;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
......@@ -3,7 +3,6 @@ package com.cdek.integration.avon.service;
import java.util.List;
import com.cdek.integration.avon.model.CSVAvonOrders;
import com.cdek.integration.avon.model.CdekToAvonAccountMapping;
import com.cdek.integration.leomax.model.CdekAccountMapping;
import com.cdek.platform.common.exceptions.FtpException;
import com.cdek.platform.common.ftp.ApiCallResult;
......
package com.cdek.integration.avon.web.controllers;
import com.cdek.platform.model.Action;
import com.cdek.platform.services.ActionService;
import com.cdek.platform.services.TextDataService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
/**
* Контроллер для работы с логами операций
*/
@Controller
@RequestMapping("avon/action")
//TODO refactor: сделать общим
public class ActionController {
private static final Logger LOGGER = LoggerFactory.getLogger(ActionController.class);
private ActionService actionService;
private TextDataService textService;
/**
* Получить список операция по ид заказа
*
* @param id ид заказа
* @return список операций
*/
@RequestMapping(value = "/byOrderId/{id}", method = RequestMethod.GET)
public
@ResponseBody
List<Action> list(@PathVariable Long id) {
List<Action> actions = actionService.findByOrderId(id);
return actions;
}
/**
* Получить текст запроса или ответа
*
* @param id ид текста запроса или ответа
* @return текст
*/
@RequestMapping(value = "/text/{id}", method = RequestMethod.GET, produces = "text/plain;charset=UTF-8")
public
@ResponseBody
String text(@PathVariable Long id) {
String text = textService.find(id).getText();
return text;
}
public void setActionService(ActionService actionService) {
this.actionService = actionService;
}
public void setTextService(TextDataService textService) {
this.textService = textService;
}
}
package com.cdek.integration.avon.web.controllers;
import com.cdek.commons.utils.DateTimeUtils;
import com.cdek.platform.common.config.CdekConstants;
import com.cdek.platform.common.exceptions.ApiException;
import com.cdek.platform.common.utils.adapters.XmlLocalDateServerDateAdapter;
import com.cdek.platform.model.order.Order;
import com.cdek.platform.model.order.OrderFilter;
import com.cdek.platform.model.order.OrderListVO;
import com.cdek.platform.model.paging.PageListTO;
import com.cdek.platform.model.user.User;
import com.cdek.platform.services.*;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
/**
* Контроллер для работы с заказми
*/
@Controller
@RequestMapping("avon/order")
public class OrderController {
private static final Logger LOGGER = LoggerFactory.getLogger(OrderController.class);
public static final String ORDER_FILTER = "avonOrderFilter";
protected ActionService actionService;
protected ManualOperationsService manualOperationsService;
protected OrderService orderService;
private SettingsService settingsService;
/**
* Сбрасывает фильтр
*
* @return новый фильтра
*/
@RequestMapping(value = "/resetFilter", method = RequestMethod.POST)
public
@ResponseBody
OrderFilter resetFilter(HttpServletRequest request) {
OrderFilter orderFilter = new OrderFilter();
orderFilter.setDateFrom(new Date(System.currentTimeMillis() - 5 * 24 * 60 * 60 * 1000));
orderFilter.setDateTo(new Date());
orderFilter.setError(false);
orderFilter.setPartnerId(CdekConstants.PARTNER_ID_ADIDAS);
request.setAttribute(ORDER_FILTER, orderFilter);
return orderFilter;
}
/**
* Получает фильтр из сессии либо создает новый
*
* @return фильтр
*/
@RequestMapping(value = "/getFilter", method = RequestMethod.POST)
public
@ResponseBody
OrderFilter getFilter(HttpServletRequest request) {
OrderFilter orderFilter = (OrderFilter) request.getSession().getAttribute(ORDER_FILTER);
if (orderFilter == null) {
orderFilter = resetFilter(request);
}
return orderFilter;
}
/**
* Получает список заказов по фильтру
*
* @param filter фильтр
* @return список заказов
* @throws Exception
*/
@RequestMapping(value = "/list", method = RequestMethod.POST)
public
@ResponseBody
PageListTO<Order> list(HttpServletRequest request, @RequestBody OrderFilter filter) throws Exception {
request.getSession().setAttribute(ORDER_FILTER, filter);
List<OrderListVO> list = orderService.findOrders(filter);
SimpleDateFormat sdf = new SimpleDateFormat(DateTimeUtils.SERVER_DATETIME_FORMAT);
list.forEach((o) -> {
o.setOrderDateDate(o.getOrderDate().format(XmlLocalDateServerDateAdapter.formatter));
if (o.getLastActionDate() != null) {
o.setLastActionDateDate(sdf.format(o.getLastActionDate()));
} else {
o.setLastActionDateDate(sdf.format(new Date(0)));
}
});
return new PageListTO<Order>(filter.getPaging().getSize(), list.toArray(new OrderListVO[0]));
}
/**
* Получает заказо по ид
*
* @param id ид заказа
* @return заказ
*/
@RequestMapping(value = "/get/{id}", method = RequestMethod.POST)
public
@ResponseBody
Order get(@PathVariable Long id) {
return orderService.findOrderById(id);
}
/**
* Вызывает {@link ActionService#cleanError(List)}
*
* @param ids список ид заказов
* @throws ServiceException
*/
@RequestMapping(value = "/cleanError", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public void cleanError(@RequestBody List<Long> ids) throws ServiceException {
actionService.cleanError(ids);
}
/**
* Вызывает {@link OrderService#solve(List,String)}
*
* @param ids список ид заказов
* @throws ServiceException
*/
@RequestMapping(value = "/solve", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public void solve(@RequestBody List<Long> ids) throws ServiceException {
User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
orderService.solve(ids,user.getLogin());
}
/**
* TODO refactor: javadoc
*
* @throws ServiceException
*/
@RequestMapping(value = "/receive", method = RequestMethod.GET)
@ResponseStatus(value = HttpStatus.OK)
public void receive() throws ServiceException {
}
/**
* Возвращает статистику о последнем времени получения заказов и обмена статусами.
*
* @return
*/
@RequestMapping(value = "/attachDispatchNumber", method = RequestMethod.GET)
@ResponseStatus(value = HttpStatus.OK)
public
void attachDispatchNumber(
@RequestParam(value = "orderId") Long orderId,
@RequestParam(value = "dispatchNumber", required = false) String dispatchNumber) throws ServiceException {
Assert.isTrue(StringUtils.isNotBlank(dispatchNumber), "Номер накладной СДЭК должен быть числом");
Assert.isTrue(StringUtils.isNumeric(dispatchNumber), "Номер накладной СДЭК должен быть числом");
LOGGER.info("Trying to attach dispatchNumber: {} to order with id: {}", dispatchNumber, orderId);
User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
try {
manualOperationsService.attachDispatchNumber(orderId, dispatchNumber, user.getLogin());
} catch (ApiException | ServiceException e) {
LOGGER.error("Can not attach dispatchNumber: {} to order with id: {}. Reason[{}]: {}",
dispatchNumber, orderId, e.getClass(), e.getMessage());
throw new ServiceException(e);
}
LOGGER.info("DispatchNumber: {} attached successfully to order with id: {}", dispatchNumber, orderId);
}
@Required
public void setActionService(ActionService actionService) {
this.actionService = actionService;
}
@Required
public void setManualOperationsService(ManualOperationsService manualOperationsService) {
this.manualOperationsService = manualOperationsService;
}
@Required
public void setOrderService(OrderService orderService) {
this.orderService = orderService;
}
@Required
public void setSettingsService(SettingsService settingsService) {
this.settingsService = settingsService;
}
}
\ No newline at end of file
......@@ -3,25 +3,20 @@
xmlns:context="http://www.springframework.org/schema/context"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd
">
" profile="avon">
<context:annotation-config />
<mvc:resources mapping="integrations/**" location="classpath:/webapp/avon" />
<task:scheduler id="taskScheduler" pool-size="1"/>
<task:annotation-driven scheduler="taskScheduler"/>
<context:property-placeholder location="file:${config.file}"/><!-- Префикс file нужен после переопределения переменной библиотекой JettyPlatform-->
<import resource="classpath:security.xml"/>
<import resource="classpath:com-cdek-platform-flyway.xml"/>
<import resource="classpath:com-cdek-platform-services.xml"/>
<import resource="classpath:com-cdek-integration-avon-datasource.xml"/>
<bean id="avonIntegration" class="com.cdek.integration.avon.AvonIntegration" p:integrationService-ref="integrationService"/>
<bean id="avonFTPApi" class="com.cdek.platform.common.ftp.FTPApi">
<property name="server" value="${avon.integration.ftp.server}"/>
......
'use strict';
var app = angular.module('app', [
'ngRoute',
'ngSanitize',
'agGrid',
'bootstrap.datetimepicker',
'ConfigProvider',
'Storage',
'AvonDataProvider',
'DataProvider',
'agGridPlugin',
'AlertProvider'
]);
app.config(function($provide, $routeProvider, configProvider) {
/**
* Routes configuration
*/
configProvider.setUrl('config/config.json');
$routeProvider
.when('/details/:id', {
templateUrl: 'app/partials/avon-orders.html',
controller: 'AvonOrdersCtrl',
resolve:{
'ConfigLoad':function(config, $route) {
$route.current.params.details = true;
return config.promise;
}
}
})
.when('/orders', {
templateUrl: 'app/partials/avon-orders.html',
controller: 'AvonOrdersCtrl',
resolve:{
'ConfigLoad':function(config) {
return config.promise;
}
}
})
.when('/', {
redirectTo: '/orders'
})
.otherwise({
redirectTo: '/'
});
});
/**
* Auth check for rest calls
*/
app.config(
function($provide, $httpProvider) {
$provide.factory('AuthInterceptor', function($q,$location) {
return {
responseError : function(rejection) {
if(rejection.status === 401) {
console.log(rejection.status);
location.reload();
}
return $q.reject(rejection);
}
};
});
$httpProvider.interceptors.push('AuthInterceptor');
});
app.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
}]);
app.service('sharedProperties', function () {
var filter = {};
return {
getFilter: function () {
return filter;
},
setFilter: function(value) {
filter = value;
}
};
});
\ No newline at end of file
'use strict';
app.controller('AvonDetailsCtrl', function ($scope, avonData, config) {
$scope.config = config;
var reload = function(resp) {
var order = $scope.$parent.order;
avonData.order(order.id, function (resp) {
$scope.$parent.details(resp, true);
});
};
var hideAndReloadOrderList = function(resp) {
$('#orderModal').modal('hide');
$scope.findOrders();
};
$scope.send = function () {
$scope.logListApi.gridOptions.api.showLoadingOverlay();
var order = $scope.$parent.order;
avonData.send([order.id], reload, reload);
}
$scope.calculate = function () {
$scope.logListApi.gridOptions.api.showLoadingOverlay();
var order = $scope.$parent.order;
avonData.calculate([order.id], reload, reload);
}
$scope.attachDispatchNumber = function () {
$scope.logListApi.gridOptions.api.showLoadingOverlay();
var order = $scope.$parent.order;
avonData.attachDispatchNumber(order.id, $scope.dispatchNumber, hideAndReloadOrderList, reload);
};
$scope.getRowHeight = function(params) {
if (params.data.errorMessage) {
return 38;
} else {
return 25;
}
}
});
\ No newline at end of file
'use strict';
app.controller('AvonOrdersCtrl', function ($scope, $interval, $timeout, $window, $location, $route, config, avonData, sharedProperties) {
$scope.orderStatuses = config.object("orderStatuses");
$scope.cdekStatuses = config.object("cdekStatuses");
$scope.avonStatuses = config.object("avonStatuses");
$scope.config = config;
$scope.showFilter = true;
var entities = function (codes) {
var result = {};
for (var i in codes) {
var entity = codes;
result[codes[i]] = true;
}
return result;
};
var cdekEntities = function (codes) {
var result = [];
for (var i in codes) {
result.push($scope.cdekStatuses[codes[i]]);
}
return result;
};
var convertFilter = function (data) {
var filter = {
dateTo: data.dateTo,
dateFrom: data.dateFrom,
error: data.error,
statuses: entities(data.status),
cdekStatuses: cdekEntities(data.cdekStatus),
avonStatuses: entities(data.externalStatus),
orderNumbers: data.orderNumbers ? data.orderNumbers.join(", ") : ""
};
return filter;
};
avonData.getOrderFilter(function (data) {
$scope.filter = convertFilter(data);
});
$scope.resetFilter = function () {
avonData.resetOrderFilter(function (data) {
$scope.filter = convertFilter(data);
});
};
var codes = function (statuses) {
var result = [];
for (var i in statuses) {
if (statuses[i]) {
result.push(i);
}
}
return result;
};
var cdekCodes = function (statuses) {
var result = [];
for (var i in statuses) {
result.push(statuses[i].code);
}
return result;
};
$scope.findOrders = function () {
$scope.orderListApi.reload(getFilter());
$scope.selectAll=false;
updateInformation();
};
var updateInformation = function () {
avonData.information(function (information) {
$scope.information = information;
})
};
updateInformation();
var selected = function () {
var result = [];
var rows = $scope.orderListApi.gridOptions.api.getSelectedRows();
for (var row in rows) {
result.push(rows[row].id);
}
return result;
};
$scope.cleanError = function () {
$scope.orderListApi.gridOptions.api.showLoadingOverlay();
avonData.cleanError(selected(), $scope.findOrders, $scope.findOrders);
};
$scope.solve = function () {
$scope.orderListApi.gridOptions.api.showLoadingOverlay();
avonData.solve(selected(), $scope.findOrders, $scope.findOrders);
};
var getFilter = function () {
return {
dateTo: $scope.filter.dateTo,
dateFrom: $scope.filter.dateFrom,
error: $scope.filter.error,
orderError: $scope.filter.orderError,
status: codes($scope.filter.statuses),
cdekStatus: cdekCodes($scope.filter.cdekStatuses),
externalStatus: codes($scope.filter.smStatuses),
partnerId: config.object("partnerId"),
orderNumbers: $scope.filter.orderNumbers ? $scope.filter.orderNumbers.split(/\s*,\s*/) : []
}
};
$scope.receive = function () {
$scope.orderListApi.gridOptions.api.showLoadingOverlay();
avonData.receive($scope.findOrders, $scope.findOrders);
};
$scope.details = function (order, skipDialog) {
$scope.order = order;
avonData.actions(order.id, function (resp) {
if (skipDialog) {
$scope.actions = resp;
return;
}
$scope.actions = [];
var resetActions = function () {
$('#orderModal').off('shown.bs.modal', resetActions);
$timeout(function() {
$scope.actions = resp;
});
};
$('#orderModal').modal('toggle').on('shown.bs.modal', resetActions);
});
};
if ($route.current.params.details) {
avonData.order($route.current.params.details, function (resp) {
$scope.details(resp);
});
}
$scope.getRowHeight = function(params) {
if (params.data.lastError && params.data.lastError.errorMessage) {
return 38;
} else {
return 25;
}
};
});
\ No newline at end of file
<div class="wrapper-cdek fillH">
<div class="row-cdek fillH">
<div class="cell-cdek fillH">
<div class="row">
<div class="col-xs-8">
<form name="filterForm" style="max-width: 1200px"
class="form-horizontal">
<div class="col-xs-3">
<div class="form-group">
<label class="col-sm-12 control-label">Номера заказов:</label>
</div>
<div class="form-group">
<input id="from" type="text" class="col-sm-12 form-control"
ng-model="filter.orderNumbers">
</div>
<div class="form-group">
<label class="col-sm-12 control-label">Дата заказа:</label>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">C:</label>
<div class="col-sm-10 input-group">
<input id="filterDateFrom" type="text" class="form-control"
bootstrap-datetimepicker="{format: 'DD.MM.YYYY', showClose: true}"
datetimepicker-api="fromDatetimepickerApi"
ng-model="filter.dateFrom"><span
class="input-group-addon"
ng-click="fromDatetimepickerApi.toggle()"><i
class="glyphicon glyphicon-calendar"></i></span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">По:</label>
<div class="col-sm-10 input-group">
<input id="filterDateTo" type="text" class="form-control"
bootstrap-datetimepicker="{format: 'DD.MM.YYYY', showClose: true}"
datetimepicker-api="toDatetimepickerApi"
ng-model="filter.dateTo"><span
class="input-group-addon"
ng-click="toDatetimepickerApi.toggle()"><i
class="glyphicon glyphicon-calendar"></i></span>
</div>
</div>
<div class="form-group checkbox-inline">
<label class="col-sm-12"><input type="checkbox"
ng-model="filter.error">Операция с ошибкой</label>
</div>
<div class="form-group checkbox-inline">
<label class="col-sm-12"><input type="checkbox"
ng-model="filter.orderError">Заказ заблокирован</label>
</div>
</div>
<div class="col-xs-4">
<div class="form-group">
<label class="col-sm-11 col-sm-offset-1 control-label">Статус заявки:</label>
</div>
<div class="form-group fillH"
ng-repeat="s in orderStatuses">
<label class="checkbox-inline col-sm-11 col-sm-offset-1"><input type="checkbox"
ng-model="filter.statuses[s.code]">{{s.title}}</label>
</div>
</div>
<div class="col-xs-2">
<div class="row">
<div class="col-xs-10">
<button type="button" ng-click="findOrders()"
class="btn btn-primary fillH" style="padding: 20px 16px;">Найти</button>
</div>
</div>
<div class="row">
<div class="col-xs-10">
<label>&nbsp;</label>
<!-- Для выравнивания по вертикали -->
<button type="button" class="btn btn-warning fillH"
ng-click="resetFilter()">Очистить</button>
</div>
</div>
</div>
</form>
</div>
<div class="col-xs-4">
<!--<div style="height: 10px"></div>-->
<div class="row" ng-show="information.failedCrFilesQuantity">
<label class="pull-right" style="color: red; font-size: x-large; font-weight: bold">Не прочитано CR файлов:
{{information.failedCrFilesQuantity}}</label>
</div>
<div class="row">
<label class="pull-right">Последние обновление заказов
получено в: {{information.getNewOrdersLastTime ? information.getNewOrdersLastTime : "Нет данных"}}</label>
</div>
<div class="row">
<label class="pull-right">Последние отправка заказов
произведена в: {{information.sendNewOrdersLastTime ? information.sendNewOrdersLastTime : "Нет данных"}}</label>
</div>
</div>
</div>
<div style="height: 10px"></div>
<button type="button" class="btn btn-primary pull-down" ng-click="orderListApi.exportDataAsCsv([0, 8, 9])">
Экспорт в CSV</button>
<button type="button" class="btn btn-primary" ng-click="cleanError()">
Очистить текст ошибки</button>
<button type="button" class="btn btn-primary" ng-click="solve()">
Снять блокировку с заказов</button>
<div style="height: 10px"></div>
</div>
</div>
<div class="row-cdek fillH">
<div class="cell-cdek fillV fillH">
<ag-grid-ext api="orderListApi" style="height: 100%;"
shared-object="this"
row-selection="multiple"
get-row-height="getRowHeight"
sorting-type="server"
class="ag-fresh fillH"
selection-changed="selectionChanged"
list-post-url="{{config.string('url.orders.list')}}">
<column width="27" suppress-size-to-fit="true" checkbox-selection="true">
<header-renderer>
<input type="checkbox" ng-model="sharedObject.selectAll" ng-change="sharedObject.orderListApi.selectAll(sharedObject.selectAll)">
</header-renderer>
</column>
<column width="80" header-name="Номер ИМ" field="orderNumber" sorting="orderNumber"></column>
<column width="80" header-name="№ накладной СДЭК" field="dispatchNumber" sorting="dispatchNumber"></column>
<column width="100" suppress-size-to-fit="true" header-name="Дата заказа" field="orderDateDate" sorting="orderDate"></column>
<column width="150" suppress-size-to-fit="true" header-name="Последнее событие" field="lastActionDateDate" sorting="lastActionDate"></column>
<column width="80" header-name="Статус" value-getter="data.status ? ctx.sharedObject.orderStatuses[data.status].title : ''"></column>
<column width="80" header-name="Статус СДЭК" value-getter="data.cdekStatus ? data.cdekStatus + ': ' + ctx.sharedObject.cdekStatuses[data.cdekStatus].title : ''"></column>
<column width="80" header-name="Статус Avon" value-getter="data.externalStatus ? data.externalStatus + ': ' + ctx.sharedObject.avonStatuses[data.externalStatus].title : ''"></column>
<column width="200" header-name="Ошибка последней операции" value-getter="data.lastError.errorCode + (data.lastError.errorMessage ? ' (' + data.lastError.errorMessage + ')' : '')">
<cell-renderer>
<span class="error-log" ng-show="{{data.lastError.status == sharedObject.config.object('actionStatusError')}}">
{{data.lastError.errorCode}}<br ng-show="data.lastError.errorMessage"/>{{data.lastError.errorMessage}}
</span>
<span class="warning-log" ng-show="{{data.lastError.status == sharedObject.config.object('actionStatusWarning')}}">
{{data.lastError.errorCode}}<br ng-show="data.lastError.errorMessage"/>{{data.lastError.errorMessage}}
</span>
</cell-renderer>
</column>
<column width="15" header-name="" field="">
<cell-renderer>
<span class="glyphicon glyphicon-exclamation-sign text-danger" ng-show="data.error" />
</cell-renderer>
</column>
<column width="100" suppress-size-to-fit="true" header-name="" field="">
<cell-renderer>
<button type="button" class="btn btn-default pull-right btn-sm fillV fillH"
ng-click="sharedObject.details(data)" >Подробнее...</button>
</cell-renderer>
</column>
</ag-grid-ext>
<label><input type="checkbox" ng-model="invertAll" ng-change="orderListApi.invertAll()">&nbsp;&nbsp;Инвертировать</label>
</div>
</div>
</div>
<div id="orderModal" class="modal fade" role="dialog" ng-controller="AvonDetailsCtrl">
<div class="modal-dialog" style="width: 90%">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">Детали заявки</h4>
</div>
<div class="modal-body">
<div class="row">
<div class="col-xs-4">
<p>№ накладной СДЭК: <b>{{$parent.order.orderNumber}}</b></p>
<p>№ накладной СДЭК: <b>{{$parent.order.dispatchNumber}}</b></p>
<p>Стоимость: <b>{{$parent.order.payment ? $parent.order.payment : ""}}</b></p>
</div>
<div class="col-xs-5">
<p>Дата заказа: <b>{{$parent.order.orderDateDate}}</b></p>
<p>Статус: <b>{{$parent.orderStatuses[$parent.order.status].title}}</b></p>
</div>
<div class="col-xs-3" ng-show="$parent.order.status == 4">
<form name="attachCdekOrderForm">
<div class="row">
<label class="col-sm-5 control-label">Задать № накладной СДЭК вручную:</label>
<input id="dispatchNumber" class="col-sm-5" type="text" ng-model="dispatchNumber">
<button type="button" ng-click="attachDispatchNumber()"
class="col-sm-2 btn btn-primary" style="padding: 8px 8px;">ОК</button>
</div>
</form>
</div>
</div>
<ag-grid-ext api="logListApi" style="height: 600px;"
class="ag-fresh fillH"
list-data="$parent.actions"
get-row-height="getRowHeight"
shared-object="config">
<column width="20" suppress-size-to-fit="true" header-name="" field="">
<cell-renderer>
<big><big>
<span class="glyphicon" ng-class="{{sharedObject.object('actionStatuses')[data.status].class}}" aria-hidden="true">
</big></big>
</cell-renderer>
</column>
<column width="35" header-name="Название операции" >
<cell-renderer>
{{data.operationName}}
</cell-renderer>
</column>
<column width="125" suppress-size-to-fit="true" header-name="Дата" field="actionDate"></column>
<column width="80" suppress-size-to-fit="true" header-name="Запрос" field="">
<cell-renderer>
<a target="_blank" ng-show="data.requestId" ng-href="{{sharedObject.string('url.actions.text', {id: data.requestId})}}"><u>Запрос</u></a>
</cell-renderer>
</column>
<column width="80" suppress-size-to-fit="true" header-name="Ответ" field="">
<cell-renderer>
<a target="_blank" ng-show="data.responseId" ng-href="{{sharedObject.string('url.actions.text', {id: data.responseId})}}"><u>Ответ</u></a>
</cell-renderer>
</column>
<column width="100" header-name="Сообщение об ошибке" field="">
<cell-renderer>
<span class="error-log" ng-show="{{data.status == sharedObject.object('actionStatusError')}}">
{{data.errorCode}}<br ng-show="data.errorMessage"/>{{data.errorMessage}}
</span>
<span class="warning-log" ng-show="{{data.status == sharedObject.object('actionStatusWarning')}}">
{{data.errorCode}}<br ng-show="data.errorMessage"/>{{data.errorMessage}}
</span>
<span class="success-log" ng-show="{{data.status == sharedObject.object('actionStatusSuccess')}}">
{{data.errorCode}}<br ng-show="data.errorMessage"/>{{data.errorMessage}}
</span>
</cell-renderer>
</column>
</ag-grid-ext>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Закрыть</button>
</div>
</div>
</div>
</div>
<a href="" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Интергация Avon <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#/orders">Заказы</a></li>
<!--<li><a href="#/settings">Настройки</a></li>-->
</ul>
angular
.module('AvonDataProvider', ['ConfigProvider'])
.provider(
'avonData',
function() {
this.$get = function($http, config) {
return {
resetOrderFilter : function(callback, error) {
$http.post(config.string('url.orders.resetFilter')).then(function successCallback(response) {
callback(response.data);
}, function errorCallback(response) {
if (error) {
error(response);
}
});
},
getOrderFilter : function(callback, error) {
$http.post(config.string('url.orders.getFilter')).then(function successCallback(response) {
callback(response.data);
}, function errorCallback(response) {
if (error) {
error(response);
}
});
},
solve : function(list, callback, error) {
$http.post(config.string('url.orders.solve'), list).then(function successCallback(response) {
callback(response.data);
}, function errorCallback(response) {
if (error) {
error(response);
}
});
},
expectRecreation : function(list, callback, error) {
$http.post(config.string('url.orders.expectRecreation'), list).then(function successCallback(response) {
callback(response.data);
}, function errorCallback(response) {
if (error) {
error(response);
}
});
},
attachDispatchNumber : function(orderId, dispatchNumber, callback, error) {
var params = {
orderId: orderId,
dispatchNumber: dispatchNumber
}
$http.get(config.string('url.orders.attachDispatchNumber'), {params: params}).then(function successCallback(response) {
callback(response.data);
}, function errorCallback(response) {
if (error) {
error(response);
}
});
},
cleanError : function(list, callback, error) {
$http.post(config.string('url.orders.cleanError'), list).then(function successCallback(response) {
callback(response.data);
}, function errorCallback(response) {
if (error) {
error(response);
}
});
},
receive : function(callback, error) {
$http.get(config.string('url.orders.receive')).then(function successCallback(response) {
callback(response.data);
}, function errorCallback(response) {
if (error) {
error(response);
}
});
},
sendPod : function(callback, error) {
$http.get(config.string('url.orders.sendPod')).then(function successCallback(response) {
callback(response.data);
}, function errorCallback(response) {
if (error) {
error(response);
}
});
},
sendCod : function(callback, error) {
$http.get(config.string('url.orders.sendCod')).then(function successCallback(response) {
callback(response.data);
}, function errorCallback(response) {
if (error) {
error(response);
}
});
},
information : function(callback, error) {
$http.get(config.string('url.orders.information')).then(function successCallback(response) {
callback(response.data);
}, function errorCallback(response) {
if (error) {
error(response);
}
});
},
actions : function(id, callback, error) {
config.ready(function () {
$http.get(config.string('url.actions.byOrderId', {id : id})).then(function successCallback(response) {
callback(response.data);
}, function errorCallback(response) {
if (error) {
error(response);
}
});
});
},
order : function(id, callback, error) {
config.ready(function () {
$http.post(config.string('url.orders.get', {id : id})).then(function successCallback(response) {
callback(response.data);
}, function errorCallback(response) {
if (error) {
error(response);
}
});
});
},
loadSettings : function(callback, error) {
config.ready(function () {
$http.get(config.string('url.settings.load')).then(function successCallback(response) {
callback(response.data);
}, function errorCallback(response) {
if (error) {
error(response);
}
});
});
},
saveSettings : function(settings, callback, error) {
config.ready(function () {
$http.post(config.string('url.settings.save'), settings).then(function successCallback(response) {
callback(response.data);
}, function errorCallback(response) {
if (error) {
error(response);
}
});
});
},
}
};
});
{
"partnerId": 3,
"actionStatusError": 1,
"actionStatusWarning": 2,
"actionStatusSuccess": 3,
"orderStatusToAttachCdekOrder": 4,
"url":{
"integration": {
"list": "/api/integration/list"
},
"orders": {
"list": "/api/avon/order/list",
"getFilter": "/api/avon/order/getFilter",
"resetFilter": "/api/avon/order/resetFilter",
"get": "/api/avon/order/get/{{id}}",
"solve": "/api/avon/order/solve",
"expectRecreation": "/api/avon/order/expectRecreation",
"attachDispatchNumber": "/api/avon/order/attachDispatchNumber",
"receive": "/api/avon/order/receive",
"cleanError": "/api/avon/order/cleanError",
"information": "/api/avon/order/information"
},
"actions": {
"byOrderId": "/api/avon/action/byOrderId/{{id}}",
"text": "/api/avon/action/text/{{id}}"
},
"settings": {
"load": "/api/avon/settings/load",
"save": "/api/avon/settings/save"
}
},
"orderStatuses": {
"1" : {"code": "1", "title": "Ожидает повторного получения"},
"2" : {"code": "2", "title": "Получен от Avon"},
"3" : {"code": "3", "title": "Создан DeliveryRequest"},
"4" : {"code": "4", "title": "Рассчитан тариф"},
"5" : {"code": "5", "title": "Обмен статусами"},
"6" : {"code": "6", "title": "Финальный"}
},
"cdekStatuses": {
"1" : {"code": "1", "title": "Создан"},
"2" : {"code": "2", "title": "Удален"},
"3" : {"code": "3", "title": "Принят на склад отправителя"},
"6" : {"code": "6", "title": "Выдан на отправку в г.-отправителе"},
"16" : {"code": "16", "title": "Возвращен на склад отправителя"},
"7" : {"code": "7", "title": "Сдан перевозчику в г.-отправителе"},
"21" : {"code": "21", "title": "Отправлен в г.-транзит"},
"22" : {"code": "22", "title": "Встречен в г.-транзите"},
"13" : {"code": "13", "title": "Принят на склад транзита"},
"17" : {"code": "17", "title": "Возвращен на склад транзита"},
"19" : {"code": "19", "title": "Выдан на отправку в г.-транзите"},
"20" : {"code": "20", "title": "Сдан перевозчику в г.-транзите"},
"8" : {"code": "8", "title": "Отправлен в г.-получатель"},
"9" : {"code": "9", "title": "Встречен в г.-получателе"},
"10" : {"code": "10", "title": "Принят на склад доставки"},
"12" : {"code": "12", "title": "Принят на склад до востребования"},
"11" : {"code": "11", "title": "Выдан на доставку"},
"18" : {"code": "18", "title": "Возвращен на склад доставки"},
"4" : {"code": "4", "title": "Вручен"},
"5" : {"code": "5", "title": "Не вручен"}
},
"avonStatuses": {
},
"actionStatuses": {
"1" : {"code": "1", "title": "Ошибка", "class": "'glyphicon-exclamation-sign text-danger'"},
"2" : {"code": "2", "title": "Предупреждение", "class": "'glyphicon-info-sign text-warning'"},
"3" : {"code": "3", "title": "Успех", "class": "'glyphicon-ok-sign text-success'"}
},
"i18n": {
"grid": {
"page": "Страница",
"more": "Еще",
"to": "по",
"of": "из",
"next": "Следующая",
"last": "Последняя",
"first": "Первая",
"previous": "Предыдущая",
"loadingOoo": "Загрузка...",
"noRowsToShow": "Нет данных для отображения"
}
},
"aggrid": {
"exportDataAsCsv": {
"skipHeader": "false",
"columnSeparator": ";"
}
}
}
\ No newline at end of file
<!DOCTYPE html>
<html lang="ru" ng-app="app">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title>Avon Integration</title>
<link rel="icon" href="/assets/img/favicon.ico">
<link rel="stylesheet" type="text/css" href="/assets/css/angular/ag-grid.min.css">
<link rel="stylesheet" type="text/css" href="/assets/css/angular/theme-fresh.css">
<link href="/assets/css/bootstrap/bootstrap-theme.min.css" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="/assets/css/jquery/bootstrap-datetimepicker.min.css">
<link rel="stylesheet" href="/assets/css/angular/select.min.css">
<link href="/assets/css/app.css" rel="stylesheet">
<script src="/assets/js/other/moment.min.js"></script>
<script src="/assets/js/jquery/jquery.min.js"></script>
<script src="/assets/js/jquery/bootstrap-datetimepicker.min.js" ></script>
<script src="/assets/js/bootstrap/bootstrap.min.js"></script>
<!--<script src="/assets/js/angular/angular.min.js"></script>-->
<script src="/assets/js/angular/angular.js"></script>
<script src="/assets/js/angular/angular-route.min.js"></script>
<script src="/assets/js/angular/angular-sanitize.min.js"></script>
<script src="/assets/js/angular/ag-grid.min.js"></script>
<script src="/assets/js/angular/ng-websocket.js"></script>
<script src="/assets/js/angular/select.min.js"></script>
<script src="/assets/js/cdek/bootstrap-datetimepicker.js"></script>
<script src="/assets/js/cdek/ag-grid-plugin.js"></script>
<script src="/assets/js/cdek/config.js"></script>
<script src="/assets/js/cdek/storage.js"></script>
<script src="/assets/js/cdek/alert-provider.js"></script>
<script src="/app/services/data.js"></script>
<script src="app/services/data.js"></script>
<script src="app/app.js"></script>
<script src="/app/controllers/MainCtrl.js"></script>
<script src="app/controllers/AvonOrdersCtrl.js"></script>
<script src="app/controllers/AvonDetailsCtrl.js"></script>
<script type="text/ng-template" id="bootstrapro/choices.tpl.html">
<ul class="ui-select-choices ui-select-choices-content ui-select-dropdown dropdown-menu" role="listbox" ng-show="$select.items.length > 0"><li class="ui-select-choices-group" id="ui-select-choices-{{ $select.generatedId }}"><div class="divider" ng-show="$select.isGrouped && $index > 0"></div><div ng-show="$select.isGrouped" class="ui-select-choices-group-label dropdown-header" ng-bind="$group.name"></div><div id="ui-select-choices-row-{{ $select.generatedId }}-{{$index}}" class="ui-select-choices-row" ng-class="{active: $select.isActive(this), disabled: $select.isDisabled(this)}" role="option"><a href="javascript:void(0)" class="ui-select-choices-row-inner"></a></div></li></ul>
</script>
<script type="text/ng-template" id="bootstrapro/match-multiple.tpl.html">
</script>
<script type="text/ng-template" id="bootstrapro/match.tpl.tpl.html">
</script>
<script type="text/ng-template" id="bootstrapro/select.tpl.html">
<div class="ui-select-container ui-select-bootstrap dropdown" ng-class="{open: $select.open}"><div class="ui-select-match"></div><input type="text" autocomplete="false" tabindex="-1" aria-expanded="true" aria-label="{{ $select.baseTitle }}" aria-owns="ui-select-choices-{{ $select.generatedId }}" aria-activedescendant="ui-select-choices-row-{{ $select.generatedId }}-{{ $select.activeIndex }}" class="form-control ui-select-search" placeholder="{{$select.placeholder}}" ng-model="$select.search" ng-show="$select.searchEnabled && $select.open"><div style="position:absolute;top:11px;right:11px"><i class="caret pull-right" ng-click="$select.toggle($event)"></i></div><div class="ui-select-choices"></div></div></div>
</script>
<script type="text/ng-template" id="bootstrapro/match.tpl.html">
<div class="ui-select-match" ng-hide="$select.open" ng-disabled="$select.disabled" ><span tabindex="-1" class="form-control ui-select-toggle" aria-label="{{ $select.baseTitle }} activate" ng-disabled="$select.disabled" ng-click="$select.activate()" style="outline: 0;"><span ng-show="$select.isEmpty()" class="ui-select-placeholder text-muted">{{$select.placeholder}}</span> <span ng-hide="$select.isEmpty()" class="ui-select-match-text pull-left" ng-class="{'ui-select-allow-clear': $select.allowClear && !$select.isEmpty()}" ng-transclude=""></span> <a ng-show="$select.allowClear && !$select.isEmpty()" aria-label="{{ $select.baseTitle }} clear" style="margin-right: 10px" ng-click="$select.clear($event)" class="btn btn-xs btn-link pull-right"><i class="glyphicon glyphicon-remove" aria-hidden="true"></i></a></span></div>
</script>
</head>
<body>
<nav ng-include="'/app/partials/navbar.html'" class="navbar navbar-default navbar-fixed-top" ng-controller="MainCtrl">
</nav>
<div class="container-fluid fillV fillH">
<div ng-view class="fillV fillH"></div>
</div>
</body>
</html>
\ No newline at end of file
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd" profile="leomax">
<import resource="classpath:com-cdek-integration-leomax-web.xml"/>
......
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd" profile="sm">
<import resource="classpath:com-cdek-integration-sm-web.xml"/>
......
......@@ -87,6 +87,11 @@
<artifactId>com.cdek.integration.sm.module</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>com.cdek.integration.avon</groupId>
<artifactId>integration-avon</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>com.cdek.integration.leomax</groupId>
<artifactId>com.cdek.integration.leomax.module</artifactId>
......
package com.cdek.platform.web;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import java.util.Arrays;
/**
* Проверка включение хотя бы одной интеграции.
*/
public class ProfileChecker implements InitializingBean,ApplicationContextAware{
private static final Logger LOGGER = LoggerFactory.getLogger(ProfileChecker.class);
protected ApplicationContext context;
@Override
public void afterPropertiesSet() throws Exception {
String[] activeProfiles = context.getEnvironment().getActiveProfiles();
if(activeProfiles==null || activeProfiles.length==0){
LOGGER.info("No active profile is specified");
LOGGER.info("Please, specify jvm parameter -Dspring.profiles.active=sm,avon");
throw new IllegalStateException("Request at least one integration profile");
}
LOGGER.info("Profiles:"+ Arrays.asList(activeProfiles));
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context=applicationContext;
}
}
......@@ -16,8 +16,11 @@
<context:property-placeholder location="file:${config.file}"/><!-- Префикс file нужен после переопределения переменной библиотекой JettyPlatform-->
<!-- Context for testing-->
<!--<import resource="classpath:com-cdek-integration-test-api.xml"/>-->
<bean id="profileChecker" class="com.cdek.platform.web.ProfileChecker"/>
<import resource="classpath:com-cdek-integration-sm.xml"/>
<import resource="classpath:com-cdek-integration-leomax.xml"/>
<import resource="classpath:com-cdek-integration-avon.xml"/>
<bean name="integrationController" class="com.cdek.platform.web.controllers.IntegrationController" >
<property name="integrationService" ref="integrationService"/>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment