Merge branch 'dev' of https://git.verdnatura.es/salix into dev
* 'dev' of https://git.verdnatura.es/salix: zone model and rest api module routes: front dev 80% filtro de rutas con paginación routes change model and first look Login sin url de redirección
This commit is contained in:
commit
a62285ff9e
|
@ -19,6 +19,7 @@ class Autocomplete extends Component {
|
|||
this.maxRow = 10;
|
||||
this.showField = this.showField || 'name';
|
||||
this.valueField = this.valueField || 'id';
|
||||
this.order = this.order || 'name ASC';
|
||||
this.items = this.data || [];
|
||||
this.displayValueMultiCheck = [];
|
||||
this._multiField = [];
|
||||
|
@ -219,38 +220,45 @@ class Autocomplete extends Component {
|
|||
}
|
||||
getItems() {
|
||||
let filter = {};
|
||||
if (!this.finding) {
|
||||
this.finding = true;
|
||||
|
||||
if (this.maxRow) {
|
||||
if (this.items) {
|
||||
filter.skip = this.items.length;
|
||||
}
|
||||
filter.limit = this.maxRow;
|
||||
filter.order = 'name ASC';
|
||||
}
|
||||
|
||||
let json = JSON.stringify(filter);
|
||||
|
||||
this.removeLoadMore = false;
|
||||
|
||||
this.$http.get(`${this.url}?filter=${json}`).then(
|
||||
json => {
|
||||
if (json.data.length) {
|
||||
json.data.forEach(
|
||||
el => {
|
||||
if (this.multiple) {
|
||||
el.checked = this.field.indexOf(el[this.valueField]) !== -1;
|
||||
}
|
||||
this.items.push(el);
|
||||
}
|
||||
);
|
||||
if (filter.skip === 0 && this.maxRow && json.data.length < this.maxRow) {
|
||||
this.removeLoadMore = true;
|
||||
}
|
||||
} else {
|
||||
this.maxRow = false;
|
||||
if (this.maxRow) {
|
||||
if (this.items) {
|
||||
filter.skip = this.items.length;
|
||||
}
|
||||
filter.limit = this.maxRow;
|
||||
filter.order = this.order;
|
||||
}
|
||||
);
|
||||
|
||||
let json = JSON.stringify(filter);
|
||||
|
||||
this.removeLoadMore = false;
|
||||
|
||||
this.$http.get(`${this.url}?filter=${json}`).then(
|
||||
json => {
|
||||
if (json.data.length) {
|
||||
json.data.forEach(
|
||||
el => {
|
||||
if (this.multiple) {
|
||||
el.checked = this.field.indexOf(el[this.valueField]) !== -1;
|
||||
}
|
||||
this.items.push(el);
|
||||
}
|
||||
);
|
||||
if (filter.skip === 0 && this.maxRow && json.data.length < this.maxRow) {
|
||||
this.removeLoadMore = true;
|
||||
}
|
||||
} else {
|
||||
this.maxRow = false;
|
||||
}
|
||||
this.finding = false;
|
||||
},
|
||||
() => {
|
||||
this.finding = false;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
$onInit() {
|
||||
this.findMore = this.url && this.maxRow;
|
||||
|
@ -308,7 +316,8 @@ module.component('vnAutocomplete', {
|
|||
data: '<?',
|
||||
field: '=',
|
||||
label: '@',
|
||||
multiple: '@?'
|
||||
multiple: '@?',
|
||||
order: '@?'
|
||||
},
|
||||
transclude: {
|
||||
tplItem: '?tplItem'
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
{}
|
||||
{
|
||||
"Production" : "Production"
|
||||
}
|
|
@ -1,13 +1,66 @@
|
|||
{
|
||||
"module": "route",
|
||||
"name": "Route",
|
||||
|
||||
"name": "Routes",
|
||||
"icon" : "local_shipping",
|
||||
"validations" : false,
|
||||
"routes": [
|
||||
{
|
||||
"url": "/routes",
|
||||
"state": "routes",
|
||||
"abstract": true,
|
||||
"component": "ui-view"
|
||||
},
|
||||
{
|
||||
"url": "/list",
|
||||
"state": "routes.index",
|
||||
"component": "vn-route-index"
|
||||
},
|
||||
{
|
||||
"url": "/create",
|
||||
"state": "routes.create",
|
||||
"component": "vn-route-create"
|
||||
},
|
||||
{
|
||||
"url": "/:id",
|
||||
"state": "routes.card",
|
||||
"abstract": true,
|
||||
"component": "vn-route-card"
|
||||
},
|
||||
{
|
||||
"url": "/basicData",
|
||||
"state": "routes.card.basicData",
|
||||
"component": "vn-route-basic-data",
|
||||
"params": {
|
||||
"route": "$ctrl.route"
|
||||
},
|
||||
"menu": {
|
||||
"description": "Datos básicos",
|
||||
"icon": "person"
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/logisticData",
|
||||
"state": "routes.card.logisticData",
|
||||
"component": "vn-route-logistic-data",
|
||||
"params": {
|
||||
"route": "$ctrl.route"
|
||||
},
|
||||
"menu": {
|
||||
"description": "Datos logísticos",
|
||||
"icon": "local_shipping"
|
||||
}
|
||||
},
|
||||
{
|
||||
"url": "/tickets",
|
||||
"state": "routes.card.tickets",
|
||||
"component": "vn-route-tickets",
|
||||
"params": {
|
||||
"route": "$ctrl.route"
|
||||
},
|
||||
"menu": {
|
||||
"description": "Tickets asignados",
|
||||
"icon": "assignment"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<vn-horizontal>
|
||||
<mg-ajax
|
||||
path="/route/api/Deliveries/{{edit.params.id}}"
|
||||
actions="$ctrl.route = edit.model"
|
||||
options="mgEdit">
|
||||
</mg-ajax>
|
||||
<vn-empty style="min-width: 18em; padding-left: 1em; padding-bottom: 1em;">
|
||||
<vn-card>
|
||||
<vn-vertical class="margin-medium" pad-medium-top pad-medium-bottom>
|
||||
<vn-horizontal>
|
||||
<vn-one>
|
||||
<i class="material-icons descriptor-icon">local_shipping</i>
|
||||
</vn-one>
|
||||
<vn-vertical vn-two>
|
||||
<div class="margin-none"><span translate>Ruta</span> {{::$ctrl.route.id}}</div>
|
||||
<div class="margin-none">{{$ctrl.route.date | date:'dd/MM/yyyy'}}</div>
|
||||
</vn-vertical>
|
||||
</vn-horizontal>
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-left-menu></vn-left-menu>
|
||||
</vn-empty>
|
||||
<vn-auto>
|
||||
<vn-vertical style="max-width: 70em; margin: 0 auto;" ui-view></vn-vertical>
|
||||
</vn-auto>
|
||||
</vn-horizontal>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import ngModule from '../module';
|
||||
|
||||
class RouteCard {
|
||||
constructor() {
|
||||
this.route = null;
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.component('vnRouteCard', {
|
||||
template: require('./card.html'),
|
||||
controller: RouteCard
|
||||
});
|
|
@ -0,0 +1,29 @@
|
|||
<mg-ajax path="/route/api/Delivery/createRoute" options="vnPost"></mg-ajax>
|
||||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
data="$ctrl.route"
|
||||
form="form"
|
||||
save="post">
|
||||
</vn-watcher>
|
||||
<form name="form" ng-submit="$ctrl.onSubmit()" margin-medium>
|
||||
<div style="max-width: 70em; margin: 0 auto;">
|
||||
<vn-card>
|
||||
<vn-vertical pad-large>
|
||||
<vn-title>Create Route</vn-title>
|
||||
<vn-horizontal>
|
||||
<vn-date-picker vn-one label="Date" field="$ctrl.route.date"></vn-date-picker>
|
||||
<vn-textfield vn-one label="Agency" field="$ctrl.route.agency"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Driver" field="$ctrl.route.driver"></vn-textfield>
|
||||
<vn-textfield vn-one label="Vehicle" field="$ctrl.route.vehicle"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
|
||||
</vn-vertical>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit label="Create and edit"></vn-submit>
|
||||
<vn-button label="Create" ng-click="watcher.submitBack()"></vn-button>
|
||||
</vn-button-bar>
|
||||
</div>
|
||||
</form>
|
|
@ -0,0 +1,21 @@
|
|||
import ngModule from '../module';
|
||||
|
||||
class RouteCreate {
|
||||
constructor($scope, $state) {
|
||||
this.$ = $scope;
|
||||
this.$state = $state;
|
||||
this.route = {};
|
||||
console.log('hi world');
|
||||
}
|
||||
onSubmit() {
|
||||
this.$.watcher.submit().then(
|
||||
json => this.$state.go('routes.card.basicData', {id: json.data.id})
|
||||
);
|
||||
}
|
||||
}
|
||||
RouteCreate.$inject = ['$scope', '$state'];
|
||||
|
||||
ngModule.component('vnRouteCreate', {
|
||||
template: require('./create.html'),
|
||||
controller: RouteCreate
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
<<mg-ajax path="/client/api/Clients/filter" options="mgIndex"></mg-ajax>
|
||||
<mg-ajax path="/route/api/Deliveries/filter" options="mgIndex"></mg-ajax>
|
||||
<div margin-medium>
|
||||
<div style="max-width: 40em; margin: 0 auto;">
|
||||
<vn-card>
|
||||
|
@ -8,16 +8,16 @@
|
|||
on-search="$ctrl.search(index)"
|
||||
advanced="true"
|
||||
search="$ctrl.model.search"
|
||||
popover="vn-client-search-panel">
|
||||
popover="vn-route-search-panel">
|
||||
</vn-searchbar>
|
||||
</vn-horizontal>
|
||||
</vn-card>
|
||||
<vn-card margin-medium-top>
|
||||
<vn-item-route ng-repeat="route in index.model.instances" title="View Route" client="route"></vn-item-route>
|
||||
<vn-item-route ng-repeat="route in index.model.instances" title="View Route" route="route"></vn-item-route>
|
||||
</vn-card>
|
||||
<vn-paging index="index" total="index.model.count"></vn-paging>
|
||||
</div>
|
||||
<a ui-sref="create" fixed-bottom-right>
|
||||
<a ui-sref="routes.create" fixed-bottom-right>
|
||||
<vn-float-button icon="add"></vn-float-button>
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
<a vn-horizontal ui-sref="routes.card.basicData({id: {{$ctrl.route.id}} })" pad-medium border-solid-bottom>
|
||||
<vn-one>
|
||||
<vn-vertical>
|
||||
<vn-one>
|
||||
<span translate>ID_RUTA</span>:
|
||||
<strong>{{$ctrl.route.id}}</strong>
|
||||
</vn-one>
|
||||
<vn-one>
|
||||
<span translate>Fecha</span>:
|
||||
<strong>{{$ctrl.route.date | date:'dd/MM/yyyy'}}</strong>
|
||||
</vn-one>
|
||||
<vn-one>
|
||||
<span>m<sup>3</sup></span>:
|
||||
<strong>{{$ctrl.route.m3}}</strong>
|
||||
</vn-one>
|
||||
</vn-vertical>
|
||||
</vn-one>
|
||||
<vn-none pad-medium-top>
|
||||
<vn-icon icon="print"></vn-icon>
|
||||
</vn-none>
|
||||
</a>
|
|
@ -0,0 +1,8 @@
|
|||
import ngModule from '../module';
|
||||
|
||||
ngModule.component('vnItemRoute', {
|
||||
template: require('./item-route.html'),
|
||||
bindings: {
|
||||
route: '<'
|
||||
}
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
vn-item-route {
|
||||
display: block;
|
||||
}
|
||||
vn-item-route a {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
vn-item-route a:hover {
|
||||
color: white;
|
||||
background-color: #424242;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"Routes" : "Routes"
|
||||
}
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
|
||||
"Routes" : "Rutas"
|
||||
}
|
||||
|
|
|
@ -2,3 +2,6 @@ export * from './module';
|
|||
|
||||
// import components
|
||||
import './index/index';
|
||||
import './search-panel/search-panel';
|
||||
import './create/create';
|
||||
import './card/card';
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"Client id": "Id cliente",
|
||||
"Tax number": "NIF/CIF",
|
||||
"Name": "Nombre",
|
||||
"Social name": "Razon social",
|
||||
"Town/City": "Ciudad",
|
||||
"Postcode": "Código postal",
|
||||
"Email": "Correo electrónico",
|
||||
"Phone": "Teléfono"
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<div pad-large style="min-width: 30em">
|
||||
<form ng-submit="$ctrl.onSearch()">
|
||||
<vn-horizontal>
|
||||
<vn-date-picker vn-one label="Date" model="$ctrl.filter.date"></vn-date-picker>
|
||||
<vn-autocomplete
|
||||
vn-one
|
||||
label="Zone"
|
||||
field="$ctrl.filter.zone"
|
||||
url="/route/api/Zones"
|
||||
order="printingOrder ASC"
|
||||
></vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
|
||||
<vn-horizontal>
|
||||
<vn-textfield vn-one label="Postcode" model="$ctrl.filter.postcode"></vn-textfield>
|
||||
<vn-textfield vn-one label="Route_Id" model="$ctrl.filter.id"></vn-textfield>
|
||||
</vn-horizontal>
|
||||
|
||||
<vn-horizontal margin-large-top>
|
||||
<vn-submit label="Search"></vn-submit>
|
||||
</vn-horizontal>
|
||||
</form>
|
||||
</div>
|
|
@ -0,0 +1,27 @@
|
|||
import ngModule from '../module';
|
||||
|
||||
export default class Controller {
|
||||
constructor($window) {
|
||||
this.$window = $window;
|
||||
// onSubmit() is defined by @vnSearchbar
|
||||
this.onSubmit = () => {};
|
||||
}
|
||||
onSearch() {
|
||||
this.setStorageValue();
|
||||
this.onSubmit(this.filter);
|
||||
}
|
||||
$onChanges() {
|
||||
var value = JSON.parse(this.$window.sessionStorage.getItem('filter'));
|
||||
if (value !== undefined)
|
||||
this.filter = value;
|
||||
}
|
||||
setStorageValue() {
|
||||
this.$window.sessionStorage.setItem('filter', JSON.stringify(this.filter));
|
||||
}
|
||||
}
|
||||
Controller.$inject = ['$window'];
|
||||
|
||||
ngModule.component('vnRouteSearchPanel', {
|
||||
template: require('./search-panel.html'),
|
||||
controller: Controller
|
||||
});
|
|
@ -45,8 +45,9 @@ vn-home {
|
|||
}
|
||||
|
||||
i{
|
||||
font-size: 50px !important;
|
||||
font-size: 75px !important;
|
||||
margin: 0 auto;
|
||||
padding-top: 15px;
|
||||
}
|
||||
&:hover{
|
||||
opacity: 0.7;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
<vn-icon icon="account_circle" translate-attr="{title: 'Profile'}" style="font-size: 35px;"></vn-icon>
|
||||
<ul class="mdl-menu mdl-js-menu mdl-menu--bottom-right" pad-small for="apps">
|
||||
<li class="mdl-menu__item" ng-repeat="mod in $ctrl.modules track by $index" ui-sref="{{::mod.route.state}}">
|
||||
<vn-icon ng-if="mod.icon && !mod.icon.startsWith('/')" icon="{{::mod.icon}}"></vn-icon>
|
||||
<img ng-if="mod.icon && mod.icon.startsWith('/')" ng-src="{{::mod.icon}}" />
|
||||
<span translate="{{::mod.name}}"></span>
|
||||
</li>
|
||||
|
|
|
@ -8,6 +8,11 @@ vn-main-menu {
|
|||
vertical-align: middle;
|
||||
margin-top: -3px;
|
||||
}
|
||||
i{
|
||||
float: left;
|
||||
padding-top: 13px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
}
|
||||
li.mdl-menu__item:hover{
|
||||
background-color: #FF9300;
|
||||
|
|
|
@ -9,5 +9,7 @@
|
|||
"Can't contact with server": "No se pudo contactar con el servidor",
|
||||
"Push on applications menu": "Para abrir un módulo pulsa en el menú de aplicaciones",
|
||||
"Clients": "Clientes",
|
||||
"Routes" : "Rutas",
|
||||
"Production" : "Producción",
|
||||
"Modules access" : "Acceso a módulos"
|
||||
}
|
|
@ -10,10 +10,11 @@ module.exports = function(app) {
|
|||
});
|
||||
|
||||
app.post('/login', function(req, res) {
|
||||
let user = req.body.user ? req.body.user : "";
|
||||
let password = req.body.password;
|
||||
let body = req.body;
|
||||
let user = body.user;
|
||||
let password = body.password;
|
||||
let syncOnFail = true;
|
||||
let usesEmail = user.indexOf('@') !== -1;
|
||||
let usesEmail = user && user.indexOf('@') !== -1;
|
||||
|
||||
login();
|
||||
|
||||
|
@ -38,26 +39,23 @@ module.exports = function(app) {
|
|||
return;
|
||||
}
|
||||
|
||||
let parsedLocation;
|
||||
let loginUrl;
|
||||
let shouldContinue = false;
|
||||
let continueUrl;
|
||||
|
||||
if (req.body.location)
|
||||
parsedLocation = url.parse(req.body.location, true);
|
||||
|
||||
if (parsedLocation && parsedLocation.query) {
|
||||
loginUrl = applications[parsedLocation.query.apiKey];
|
||||
shouldContinue = parsedLocation.query.continue;
|
||||
}
|
||||
|
||||
try {
|
||||
let query = url.parse(req.body.location, true).query;
|
||||
loginUrl = applications[query.apiKey];
|
||||
continueUrl = query.continue;
|
||||
} catch (e) {}
|
||||
|
||||
if (!loginUrl)
|
||||
loginUrl = applications.default;
|
||||
|
||||
res.json({
|
||||
res.send(JSON.stringify({
|
||||
token: token.id,
|
||||
continue: shouldContinue,
|
||||
continue: continueUrl,
|
||||
loginUrl: loginUrl
|
||||
});
|
||||
}));
|
||||
}
|
||||
function findCb(err, instance) {
|
||||
if (!instance || instance.password !== md5(password)) {
|
||||
|
@ -78,9 +76,9 @@ module.exports = function(app) {
|
|||
}
|
||||
function badLogin() {
|
||||
res.status(401);
|
||||
res.json({
|
||||
res.send(JSON.stringify({
|
||||
message: 'Login failed'
|
||||
});
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
module.exports = function(Delivery) {
|
||||
Delivery.installMethod('filter', filterRoutes);
|
||||
|
||||
function filterRoutes(params) {
|
||||
if (params.search)
|
||||
return searchWhere(params);
|
||||
return andWhere(params);
|
||||
}
|
||||
|
||||
function searchWhere(params) {
|
||||
return {
|
||||
where: {
|
||||
or: [
|
||||
{id: params.search},
|
||||
{name: {regexp: params.search}}
|
||||
]
|
||||
|
||||
},
|
||||
skip: (params.page - 1) * params.size,
|
||||
limit: params.size
|
||||
};
|
||||
}
|
||||
|
||||
function andWhere(params) {
|
||||
let filters = {
|
||||
where: {},
|
||||
skip: (params.page - 1) * params.size,
|
||||
limit: params.size
|
||||
};
|
||||
|
||||
delete params.page;
|
||||
delete params.size;
|
||||
|
||||
if (params.phone) {
|
||||
filters.where.or = [
|
||||
{phone: params.phone},
|
||||
{mobile: params.phone}
|
||||
];
|
||||
delete params.phone;
|
||||
}
|
||||
Object.keys(params).forEach(
|
||||
key => {
|
||||
filters.where[key] = {regexp: params[key]};
|
||||
}
|
||||
);
|
||||
return filters;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = function(Delivery) {
|
||||
require('../methods/filter.js')(Delivery);
|
||||
};
|
|
@ -1,15 +1,21 @@
|
|||
{
|
||||
"name": "Route",
|
||||
"name": "Delivery",
|
||||
"base": "MyModel",
|
||||
"validateUpsert": true,
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "Number",
|
||||
"id": true,
|
||||
"description": "Identifier"
|
||||
"type": "Number",
|
||||
"forceId": false
|
||||
},
|
||||
"date": {
|
||||
"type": "date"
|
||||
"type": "Date"
|
||||
},
|
||||
"m3":{
|
||||
"type": "Number"
|
||||
},
|
||||
"warehouseFk":{
|
||||
"type": "Number"
|
||||
}
|
||||
},
|
||||
"acls": [
|
||||
|
@ -25,7 +31,5 @@
|
|||
"principalId": "root",
|
||||
"permission": "ALLOW"
|
||||
}
|
||||
],
|
||||
"validations": [],
|
||||
"methods": {}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "Zone",
|
||||
"base": "MyModel",
|
||||
"validateUpsert": true,
|
||||
"properties": {
|
||||
"id": {
|
||||
"id": true,
|
||||
"type": "Number",
|
||||
"forceId": false
|
||||
},
|
||||
"name": {
|
||||
"type": "String"
|
||||
},
|
||||
"printingOrder":{
|
||||
"type": "Number"
|
||||
}
|
||||
},
|
||||
"acls": [
|
||||
{
|
||||
"accessType": "*",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "DENY"
|
||||
},
|
||||
{
|
||||
"accessType": "*",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "root",
|
||||
"permission": "ALLOW"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -27,15 +27,5 @@
|
|||
"password": "",
|
||||
"connectTimeout": 20000,
|
||||
"acquireTimeout": 20000
|
||||
},
|
||||
"client": {
|
||||
"name": "client",
|
||||
"connector": "remote",
|
||||
"url": "http://localhost:3002/api"
|
||||
},
|
||||
"route": {
|
||||
"name": "route",
|
||||
"connector": "remote",
|
||||
"url": "http://localhost:3004/api"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,10 +27,5 @@
|
|||
"password": "",
|
||||
"connectTimeout": 20000,
|
||||
"acquireTimeout": 20000
|
||||
},
|
||||
"client": {
|
||||
"name": "client",
|
||||
"connector": "remote",
|
||||
"url": "http://localhost:3002/api"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,9 +42,10 @@
|
|||
"Account": {
|
||||
"dataSource": "auth"
|
||||
},
|
||||
"Route": {
|
||||
"dataSource": "route",
|
||||
"public": true
|
||||
"Delivery": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"Zone": {
|
||||
"dataSource": "vn"
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue