Ticket summary

This commit is contained in:
joan 2018-04-10 07:48:04 +02:00
parent a82260d4fb
commit 4b8ae91705
19 changed files with 454 additions and 18 deletions

View File

@ -56,7 +56,7 @@
} }
.buttons { .buttons {
margin: 0 margin-top: 2em
} }
vn-check label span { vn-check label span {

View File

@ -37,15 +37,18 @@
{ {
"url" : "/data", "url" : "/data",
"state": "ticket.card.data", "state": "ticket.card.data",
"component": "ui-view", "component": "vn-ticket-data",
"abstract": true "abstract": true,
"params": {
"ticket": "$ctrl.ticket"
}
}, },
{ {
"url" : "/step-one", "url" : "/step-one",
"state": "ticket.card.data.stepOne", "state": "ticket.card.data.stepOne",
"component": "vn-ticket-data-step-one", "component": "vn-ticket-data-step-one",
"params": { "params": {
"ticket": "$ctrl.ticket" "ticket": "$ctrl.data"
}, },
"menu": { "menu": {
"description": "Basic data", "description": "Basic data",
@ -55,12 +58,18 @@
{ {
"url" : "/step-two", "url" : "/step-two",
"state": "ticket.card.data.stepTwo", "state": "ticket.card.data.stepTwo",
"component": "vn-ticket-data-step-two" "component": "vn-ticket-data-step-two",
"params": {
"ticket": "$ctrl.data"
}
}, },
{ {
"url" : "/step-three", "url" : "/step-three",
"state": "ticket.card.data.stepThree", "state": "ticket.card.data.stepThree",
"component": "vn-ticket-data-step-three" "component": "vn-ticket-data-step-three",
"params": {
"ticket": "$ctrl.data"
}
}, },
{ {
"url": "/observation", "url": "/observation",

View File

@ -0,0 +1,12 @@
<vn-button-bar>
<vn-step-control
step-count="10"
steps="[
{name: 'Basic data', state: 'ticket.card.data.stepOne'},
{name: 'Price gap', state: 'ticket.card.data.stepTwo'},
{name: 'Step 3', state: 'ticket.card.data.stepThree'}]"
on-step-change="$ctrl.onStepChange(state)"
on-step-end="$ctrl.onSubmit()">
</vn-step-control>
</vn-button-bar>
<ui-view></ui-view>

View File

@ -0,0 +1,32 @@
import ngModule from '../module';
class Controller {
constructor($scope, $state) {
this.$scope = $scope;
this.$state = $state;
}
set ticket(data) {
this.data = Object.assign({}, data);
}
onSubmit() {
//post data
alert('Data saved');
console.log(this.data);
}
onStepChange(state) {
return true;
}
}
Controller.$inject = ['$scope', '$state'];
ngModule.component('vnTicketData', {
template: require('./data.html'),
bindings: {
ticket: '<'
},
controller: Controller
});

View File

@ -1,6 +1,6 @@
<a <a
ui-sref="ticket.card.summary({ id: {{::$ctrl.ticket.id}} })" ui-sref="ticket.card.summary({ id: {{::$ctrl.ticket.id}} })"
translate-attr="{title: 'View client'}" translate-attr="{title: 'View ticket'}"
class="vn-list-item"> class="vn-list-item">
<vn-horizontal ng-click="$ctrl.onClick($event)"> <vn-horizontal ng-click="$ctrl.onClick($event)">
<vn-one> <vn-one>
@ -9,8 +9,9 @@
</vn-one> </vn-one>
<vn-horizontal class="buttons"> <vn-horizontal class="buttons">
<vn-icon <vn-icon
ng-click="$ctrl.preview($event)"
vn-tooltip="Preview" vn-tooltip="Preview"
icon="icon-preview"> icon="desktop_windows">
</vn-icon> </vn-icon>
</vn-horizontal> </vn-horizontal>
</vn-horizontal> </vn-horizontal>

View File

@ -8,6 +8,7 @@ class Controller {
preview(event) { preview(event) {
event.preventDefault(); event.preventDefault();
this.list.openSummary(this.ticket);
} }
} }
@ -16,5 +17,8 @@ ngModule.component('vnTicketItem', {
template: require('./ticket-item.html'), template: require('./ticket-item.html'),
bindings: { bindings: {
ticket: '<' ticket: '<'
},
require: {
list: '^vnTicketList'
} }
}); });

View File

@ -22,3 +22,9 @@
<a ui-sref="ticket.create" fixed-bottom-right> <a ui-sref="ticket.create" fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button> <vn-float-button icon="add"></vn-float-button>
</a> </a>
<vn-dialog class="dialog-summary"
vn-id="dialog-summary-ticket">
<tpl-body>
<vn-ticket-summary ticket="$ctrl.ticketSelected"></vn-ticket-summary>
</tpl-body>
</vn-dialog>

View File

@ -3,11 +3,22 @@ import './ticket-item';
import './style.scss'; import './style.scss';
export default class Controller { export default class Controller {
constructor($scope) {
this.$scope = $scope;
this.ticketSelected = null;
}
search(index) { search(index) {
index.accept(); index.accept();
} }
openSummary(ticket) {
this.ticketSelected = ticket;
this.$scope.dialogSummaryTicket.show();
}
} }
Controller.$inject = [];
Controller.$inject = ['$scope'];
ngModule.component('vnTicketList', { ngModule.component('vnTicketList', {
template: require('./ticket-list.html'), template: require('./ticket-list.html'),

View File

@ -39,3 +39,5 @@ Is checked: Comprobado
Volume: Volumen Volume: Volumen
m³ per unit: m³ por unidad m³ per unit: m³ por unidad
m³ per quantity: m³ por cantidad m³ per quantity: m³ por cantidad
New price: Nuevo precio
Price gap: Diferencia de precio

View File

@ -1,9 +1,69 @@
<vn-card class="summary"> <vn-card class="summary">
<vn-vertical pad-medium> <vn-vertical pad-medium>
<vn-auto> <vn-auto>
<h5 text-center pad-small-v class="tittle">Ticket Summary</h5> <h5 text-center pad-small-v class="tittle">{{$ctrl.summary.id}} - {{$ctrl.summary.client.name}} - {{$ctrl.summary.nickname}}</h5>
</vn-auto> </vn-auto>
<vn-horizontal> <vn-horizontal>
<vn-one>
<p><vn-label translate>State</vn-label> {{$ctrl.summary.ticketTracking.state.name}}</p>
<p>
<vn-label translate>Comercial Name</vn-label>
{{$ctrl.summary.client.salesPerson.firstName}}
{{$ctrl.summary.client.salesPerson.name}}
</p>
<p><vn-label translate>Agency</vn-label> {{$ctrl.summary.agencyMode.name}}</p>
<p><vn-label translate>Warehouse</vn-label> {{$ctrl.summary.warehouse.name}}</p>
<p><vn-label translate>Packages</vn-label> {{$ctrl.summary.packages}}</p>
</vn-one>
<vn-one>
<p><vn-label translate>Shipped</vn-label> {{$ctrl.summary.shipped | date: 'dd/MM/yyyy'}}</p>
<p><vn-label translate>Landed</vn-label> {{$ctrl.summary.landed | date: 'dd/MM/yyyy'}}</p>
<p><vn-label translate>Route</vn-label> {{$ctrl.summary.routeFk}}</p>
<p><vn-label translate>Address</vn-label> {{$ctrl.summary.address.street}}</p>
<p><vn-label translate>Phone</vn-label> {{$ctrl.summary.address.phone}}</p>
</vn-one>
<vn-one>
<p ng-repeat="note in $ctrl.summary.notes track by note.id">
<vn-label translate>{{note.observationType.description}}</vn-label>
{{note.description}}
</p>
</vn-one>
<vn-one>
<p><vn-label translate>Subtotal</vn-label> {{0}}</p>
<p><vn-label translate>IVA(n%)</vn-label> {{0}}</p>
<p><vn-label translate>TOTAL</vn-label> {{0}}</p>
</vn-one>
</vn-horizontal>
<vn-horizontal>
<table class="vn-grid">
<thead>
<tr>
<th></th>
<th number translate>Item</th>
<th translate style="text-align:center">Description</th>
<th number translate>Quantity</th>
<th number translate>Price</th>
<th number translate>Discount</th>
<th number translate>Amount</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="sale in $ctrl.summary.sales track by sale.id">
<td>
<!-- <i pointer
class="material-icons"
vn-tooltip="delete expedition"
ng-click="$ctrl.deleteExpedition(expedition)">warning</i> -->
</td>
<td number>{{("000000"+sale.itemFk).slice(-6)}}</td>
<td><vn-fetched-tags sale="sale"/></td>
<td number>{{::sale.quantity}}</td>
<td number>{{::sale.price | currency:'€':2}}</td>
<td number>{{::sale.discount}} %</td>
<td number>{{::sale.quantity * sale.price | currency:'€':2}}</td>
</tr>
</tbody>
</table>
</vn-horizontal> </vn-horizontal>
</vn-vertical> </vn-vertical>
</vn-card> </vn-card>

View File

@ -1,11 +1,26 @@
import ngModule from '../module'; import ngModule from '../module';
class TicketSummary {} class Controller {
TicketSummary.$inject = []; constructor($http) {
this.$http = $http;
}
$onChanges() {
if (!this.ticket)
return;
this.$http.get(`/client/api/Tickets/${this.ticket.id}/summary`).then(res => {
if (res && res.data)
this.summary = res.data;
});
}
}
Controller.$inject = ['$http'];
ngModule.component('vnTicketSummary', { ngModule.component('vnTicketSummary', {
template: require('./ticket-summary.html'), template: require('./ticket-summary.html'),
controller: TicketSummary, controller: Controller,
bindings: { bindings: {
ticket: '<' ticket: '<'
} }

View File

@ -0,0 +1,39 @@
module.exports = Self => {
Self.installMethod('filter', filterParams);
function filterParams(params) {
return {
where: {
ticketFk: params.ticketFk
},
skip: (params.page - 1) * params.size,
limit: params.size,
order: params.order || 'concept ASC',
include: [{
relation: "item",
scope: {
include: {
relation: "itemTag",
scope: {
fields: ["tagFk", "value"],
include: {
relation: "tag",
scope: {
fields: ["name"]
}
},
limit: 6
}
},
fields: ["itemFk", "name"]
}
},
{
relation: "isChecked",
scope: {
fields: ["isChecked"]
}
}]
};
}
};

View File

@ -0,0 +1,51 @@
module.exports = Self => {
Self.remoteMethod('priceGap', {
description: 'Returns a sale price gap',
accessType: 'READ',
accepts: [{
arg: 'ticketFk',
type: 'number',
required: true,
description: 'ticket id',
http: {source: 'path'}
}],
returns: {
type: ['Object'],
root: true
},
http: {
path: `/:ticketFk/priceGap`,
verb: 'GET'
}
});
Self.priceGap = async ticketFk => {
let filter = {
where: {
ticketFk: ticketFk
},
order: 'concept ASC',
include: [{
relation: 'item',
scope: {
include: {
relation: 'itemTag',
scope: {
fields: ['tagFk', 'value'],
include: {
relation: 'tag',
scope: {
fields: ['name']
}
},
limit: 6
}
},
fields: ['itemFk', 'name']
}
}]
};
return await Self.find(filter);
};
};

View File

@ -0,0 +1,120 @@
module.exports = Self => {
Self.remoteMethod('summary', {
description: 'Returns a ticket summary',
accessType: 'READ',
accepts: [{
arg: 'id',
type: 'number',
required: true,
description: 'ticket id',
http: {source: 'path'}
}],
returns: {
type: [this.modelName],
root: true
},
http: {
path: `/:id/summary`,
verb: 'GET'
}
});
Self.summary = async ticketFk => {
let models = Self.app.models;
let summaryObj = await getTicketData(Self, ticketFk);
summaryObj.sales = await getSales(models.Sale, ticketFk);
return summaryObj;
};
async function getTicketData(Self, ticketFk) {
let filter = {
include: [
{relation: 'warehouse', scope: {fields: ['name']}},
{relation: 'agencyMode', scope: {fields: ['name']}},
{
relation: 'client',
scope: {
fields: ['salesPersonFk', 'name'],
include: {
relation: 'salesPerson',
fields: ['firstName', 'name']
}
}
},
{relation: 'address', scope: {fields: ['street', 'phone']}},
{
relation: 'notes',
scope: {
fields: ['id', 'observationTypeFk', 'description'],
include: {
relation: 'observationType',
fields: ['description']
}
}
},
{
relation: 'ticketTracking',
scope: {
fields: ['stateFk'],
include: {
relation: 'state',
fields: ['name']
}
}
}
],
where: {id: ticketFk}
};
return await Self.findOne(filter);
}
async function getSales(Sale, ticketFk) {
let filter = {
where: {
ticketFk: ticketFk
},
order: 'concept ASC',
include: [{
relation: 'item',
scope: {
include: {
relation: 'itemTag',
scope: {
fields: ['tagFk', 'value'],
include: {
relation: 'tag',
scope: {
fields: ['name']
}
},
limit: 6
}
},
fields: ['itemFk', 'name']
}
},
{
relation: 'isChecked',
scope: {
fields: ['isChecked']
}
}]
};
return await Sale.find(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

@ -0,0 +1,4 @@
module.exports = function(Self) {
require('../methods/sale/filter')(Self);
require('../methods/sale/priceGap')(Self);
};

View File

@ -0,0 +1,56 @@
{
"name": "Sale",
"base": "VnModel",
"options": {
"mysql": {
"table": "sale"
}
},
"properties": {
"id": {
"id": true,
"type": "Number",
"description": "Identifier"
},
"concept": {
"type": "String"
},
"quantity": {
"type": "Number"
},
"price": {
"type": "Number"
},
"discount": {
"type": "Number"
},
"reserved": {
"type": "Number"
},
"isPicked": {
"type": "Number"
},
"created": {
"type": "date"
}
},
"relations": {
"item": {
"type": "belongsTo",
"model": "Item",
"foreignKey": "itemFk",
"required": true
},
"ticket": {
"type": "belongsTo",
"model": "Ticket",
"foreignKey": "ticketFk",
"required": true
},
"isChecked": {
"type": "hasOne",
"model": "SaleChecked",
"foreignKey": "saleFk"
}
}
}

View File

@ -1,6 +1,7 @@
module.exports = function(Self) { module.exports = function(Self) {
require('../methods/ticket/change-time.js')(Self); require('../methods/ticket/change-time')(Self);
require('../methods/ticket/change-worker.js')(Self); require('../methods/ticket/change-worker')(Self);
require('../methods/ticket/filter.js')(Self); require('../methods/ticket/filter')(Self);
require('../methods/ticket/get-volume.js')(Self); require('../methods/ticket/get-volume')(Self);
require('../methods/ticket/summary')(Self);
}; };

View File

@ -76,6 +76,16 @@
"type": "hasMany", "type": "hasMany",
"model": "TicketPackaging", "model": "TicketPackaging",
"foreignKey": "ticketFk" "foreignKey": "ticketFk"
},
"ticketTracking": {
"type": "hasOne",
"model": "TicketTracking",
"foreignKey": "ticketFk"
},
"notes": {
"type": "hasMany",
"model": "TicketObservation",
"foreignKey": "ticketFk"
} }
} }
} }

View File

@ -69,6 +69,9 @@
"State":{ "State":{
"dataSource": "vn" "dataSource": "vn"
}, },
"Sale": {
"dataSource": "vn"
},
"TicketState":{ "TicketState":{
"dataSource": "vn" "dataSource": "vn"
}, },