Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 3291-entryLatestBuys-buscador
gitea/salix/pipeline/head This commit looks good Details

This commit is contained in:
Alexandre Riera 2023-02-03 11:56:09 +01:00
commit 141ae7b11c
10 changed files with 188 additions and 108 deletions

View File

@ -3,9 +3,9 @@ module.exports = Self => {
description: 'Send email to the user', description: 'Send email to the user',
accepts: [ accepts: [
{ {
arg: 'email', arg: 'user',
type: 'string', type: 'string',
description: 'The email of user', description: 'The user name or email',
required: true required: true
} }
], ],
@ -15,11 +15,20 @@ module.exports = Self => {
} }
}); });
Self.recoverPassword = async function(email) { Self.recoverPassword = async function(user) {
const models = Self.app.models; const models = Self.app.models;
const usesEmail = user.indexOf('@') !== -1;
if (!usesEmail) {
const account = await models.Account.findOne({
fields: ['email'],
where: {name: user}
});
user = account.email;
}
try { try {
await models.user.resetPassword({email, emailTemplate: 'recover-password'}); await models.user.resetPassword({email: user, emailTemplate: 'recover-password'});
} catch (err) { } catch (err) {
if (err.code === 'EMAIL_NOT_FOUND') if (err.code === 'EMAIL_NOT_FOUND')
return; return;

View File

@ -31,7 +31,7 @@ export default {
}, },
recoverPassword: { recoverPassword: {
recoverPasswordButton: 'vn-login a[ui-sref="recover-password"]', recoverPasswordButton: 'vn-login a[ui-sref="recover-password"]',
email: 'vn-recover-password vn-textfield[ng-model="$ctrl.email"]', email: 'vn-recover-password vn-textfield[ng-model="$ctrl.user"]',
sendEmailButton: 'vn-recover-password vn-submit', sendEmailButton: 'vn-recover-password vn-submit',
}, },
accountIndex: { accountIndex: {
@ -562,15 +562,15 @@ export default {
payoutBank: '.vn-dialog vn-autocomplete[ng-model="$ctrl.bankFk"]', payoutBank: '.vn-dialog vn-autocomplete[ng-model="$ctrl.bankFk"]',
payoutDescription: 'vn-textfield[ng-model="$ctrl.receipt.description"]', payoutDescription: 'vn-textfield[ng-model="$ctrl.receipt.description"]',
submitPayout: '.vn-dialog button[response="accept"]', submitPayout: '.vn-dialog button[response="accept"]',
searchWeeklyResult: 'vn-ticket-weekly-index vn-table vn-tbody > vn-tr', searchWeeklyResult: 'vn-ticket-weekly-index vn-card smart-table slot-table table tbody tr',
searchResultDate: 'vn-ticket-summary [label=Landed] span', searchResultDate: 'vn-ticket-summary [label=Landed] span',
topbarSearch: 'vn-searchbar', topbarSearch: 'vn-searchbar',
moreMenu: 'vn-ticket-index vn-icon-button[icon=more_vert]', moreMenu: 'vn-ticket-index vn-icon-button[icon=more_vert]',
fourthWeeklyTicket: 'vn-ticket-weekly-index vn-table vn-tbody vn-tr:nth-child(4)', fourthWeeklyTicket: 'vn-ticket-weekly-index vn-card smart-table slot-table tr:nth-child(4)',
fiveWeeklyTicket: 'vn-ticket-weekly-index vn-table vn-tbody vn-tr:nth-child(5)', fiveWeeklyTicket: 'vn-ticket-weekly-index vn-card smart-table slot-table tr:nth-child(5)',
weeklyTicket: 'vn-ticket-weekly-index vn-table > div > vn-tbody > vn-tr', weeklyTicket: 'vn-ticket-weekly-index vn-card smart-table slot-table table tbody tr',
firstWeeklyTicketDeleteIcon: 'vn-ticket-weekly-index vn-tr:nth-child(1) vn-icon-button[icon="delete"]', firstWeeklyTicketDeleteIcon: 'vn-ticket-weekly-index vn-card smart-table slot-table tr:nth-child(1) vn-icon-button[icon="delete"]',
firstWeeklyTicketAgency: 'vn-ticket-weekly-index vn-tr:nth-child(1) [ng-model="weekly.agencyModeFk"]', firstWeeklyTicketAgency: 'vn-ticket-weekly-index vn-card smart-table slot-table tr:nth-child(1) [ng-model="weekly.agencyModeFk"]',
acceptDeleteTurn: '.vn-confirm.shown button[response="accept"]' acceptDeleteTurn: '.vn-confirm.shown button[response="accept"]'
}, },
createTicketView: { createTicketView: {

View File

@ -26,7 +26,7 @@ describe('RecoverPassword path', async() => {
expect(message.text).toContain('Notification sent!'); expect(message.text).toContain('Notification sent!');
}); });
it('should send email', async() => { it('should send email using email', async() => {
await page.waitForState('login'); await page.waitForState('login');
await page.waitToClick(selectors.recoverPassword.recoverPasswordButton); await page.waitToClick(selectors.recoverPassword.recoverPasswordButton);
@ -37,4 +37,16 @@ describe('RecoverPassword path', async() => {
expect(message.text).toContain('Notification sent!'); expect(message.text).toContain('Notification sent!');
}); });
it('should send email using username', async() => {
await page.waitForState('login');
await page.waitToClick(selectors.recoverPassword.recoverPasswordButton);
await page.write(selectors.recoverPassword.email, 'BruceWayne');
await page.waitToClick(selectors.recoverPassword.sendEmailButton);
const message = await page.waitForSnackbar();
await page.waitForState('login');
expect(message.text).toContain('Notification sent!');
});
}); });

View File

@ -478,8 +478,8 @@ export default class SmartTable extends Component {
const params = {q: JSON.stringify(stateFilter)}; const params = {q: JSON.stringify(stateFilter)};
this.$state.go(this.$state.current.name, params, {location: 'replace'}); this.$state.go(this.$state.current.name, params, {location: 'replace'})
this.refresh(); .then(() => this.refresh());
} }
applySort() { applySort() {
@ -499,8 +499,8 @@ export default class SmartTable extends Component {
stateFilter.tableOrder = order; stateFilter.tableOrder = order;
const params = {q: JSON.stringify(stateFilter)}; const params = {q: JSON.stringify(stateFilter)};
this.$state.go(this.$state.current.name, params, {location: 'replace'}); this.$state.go(this.$state.current.name, params, {location: 'replace'})
this.refresh(); .then(() => this.refresh());
} }
filterSanitizer(field) { filterSanitizer(field) {
@ -589,7 +589,7 @@ export default class SmartTable extends Component {
refresh() { refresh() {
this.isRefreshing = true; this.isRefreshing = true;
this.model.refresh() this.model.refresh()
.then(() => this.isRefreshing = false); .finally(() => this.isRefreshing = false);
} }
} }

View File

@ -160,7 +160,7 @@ describe('Component smartTable', () => {
describe('applySort()', () => { describe('applySort()', () => {
it('should call the $state go and model refresh without making changes on the model order', () => { it('should call the $state go and model refresh without making changes on the model order', () => {
controller.$state = { controller.$state = {
go: jest.fn(), go: jest.fn().mockReturnValue(new Promise(resolve => resolve())),
current: { current: {
name: 'section' name: 'section'
} }
@ -171,13 +171,12 @@ describe('Component smartTable', () => {
expect(controller.model.order).toBeUndefined(); expect(controller.model.order).toBeUndefined();
expect(controller.$state.go).toHaveBeenCalled(); expect(controller.$state.go).toHaveBeenCalled();
expect(controller.refresh).toHaveBeenCalled();
}); });
it('should call the $state go and model refresh after setting model order according to the controller sortCriteria', () => { it('should call the $state go and model refresh after setting model order according to the controller sortCriteria', () => {
const orderBy = {field: 'myField', sortType: 'ASC'}; const orderBy = {field: 'myField', sortType: 'ASC'};
controller.$state = { controller.$state = {
go: jest.fn(), go: jest.fn().mockReturnValue(new Promise(resolve => resolve())),
current: { current: {
name: 'section' name: 'section'
} }
@ -190,7 +189,6 @@ describe('Component smartTable', () => {
expect(controller.model.order).toEqual(`${orderBy.field} ${orderBy.sortType}`); expect(controller.model.order).toEqual(`${orderBy.field} ${orderBy.sortType}`);
expect(controller.$state.go).toHaveBeenCalled(); expect(controller.$state.go).toHaveBeenCalled();
expect(controller.refresh).toHaveBeenCalled();
}); });
}); });
@ -293,12 +291,10 @@ describe('Component smartTable', () => {
controller.$inputsScope = { controller.$inputsScope = {
searchProps: {} searchProps: {}
}; };
jest.spyOn(controller, 'refresh');
controller.defaultFilter(); controller.defaultFilter();
expect(controller.model.addFilter).toHaveBeenCalled(); expect(controller.model.addFilter).toHaveBeenCalled();
expect(controller.refresh).toHaveBeenCalled();
}); });
}); });

View File

@ -1,7 +1,7 @@
<h5 class="vn-mb-md vn-mt-lg" translate>Recover password</h5> <h5 class="vn-mb-md vn-mt-lg" translate>Recover password</h5>
<vn-textfield <vn-textfield
label="Email" label="User or recovery email"
ng-model="$ctrl.email" ng-model="$ctrl.user"
vn-focus> vn-focus>
</vn-textfield> </vn-textfield>
<div <div

View File

@ -20,7 +20,7 @@ export default class Controller {
submit() { submit() {
const params = { const params = {
email: this.email user: this.user
}; };
this.$http.post('Accounts/recoverPassword', params) this.$http.post('Accounts/recoverPassword', params)

View File

@ -1,3 +1,4 @@
Recover password: Recuperar contraseña Recover password: Recuperar contraseña
We will sent you an email to recover your password: Te enviaremos un correo para restablecer tu contraseña We will sent you an email to recover your password: Te enviaremos un correo para restablecer tu contraseña
Notification sent!: ¡Notificación enviada! Notification sent!: ¡Notificación enviada!
User or recovery email: Usuario o correo de recuperación

View File

@ -17,43 +17,65 @@
model="model"> model="model">
</vn-searchbar> </vn-searchbar>
</vn-portal> </vn-portal>
<vn-data-viewer
model="model"
class="vn-w-xl">
<vn-card> <vn-card>
<vn-table model="model"> <smart-table
<vn-thead> model="model"
<vn-tr> options="$ctrl.smartTableOptions"
<vn-th field="ticketFk" number>Ticket ID</vn-th> view-config-id="ticketsMonitor"
<vn-th field="clientName">Client</vn-th> expr-builder="$ctrl.exprBuilder(param, value)"
<vn-th>Shipment</vn-th> >
<vn-th>Agency</vn-th> <slot-actions>
<vn-th>Warehouse</vn-th> <vn-check
<vn-th>Salesperson</vn-th> label="Auto-refresh"
<vn-th shrink></vn-th> vn-tooltip="Toggle auto-refresh every 2 minutes"
</vn-tr> on-change="$ctrl.autoRefresh(value)">
</vn-thead> </vn-check>
<vn-tbody> </slot-actions>
<vn-tr <slot-table>
<table>
<thead>
<tr>
<th field="ticketFk">
<span translate>Ticket ID</span>
</th>
<th field="clientName">
<span translate>Client</span>
</th>
<th field="weekDay">
<span translate>Shipment</span>
</th>
<th field="agencyModeFk">
<span translate>Agency</span>
</th>
<th field="warehouseFk">
<span translate>Warehouse</span>
</th>
<th field="nickName">
<span translate>Salesperson</span>
</th>
</tr>
</thead>
<tbody>
<tr
ng-repeat="weekly in weeklies" ng-repeat="weekly in weeklies"
ui-sref="ticket.card.summary({id: {{::weekly.ticketFk}}})" ui-sref="ticket.card.summary({id: {{::weekly.ticketFk}}})"
class="clickable"> class="clickable">
<vn-td number> <td number>
<span <span
vn-click-stop="ticketDescriptor.show($event, weekly.ticketFk)" vn-click-stop="ticketDescriptor.show($event, weekly.ticketFk)"
class="link"> class="link">
{{weekly.ticketFk}} {{weekly.ticketFk}}
</span> </span>
</vn-td> </td>
<vn-td> <td>
<span <span
vn-click-stop="clientDescriptor.show($event, weekly.clientFk)" vn-click-stop="clientDescriptor.show($event, weekly.clientFk)"
title ="{{::weekly.clientName}}" title ="{{::weekly.clientName}}"
class="link"> class="link">
{{::weekly.clientName}} {{::weekly.clientName}}
</span> </span>
</vn-td> </td>
<vn-td vn-click-stop> <td vn-click-stop>
<vn-autocomplete <vn-autocomplete
vn-id="weekday" vn-id="weekday"
ng-model="weekly.weekDay" ng-model="weekly.weekDay"
@ -65,8 +87,8 @@
order="id" order="id"
class="dense"> class="dense">
</vn-autocomplete> </vn-autocomplete>
</vn-td> </td>
<vn-td vn-click-stop> <td vn-click-stop>
<vn-autocomplete <vn-autocomplete
vn-id="agencyMode" vn-id="agencyMode"
ng-model="weekly.agencyModeFk" ng-model="weekly.agencyModeFk"
@ -77,27 +99,28 @@
order="name" order="name"
class="dense"> class="dense">
</vn-autocomplete> </vn-autocomplete>
</vn-td> </td>
<vn-td>{{::weekly.warehouseName}}</vn-td> <td vn-click-stop>{{weekly.warehouseName}}</td>
<vn-td> <td>
<span <span
vn-click-stop="workerDescriptor.show($event, weekly.workerFk)" vn-click-stop="workerDescriptor.show($event, weekly.workerFk)"
class="link" > class="link" >
{{::weekly.userName}} {{::weekly.userName}}
</span> </span>
</vn-td> </td>
<vn-td shrink> <td shrink>
<vn-icon-button <vn-icon-button
icon="delete" icon="delete"
vn-click-stop="deleteWeekly.show(weekly.ticketFk)" vn-click-stop="deleteWeekly.show(weekly.ticketFk)"
vn-tooltip="Delete"> vn-tooltip="Delete">
</vn-icon-button> </vn-icon-button>
</vn-td> </td>
</vn-tr> </tr>
</vn-tbody> </tbody>
</vn-table> </table>
</slot-table>
</smart-table>
</vn-card> </vn-card>
</vn-data-viewer>
<vn-client-descriptor-popover <vn-client-descriptor-popover
vn-id="clientDescriptor"> vn-id="clientDescriptor">
</vn-client-descriptor-popover> </vn-client-descriptor-popover>

View File

@ -1,3 +1,4 @@
import ngModule from '../module'; import ngModule from '../module';
import Section from 'salix/components/section'; import Section from 'salix/components/section';
@ -15,6 +16,44 @@ export default class Controller extends Section {
{id: 5, name: 'Saturday'}, {id: 5, name: 'Saturday'},
{id: 6, name: 'Sunday'} {id: 6, name: 'Sunday'}
]; ];
this.smartTableOptions = {
activeButtons: {
search: true,
shownColumns: true,
},
columns: [
{
field: 'ticketFk',
searchable: false
},
{
field: 'clientName',
},
{
field: 'weekDay',
searchable: false,
},
{
field: 'agencyModeFk',
searchable: false,
},
{
field: 'warehouseFk',
searchable: false,
},
{
field: 'nickname',
},
]
};
}
exprBuilder(param, value) {
switch (param) {
case 'clientName': return {'c.name': value};
case 'nickName': return {'u.name': value};
}
} }
onUpdate(ticketFk, field, value) { onUpdate(ticketFk, field, value) {