refs #4866 instance log added and e2e done
gitea/salix/pipeline/head Build queued... Details

This commit is contained in:
Alexandre Riera 2022-12-07 13:53:59 +01:00
parent 569a0e8cc9
commit 704cb71b61
10 changed files with 291 additions and 27 deletions

View File

@ -1,6 +1,6 @@
const models = require('vn-loopback/server/server').models; const models = require('vn-loopback/server/server').models;
describe('ticket getCollection()', () => { fdescribe('ticket getCollection()', () => {
it('should return a list of collections', async() => { it('should return a list of collections', async() => {
let ctx = {req: {accessToken: {userId: 1107}}}; let ctx = {req: {accessToken: {userId: 1107}}};
let response = await models.Collection.getCollection(ctx); let response = await models.Collection.getCollection(ctx);

View File

@ -678,7 +678,10 @@ export default {
moveToTicketButton: '.vn-popover.shown vn-icon[icon="arrow_forward_ios"]', moveToTicketButton: '.vn-popover.shown vn-icon[icon="arrow_forward_ios"]',
moveToNewTicketButton: '.vn-popover.shown vn-button[label="New ticket"]', moveToNewTicketButton: '.vn-popover.shown vn-button[label="New ticket"]',
stateMenuButton: 'vn-ticket-sale vn-tool-bar > vn-button-menu[label="State"]', stateMenuButton: 'vn-ticket-sale vn-tool-bar > vn-button-menu[label="State"]',
moreMenuState: 'body > div > div > div.content > div.filter.ng-scope > vn-textfield' moreMenuState: 'body > div > div > div.content > div.filter.ng-scope > vn-textfield',
firstSaleHistoryButton: 'vn-ticket-sale vn-tr:nth-child(1) vn-icon-button[icon="history"]',
firstSaleHistory: 'form vn-table div > vn-tbody > vn-tr',
closeHistory: 'div.window vn-button[icon="clear"]'
}, },
ticketTracking: { ticketTracking: {
createStateButton: 'vn-float-button' createStateButton: 'vn-float-button'

View File

@ -196,6 +196,15 @@ describe('Ticket Edit sale path', () => {
expect(result).toContain('22.50'); expect(result).toContain('22.50');
}); });
it('should check in the history that logs has been added', async() => {
await page.waitToClick(selectors.ticketSales.firstSaleHistoryButton);
await page.waitForSelector(selectors.ticketSales.firstSaleHistory);
const result = await page.countElement(selectors.ticketSales.firstSaleHistory);
expect(result).toBeGreaterThan(0);
await page.waitToClick(selectors.ticketSales.closeHistory);
});
it('should recalculate price of sales', async() => { it('should recalculate price of sales', async() => {
await page.waitToClick(selectors.ticketSales.firstSaleCheckbox); await page.waitToClick(selectors.ticketSales.firstSaleCheckbox);
await page.waitToClick(selectors.ticketSales.secondSaleCheckbox); await page.waitToClick(selectors.ticketSales.secondSaleCheckbox);

View File

@ -19,3 +19,4 @@ import './user-popover';
import './upload-photo'; import './upload-photo';
import './bank-entity'; import './bank-entity';
import './log'; import './log';
import './instance-log';

View File

@ -0,0 +1,69 @@
<vn-dialog
vn-id="instanceLog">
<tpl-body>
<vn-crud-model
vn-id="model"
url="{{$ctrl.url}}"
filter="$ctrl.filter"
link="{originFk: $ctrl.originId}"
data="$ctrl.logs"
limit="20"
auto-load="true">
</vn-crud-model>
<vn-data-viewer model="model">
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th field="creationDate">Date</vn-th>
<vn-th field="userFk" shrink>User</vn-th>
<vn-th field="action" shrink>Action</vn-th>
<vn-th expand>Changes</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="log in $ctrl.logs">
<vn-td shrink-datetime>
{{::log.creationDate | date:'dd/MM/yyyy HH:mm'}}
</vn-td>
<vn-td>
<span ng-class="{'link': log.user.worker.id, 'value': !log.user.worker.id}"
ng-click="$ctrl.showWorkerDescriptor($event, log.user.worker.id)"
translate>{{::log.user.name || 'System' | translate}}
</span>
</vn-td>
<vn-td translate>
{{::$ctrl.actionsText[log.action]}}
</vn-td>
<vn-td expand>
<table class="attributes">
<thead>
<tr>
<th translate class="field">Field</th>
<th translate>Before</th>
<th translate>After</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="prop in ::log.props">
<td class="field">{{prop.name}}</td>
<td class="before">{{::$ctrl.formatValue(prop.old)}}</td>
<td class="after">{{::$ctrl.formatValue(prop.new)}}</td>
</tr>
</tbody>
</table>
<vn-one ng-if="!log.newProperties" id="description">
<div>
<span no-ellipsize>{{::log.description}}</span>
</div>
</vn-one>
</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
<vn-pagination model="model"></vn-pagination>
</vn-data-viewer>
</tpl-body>
</vn-dialog>
<vn-worker-descriptor-popover vn-id="workerDescriptor">
</vn-worker-descriptor-popover>

View File

@ -0,0 +1,106 @@
import ngModule from '../../module';
import './style.scss';
import Section from '../section';
export default class Controller extends Section {
constructor($element, $) {
super($element, $);
this.actionsText = {
'insert': 'Creates',
'update': 'Updates',
'delete': 'Deletes',
'select': 'Views'
}; ``;
}
open() {
this.filter = {
where:
{changedModel: this.changedModel,
changedModelId: this.changedModelId},
include: [{
relation: 'user',
scope: {
fields: ['name'],
include: {
relation: 'worker',
scope: {
fields: ['id']
}
}
},
}],
};
this.$.instanceLog.show();
}
get logs() {
return this._logs;
}
set logs(value) {
this._logs = value;
if (!this.logs) return;
const validations = window.validations;
for (const log of value) {
const locale = validations[log.changedModel] && validations[log.changedModel].locale
? validations[log.changedModel].locale : {};
log.oldProperties = this.getInstance(log.oldInstance, locale);
log.newProperties = this.getInstance(log.newInstance, locale);
let props = [].concat(log.oldProperties.map(p => p.key), log.newProperties.map(p => p.key));
props = [...new Set(props)];
log.props = [];
for (const prop of props) {
const matchOldProp = log.oldProperties.find(p => p.key === prop);
const matchNewProp = log.newProperties.find(p => p.key === prop);
log.props.push({
name: prop,
old: matchOldProp ? matchOldProp.value : null,
new: matchNewProp ? matchNewProp.value : null,
});
}
}
}
formatValue(value) {
switch (typeof value) {
case 'boolean':
return value ? '✓' : '✗';
default:
return value;
}
}
getInstance(instance, locale) {
const properties = [];
let validDate = /^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?(Z)?$/;
if (typeof instance == 'object' && instance != null) {
Object.keys(instance).forEach(property => {
if (validDate.test(instance[property]))
instance[property] = new Date(instance[property]).toLocaleString('es-ES');
const key = locale[property] || property;
properties.push({key, value: instance[property]});
});
return properties;
}
return null;
}
showWorkerDescriptor(event, workerId) {
if (!workerId) return;
this.$.workerDescriptor.show(event.target, workerId);
}
}
ngModule.vnComponent('vnInstanceLog', {
controller: Controller,
template: require('./index.html'),
bindings: {
model: '<',
originId: '<',
changedModel: '<',
changedModelId: '<',
url: '@'
}
});

View File

@ -0,0 +1,15 @@
Date: Fecha
Model: Modelo
Action: Acción
Author: Autor
Before: Antes
After: Despues
History: Historial
Name: Nombre
Creates: Crea
Updates: Actualiza
Deletes: Elimina
Views: Visualiza
System: Sistema
note: nota
Changes: Cambios

View File

@ -0,0 +1,43 @@
@import "variables";
vn-instance-log {
vn-td {
vertical-align: initial !important;
}
.changes {
display: none;
}
.label {
color: $color-font-secondary;
}
.value {
color: $color-font;
}
@media screen and (max-width: 1570px) {
vn-table .expendable {
display: none;
}
.changes {
padding-top: 10px;
display: block;
}
}
.attributes {
width: 100%;
white-space: inherit !important;
tr {
height: 10px;
& > td {
padding: 2px;
width: 33%;
}
& > td.field,
& > th.field {
color: gray;
}
}
}
}

View File

@ -30,7 +30,7 @@
ng-click="moreOptions.show($event)" ng-click="moreOptions.show($event)"
ng-show="$ctrl.hasSelectedSales()"> ng-show="$ctrl.hasSelectedSales()">
</vn-button> </vn-button>
<vn-button <vn-button
disabled="!$ctrl.hasSelectedSales() || !$ctrl.isEditable" disabled="!$ctrl.hasSelectedSales() || !$ctrl.isEditable"
ng-click="deleteLines.show()" ng-click="deleteLines.show()"
vn-tooltip="Remove lines" vn-tooltip="Remove lines"
@ -53,7 +53,7 @@
<vn-thead> <vn-thead>
<vn-tr> <vn-tr>
<vn-th shrink> <vn-th shrink>
<vn-multi-check model="model" <vn-multi-check model="model"
on-change="$ctrl.resetChanges()"> on-change="$ctrl.resetChanges()">
</vn-multi-check> </vn-multi-check>
</vn-th> </vn-th>
@ -68,6 +68,7 @@
<vn-th number>Disc</vn-th> <vn-th number>Disc</vn-th>
<vn-th number>Amount</vn-th> <vn-th number>Amount</vn-th>
<vn-th shrink>Packaging</vn-th> <vn-th shrink>Packaging</vn-th>
<vn-th shrink></vn-th>
</vn-tr> </vn-tr>
</vn-thead> </vn-thead>
<vn-tbody> <vn-tbody>
@ -84,13 +85,13 @@
vn-tooltip="{{::$ctrl.$t('Claim')}}: {{::sale.claim.claimFk}}"> vn-tooltip="{{::$ctrl.$t('Claim')}}: {{::sale.claim.claimFk}}">
</vn-icon> </vn-icon>
</a> </a>
<vn-icon <vn-icon
ng-show="::(sale.visible < 0)" ng-show="::(sale.visible < 0)"
color-main color-main
icon="warning" icon="warning"
vn-tooltip="Visible: {{::sale.visible || 0}}"> vn-tooltip="Visible: {{::sale.visible || 0}}">
</vn-icon> </vn-icon>
<vn-icon ng-show="sale.reserved" <vn-icon ng-show="sale.reserved"
icon="icon-reserve" icon="icon-reserve"
translate-attr="{title: 'Reserved'}"> translate-attr="{title: 'Reserved'}">
</vn-icon> </vn-icon>
@ -108,21 +109,21 @@
</vn-icon> </vn-icon>
</vn-td> </vn-td>
<vn-td shrink> <vn-td shrink>
<img <img
ng-src="{{$root.imagePath('catalog', '50x50', sale.itemFk)}}" ng-src="{{$root.imagePath('catalog', '50x50', sale.itemFk)}}"
zoom-image="{{$root.imagePath('catalog', '1600x900', sale.itemFk)}}" zoom-image="{{$root.imagePath('catalog', '1600x900', sale.itemFk)}}"
on-error-src/> on-error-src/>
</vn-td> </vn-td>
<vn-td shrink> <vn-td shrink>
<vn-chip <vn-chip
class="transparent" class="transparent"
ng-class="{'alert': sale.visible < 0}"> ng-class="{'alert': sale.visible < 0}">
{{::sale.visible}} {{::sale.visible}}
</vn-chip> </vn-chip>
</vn-td> </vn-td>
<vn-td shrink> <vn-td shrink>
<vn-chip <vn-chip
class="transparent" class="transparent"
ng-class="{'alert': sale.available < 0}"> ng-class="{'alert': sale.available < 0}">
{{::sale.available}} {{::sale.available}}
</vn-chip> </vn-chip>
@ -195,7 +196,7 @@
translate-attr="{title: !$ctrl.isLocked ? 'Edit discount' : ''}" translate-attr="{title: !$ctrl.isLocked ? 'Edit discount' : ''}"
ng-click="$ctrl.showEditDiscountPopover($event, sale)" ng-click="$ctrl.showEditDiscountPopover($event, sale)"
ng-if="sale.id"> ng-if="sale.id">
{{(sale.discount / 100) | percentage}} {{(sale.discount / 100) | percentage}}
</span> </span>
</vn-td> </vn-td>
<vn-td number> <vn-td number>
@ -204,6 +205,22 @@
<vn-td shrink> <vn-td shrink>
{{::sale.item.itemPackingTypeFk | dashIfEmpty}} {{::sale.item.itemPackingTypeFk | dashIfEmpty}}
</vn-td> </vn-td>
<vn-td shrink>
<vn-icon-button
vn-none
vn-tooltip="History"
icon="history"
ng-click="log.open()">
</vn-icon-button>
<vn-instance-log
vn-id="log"
url="TicketLogs"
origin-id="$ctrl.$params.id"
changed-model="Sale"
changed-model-id="sale.id">
</vn-instance-log>
</vn-td>
</vn-tr> </vn-tr>
</vn-tbody> </vn-tbody>
</vn-table> </vn-table>
@ -383,8 +400,8 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr <tr
class="clickable" class="clickable"
ng-repeat="ticket in $ctrl.transfer.lastActiveTickets track by ticket.id" ng-repeat="ticket in $ctrl.transfer.lastActiveTickets track by ticket.id"
ng-click="$ctrl.transferSales(ticket.id)"> ng-click="$ctrl.transferSales(ticket.id)">
<td shrink>{{::ticket.id}}</td> <td shrink>{{::ticket.id}}</td>
@ -392,22 +409,22 @@
<td shrink>{{::ticket.agencyName}}</td> <td shrink>{{::ticket.agencyName}}</td>
<td expand>{{::ticket.address}} <td expand>{{::ticket.address}}
<span vn-tooltip=" <span vn-tooltip="
{{::ticket.nickname}} {{::ticket.nickname}}
{{::ticket.name}} {{::ticket.name}}
{{::ticket.street}} {{::ticket.street}}
{{::ticket.postalCode}} {{::ticket.postalCode}}
{{::ticket.city}}"> {{::ticket.city}}">
{{::ticket.nickname}} {{::ticket.nickname}}
{{::ticket.name}} {{::ticket.name}}
{{::ticket.street}} {{::ticket.street}}
{{::ticket.postalCode}} {{::ticket.postalCode}}
{{::ticket.city}} {{::ticket.city}}
</span> </span>
</td> </td>
</tr> </tr>
<tr> <tr>
<td <td
ng-if="!$ctrl.transfer.lastActiveTickets.length" ng-if="!$ctrl.transfer.lastActiveTickets.length"
class="empty-rows" class="empty-rows"
colspan="4" colspan="4"
translate> translate>
@ -502,4 +519,4 @@
vn-acl-action="remove"> vn-acl-action="remove">
Refund Refund
</vn-item> </vn-item>
</vn-menu> </vn-menu>

View File

@ -13,9 +13,9 @@ New ticket: Nuevo ticket
Edit price: Editar precio Edit price: Editar precio
You are going to delete lines of the ticket: Vas a eliminar lineas del ticket You are going to delete lines of the ticket: Vas a eliminar lineas del ticket
This ticket will be removed from current route! Continue anyway?: ¡Se eliminará el ticket de la ruta actual! ¿Continuar de todas formas? This ticket will be removed from current route! Continue anyway?: ¡Se eliminará el ticket de la ruta actual! ¿Continuar de todas formas?
You have to allow pop-ups in your web browser to use this functionality: You have to allow pop-ups in your web browser to use this functionality:
Debes permitir los pop-pups en tu navegador para que esta herramienta funcione correctamente Debes permitir los pop-pups en tu navegador para que esta herramienta funcione correctamente
Disc: Dto Disc: Dto
Available: Disponible Available: Disponible
What is the day of receipt of the ticket?: ¿Cual es el día de preparación del pedido? What is the day of receipt of the ticket?: ¿Cual es el día de preparación del pedido?
Add claim: Crear reclamación Add claim: Crear reclamación
@ -39,3 +39,4 @@ Packaging: Encajado
Refund: Abono Refund: Abono
Promotion mana: Maná promoción Promotion mana: Maná promoción
Claim mana: Maná reclamación Claim mana: Maná reclamación
History: Historial