Merge branch 'dev' of https://git.verdnatura.es/salix into dev

This commit is contained in:
Javi Gallego 2018-03-28 10:45:13 +02:00
commit 6136a1c8de
46 changed files with 456 additions and 144 deletions

View File

@ -30,7 +30,7 @@
<vn-horizontal>
<vn-autocomplete vn-one
field="$ctrl.address.agencyModeFk"
url="/client/api/AgencyModes"
url="/client/api/AgencyModes/active"
show-field="name"
value-field="id"
label="Agency">

View File

@ -1,5 +1,4 @@
import ngModule from '../module';
import './style.css';
export default class Controller {
constructor($http, $state) {

View File

@ -105,9 +105,9 @@
<vn-horizontal vn-one>
<vn-one margin-medium>
<h5 translate>Default address</h5>
<p>{{$ctrl.address.nickname}}</p>
<p><span>{{$ctrl.address.street}}</span></p>
<p><span>{{$ctrl.address.city}}</span></p>
<p>{{$ctrl.summary.addresses[0].nickname}}</p>
<p><span>{{$ctrl.summary.addresses[0].street}}</span></p>
<p><span>{{$ctrl.summary.addresses[0].city}}</span></p>
</vn-one>
<vn-one margin-medium>
<h5 translate>Web access</h5>
@ -122,23 +122,19 @@
</vn-one>
<vn-one margin-medium>
<h5 translate>Recovery</h5>
<vn-vertical ng-if="$ctrl.recovery">
<p><vn-label translate>Since</vn-label> {{$ctrl.recovery.started | date:'dd/MM/yyyy'}}</p>
<p><vn-label translate>To</vn-label> {{$ctrl.recovery.finished | date:'dd/MM/yyyy'}}</p>
<p><vn-label translate>Amount</vn-label> {{$ctrl.recovery.amount | currency:'€':2}}</p>
<p><vn-label translate>Period</vn-label> {{$ctrl.recovery.period}}</p>
<vn-vertical ng-if="$ctrl.summary.recovery">
<p><vn-label translate>Since</vn-label> {{$ctrl.summary.recovery.started | date:'dd/MM/yyyy'}}</p>
<p><vn-label translate>To</vn-label> {{$ctrl.summary.recovery.finished | date:'dd/MM/yyyy'}}</p>
<p><vn-label translate>Amount</vn-label> {{$ctrl.summary.recovery.amount | currency:'€ ':2}}</p>
<p><vn-label translate>Period</vn-label> {{$ctrl.summary.recovery.period}}</p>
</vn-vertical>
</vn-one>
</vn-horizontal>
<vn-horizontal vn-one>
<vn-one margin-medium>
<h5 translate>Total greuge</h5>
<p><vn-label translate>Total</vn-label> {{$ctrl.greuge.sumAmount | currency:'€':2}}</p>
</vn-one>
<vn-one margin-medium>
<h5 translate>Credit</h5>
<h5 translate>Financial data</h5>
<p><vn-label translate>Mana</vn-label> {{$ctrl.summary.mana.mana | currency:'€ ':2}}</p>
<p><vn-label translate>Risk</vn-label> {{$ctrl.summary.debt.debt | currency:'€ ':2}}</p>
<p><vn-label translate>Average invoiced</vn-label> {{$ctrl.summary.averageInvoiced.invoiced | currency:'€ ':2}}</p>
<p><vn-label translate>Total greuge</vn-label> {{$ctrl.summary.totalGreuge | currency:'€ ':2}}</p>
<p>
<vn-label translate>Credit</vn-label>
{{$ctrl.summary.credit | currency:'€ ':2}}

View File

@ -1,73 +1,22 @@
import ngModule from '../module';
import './style.scss';
class Controller {
constructor($http) {
this.$http = $http;
}
$onChanges() {
if (!this.client || !this.client.id)
if (!this.client)
return;
this.getSummary();
this.getGreuse();
this.getRecoveries();
}
getSummary() {
let filter = {
include: [
{relation: 'account', scope: {fields: ['name', 'active']}},
{relation: 'salesPerson', scope: {fields: ['name']}},
{relation: 'country', scope: {fields: ['country']}},
{relation: 'province', scope: {fields: ['name']}},
{relation: 'contactChannel', scope: {fields: ['name']}},
{relation: 'payMethod', scope: {fields: ['name']}},
{
relation: 'addresses',
scope: {
where: {isDefaultAddress: true},
fields: ['nickname', 'street', 'city', 'postalCode']
}
}
]
};
filter = encodeURIComponent(JSON.stringify(filter));
let query = `/client/api/Clients/${this.client.id}?filter=${filter}`;
this.$http.get(query).then(res => {
if (res.data) {
this.$http.get(`/client/api/Clients/${this.client.id}/summary`).then(res => {
if (res && res.data)
this.summary = res.data;
this.address = res.data.addresses[0];
}
});
}
getGreuse() {
let query = `/client/api/Greuges/${this.client.id}/sumAmount`;
this.$http.get(query).then(res => {
if (res.data)
this.greuge = res.data;
});
}
getRecoveries() {
let filter = {
where: {
and: [{clientFk: this.client.id}, {or: [{finished: null}, {finished: {gt: Date.now()}}]}]
},
limit: 1
};
filter = encodeURIComponent(JSON.stringify(filter));
let query = `/client/api/Recoveries?filter=${filter}`;
this.$http.get(query).then(res => {
if (res.data)
this.recovery = res.data[0];
});
}
}
Controller.$inject = ['$http'];
ngModule.component('vnClientSummary', {

View File

@ -1,2 +1,7 @@
Default address: Consignatario pred.
Total greuge: Greuge total
Financial data: Datos financieros
Mana: Maná
Risk: Riesgo
Secured credit: Crédito asegurado
Average invoiced: Consumo medio

View File

@ -0,0 +1,3 @@
vn-dialog vn-one {
min-width: 200px
}

View File

@ -8,6 +8,7 @@
</input>
<div class="icons">
<vn-icon
ng-show="!$ctrl.disabled"
icon="clear"
class="clear"
ng-click="$ctrl.onClearClick($event)"

View File

@ -1,5 +1,5 @@
import ngModule from '../../module';
import Component from '../../lib/component';
import Input from '../../lib/input';
import './style.scss';
/**
@ -11,7 +11,7 @@ import './style.scss';
* @property {Object} intialData A initial data to avoid the server request used to get the selection
* @property {Boolean} multiple Wether to allow multiple selection
*/
export default class Autocomplete extends Component {
export default class Autocomplete extends Input {
constructor($element, $scope, $http, $transclude) {
super($element, $scope);
this.$http = $http;

View File

@ -1,3 +1,5 @@
vn-check {
float: left;
& > .mdl-checkbox {
width: initial;
}
}

View File

@ -1,5 +1,6 @@
.vn-grid {
border-collapse: collapse;
width: 100%;
td, th{
text-align: left;
padding: 10px;

View File

@ -20,7 +20,7 @@
info_outline
</i>
<i class="material-icons pointer"
ng-show="$ctrl.hasValue && ($ctrl.hasFocus || $ctrl.hasMouseIn)"
ng-show="!$ctrl.disabled && $ctrl.hasValue && ($ctrl.hasFocus || $ctrl.hasMouseIn)"
ng-click="$ctrl.clear()">
clear
</i>

View File

@ -37,7 +37,7 @@ class ItemTags {
addItemTag() {
if (this.instancedItemTags) {
this.instancedItemTags.push({value: null, itemFk: this.params.id, tagFk: null, priority: null, showAddIcon: true});
this.instancedItemTags.push({value: null, itemFk: this.params.id, tagFk: null, priority: this.getMaxPriority(this.instancedItemTags) + 1, showAddIcon: true});
this._setIconAdd();
}
}
@ -54,6 +54,15 @@ class ItemTags {
}
}
getMaxPriority(instancedItemTags) {
let max = 0;
instancedItemTags.forEach(tag => {
if (tag.priority > max)
max = tag.priority;
});
return max;
}
_equalItemTags(oldTag, newTag) {
return oldTag.tagFk === newTag.tagFk && oldTag.value === newTag.value && oldTag.priority === newTag.priority;
}

View File

@ -96,8 +96,14 @@
},
{
"url" : "/package",
"abstract": true,
"state": "ticket.card.package",
"component": "vn-ticket-package",
"component": "ui-view"
},
{
"url" : "/index",
"state": "ticket.card.package.index",
"component": "vn-ticket-package-index",
"params": {
"ticket": "$ctrl.ticket"
},
@ -109,6 +115,12 @@
{
"url": "/tracking",
"state": "ticket.card.tracking",
"abstract": true,
"component": "ui-view"
},
{
"url" : "/index",
"state": "ticket.card.tracking.index",
"component": "vn-ticket-tracking",
"params": {
"ticket": "$ctrl.ticket"
@ -118,6 +130,14 @@
"icon": "remove_red_eye"
}
},
{
"url": "/edit",
"state": "ticket.card.tracking.edit",
"component": "vn-ticket-tracking-edit",
"params": {
"ticket": "$ctrl.ticket"
}
},
{
"url": "/create",
"state": "ticket.card.tracking.create",
@ -137,6 +157,18 @@
"description": "Sale",
"icon": "icon-lines"
}
},
{
"url" : "/sale-checked",
"state": "ticket.card.saleChecked",
"component": "vn-ticket-sale-checked",
"params": {
"ticket": "$ctrl.ticket"
},
"menu": {
"description": "Sale Checked",
"icon": "assignment"
}
}
]
}

View File

@ -18,13 +18,13 @@
vn-one class="list list-element text-center"
pad-small-bottom
ng-repeat="expedition in index.model.instances track by expedition.id">
<vn-none pad-medium-h style="color:#FFA410;">
<vn-one pad-medium-h style="color:#FFA410;">
<i
pointer
class="material-icons"
vn-tooltip="delete expedition"
ng-click="$ctrl.deleteExpedition(expedition)">delete</i>
</vn-none>
</vn-one>
<vn-one pad-medium-h>{{expedition.itemFk}}</vn-one>
<vn-one pad-medium-h>{{expedition.item.name}}</vn-one>
<vn-one pad-medium-h>{{expedition.package.name}}</vn-one>

View File

@ -19,3 +19,11 @@ Sale: Lineas del pedido
Some fields are invalid: Algunos campos no son válidos
The observation type must be unique: El tipo de observación debe ser único
Tickets: Tickets
Date : Fecha
Employee : Empleado
State: Estado
Tracking: Revisión
Created : Añadido
New : Nuevo
New state: Nuevo estado
Sale Checked: Control clientes

View File

@ -1,4 +1,4 @@
import ngModule from '../module';
import ngModule from '../../module';
class Controller {
@ -8,7 +8,6 @@ class Controller {
this.$translate = $translate;
this.vnApp = vnApp;
this.removedPackages = [];
this.updatedPackages = [];
}
submit() {
@ -23,10 +22,16 @@ class Controller {
if (typeof item.id === 'undefined')
packagesObj.create.push(item);
if (typeof item.id !== 'undefined' && angular.equals(item, this.oldPackages[item.id]))
if (typeof item.id !== 'undefined' && !this.packageEquals(item, this.oldPackages[item.id]))
packagesObj.update.push(item);
});
if (this.$.form.$invalid)
return this.vnApp.showMessage(this.$translate.instant('Some fields are invalid'));
if (!this.hasChanges(packagesObj))
return this.vnApp.showMessage(this.$translate.instant('No changes to save'));
this.$http.post(query, packagesObj).then(res => {
this.$.index.accept();
});
@ -55,16 +60,31 @@ class Controller {
}
setOldPackages() {
if (this.oldPackages && !this.$.watcher.dataChanged())
return;
this.oldPackages = [];
this.removedPackages = [];
this.packages.forEach(item => {
this.oldPackages[item.id] = item;
this.oldPackages[item.id] = Object.assign({}, item);
});
}
packageEquals(newPackage, oldPackage) {
return newPackage.packagingFk === oldPackage.packagingFk && newPackage.quantity == oldPackage.quantity;
}
Controller.$inject = ['$http', '$scope'];
hasChanges(packagesObj) {
if (packagesObj.create.length || packagesObj.update.length || packagesObj.delete.length)
return true;
ngModule.component('vnTicketPackage', {
return false;
}
}
Controller.$inject = ['$http', '$scope', '$translate', 'vnApp'];
ngModule.component('vnTicketPackageIndex', {
template: require('./package.html'),
controller: Controller,
bindings: {

View File

@ -1,7 +1,7 @@
import './package.js';
describe('Ticket', () => {
describe('Component vnTicketPackage', () => {
describe('Component vnTicketPackageIndex', () => {
let $componentController;
let controller;
let $httpBackend;
@ -19,7 +19,7 @@ describe('Ticket', () => {
accept: function() {}
}
};
controller = $componentController('vnTicketPackage', {$scope: $scope});
controller = $componentController('vnTicketPackageIndex', {$scope: $scope});
}));
describe('removePackage()', () => {
@ -37,11 +37,11 @@ describe('Ticket', () => {
let query = '/ticket/api/TicketPackagings/crudTicketPackaging';
controller.removedPackages = [];
controller.oldPackages = [
{id: 1, quantity: 5, ticketFk: 1}
{id: 1, packagingFk: 1, quantity: 5, ticketFk: 1}
];
controller.packages = [
{quantity: 5, ticketFk: 1},
{id: 1, quantity: 25, ticketFk: 1}
{quantity: 5, packagingFk: 2, ticketFk: 1},
{id: 1, packagingFk: 1, quantity: 25, ticketFk: 1}
];
let packagesObj = {
delete: controller.removedPackages,

View File

@ -0,0 +1,36 @@
<mg-ajax path="/ticket/api/sales/filter" options="vnIndexNonAuto"></mg-ajax>
<vn-vertical>
<vn-card pad-large>
<vn-vertical>
<vn-title>Sale checked</vn-title>
<table class="vn-grid">
<thead>
<tr>
<th style="text-align:center" translate>Is checked</th>
<th number translate>Item</th>
<th translate style="text-align:center">Description</th>
<th number translate>Quantity</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="sale in index.model.instances track by sale.id">
<td style="text-align:center!important"><vn-check style="text-align:center!important" vn-one field="sale.isChecked.isChecked" disabled="true"></vn-check></td>
<td number>{{::sale.itemFk}}</td>
<td>
<vn-vertical style="text-align:center">
<vn-one>{{::sale.concept}}</vn-one>
<vn-one>
<vn-one ng-repeat="fetchedTag in sale.itemTag track by $index">
<vn-label>{{::fetchedTag.tag.name}} </vn-label>{{::fetchedTag.value}}
</vn-one>
</vn-one>
</vn-vertical>
</td>
<td number>{{::sale.quantity}}</td>
</tr>
</tbody>
</table>
</vn-vertical>
</vn-card>
</vn-vertical>

View File

@ -0,0 +1,17 @@
import ngModule from '../module';
import FilterTicketList from '../filter-ticket-list';
class Controller extends FilterTicketList {
constructor($scope, $timeout, $state) {
super($scope, $timeout, $state);
this.onOrder('quantity', 'ASC');
}
}
Controller.$inject = ['$scope', '$timeout', '$state'];
ngModule.component('vnTicketSaleChecked', {
template: require('./sale-checked.html'),
controller: Controller
});

View File

@ -1,4 +1,4 @@
<mg-ajax path="/ticket/api/sales/filter" options="vnIndex"></mg-ajax>
<mg-ajax path="/ticket/api/sales/filter" options="vnIndexNonAuto"></mg-ajax>
<vn-vertical>
<vn-card pad-large>
<vn-vertical>

View File

@ -1,7 +1,17 @@
import ngModule from '../module';
import FilterTicketList from '../filter-ticket-list';
class Controller extends FilterTicketList {
constructor($scope, $timeout, $state) {
super($scope, $timeout, $state);
this.onOrder('quantity', 'ASC');
}
}
Controller.$inject = ['$scope', '$timeout', '$state'];
ngModule.component('vnTicketSale', {
template: require('./sale.html'),
controller: FilterTicketList
controller: Controller
});

View File

@ -8,7 +8,9 @@ import './data/ticket-data';
import './note/ticket-observation';
import './expedition/ticket-expedition';
import './volume/ticket-volume';
import './package/package';
import './package/index/package';
import './sale/sale';
import './tracking/tracking';
import './tracking/index';
import './tracking/edit/edit';
import './fetched-tags/fetched-tags';
import './sale-checked/sale-checked';

View File

@ -0,0 +1,23 @@
<mg-ajax path="/ticket/api/TicketTrackings/" options="vnPost"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.ticket"
form="form"
save="post">
</vn-watcher>
<form name="form" ng-submit="$ctrl.onSubmit()">
<vn-card pad-large>
<vn-title>New state</vn-title>
<vn-horizontal>
<vn-autocomplete vn-one
field="$ctrl.ticket.stateFk"
url="/ticket/api/States"
label="State"
vn-focus>
</vn-autocomplete>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit label="Save"></vn-submit>
</vn-button-bar>
</form>

View File

@ -0,0 +1,25 @@
import ngModule from '../../module';
export default class Controller {
constructor($scope, $state) {
this.$ = $scope;
this.$state = $state;
this.ticket = {
ticketFk: $state.params.id,
text: null
};
}
onSubmit() {
this.$.watcher.submit().then(
() => {
this.$state.go('ticket.card.tracking.index');
}
);
}
}
Controller.$inject = ['$scope', '$state'];
ngModule.component('vnTicketTrackingEdit', {
template: require('./edit.html'),
controller: Controller
});

View File

@ -1,5 +1,5 @@
<mg-ajax path="/ticket/api/TicketTrackings/filter" options="vnIndexNonAuto"></mg-ajax>
<vn-vertical pad-medium>
<vn-vertical>
<vn-card pad-large>
<vn-vertical>
<vn-title>Tracking</vn-title>
@ -7,7 +7,6 @@
<vn-column-header vn-one pad-medium-h field="state.name" text="State"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="employee" text="Employee"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="created" text="Created" default-order="ASC"></vn-column-header>
</vn-grid-header>
<vn-one class="list list-content">
<vn-horizontal
@ -15,9 +14,8 @@
pad-small-bottom
ng-repeat="ticket in index.model.instances track by ticket.id">
<vn-one pad-medium-h>{{::ticket.state.name}}</vn-one>
<vn-two pad-medium-h>{{::ticket.worker.firstName}} {{::ticket.worker.name}}</vn-two>
<vn-two pad-medium-h>{{::ticket.worker.firstName}} {{ticket.worker.name}}</vn-two>
<vn-two pad-medium-h>{{::ticket.created | date:'dd/MM/yyyy HH:mm'}}</vn-two>
</vn-horizontal>
</vn-one>
<vn-one class="text-center pad-small-v" ng-if="index.model.count === 0" translate>No results</vn-one>
@ -26,6 +24,6 @@
</vn-vertical>
</vn-card>
</vn-vertical>
<a ui-sref="ticket.card.tracking.create" fixed-bottom-right>
<a ui-sref="ticket.card.tracking.edit" fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>
</a>

View File

@ -2,6 +2,6 @@ import ngModule from '../module';
import FilterTicketList from '../filter-ticket-list';
ngModule.component('vnTicketTracking', {
template: require('./tracking.html'),
template: require('./index.html'),
controller: FilterTicketList
});

View File

@ -1,5 +0,0 @@
Date : Fecha
Employee : Empleado
State: Estado
Tracking: Revisión
Created : Añadido

View File

@ -18,14 +18,14 @@ module.exports = Self => {
}
});
Self.sumAmount = (clientFk, callback) => {
Self.sumAmount = async clientFk => {
let query = `SELECT SUM(amount) AS sumAmount FROM vn.greuge WHERE clientFk = ?`;
Self.rawSql(query, [clientFk])
.then(response => {
callback(null, response.length ? response[0].sumAmount : 0);
})
.catch(err => {
callback(err);
});
try {
let [response] = await Self.rawSql(query, [clientFk]);
return response ? response.sumAmount : 0;
} catch (e) {
throw new Error(e);
}
};
};

View File

@ -1,22 +1,22 @@
module.exports = function(Self) {
require('../methods/creditInsurance/filter.js')(Self);
Self.validateCredit = function(credit) {
return credit >= 0;
};
Self.validateBinded('credit', Self.validateCredit, {
message: 'The credit must be an integer greater than or equal to zero',
allowNull: false, // FIXME: Ignored by loopback when it's false
allowBlank: false
});
Self.validateCredit = function(credit) {
return (credit >= 0 && credit % 1 == 0);
Self.validateGrade = function(grade) {
return typeof grade === 'undefined' || grade >= 0;
};
Self.validateBinded('grade', Self.validateGrade, {
message: 'The grade must be an integer greater than or equal to zero',
allowNull: true
});
Self.validateGrade = function(grade) {
return (typeof grade === 'undefined' || (grade >= 0 && grade % 1 == 0));
};
};

View File

@ -0,0 +1,22 @@
USE `vn`;
CREATE
OR REPLACE ALGORITHM = UNDEFINED
DEFINER = `root`@`%`
SQL SECURITY DEFINER
VIEW `agencyMode` AS
SELECT
`a`.`Id_Agencia` AS `id`,
`a`.`Agencia` AS `name`,
`a`.`description` AS `description`,
`a`.`Vista` AS `deliveryMethodFk`,
`a`.`m3` AS `m3`,
`a`.`cod71` AS `cod71`,
`a`.`web` AS `web`,
`a`.`agency_id` AS `agencyFk`,
`a`.`agency_service_id` AS `agencyServiceFk`,
`a`.`inflacion` AS `inflation`,
`a`.`is_volumetric` AS `isVolumetric`,
`a`.`send_mail` AS `reportMail`,
`a`.`tpv` AS `isActive`
FROM
`vn2008`.`Agencias` `a`;

View File

@ -6,5 +6,6 @@
"Unable to default a disabled consignee": "Unable to default a disabled consignee",
"El método de pago seleccionado requiere que se especifique el IBAN": "El método de pago seleccionado requiere que se especifique el IBAN",
"Ya existe un usuario con ese nombre": "Ya existe un usuario con ese nombre",
"Quantity cannot be zero": "Quantity cannot be zero"
"Quantity cannot be zero": "Quantity cannot be zero",
"can't be blank": "can't be blank"
}

View File

@ -9,5 +9,6 @@
"DNI Incorrecto": "DNI Incorrecto",
"Ya existe un usuario con ese nombre": "Ya existe un usuario con ese nombre",
"is invalid": "is invalid",
"Quantity cannot be zero": "La cantidad no puede ser cero"
"Quantity cannot be zero": "La cantidad no puede ser cero",
"Package cannot be blank": "Package cannot be blank"
}

View File

@ -0,0 +1,28 @@
module.exports = Self => {
Self.remoteMethod('getAverageInvoiced', {
description: 'Returns the annual average invoiced of a client',
accessType: 'READ',
accepts: [{
arg: 'id',
type: 'number',
required: true,
description: 'client id',
http: {source: 'path'}
}],
returns: {
type: 'number',
root: true
},
http: {
path: `/:id/getAverageInvoiced`,
verb: 'GET'
}
});
Self.getAverageInvoiced = async clientFk => {
let query = `SELECT invoiced FROM vn.annualAverageInvoiced WHERE clientFk = ?`;
let [invoiced] = await Self.rawSql(query, [clientFk]);
return invoiced;
};
};

View File

@ -21,7 +21,8 @@ module.exports = Self => {
Self.getDebt = async clientFk => {
let query = `SELECT vn.clientGetDebt(?, CURDATE()) AS debt`;
let response = await Self.rawSql(query, [clientFk]);
return response[0];
let [debt] = await Self.rawSql(query, [clientFk]);
return debt;
};
};

View File

@ -0,0 +1,28 @@
module.exports = Self => {
Self.remoteMethod('getMana', {
description: 'Returns the boolean mana of a client',
accessType: 'READ',
accepts: [{
arg: 'id',
type: 'number',
required: true,
description: 'client id',
http: {source: 'path'}
}],
returns: {
type: 'number',
root: true
},
http: {
path: `/:id/getMana`,
verb: 'GET'
}
});
Self.getMana = async clientFk => {
let query = `SELECT vn.clientGetMana(?) AS mana`;
let [mana] = await Self.rawSql(query, [clientFk]);
return mana;
};
};

View File

@ -0,0 +1,68 @@
module.exports = Self => {
Self.remoteMethod('summary', {
description: 'Returns a client summary',
accessType: 'READ',
accepts: [{
arg: 'id',
type: 'number',
required: true,
description: 'client id',
http: {source: 'path'}
}],
returns: {
type: [this.modelName],
root: true
},
http: {
path: `/:id/summary`,
verb: 'GET'
}
});
Self.summary = async clientFk => {
let models = Self.app.models;
let summaryObj = await getSummary(models.Client, clientFk);
summaryObj.mana = await models.Client.getMana(clientFk);
summaryObj.debt = await models.Client.getDebt(clientFk);
summaryObj.averageInvoiced = await models.Client.getAverageInvoiced(clientFk);
summaryObj.totalGreuge = await models.Greuge.sumAmount(clientFk);
summaryObj.recovery = await getRecoveries(models.Recovery, clientFk);
return summaryObj;
};
async function getSummary(client, clientId) {
let filter = {
include: [
{relation: 'account', scope: {fields: ['name', 'active']}},
{relation: 'salesPerson', scope: {fields: ['name']}},
{relation: 'country', scope: {fields: ['country']}},
{relation: 'province', scope: {fields: ['name']}},
{relation: 'contactChannel', scope: {fields: ['name']}},
{relation: 'payMethod', scope: {fields: ['name']}},
{
relation: 'addresses',
scope: {
where: {isDefaultAddress: true},
fields: ['nickname', 'street', 'city', 'postalCode']
}
}
],
where: {id: clientId}
};
return await client.findOne(filter);
}
async function getRecoveries(recovery, clientId) {
let filter = {
where: {
and: [{clientFk: clientId}, {or: [{finished: null}, {finished: {gt: Date.now()}}]}]
},
limit: 1
};
return await recovery.findOne(filter);
}
};

View File

@ -28,6 +28,9 @@
},
"reportMail": {
"type": "string"
},
"isActive":{
"type": "boolean"
}
},
"relations": {
@ -49,5 +52,8 @@
"principalId": "$everyone",
"permission": "ALLOW"
}
]
],
"scopes" : {
"active" : {"where": {"isActive": {"neq": false}}}
}
}

View File

@ -16,6 +16,9 @@ module.exports = function(Self) {
require('../methods/client/activeSalesPerson')(Self);
require('../methods/client/addressesPropagateRe')(Self);
require('../methods/client/getDebt')(Self);
require('../methods/client/getMana')(Self);
require('../methods/client/getAverageInvoiced')(Self);
require('../methods/client/summary')(Self);
// Validations

View File

@ -20,6 +20,12 @@ module.exports = Self => {
}
}
}
},
{
relation: "isChecked",
scope: {
fields: ["isChecked"]
}
}]
};
}

View File

@ -51,6 +51,11 @@
"model": "Ticket",
"foreignKey": "ticketFk",
"required": true
},
"isChecked": {
"type": "hasOne",
"model": "SaleChecked",
"foreignKey": "saleFk"
}
}
}

View File

@ -1,3 +1,14 @@
module.exports = function(Self) {
require('../methods/ticketTracking/filter')(Self);
Self.observe('before save', function(ctx, next) {
let token = ctx.options.accessToken;
let userId = token && token.userId;
Self.app.models.Worker.findOne({where: {userFk: userId}}, (err, user) => {
if (err) return next(err);
ctx.instance.workerFk = user.id;
next();
});
});
};

View File

@ -3,7 +3,8 @@
"base": "VnModel",
"options": {
"mysql": {
"table": "ticketTracking"
"table": "ticketTracking",
"database": "vn"
}
},
"properties": {