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

This commit is contained in:
Carlos Jimenez 2018-12-03 00:45:52 +01:00
commit de24fdfac8
113 changed files with 1062 additions and 362 deletions

View File

@ -1,4 +1,4 @@
<vn-crud-model
<vn-crud-model auto-load="false"
vn-id="model"
url="claim/api/ClaimEnds"
filter="$ctrl.filter"
@ -9,7 +9,7 @@
<vn-vertical>
<vn-horizontal>
<vn-title vn-two>Action</vn-title>
<div class="totalBox">
<div class="totalBox" ng-show="$ctrl.salesClaimed.length > 0">
<vn-label-value label="Total claimed"
value="{{$ctrl.claimedTotal | currency:'€':2}}">
</vn-label-value>
@ -152,7 +152,7 @@
<!-- Transfer Popover -->
<vn-popover class="lastTicketsPopover" vn-id="lastTicketsPopover">
<div class="ticketList" pad-medium>
<vn-table model="lastTicketsModel" class="vn-grid">
<vn-table model="lastTicketsModel" auto-load="false" class="vn-grid">
<vn-thead>
<vn-tr>
<vn-th field="id" number>ID</vn-th>

View File

@ -30,7 +30,7 @@
</vn-autocomplete>
<vn-date-picker
vn-one
enabled="false"
disabled="true"
label="Created"
model="$ctrl.claim.created"
ini-options="{enableTime: true, dateFormat: 'd-m-Y', time_24hr: true}">

View File

@ -1,4 +1,4 @@
<vn-crud-model
<vn-crud-model auto-load="false"
vn-id="model"
url="claim/api/ClaimBeginnings"
filter="$ctrl.filter"

View File

@ -22,7 +22,8 @@
order="description">
</vn-crud-model>
<vn-crud-model
url="/client/api/Clients/activeSalesPerson"
url="/client/api/Clients/activeWorkersWithRole"
where="{role: 'salesPerson'}"
data="activeSalesPersons"
order="firstName">
</vn-crud-model>

View File

@ -4,9 +4,7 @@
<vn-auto>
<h5 text-center pad-small-v class="title">{{$ctrl.summary.claim.id}} - {{$ctrl.summary.claim.client.name}}</h5>
</vn-auto>
<vn-horizontal>
<vn-horizontal pad-medium-v>
<vn-one>
<vn-label-value label="Created"
value="{{$ctrl.summary.claim.created | dateTime: 'dd/MM/yyyy'}}">

View File

@ -30,9 +30,11 @@
<vn-autocomplete vn-one
initial-data="$ctrl.client.salesPerson"
field="$ctrl.client.salesPersonFk"
url="/client/api/Clients/activeSalesPerson"
url="/client/api/Clients/activeWorkersWithRole"
show-field="firstName"
search-function="{firstName: $search}"
value-field="id"
where="{role: 'employee'}"
label="Salesperson"
vn-acl="salesAssistant">
<tpl-item>{{firstName}} {{name}}</tpl-item>

View File

@ -13,9 +13,11 @@
<vn-textfield vn-two label="Comercial Name" field="$ctrl.client.name" vn-focus></vn-textfield>
<vn-autocomplete vn-one
field="$ctrl.client.salesPersonFk"
url="/client/api/Clients/activeSalesPerson"
url="/client/api/Clients/activeWorkersWithRole"
search-function="{firstName: $search}"
show-field="firstName"
value-field="id"
where="{role: 'salesPerson'}"
label="Salesperson">
<tpl-item>{{firstName}} {{name}}</tpl-item>
</vn-autocomplete>

View File

@ -3,7 +3,7 @@
url="/client/api/CreditInsurances"
link="{creditClassification: $ctrl.$stateParams.classificationId}"
limit="20"
data="insurances">
data="insurances" auto-load="false">
</vn-crud-model>
<vn-vertical>

View File

@ -4,7 +4,7 @@
filter="::$ctrl.filter"
link="{clientFk: $ctrl.$stateParams.id}"
limit="20"
data="credits">
data="credits" auto-load="false">
</vn-crud-model>
<vn-vertical>

View File

@ -106,7 +106,7 @@
vn-one
label="Verified data"
field="$ctrl.client.isTaxDataChecked"
vn-acl="administrative">
vn-acl="salesAssistant">
</vn-check>
</vn-horizontal>
</vn-card>

View File

@ -4,7 +4,7 @@
filter="::$ctrl.filter"
link="{clientFk: $ctrl.$stateParams.id}"
limit="20"
data="greuges">
data="greuges" auto-load="false">
</vn-crud-model>
<mg-ajax

View File

@ -55,3 +55,4 @@ Requested credits: Créditos solicitados
Contacts: Contactos
Samples: Plantillas
Send sample: Enviar plantilla
Log: Historial

View File

@ -4,7 +4,7 @@
link="{originFk: $ctrl.$stateParams.id}"
filter="$ctrl.filter"
limit="20"
data="$ctrl.logs">
data="$ctrl.logs" auto-load="false">
</vn-crud-model>
<vn-log model="model"></vn-log>

View File

@ -21,22 +21,24 @@ class Controller {
set logs(value) {
this._logs = value;
if (this.logs)
if (this.logs) {
this.logs.forEach(log => {
log.oldProperties = this.getInstance(log.oldInstance);
log.newProperties = this.getInstance(log.newInstance);
});
}
}
getInstance(instance) {
const properties = [];
if (typeof instance == 'object' && instance != null) {
Object.keys(instance).forEach(property => {
properties.push({key: property, value: instance[property]});
});
return properties;
}
return null;
}
}
Controller.$inject = ['$scope', '$stateParams'];

View File

@ -4,7 +4,7 @@
filter="::$ctrl.filter"
link="{clientFk: $ctrl.$stateParams.id}"
limit="20"
data="mandates">
data="mandates" auto-load="false">
</vn-crud-model>
<vn-vertical>

View File

@ -4,7 +4,7 @@
filter="{}"
link="{clientFk: $ctrl.$stateParams.id}"
limit="20"
data="recoveries">
data="recoveries" auto-load="false">
</vn-crud-model>
<vn-vertical>

View File

@ -3,7 +3,7 @@
url="/client/api/receipts/filter"
params="$ctrl.params"
limit="20"
data="$ctrl.risks">
data="$ctrl.risks" auto-load="false">
</vn-crud-model>
<vn-crud-model
vn-id="riskModel"

View File

@ -4,7 +4,7 @@
filter="::$ctrl.filter"
link="{clientFk: $ctrl.$stateParams.id}"
limit="20"
data="samples">
data="samples" auto-load="false">
</vn-crud-model>
<vn-vertical>

View File

@ -28,9 +28,11 @@
<vn-autocomplete
vn-one
field="filter.salesPersonFk"
url="/client/api/Clients/activeSalesPerson"
show-field="name"
url="/client/api/Clients/activeWorkersWithRole"
search-function="{firstName: $search}"
show-field="firstName"
value-field="id"
where="{role: 'salesPerson'}"
label="Salesperson">
<tpl-item>{{firstName}} {{name}}</tpl-item>
</vn-autocomplete>

View File

@ -3,7 +3,7 @@
url="/client/api/clients/getTransactions"
link="{clientFk: $ctrl.$stateParams.id}"
limit="20"
data="transactions">
data="transactions" auto-load="false">
</vn-crud-model>
<vn-vertical>

View File

@ -124,6 +124,7 @@ export default class CrudModel extends ModelProxy {
clear() {
this.orgData = null;
this.moreRows = null;
}
/**
@ -144,12 +145,13 @@ export default class CrudModel extends ModelProxy {
for (let row of this.removed)
deletes.push(row.$orgRow[pk]);
for (let row of this._data)
for (let row of this._data) {
if (row.$isNew) {
let data = {};
for (let prop in row)
for (let prop in row) {
if (prop.charAt(0) !== '$')
data[prop] = row[prop];
}
creates.push(data);
} else if (row.$oldData) {
let data = {};
@ -160,12 +162,14 @@ export default class CrudModel extends ModelProxy {
where: {[pk]: row.$orgRow[pk]}
});
}
}
let changes = {deletes, updates, creates};
for (let prop in changes)
for (let prop in changes) {
if (changes[prop].length === 0)
changes[prop] = undefined;
}
return changes;
}

View File

@ -7,7 +7,7 @@
<input type="text"
class="mdl-textfield__input"
name="{{::$ctrl.name}}"
ng-disabled="{{!$ctrl.enabled}}"
ng-disabled="$ctrl.disabled"
rule="{{::$ctrl.rule}}"/>
<div class="mdl-chip__action">
<i

View File

@ -9,7 +9,6 @@ class DatePicker extends Component {
this.input = $element[0].querySelector('input');
this.$translate = $translate;
this.$attrs = $attrs;
this.enabled = true;
this._model = undefined;
this.dateValue = undefined;
this.hasMouseIn = false;
@ -98,7 +97,7 @@ ngModule.component('vnDatePicker', {
model: '=',
label: '@?',
name: '@?',
enabled: '<?',
disabled: '<?',
rule: '<?',
iniOptions: '<?',
isLocale: '<?'

View File

@ -9,7 +9,7 @@
<vn-th field="userFk" class="expendable">Changed by</vn-th>
<vn-th field="changedModel" class="expendable">Model</vn-th>
<vn-th field="action" class="expendable">Action</vn-th>
<vn-th field="changedModelValue" class="expendable">Instance</vn-th>
<vn-th field="changedModelValue" class="expendable">Name</vn-th>
<vn-th>Before</vn-th>
<vn-th>After</vn-th>
</vn-tr>
@ -21,19 +21,19 @@
<div class="changes">
<div>
<span translate class="label">Changed by</span><span class="label">: </span>
<span translate class="value">{{::log.user.name}}</span>
<span translate class="value">{{::log.user.name | dashIfEmpty}}</span>
</div>
<div>
<span translate class="label">Model</span><span class="label">: </span>
<span translate class="value">{{::log.changedModel}}</span>
<span translate class="value">{{::log.changedModel | dashIfEmpty}}</span>
</div>
<div>
<span translate class="label">Action</span><span class="label">: </span>
<span translate class="value">{{::log.action}}</span>
<span translate class="value">{{::$ctrl.actionsText[log.action] | dashIfEmpty}}</span>
</div>
<div>
<span translate class="label">Instance</span><span class="label">: </span>
<span translate class="value">{{::log.changedModelValue}}</span>
<span translate class="label">Name</span><span class="label">: </span>
<span translate class="value">{{::log.changedModelValue | dashIfEmpty}}</span>
</div>
</div>
</vn-td>
@ -44,7 +44,7 @@
{{::log.changedModel}}
</vn-td>
<vn-td translate class="expendable">
{{::log.action}}
{{::$ctrl.actionsText[log.action]}}
</vn-td>
<vn-td class="expendable">
{{::log.changedModelValue}}
@ -58,12 +58,22 @@
</vn-one>
</vn-td>
<vn-td class="after">
<vn-one ng-repeat="new in log.newProperties">
<vn-one
ng-repeat="new in log.newProperties"
ng-if="!log.description"
id="newInstance">
<div>
<span translate class="label">{{::new.key}}</span><span class="label">: </span>
<span translate class="value">{{::new.value}}</span>
</div>
</vn-one>
<vn-one
ng-if="!log.newProperties"
id="description">
<div>
<span>{{log.description}}</span>
</div>
</vn-one>
</vn-td>
</vn-tr>
</vn-tbody>

View File

@ -1,7 +1,19 @@
import ngModule from '../../module';
import './style.scss';
export default class Controller {
constructor() {
this.actionsText = {
'insert': 'Creates',
'update': 'Updates',
'delete': 'Deletes',
'select': 'Views'
};
}
}
ngModule.component('vnLog', {
controller: Controller,
template: require('./index.html'),
bindings: {
model: '<'

View File

@ -4,6 +4,8 @@ Changed by: Cambiado por
Before: Antes
After: Despues
History: Historial
insert: Crear
delete: Eliminar
update: Actualizar
Name: Nombre
Creates: Crea
Updates: Actualiza
Deletes: Elimina
Views: Visualiza

View File

@ -7,6 +7,7 @@ export default class Table {
this.table = $element[0];
this.field = null;
this.order = null;
this.autoLoad = true;
$transclude($scope.$parent, clone => {
angular.element($element[0].querySelector('div')).append(clone);
@ -19,15 +20,16 @@ export default class Table {
}
applyOrder(field = this.field, order = this.order) {
if (!field) return;
if (field && order) {
this.model.order = `${field} ${order}`;
this.model.refresh();
this.setActiveArrow();
}
this.model.refresh();
}
$onChanges() {
if (this.model)
if (this.model && this.autoLoad)
this.applyOrder();
}
@ -50,6 +52,7 @@ ngModule.component('vnTable', {
transclude: true,
controller: Table,
bindings: {
model: '<?'
model: '<?',
autoLoad: '<?'
}
});

View File

@ -7,6 +7,8 @@ import ngModule from '../module';
*/
export default function currency() {
return function(input, symbol, fractionSize) {
if (!fractionSize)
fractionSize = 2;
if (typeof input == 'number' && fractionSize) {
input = input.toFixed(fractionSize);
return `${input} ${symbol}`;

View File

@ -56,15 +56,6 @@
"description": "Tax",
"acl": ["administrative","buyer"]
},
{
"url" : "/history",
"state": "item.card.history",
"component": "vn-item-history",
"description": "History",
"params": {
"item": "$ctrl.item"
}
},
{
"url" : "/niche",
"state": "item.card.niche",
@ -129,7 +120,6 @@
{"state": "item.card.data", "icon": "settings"},
{"state": "item.card.tags", "icon": "icon-tags"},
{"state": "item.card.tax", "icon": "icon-tax"},
{"state": "item.card.history", "icon": "history"},
{"state": "item.card.niche", "icon": "place"},
{"state": "item.card.botanical", "icon": "local_florist"},
{"state": "item.card.itemBarcode", "icon": "icon-barcode"},

View File

@ -2,7 +2,7 @@
vn-id="model"
url="item/api/Items/getDiary"
filter="$ctrl.filter"
data="sales">
data="sales" auto-load="false">
</vn-crud-model>
<vn-vertical>
<vn-card pad-large>

View File

@ -4,7 +4,7 @@
filter="::$ctrl.filter"
link="{originFk: $ctrl.$stateParams.id}"
limit="20"
data="logs">
data="logs" auto-load="false">
</vn-crud-model>
<vn-vertical>

View File

@ -2,7 +2,7 @@
vn-id="model"
url="/item/api/Items/filter"
limit="8"
order="isActive DESC, name"
order="isActive DESC, name, id"
data="items"
auto-load="false">
</vn-crud-model>

View File

@ -12,7 +12,7 @@ import './ticket-descriptor-popover';
import './data';
import './tags';
import './tax';
import './history';
// import './history';
import './last-entries';
import './niche';
import './botanical';

View File

@ -2,7 +2,7 @@
vn-id="model"
url="/item/api/Items/getLastEntries"
filter="::$ctrl.filter"
data="entries">
data="entries" auto-load="false">
</vn-crud-model>
<vn-vertical>
<vn-card pad-large>

View File

@ -64,9 +64,19 @@
"state": "order.create",
"component": "vn-order-create",
"description": "New order"
},
{
"url": "/basic-data",
"state": "order.card.basicData",
"component": "vn-order-basic-data",
"description": "Basic data",
"params": {
"order": "$ctrl.order"
}
}
],
"menu": [
{"state": "order.card.basicData", "icon": "settings"},
{"state": "order.card.catalog", "icon": "shopping_cart"},
{"state": "order.card.volume", "icon": "icon-volume"},
{"state": "order.card.line", "icon": "icon-lines"}

View File

@ -0,0 +1,55 @@
<mg-ajax path="/order/api/Orders/{{patch.params.id}}/updateBasicData" options="vnPatch"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.order"
form="form"
save="patch">
</vn-watcher>
<form name="form" ng-submit="watcher.submit()">
<vn-card pad-large>
<vn-title>Basic data</vn-title>
<vn-horizontal>
<vn-autocomplete
disabled="$ctrl.order.rows.length || $ctrl.order.isConfirmed"
vn-one
url="/api/Clients"
label="Client"
search-function="{or: [{id: {regexp: $search}}, {name: {regexp: $search}}]}"
show-field="name"
value-field="id"
field="$ctrl.order.clientFk">
<tpl-item>{{id}}: {{name}}</tpl-item>
</vn-autocomplete>
<vn-autocomplete
vn-one
disabled="$ctrl.order.rows.length || $ctrl.order.isConfirmed"
url="/api/Companies"
label="Company"
show-field="code"
value-field="id"
field="$ctrl.order.companyFk">
</vn-autocomplete>
<vn-date-picker
vn-one
disabled="$ctrl.order.rows.length || $ctrl.order.isConfirmed"
label="Landed"
model="$ctrl.order.landed"
ini-options="{enableTime: false}">
</vn-date-picker>
</vn-horizontal>
<vn-horizontal>
<vn-textarea
vn-three
disabled="$ctrl.order.rows.length || $ctrl.order.isConfirmed"
label="Observation"
field="$ctrl.order.note">
</vn-textarea>
</vn-horizontal>
<vn-horizontal ng-if="$ctrl.order.rows.length|| $ctrl.order.isConfirmed" class="disabledForm">
<span class="disabled" translate>This form has been disabled because there are lines in this order or it's confirmed</span>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit label="Save"></vn-submit>
</vn-button-bar>
</form>

View File

@ -0,0 +1,9 @@
import ngModule from '../module';
import './style.scss';
ngModule.component('vnOrderBasicData', {
template: require('./index.html'),
bindings: {
order: '<'
}
});

View File

@ -0,0 +1 @@
This form has been disabled because there are lines in this order or it's confirmed: Este formulario ha sido deshabilitado por que esta orden contiene líneas o está confirmada

View File

@ -0,0 +1,9 @@
vn-order-basic-data {
.disabledForm {
text-align: center;
color: red;
span {
margin: 0 auto;
}
}
}

View File

@ -33,6 +33,8 @@ class Controller {
let query = `/order/api/Orders/${this.$state.params.id}?filter=${json}`;
this.$http.get(query).then(res => {
if (res.data) {
if (res.data.rows.length == 0)
delete res.data.rows;
this.order = res.data;
this.getTotal();
}
@ -46,9 +48,8 @@ class Controller {
getTotal() {
let query = `/order/api/Orders/${this.$state.params.id}/getTotal`;
this.$http.get(query).then(res => {
if (res.data) {
if (res.data)
this.order.total = res.data;
}
});
}
}

View File

@ -31,7 +31,7 @@
value="{{$ctrl.order.address.nickname}}">
</vn-label-value>
<vn-label-value label="Items"
value="{{$ctrl.order.rows.length}}">
value="{{$ctrl.order.rows.length || 0}}">
</vn-label-value>
<vn-label-value label="Total"
value="{{$ctrl.order.total | currency: ' €': 2}}">

View File

@ -13,3 +13,4 @@ import './prices-popover';
import './volume';
import './create';
import './create/card';
import './basic-data';

View File

@ -3,12 +3,12 @@
url="/order/api/Orders"
filter="::$ctrl.filter"
limit="20"
data="orders">
data="orders" auto-load="false">
</vn-crud-model>
<div margin-medium>
<div class="vn-list">
<vn-card pad-medium-h>
<vn-searchbar
<vn-searchbar auto-load="false"
panel="vn-order-search-panel"
model="model"
expr-builder="$ctrl.exprBuilder(param, value)"

View File

@ -20,7 +20,7 @@
<strong>{{$ctrl.order.total | currency:'€':2}}</strong>
</div>
</vn-horizontal>
<vn-table model="model">
<vn-table>
<vn-thead>
<vn-tr>
<vn-th style="text-align: center">Item</vn-th>

View File

@ -1,4 +1,4 @@
<vn-crud-model
<vn-crud-model auto-load="false"
vn-id="model"
url="/order/api/OrderRows"
filter="::$ctrl.filter"

View File

@ -180,12 +180,12 @@
"ticket": "$ctrl.ticket"
}
},
/* {
{
"url" : "/log",
"state": "ticket.card.log",
"component": "vn-ticket-log",
"description": "Log"
}, */
},
{
"url" : "/weekly",
"state": "ticket.weekly",
@ -198,6 +198,12 @@
"abstract": true,
"component": "ui-view"
},
{
"url": "/service",
"state": "ticket.card.service",
"component": "vn-ticket-service",
"description": "Service"
},
{
"url" : "/index",
"state": "ticket.card.request.index",
@ -211,6 +217,12 @@
"component": "vn-ticket-request-create",
"description": "Purchase request",
"acl": ["salesPerson"]
},
{
"url": "/create?clientFk",
"state": "ticket.create",
"component": "vn-ticket-create",
"description": "New ticket"
}
],
"menu": [
@ -220,6 +232,7 @@
{"state": "ticket.card.volume", "icon": "icon-volume"},
{"state": "ticket.card.expedition", "icon": "icon-volum"},
{"state": "ticket.card.package.index", "icon": "icon-bucket"},
{"state": "ticket.card.service"},
{"state": "ticket.card.tracking.index", "icon": "remove_red_eye"},
{"state": "ticket.card.saleChecked", "icon": "assignment"},
{"state": "ticket.card.components", "icon": "icon-components"},

View File

@ -0,0 +1,44 @@
<vn-title>New ticket</vn-title>
<vn-autocomplete
vn-focus
vn-id="client"
url="/api/Clients"
label="Client"
search-function="{or: [{id: {regexp: $search}}, {name: {regexp: $search}}]}"
show-field="name"
value-field="id"
field="$ctrl.clientFk">
<tpl-item>{{id}}: {{name}}</tpl-item>
</vn-autocomplete>
<vn-autocomplete
disabled="!$ctrl.clientFk"
url="{{ $ctrl.clientFk ? '/api/Clients/'+ $ctrl.clientFk +'/addresses' : null }}"
fields="['nickname', 'street', 'city']"
field="$ctrl.addressFk"
show-field="nickname"
value-field="id"
label="Address">
<tpl-item>{{nickname}}: {{street}}, {{city}}</tpl-item>
</vn-autocomplete>
<vn-date-picker
label="Landed"
model="$ctrl.landed"
ini-options="{enableTime: false}">
</vn-date-picker>
<vn-autocomplete
vn-one
disabled="!$ctrl.clientFk || !$ctrl.landed"
field="$ctrl.warehouseFk"
url="/agency/api/Warehouses"
show-field="name"
value-field="id"
label="Warehouse">
</vn-autocomplete>
<vn-autocomplete
disabled="!$ctrl.clientFk || !$ctrl.landed || !$ctrl.warehouseFk"
data="$ctrl._availableAgencies"
label="Agency"
show-field="agency"
value-field="id"
field="$ctrl.ticket.agencyModeFk">
</vn-autocomplete>

View File

@ -0,0 +1,110 @@
import ngModule from '../module';
class Controller {
constructor($http, vnApp, $translate, $state, $stateParams) {
this.$stateParams = $stateParams;
this.$http = $http;
this.translate = $translate;
this.vnApp = vnApp;
this.ticket = {};
this.$state = $state;
this.clientFk = $stateParams.clientFk;
}
$onInit() {
if (this.$stateParams && this.$stateParams.clientFk)
this.clientFk = this.$stateParams.clientFk;
}
set ticket(value) {
if (value)
this._ticket = value;
}
get ticket() {
return this._ticket;
}
set clientFk(value) {
this.ticket.clientFk = value;
if (value) {
let filter = {where: {clientFk: value, isDefaultAddress: true}};
filter = encodeURIComponent(JSON.stringify(filter));
let query = `/api/Addresses?filter=${filter}`;
this.$http.get(query).then(res => {
this.addressFk = res.data[0].id;
});
} else
this.addressFk = null;
}
get clientFk() {
return this.ticket.clientFk;
}
set addressFk(value) {
this.ticket.addressFk = value;
}
get addressFk() {
return this.ticket.addressFk;
}
set landed(value) {
this.ticket.landed = value;
}
get landed() {
return this.ticket.landed;
}
set warehouseFk(value) {
this.ticket.warehouseFk = value;
this.getAvailableAgencies();
}
get warehouseFk() {
return this.ticket.warehouseFk;
}
getAvailableAgencies() {
this.ticket.agencyModeFk = null;
if (this.ticket.landed && this.ticket.addressFk) {
let filter = {warehouseFk: this.ticket.warehouseFk, addressFk: this.ticket.addressFk, landed: this.ticket.landed};
filter = encodeURIComponent(JSON.stringify(filter));
let query = `/api/Agencies/getAgenciesWithWarehouse?filter=${filter}`;
this.$http.get(query).then(res => {
this._availableAgencies = res.data[0];
});
}
}
onSubmit() {
this.createTicket();
}
createTicket() {
let params = {
clientFk: this.ticket.clientFk,
landed: this.ticket.landed,
addressFk: this.ticket.addressFk,
agencyModeFk: this.ticket.agencyModeFk,
warehouseFk: this.ticket.warehouseFk,
};
this.$http.post(`ticket/api/Tickets/new`, params).then(res => {
this.vnApp.showSuccess(this.translate.instant('Data saved!'));
this.$state.go('ticket.card.summary', {id: res.data.id});
});
}
}
Controller.$inject = ['$http', 'vnApp', '$translate', '$state', '$stateParams'];
ngModule.component('vnTicketCreateCard', {
template: require('./card.html'),
controller: Controller,
bindings: {
ticket: '<?'
}
});

View File

@ -0,0 +1,11 @@
<div margin-medium>
<div style="max-width: 70em; margin: 0 auto;" >
<vn-card pad-large>
<vn-ticket-create-card vn-id="card" on-save=""></vn-ticket-create-card>
</vn-card>
<vn-button-bar>
<vn-submit ng-click="$ctrl.onSubmit()" label="Create">
</vn-submit>
</vn-button-bar>
</div>
</div>

View File

@ -0,0 +1,20 @@
import ngModule from '../module';
class Controller {
constructor($scope, $http, $state) {
this.$ = $scope;
this.$http = $http;
this.$state = $state;
}
async onSubmit() {
let newTicketID = await this.$.card.createTicket();
this.$state.go('ticket.card.summary', {id: newTicketID});
}
}
Controller.$inject = ['$scope', '$http', '$state'];
ngModule.component('vnTicketCreate', {
template: require('./index.html'),
controller: Controller
});

View File

@ -0,0 +1 @@
New ticket: Nueva ticket

View File

@ -1,4 +1,4 @@
<vn-crud-model
<vn-crud-model auto-load="false"
vn-id="model"
url="/ticket/api/Expeditions/filter"
link="{ticketFk: $ctrl.$stateParams.id}"

View File

@ -1,10 +1,9 @@
<vn-crud-model
<vn-crud-model auto-load="false"
vn-id="model"
url="/ticket/api/Tickets/filter"
limit="20"
data="tickets"
order="shipped DESC"
auto-load="false">
order="shipped DESC">
</vn-crud-model>
<div margin-medium>
<div class="vn-list">
@ -30,7 +29,7 @@
</vn-card>
</div>
<vn-card margin-medium-v pad-medium>
<vn-table model="model">
<vn-table model="model" auto-load="false">
<vn-thead>
<vn-tr>
<vn-th></vn-th>
@ -97,6 +96,9 @@
scroll-selector="ui-view">
</vn-pagination>
</div>
<a ui-sref="ticket.create" vn-tooltip="New ticket" vn-bind="+" fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>
</a>
<vn-dialog
vn-id="summary"
class="dialog-summary">

View File

@ -74,3 +74,4 @@ Sale checked: Control clientes
Components: Componentes
Sale tracking: Líneas preparadas
Pictures: Fotos
Log: Historial

View File

@ -0,0 +1,10 @@
<vn-crud-model auto-load="false"
vn-id="model"
url="/ticket/api/TicketLogs"
link="{originFk: $ctrl.$stateParams.id}"
filter="$ctrl.filter"
limit="20"
data="$ctrl.logs">
</vn-crud-model>
<vn-log model="model"></vn-log>

View File

@ -0,0 +1,49 @@
import ngModule from '../module';
class Controller {
constructor($scope, $stateParams) {
this.$scope = $scope;
this.$stateParams = $stateParams;
this.filter = {
include: [{
relation: 'user',
scope: {
fields: ['name'],
},
}],
};
}
get logs() {
return this._logs;
}
set logs(value) {
this._logs = value;
if (this.logs) {
this.logs.forEach(log => {
log.oldProperties = this.getInstance(log.oldInstance);
log.newProperties = this.getInstance(log.newInstance);
});
}
}
getInstance(instance) {
const properties = [];
if (typeof instance == 'object' && instance != null) {
Object.keys(instance).forEach(property => {
properties.push({key: property, value: instance[property]});
});
return properties;
}
return null;
}
}
Controller.$inject = ['$scope', '$stateParams'];
ngModule.component('vnTicketLog', {
template: require('./index.html'),
controller: Controller,
});

View File

@ -0,0 +1,39 @@
import './index';
describe('Ticket', () => {
describe('Component vnTicketLog', () => {
let $componentController;
let $scope;
let controller;
beforeEach(() => {
ngModule('ticket');
});
beforeEach(angular.mock.inject((_$componentController_, $rootScope) => {
$componentController = _$componentController_;
$scope = $rootScope.$new();
controller = $componentController('vnTicketLog', {$scope: $scope});
controller.$scope.model = {data: [{newInstance: {id: 1}, oldInstance: {id: 2}}]};
}));
describe('logs setter', () => {
it('should call the function getInstance() twice', () => {
spyOn(controller, 'getInstance');
controller.logs = [{newInstance: {id: 1}, oldInstance: {id: 2}}];
expect(controller.getInstance.calls.count()).toBe(2);
expect(controller.getInstance).toHaveBeenCalledWith({id: 1});
expect(controller.getInstance).toHaveBeenCalledWith({id: 2});
});
});
describe('getInstance(instance)', () => {
it('should transform the object given in to an array', () => {
const newInstance = controller.getInstance(controller.$scope.model.data[0].newInstance);
expect(newInstance).toEqual([{key: 'id', value: 1}]);
});
});
});
});

View File

@ -0,0 +1,9 @@
Model: Modelo
Action: Acción
Changed by: Cambiado por
Before: Antes
After: Despues
History: Historial
insert: Crear
delete: Eliminar
update: Actualizar

View File

@ -15,9 +15,11 @@
vn-one
label="Buyer"
field="$ctrl.ticketRequest.atenderFk"
select-fields="['id', 'name']"
url="/client/api/Clients/activeBuyer"
show-field="name">
select-fields="['id', 'firstName']"
url="/client/api/Clients/activeWorkersWithRole"
where="{role: 'buyer'}"
search-function="{firstName: $search}"
show-field="firstName">
<tpl-item>{{firstName}} {{name}}</tpl-item>
</vn-autocomplete>
</vn-horizontal>

View File

@ -1,4 +1,4 @@
<vn-crud-model
<vn-crud-model auto-load="false"
vn-id="model"
url="/ticket/api/TicketRequests"
fields="['id', 'description', 'created', 'requesterFk', 'atenderFk', 'quantity', 'price', 'saleFk', 'isOk']"

View File

@ -1,4 +1,4 @@
<vn-crud-model
<vn-crud-model auto-load="false"
vn-id="model"
url="/ticket/api/sales"
filter="::$ctrl.filter"

View File

@ -1,4 +1,4 @@
<vn-crud-model
<vn-crud-model auto-load="false"
vn-id="model"
url="/ticket/api/SaleTrackings/listSaleTracking"
link="{ticketFk: $ctrl.$stateParams.id}"

View File

@ -1,4 +1,4 @@
<vn-crud-model
<vn-crud-model auto-load="false"
vn-id="model"
url="/api/Tickets/{{$ctrl.$stateParams.id}}/getSales"
data="$ctrl.sales">

View File

@ -56,9 +56,13 @@
<vn-horizontal>
<vn-autocomplete
vn-one
label="Sales person"
field="filter.salesPersonFk"
url="/api/Workers">
url="/client/api/Clients/activeWorkersWithRole"
search-function="{firstName: $search}"
value-field="id"
where="{role: 'employee'}"
label="Sales person">
<tpl-item>{{firstName}} {{name}}</tpl-item>
</vn-autocomplete>
<vn-autocomplete
vn-one

View File

@ -0,0 +1,67 @@
<vn-crud-model
vn-id="model"
url="/ticket/api/TicketServices"
link="{ticketFk: $ctrl.$stateParams.id}"
data="services" on-data-change="$ctrl.onDataChange()">
</vn-crud-model>
<vn-watcher
vn-id="watcher"
data="services"
form="form">
</vn-watcher>
<form name="form" ng-submit="$ctrl.onSubmit()">
<vn-card pad-large>
<vn-title>Service</vn-title>
<vn-one>
<vn-horizontal ng-repeat="service in services track by $index">
<vn-textfield
vn-one
vn-focus
label="Description"
model="service.description"
rule="TicketService.description">
</vn-textfield>
<vn-textfield
vn-one
label="Quantity"
model="service.quantity"
rule="TicketService.quantity">
</vn-textfield>
<vn-textfield
vn-one
label="Price"
model="service.price"
rule="TicketService.price">
</vn-textfield>
<vn-autocomplete vn-one
url="/api/TaxClasses"
label="Tax class"
show-field="description"
value-field="id"
field="service.taxClassFk">
</vn-autocomplete>
<vn-auto pad-medium-top>
<vn-icon
pointer
medium-grey
vn-tooltip="Remove service"
icon="delete"
ng-click="model.remove($index)">
</vn-icon>
</vn-one>
</vn-horizontal>
</vn-one>
<vn-one>
<vn-icon-button
pointer
vn-tooltip="Add service"
vn-bind="+"
icon="add_circle"
ng-click="$ctrl.add()">
</vn-icon-button>
</vn-one>
</vn-card>
<vn-button-bar>
<vn-submit label="Save"></vn-submit>
</vn-button-bar>
</form>

View File

@ -0,0 +1,31 @@
import ngModule from '../module';
class Controller {
constructor($scope, $stateParams) {
this.$scope = $scope;
this.$stateParams = $stateParams;
}
add() {
this.$scope.model.insert({
taxClassFk: 2,
quantity: 1,
ticketFk: this.$stateParams.id
});
}
onSubmit() {
this.$scope.watcher.check();
this.$scope.model.save().then(() => {
this.$scope.watcher.notifySaved();
this.$scope.model.refresh();
});
}
}
Controller.$inject = ['$scope', '$stateParams'];
ngModule.component('vnTicketService', {
template: require('./index.html'),
controller: Controller
});

View File

@ -0,0 +1,4 @@
Service: Servicios
Tax class: Tipo IVA
Add service: Añadir servicio
Remove service: Quitar servicio

View File

@ -3,6 +3,8 @@ export * from './module';
import './search-panel';
import './index';
import './card';
import './create/card';
import './create/index';
import './summary';
import './data';
import './data/step-one';
@ -17,10 +19,11 @@ import './sale/editDiscount';
import './tracking/index';
import './tracking/edit';
import './sale-checked';
import './services';
import './component';
import './sale-tracking';
import './picture';
import './request/index';
import './request/create';
// import './log';
import './log';
import './weekly';

View File

@ -1,4 +1,4 @@
<vn-crud-model
<vn-crud-model auto-load="false"
vn-id="model"
url="/ticket/api/TicketTrackings"
filter="::$ctrl.filter"

View File

@ -1,4 +1,4 @@
<vn-crud-model
<vn-crud-model auto-load="false"
vn-id="model"
url="/ticket/api/sales"
filter="::$ctrl.filter"

View File

@ -1,4 +1,4 @@
<vn-crud-model
<vn-crud-model auto-load="false"
vn-id="model"
url="/ticket/api/TicketWeeklies"
filter="::$ctrl.filter"

View File

@ -1,4 +1,4 @@
<vn-crud-model
<vn-crud-model auto-load="false"
vn-id="model"
url="/travel/api/Travels"
filter="::$ctrl.filter"
@ -18,7 +18,7 @@
</vn-card>
</div>
<vn-card margin-medium-v pad-medium>
<vn-table model="model">
<vn-table model="model" auto-load="false">
<vn-thead>
<vn-tr>
<vn-th field="id" number>Id</vn-th>

View File

@ -87,7 +87,7 @@ describe('Client Edit basicData path', () => {
});
});
describe('as salesAssistanrt', () => {
describe('as salesAssistant', () => {
beforeAll(() => {
nightmare
.waitToClick(selectors.globalItems.logOutButton)
@ -170,7 +170,7 @@ describe('Client Edit basicData path', () => {
const result = await nightmare
.waitToGetProperty(selectors.clientBasicData.salesPersonInput, 'value');
expect(result).toEqual('adminAssistant adminAssistant');
expect(result).toEqual('accessory accessory');
});
it('should now confirm the channel have been selected', async () => {

View File

@ -267,14 +267,14 @@ describe('Client lock verified data path', () => {
expect(url.hash).toContain('fiscal-data');
});
it('should confirm verified data button is disabled for salesAssistant', async () => {
it('should confirm verified data button is enabled for salesAssistant', async () => {
const result = await nightmare
.wait(selectors.clientFiscalData.verifiedDataCheckboxInput)
.evaluate(selector => {
return document.querySelector(selector).disabled;
}, selectors.clientFiscalData.verifiedDataCheckbox);
expect(result).toBeTruthy();
expect(result).toBeFalsy();
});
it('should now edit the social name', async () => {

View File

@ -381,10 +381,25 @@ describe('Ticket Edit sale path', () => {
expect(result).toEqual(3);
});
it('should go back to the original ticket sales section', async () => {
const url = await nightmare
.waitToClick(selectors.itemsIndex.goBackToModuleIndexButton)
.wait(selectors.ticketsIndex.searchTicketInput)
.type(selectors.ticketsIndex.searchTicketInput, 'id:8')
.click(selectors.ticketsIndex.searchButton)
.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1)
.waitForTextInElement(selectors.ticketsIndex.searchResult, 'address 24')
.waitToClick(selectors.ticketsIndex.searchResult)
.waitToClick(selectors.ticketSales.saleButton)
.waitForURL('/sale')
.parsedUrl();
expect(url.hash).toContain('/sale');
});
it('should select the second and third sale and tranfer them to a new ticket then get to the ticket index', async () => {
const url = await nightmare
.waitToClick(selectors.ticketSales.secondSaleCheckbox)
.waitToClick(selectors.ticketSales.thirdSaleCheckbox)
.waitToClick(selectors.ticketSales.firstSaleCheckbox)
.waitToClick(selectors.ticketSales.transferSaleButton)
.waitToClick(selectors.ticketSales.moveToNewTicketButton)
.waitForLogin('salesPerson')
@ -400,7 +415,7 @@ describe('Ticket Edit sale path', () => {
it('should search for a specific created ticket', async () => {
const result = await nightmare
.wait(selectors.ticketsIndex.searchTicketInput)
.type(selectors.ticketsIndex.searchTicketInput, 'nickname:(address 21) stateFk:2')
.type(selectors.ticketsIndex.searchTicketInput, 'nickname:(address 24) stateFk:2')
.click(selectors.ticketsIndex.searchButton)
.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1)
.countElement(selectors.ticketsIndex.searchResult);
@ -410,7 +425,7 @@ describe('Ticket Edit sale path', () => {
it(`should click on the search result to access to the ticket Sale once more`, async () => {
const url = await nightmare
.waitForTextInElement(selectors.ticketsIndex.searchResult, 'address 21')
.waitForTextInElement(selectors.ticketsIndex.searchResult, 'address 24')
.waitToClick(selectors.ticketsIndex.searchResult)
.waitToClick(selectors.ticketSales.saleButton)
.waitForURL('/sale')
@ -423,7 +438,7 @@ describe('Ticket Edit sale path', () => {
const result = await nightmare
.countElement(selectors.ticketSales.saleLine);
expect(result).toEqual(2);
expect(result).toEqual(1);
});
it('should check the first sale reserved icon isnt visible', async () => {

View File

@ -6,3 +6,5 @@ INSERT INTO `salix`.`ACL` (`id`, `model`, `property`, `accessType`, `permission`
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`, `role`) VALUES('TicketRequest', '*', '*', 'salesPerson');
UPDATE `salix`.`ACL` SET model='TicketRequest', property='*', accessType='*', permission='ALLOW', principalType='ROLE', principalId='salesPerson' WHERE id=122;
INSERT INTO `salix`.`fieldAcl` (`model`, `property`, `actionType`,`role`) VALUES ('ClaimBeginning','*','*','salesAssistant');
INSERT INTO `salix`.`ACL`(`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('Agency', 'getAgenciesWithWarehouse', '*', 'ALLOW', 'ROLE', 'employee');
INSERT INTO `salix`.`ACL`(`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES('Client', 'activeWorkersWithRole', '*', 'ALLOW', 'ROLE', 'employee');

View File

@ -0,0 +1,11 @@
use `vn`;
CREATE TABLE `ticketService` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`description` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL,
`quantity` int(11) NOT NULL DEFAULT '0',
`price` decimal(10,2) unsigned NOT NULL DEFAULT '0.00',
`taxClassFk` tinyint(3) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `ticketServiceIvaGroup_idx` (`taxClassFk`),
CONSTRAINT `ticketServiceIvaGroup` FOREIGN KEY (`taxClassFk`) REFERENCES `vn2008`.`iva_group` (`iva_group_id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

View File

@ -188,7 +188,7 @@ INSERT INTO `vn`.`client`(`id`,`name`,`fi`,`socialName`,`contact`,`street`,`city
VALUES
(101, 'Bruce Wayne', '84612325V', 'Batman', 'Alfred', '1007 Mountain Drive, Gotham', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'BruceWayne@verdnatura.es', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1,NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1,'0000-00-00', 1, NULL, 1, 1, 1, 1, NULL, 0, 0, 18, 0, 1),
(102, 'Petter Parker', '87945234L', 'Spider-Man', 'Aunt May', '20 Ingram Street', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'PetterParker@verdnatura.es', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1,NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1,'0000-00-00', 1, NULL, 1, 1, 0, 1, NULL, 0, 0, 18, 0, 1),
(103, 'Clark Kent', '06815934E', 'Super-Man', 'lois lane', '344 Clinton Street', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'ClarkKent@verdnatura.es', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1,NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1,'0000-00-00', 1, NULL, 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1),
(103, 'Clark Kent', '06815934E', 'Super-Man', 'lois lane', '344 Clinton Street', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'ClarkKent@verdnatura.es', NULL, 0, 1234567890, 0, 1, 1, 0, 1, 1,NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1,'0000-00-00', 1, NULL, 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1),
(104, 'Tony Stark', '06089160W', 'Iron-Man', 'Pepper Potts', '10880 Malibu Point', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'TonyStark@verdnatura.es', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1,NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1,'0000-00-00', 1, NULL, 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1),
(105, 'Max Eisenhardt', '39182496H', 'Magneto', 'Rogue', 'Unknown Whereabouts', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'MaxEisenhardt@verdnatura.es', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1,NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1,'0000-00-00', 1, NULL, 1, 1, 1, 0, NULL, 0, 0, NULL, 0, 1),
(106, 'DavidCharlesHaller', '53136686Q', 'Legion', 'Charles Xavier', 'Evil hideout', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'DavidCharlesHaller@verdnatura.es', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 0,NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1,'0000-00-00', 1, NULL, 1, 1, 1, 0, NULL, 0, 0, 19, 0, 1),
@ -349,7 +349,7 @@ INSERT INTO `vn`.`invoiceOut`(`id`,`ref`, `serial`, `amount`, `issued`,`clientFk
(5 , 3, 3, 3, DATE_ADD(CURDATE(), INTERVAL -3 DAY) , DATE_ADD(CURDATE(), INTERVAL -3 DAY) , 103, 'address 23', 123, NULL, 0, DATE_ADD(CURDATE(), INTERVAL -3 DAY) ),
(6 , 3, 3, 4, DATE_ADD(CURDATE(), INTERVAL -2 DAY) , DATE_ADD(CURDATE(), INTERVAL -2 DAY) , 103, 'address 23', 123, NULL, 0, DATE_ADD(CURDATE(), INTERVAL -2 DAY) ),
(7 , 4, 4, 4, DATE_ADD(CURDATE(), INTERVAL -1 DAY) , DATE_ADD(CURDATE(), INTERVAL -1 DAY) , 104, 'address 24', 124, NULL, 0, DATE_ADD(CURDATE(), INTERVAL -1 DAY) ),
(8 , 4, 4, 4, DATE_ADD(CURDATE(), INTERVAL +1 MONTH), DATE_ADD(CURDATE(), INTERVAL +1 MONTH), 104, 'address 24', 124, NULL, 0, DATE_ADD(CURDATE(), INTERVAL +1 MONTH)),
(8 , 1, 1, 4, DATE_ADD(CURDATE(), INTERVAL +1 MONTH), DATE_ADD(CURDATE(), INTERVAL +1 MONTH), 104, 'address 24', 124, NULL, 0, DATE_ADD(CURDATE(), INTERVAL +1 MONTH)),
(9 , 5, 5, 4, DATE_ADD(CURDATE(), INTERVAL -2 MONTH), DATE_ADD(CURDATE(), INTERVAL -2 MONTH), 105, 'address 25', 125, NULL, 0, DATE_ADD(CURDATE(), INTERVAL -2 MONTH)),
(10, 6, 5, 5, DATE_ADD(CURDATE(), INTERVAL -3 MONTH), DATE_ADD(CURDATE(), INTERVAL -3 MONTH), 105, 'address 25', 125, NULL, 0, DATE_ADD(CURDATE(), INTERVAL -3 MONTH)),
(11, 7, 1, 1, CURDATE() , CURDATE() , 101, 'address 21', 121, NULL, 0, CURDATE() ),

View File

@ -1,13 +1,4 @@
{
"TaxClass": {
"dataSource": "vn"
},
"TaxCode": {
"dataSource": "vn"
},
"TaxType": {
"dataSource": "vn"
},
"ItemNiche": {
"dataSource": "vn"
},
@ -23,9 +14,6 @@
"ItemPlacement": {
"dataSource": "vn"
},
"ItemTaxCountry": {
"dataSource": "vn"
},
"Warehouse": {
"dataSource": "vn"
},

View File

@ -18,5 +18,7 @@
"Package cannot be blank": "Package cannot be blank",
"The new quantity should be smaller than the old one": "The new quantity should be smaller than the old one",
"The sales of this ticket can't be modified": "The sales of this ticket can't be modified",
"Cannot check VIES and Equalization Tax": "Cannot check VIES and Equalization Tax"
"Cannot check VIES and Equalization Tax": "Cannot check VIES and Equalization Tax",
"Cannot check Equalization Tax in this NIF/CIF": "Cannot check Equalization Tax in this NIF/CIF",
"You can't create an order for a frozen client": "You can't create an order for a frozen client"
}

View File

@ -61,7 +61,7 @@ module.exports = Self => {
], transaction);
}
Self.importToNewRefundTicket = async(ctx, id) => {
Self.importToNewRefundTicket = async (ctx, id) => {
let models = Self.app.models;
let token = ctx.req.accessToken;
let userId = token.userId;
@ -112,7 +112,7 @@ module.exports = Self => {
let transaction = await Self.beginTransaction({});
try {
let newRefundTicket = await models.Ticket.new(params, {transaction: transaction});
let newRefundTicket = await models.Ticket.new(ctx, params, {transaction: transaction});
let observation = {
description: `Reclama ticket: ${claim.ticketFk}`,
@ -135,6 +135,10 @@ module.exports = Self => {
newRefundTicket.id, claim.ticketFk
], {transaction: transaction});
let claimState = await Self.app.models.ClaimState.findOne({where: {description: 'Resuelto'}});
await claim.updateAttribute('claimStateFk', claimState.id, {transaction: transaction});
await transaction.commit();
return newRefundTicket;

View File

@ -1,6 +1,6 @@
const app = require(`${servicesDir}/claim/server/server`);
describe('claimBeginning', () => {
// xcluded waiting for fixtures
xdescribe('claimBeginning', () => {
let ticket;
let refundTicketObservations;
let refundTicketSales;

View File

@ -132,9 +132,9 @@ module.exports = Self => {
return ticket && ticket.id;
}
async function createTicket(params, transaction) {
let ticket = await Self.app.models.Ticket.new({
shipped: new Date(),
async function createTicket(ctx, params, transaction) {
let ticket = await Self.app.models.Ticket.new(ctx,
{shipped: new Date(),
landed: new Date(),
clientFk: params.clientFk,
warehouseFk: params.warehouseFk,

View File

@ -1,6 +1,6 @@
const app = require(`${servicesDir}/claim/server/server`);
describe('regularizeClaim()', () => {
// xcluded waiting for fixtures
xdescribe('regularizeClaim()', () => {
const claimFk = 1;
const pendentState = 1;
const resolvedState = 3;
@ -9,7 +9,7 @@ describe('regularizeClaim()', () => {
let claimEnds = [];
let trashTicket;
afterAll(async() => {
afterAll(async () => {
let claim = await app.models.Claim.findById(claimFk);
await claim.updateAttributes({claimStateFk: pendentState});
await app.models.Ticket.destroyById(trashTicket.id);
@ -19,7 +19,7 @@ describe('regularizeClaim()', () => {
});
});
it('should change claim state to resolved', async() => {
it('should change claim state to resolved', async () => {
let ctx = {req: {accessToken: {userId: 18}}};
let params = {claimFk: claimFk};

View File

@ -1,62 +0,0 @@
module.exports = Self => {
Self.remoteMethod('activeBuyer', {
description: 'Returns actives workers with Buyer role',
accessType: 'READ',
accepts: [{
arg: 'filter',
type: 'Object',
required: false,
description: 'Filter defining where and paginated data',
http: {source: 'query'}
}],
returns: {
type: 'Worker',
root: true
},
http: {
path: `/activeBuyer`,
verb: 'get'
}
});
Self.activeBuyer = async filter => {
let sqlWhere = '';
let sqlLimit = '';
let sqlOffset = '';
let params = [];
if (filter.where) {
if (filter.where.firstName) {
sqlWhere = `AND (worker.firstName LIKE ? OR worker.name LIKE ?)`;
let search = where.firstName.like;
params.push(search);
params.push(search);
}
if (filter.where.id) {
sqlWhere = `AND worker.id = ?`;
params.push(filter.where.id);
}
}
if (filter.limit) {
sqlLimit = `LIMIT ?`;
params.push(filter.limit);
}
if (filter.skip) {
sqlOffset = `OFFSET ?`;
params.push(filter.skip);
}
let query =
`SELECT worker.id, worker.firstName, worker.name
FROM vn.worker
JOIN account.user user ON user.id = worker.userFk
JOIN account.role role ON role.name = 'buyer'
JOIN account.roleRole inheritance ON inheritance.role = user.role AND inheritance.inheritsFrom = role.id
WHERE user.active IS TRUE ${sqlWhere}
ORDER BY worker.firstName ASC
${sqlLimit} ${sqlOffset}`;
return await Self.rawSql(query, params);
};
};

View File

@ -1,62 +0,0 @@
module.exports = Self => {
Self.remoteMethod('activeSalesPerson', {
description: 'Returns actives workers with salesperson role',
accessType: 'READ',
accepts: [{
arg: 'filter',
type: 'Object',
required: false,
description: 'Filter defining where and paginated data',
http: {source: 'query'}
}],
returns: {
type: 'Worker',
root: true
},
http: {
path: `/activeSalesPerson`,
verb: 'get'
}
});
Self.activeSalesPerson = async filter => {
let sqlWhere = '';
let sqlLimit = '';
let sqlOffset = '';
let params = [];
if (filter.where) {
if (filter.where.firstName) {
sqlWhere = `AND (worker.firstName LIKE ? OR worker.name LIKE ?)`;
let search = where.firstName.like;
params.push(search);
params.push(search);
}
if (filter.where.id) {
sqlWhere = `AND worker.id = ?`;
params.push(filter.where.id);
}
}
if (filter.limit) {
sqlLimit = `LIMIT ?`;
params.push(filter.limit);
}
if (filter.skip) {
sqlOffset = `OFFSET ?`;
params.push(filter.skip);
}
let query =
`SELECT worker.id, worker.firstName, worker.name
FROM vn.worker
JOIN account.user user ON user.id = worker.userFk
JOIN account.role role ON role.name = 'salesPerson'
JOIN account.roleRole inheritance ON inheritance.role = user.role AND inheritance.inheritsFrom = role.id
WHERE user.active IS TRUE ${sqlWhere}
ORDER BY worker.firstName ASC
${sqlLimit} ${sqlOffset}`;
return await Self.rawSql(query, params);
};
};

View File

@ -0,0 +1,62 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
const buildFilter = require('../../filter.js').buildFilter;
const mergeFilters = require('../../filter.js').mergeFilters;
module.exports = Self => {
Self.remoteMethod('activeWorkersWithRole', {
description: 'Returns actives workers with salesperson role',
accessType: 'READ',
accepts: [{
arg: 'filter',
type: 'Object',
description: 'Filter defining where and paginated data',
required: true
}],
returns: {
type: 'Worker',
root: true
},
http: {
path: `/activeWorkersWithRole`,
verb: 'get'
}
});
Self.activeWorkersWithRole = async filter => {
let conn = Self.dataSource.connector;
if (filter.where && filter.where.and && Array.isArray(filter.where.and)) {
let where = {};
filter.where.and.forEach(element => {
where[Object.keys(element)[0]] = Object.values(element)[0];
});
filter.where = where;
}
let clientFilter = Object.assign({}, filter);
clientFilter.where = buildFilter(filter.where, (param, value) => {
switch (param) {
case 'role':
return {'r.name': value};
case 'firstName':
return {or: [{'w.firstName': {like: `%${value}%`}}, {'w.name': {like: `%${value}%`}}]};
case 'id':
return {'w.id': value};
}
});
let myFilter = {
where: {'u.active': true}
};
myFilter = mergeFilters(myFilter, clientFilter);
let stmt = new ParameterizedSQL(
`SELECT DISTINCT w.id, w.firstName, w.name
FROM worker w
JOIN account.user u ON u.id = w.userFk
JOIN account.roleRole i ON i.role = u.role
JOIN account.role r ON r.id = i.inheritsFrom`
);
stmt.merge(conn.makeSuffix(myFilter));
return await conn.executeStmt(stmt);
};
};

View File

@ -43,7 +43,7 @@ module.exports = function(Self) {
countryFk: data.countryFk,
isEqualizated: data.isEqualizated
};
newClient = await Self.create(client, {transaction});
newClient = await Self.create(client);
await transaction.commit();
return newClient;
} catch (e) {

View File

@ -1,13 +0,0 @@
const app = require(`${servicesDir}/client/server/server`);
describe('Client activeBuyer', () => {
it('should return the buyers as result', async () => {
let filter = {};
let result = await app.models.Client.activeBuyer(filter);
let isBuyer = await app.models.Account.hasRole(result[0].id, 'buyer');
expect(result.length).toEqual(9);
expect(isBuyer).toBeTruthy();
});
});

View File

@ -1,13 +0,0 @@
const app = require(`${servicesDir}/client/server/server`);
describe('Client activeSalesPerson', () => {
it('should return the sales people as result', async () => {
let filter = {};
let result = await app.models.Client.activeSalesPerson(filter);
let isSalesPerson = await app.models.Account.hasRole(result[0].id, 'salesPerson');
expect(result.length).toEqual(10);
expect(isSalesPerson).toBeTruthy();
});
});

View File

@ -0,0 +1,23 @@
const app = require(`${servicesDir}/client/server/server`);
describe('Client activeWorkersWithRole', () => {
it('should return the sales people as result', async () => {
let filter = {where: {role: 'salesPerson'}};
let result = await app.models.Client.activeWorkersWithRole(filter);
let isSalesPerson = await app.models.Account.hasRole(result[0].id, 'salesPerson');
expect(result.length).toEqual(10);
expect(isSalesPerson).toBeTruthy();
});
it('should return the buyers as result', async () => {
let filter = {where: {role: 'buyer'}};
let result = await app.models.Client.activeWorkersWithRole(filter);
let isBuyer = await app.models.Account.hasRole(result[0].id, 'buyer');
expect(result.length).toEqual(9);
expect(isBuyer).toBeTruthy();
});
});

View File

@ -16,7 +16,7 @@ module.exports = Self => {
arg: 'warehouseFk',
type: 'number',
required: true,
description: 'The id of the wharehouse where the inventory happened',
description: 'The id of the warehouse where the inventory happened',
}],
returns: {
type: 'boolean',
@ -86,7 +86,9 @@ module.exports = Self => {
}
async function createTicket(params, transaction) {
let ticket = await Self.app.models.Ticket.new({
let ticket = await Self.app.models.Ticket.new(
ctx,
{
shipped: new Date(),
landed: new Date(),
clientFk: params.clientFk,

View File

@ -22,28 +22,26 @@ module.exports = Self => {
Self.new = async params => {
let address = await Self.app.models.Address.findOne({
where: {id: params.addressFk},
fields: ['clientFk']
fields: ['clientFk'],
include: [
{relation: 'client'}
]
});
let clientFk = address.clientFk;
let client = await Self.app.models.Client.findOne({
where: {id: clientFk},
fields: ['isTaxDataChecked', 'isFreezed', 'isActive']
});
if (client.isFreezed)
if (address.client().isFreezed)
throw new UserError(`You can't create an order for a frozen client`);
if (!client.isActive)
if (!address.client().isActive)
throw new UserError(`You can't create an order for a inactive client`);
if (!client.isTaxDataChecked)
if (!address.client().isTaxDataChecked)
throw new UserError(`You can't create an order for a client that doesn't has tax data verified`);
let query = `SELECT vn.clientGetDebt(?, CURDATE()) AS debt`;
let clientDebt = await Self.rawSql(query, [clientFk]);
if (clientDebt[0].debt > 0)
if (address.client().credit - clientDebt[0].debt <= 0)
throw new UserError(`You can't create an order for a client that has a debt`);
query = `CALL vn.orderListCreate(?, ?, ?, ?);`;

View File

@ -0,0 +1,65 @@
const app = require(`${servicesDir}/order/server/server`);
describe('Order updateBasicData', () => {
afterAll(async () => {
let validparams = {note: null};
let orderId = 21;
await app.models.Order.updateBasicData(validparams, orderId);
});
it('should return an error if the order is confirmed', async () => {
let error;
let params = [];
let orderConfirmed = 1;
await app.models.Order.updateBasicData(params, orderConfirmed)
.catch(e => {
error = e;
});
expect(error.toString()).toContain(`You can't make changes on the basic data of an confirmed order or with rows`);
});
it('should return an error if the order has rows', async () => {
let error;
let params = [];
let orderWithRows = 16;
await app.models.Order.updateBasicData(params, orderWithRows)
.catch(e => {
error = e;
});
expect(error.toString()).toContain(`You can't make changes on the basic data of an confirmed order or with rows`);
});
it('should return an error if the user is administrative and the isTaxDataChecked value is true BUT the params aint valid', async () => {
let error;
let invalidparams = {invalid: 'param for update'};
let orderId = 21;
await app.models.Order.updateBasicData(invalidparams, orderId)
.catch(e => {
error = e;
});
expect(error.toString()).toContain(`You don't have enough privileges to do that`);
});
it('should update the client fiscal data and return the count if changes made', async () => {
let validparams = {note: 'test note'};
let orderId = 21;
let order = await app.models.Order.findById(orderId);
expect(order.note).toEqual(null);
let result = await app.models.Order.updateBasicData(validparams, orderId);
expect(result.note).toEqual('test note');
});
});

View File

@ -0,0 +1,51 @@
let UserError = require('../../helpers').UserError;
module.exports = Self => {
Self.remoteMethod('updateBasicData', {
description: 'Updates basic data of an order',
accessType: 'WRITE',
accepts: [{
arg: 'data',
type: 'Object',
required: true,
description: 'Params to update',
http: {source: 'body'}
}, {
arg: 'id',
type: 'string',
required: true,
description: 'Model id',
http: {source: 'path'}
}],
returns: {
arg: 'order',
type: 'Object',
root: true
},
http: {
path: `/:id/updateBasicData`,
verb: 'PATCH'
}
});
Self.updateBasicData = async (params, id) => {
let order = await Self.app.models.Order.findById(id);
let orderRows = await Self.app.models.OrderRow.find({where: {orderFk: id}});
if (order.isConfirmed || orderRows.length != 0)
throw new UserError(`You can't make changes on the basic data of an confirmed order or with rows`);
let validUpdateParams = [
'clientFk',
'companyFk',
'landed',
'note',
];
for (const key in params) {
if (validUpdateParams.indexOf(key) === -1)
throw new UserError(`You don't have enough privileges to do that`);
}
return await order.updateAttributes(params);
};
};

View File

@ -27,7 +27,7 @@ module.exports = Self => {
}
});
Self.moveToNewTicket = async(ctx, params) => {
Self.moveToNewTicket = async (ctx, params) => {
let userId = ctx.req.accessToken.userId;
let model = Self.app.models;
let thisTicketIsEditable = await model.Ticket.isEditable(params.ticket.oldTicketFk);
@ -53,7 +53,7 @@ module.exports = Self => {
let transaction = await Self.beginTransaction({});
try {
let newTicket = await model.Ticket.new(newTicketParams, {transaction: transaction});
let newTicket = await model.Ticket.new(ctx, newTicketParams, {transaction: transaction});
let selectedSalesId = [];
params.sales.forEach(sale => {

View File

@ -1,7 +1,7 @@
let UserError = require('../../helpers').UserError;
module.exports = Self => {
Self.remoteMethod('new', {
Self.remoteMethodCtx('new', {
description: 'Create a newticket and returns the new ID',
accessType: 'WRITE',
accepts: [{
@ -21,17 +21,46 @@ module.exports = Self => {
}
});
Self.new = async(params, transaction) => {
let existsAddress = await Self.app.models.Address.findOne({
where: {
id: params.addressFk,
clientFk: params.clientFk}
Self.new = async (ctx, params, transaction) => {
let address = await Self.app.models.Address.findOne({
where: {id: params.addressFk},
fields: ['clientFk'],
include: [
{relation: 'client'}
]
});
if (!existsAddress)
if (!address)
throw new UserError(`This address doesn't exist`);
let query = `CALL vn.ticketCreateWithUser(?, ?, ?, ?, ?, ?, ?, ?, ?, @result);
if (address.client().isFreezed)
throw new UserError(`You can't create a ticket for a frozen client`);
if (!address.client().isActive)
throw new UserError(`You can't create a ticket for a inactive client`);
if (!address.client().isTaxDataChecked)
throw new UserError(`You can't create a ticket for a client that doesn't has tax data verified`);
let clientFk = address.clientFk;
let agency;
if (params.agency)
agency = await Self.app.models.AgencyMode.findById(params.agencyModeFk);
else
agency = {code: null};
if (agency.code != 'refund') {
let query = `SELECT vn.clientGetDebt(?, CURDATE()) AS debt`;
let clientDebt = await Self.rawSql(query, [clientFk]);
if (address.client().credit - clientDebt[0].debt <= 0)
throw new UserError(`You can't create a ticket for a client that has a debt`);
}
if (!params.userId && ctx.req && ctx.req.accessToken.userId)
params.userId = ctx.req.accessToken.userId;
query = `CALL vn.ticketCreateWithUser(?, ?, ?, ?, ?, ?, ?, ?, ?, @result);
SELECT @result newTicketId;`;
let result = await Self.rawSql(query, [
params.clientFk,
@ -43,10 +72,10 @@ module.exports = Self => {
params.routeFk | null,
params.landed,
params.userId
], transaction);
], {options: transaction});
return await Self.findOne({
where: {id: result[1][0].newTicketId}
}, transaction);
}, {options: transaction});
};
};

View File

@ -3,16 +3,17 @@ const app = require(`${servicesDir}/ticket/server/server`);
describe('ticket new()', () => {
let ticket;
let today = new Date();
let ctx = {req: {accessToken: {userId: 1}}};
afterAll(async() => {
afterAll(async () => {
await app.models.Ticket.destroyById(ticket.id);
});
it('should throw an error if the address doesnt exist', async() => {
it('should throw an error if the address doesnt exist', async () => {
let error;
let params = {addressFk: 'invalid address', clientFk: 101};
let params = {addressFk: 'invalid address', clientFk: 104};
await app.models.Ticket.new(params)
await app.models.Ticket.new(ctx, params)
.catch(response => {
expect(response.message).toEqual(`This address doesn't exist`);
error = response;
@ -21,19 +22,19 @@ describe('ticket new()', () => {
expect(error).toBeDefined();
});
it('should return the id of the created ticket', async() => {
it('should return the id of the created ticket', async () => {
let params = {
warehouseFk: 1,
clientFk: 101,
clientFk: 104,
companyFk: 442,
addressFk: 1,
addressFk: 4,
agencyModeFk: 1,
userId: 9,
shipped: today,
landed: today
};
ticket = await app.models.Ticket.new(params);
ticket = await app.models.Ticket.new(ctx, params);
let newestTicketIdInFixtures = 21;

View File

@ -17,8 +17,13 @@ module.exports = Self => {
async function cannotHaveET(err, done) {
let client = await Self.app.models.Client.findById(this.clientFk);
let cannotHaveET;
if (client && client.fi) {
let tin = client.fi.toUpperCase();
let cannotHaveET = /^[A-B]/.test(tin);
cannotHaveET = /^[A-B]/.test(tin);
} else
cannotHaveET = false;
if (cannotHaveET && this.isEqualizated)
err();

View File

@ -40,6 +40,9 @@
},
"changedModelValue": {
"type": "String"
},
"description": {
"type": "String"
}
},
"relations": {

Some files were not shown because too many files have changed in this diff Show More