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

This commit is contained in:
jorgebl 2021-02-23 11:10:06 +01:00
commit 799bb3a955
24 changed files with 32957 additions and 178 deletions

View File

@ -2,9 +2,11 @@ module.exports = Self => {
Self.validatesPresenceOf('name', {
message: 'Name cannot be blank'
});
Self.validatesPresenceOf('bic', {
message: 'Swift / BIC cannot be empty'
});
Self.validatesUniquenessOf('bic', {
message: 'This BIC already exist.'
});

View File

@ -1,5 +1,6 @@
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('FixedPrice', '*', '*', 'ALLOW', 'ROLE', 'buyer');
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('PayDem', '*', 'READ', 'ALLOW', 'ROLE', 'employee');
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('Client', 'createReceipt', '*', 'ALLOW', 'ROLE', 'administrative');
INSERT INTO salix.ACL (model, property, accessType, permission, principalType, principalId)
VALUES ('PrintServerQueue', '*', 'WRITE', 'ALLOW', 'ROLE', 'employee');
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('FixedPrice', '*', '*', 'ALLOW', 'ROLE', 'buyer'),
('PayDem', '*', 'READ', 'ALLOW', 'ROLE', 'employee'),
('Client', 'createReceipt', '*', 'ALLOW', 'ROLE', 'administrative'),
('PrintServerQueue', '*', 'WRITE', 'ALLOW', 'ROLE', 'employee');

View File

@ -1,3 +1,4 @@
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('SupplierAccount', '*', '*', 'ALLOW', 'ROLE', 'administrative'),
('Entry', '*', '*', 'ALLOW', 'ROLE', 'administrative');

View File

@ -174,4 +174,9 @@ vn-table {
.vn-check {
margin: 0;
}
.empty-rows {
color: $color-font-secondary;
font-size: 1.375rem;
text-align: center;
}
}

View File

@ -0,0 +1,40 @@
<vn-dialog class="edit"
vn-id="bankEntityDialog"
on-open="$ctrl.resetData()"
on-accept="$ctrl.onAccept()"
message="New bank entity">
<tpl-body>
<p translate>Please, ensure you put the correct data!</p>
<vn-horizontal>
<vn-textfield
vn-one
vn-focus
vn-id="entityName"
label="Name"
ng-model="$ctrl.data.name"
required="true">
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
vn-focus
vn-id="bic"
label="Swift"
ng-model="$ctrl.data.bic"
required="true">
</vn-textfield>
<vn-autocomplete vn-one
ng-model="$ctrl.data.countryFk"
url="Countries"
show-field="country"
value-field="id"
label="Country">
</vn-autocomplete>
</vn-horizontal>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button id= "saveBankEntity" response="accept" translate>Save</button>
</tpl-buttons>
</vn-dialog>

View File

@ -0,0 +1,37 @@
import ngModule from '../../module';
import Component from 'core/lib/component';
import './style.scss';
class Controller extends Component {
open() {
this.$.bankEntityDialog.show();
}
resetData() {
this.data = {};
}
onAccept() {
try {
if (!this.data.countryFk)
throw new Error(`The country can't be empty`);
this.$http.post(`bankEntities`, this.data).then(res => {
this.vnApp.showMessage(this.$t('The bank entity has been created. You can save the data now'));
this.emit('response', {$response: res.data});
});
} catch (e) {
this.vnApp.showError(this.$t(e.message));
return false;
}
return true;
}
}
ngModule.vnComponent('vnNewBankEntity', {
template: require('./index.html'),
controller: Controller,
bindings: {
data: '<',
}
});

View File

@ -0,0 +1,53 @@
import './index';
describe('Salix Component vnNewBankEntity', () => {
let controller;
let $httpBackend;
let $scope;
let $element;
let vnApp;
beforeEach(ngModule('salix'));
beforeEach(inject(($componentController, $rootScope, _$httpBackend_, _vnApp_) => {
$httpBackend = _$httpBackend_;
vnApp = _vnApp_;
jest.spyOn(vnApp, 'showError');
$scope = $rootScope.$new();
$element = angular.element('<vn-dialog></dialog>');
controller = $componentController('vnNewBankEntity', {$element, $scope});
}));
describe('resetData()', () => {
it('should reset the location in the controller', () => {
expect(controller.data).toBeUndefined();
controller.resetData();
expect(controller.data).toEqual({});
});
});
describe('onAccept()', () => {
it('should throw an error if there is no country id in the location', () => {
jest.spyOn(controller.vnApp, 'showMessage');
controller.data = {};
controller.onAccept();
expect(controller.vnApp.showError).toHaveBeenCalledWith(`The country can't be empty`);
});
it('should do add the new bank entity', () => {
controller.data = {
countryFk: 1
};
$httpBackend.expectPOST('bankEntities', controller.data).respond(200, controller.data);
controller.onAccept();
$httpBackend.flush();
});
});
});

View File

@ -0,0 +1,12 @@
New postcode: Nuevo código postal
New city: Nueva ciudad
New province: Nueva provincia
Please, ensure you put the correct data!: ¡Por favor, asegúrate de poner los datos correctos!
The postcode can't be empty: El código postal no puede quedar vacío
The town can't be empty: La población no puede quedar vacía
The province can't be empty: La provincia no puede quedar vacía
The country can't be empty: El país no puede quedar vacío
The postcode has been created. You can save the data now: Se ha creado el código postal. Ahora puedes guardar los datos
The city has been created: Se ha creado la ciudad
The province has been created: Se ha creado la provincia
The bank entity has been created. You can save the data now: Se ha creado la entidad bancaria. Puedes guardar los datos ahora

View File

@ -0,0 +1,9 @@
@import "variables";
vn-new-bank-entity {
vn-dialog {
p {
color: $color-alert
}
}
}

View File

@ -14,3 +14,4 @@ import './summary';
import './topbar/topbar';
import './user-popover';
import './upload-photo';
import './bank-entity';

View File

@ -26,15 +26,25 @@ module.exports = Self => {
Self.lastActiveTickets = async(id, ticketId) => {
const ticket = await Self.app.models.Ticket.findById(ticketId);
const query = `
SELECT t.id, t.shipped, a.name AS agencyName, w.name AS warehouseName, ad.city AS address
FROM vn.ticket t
JOIN vn.ticketState ts ON t.id = ts.ticketFk
JOIN vn.agencyMode a ON t.agencyModeFk = a.id
JOIN vn.warehouse w ON t.warehouseFk = w.id
JOIN vn.address ad ON t.addressFk = ad.id
WHERE t.shipped >= CURDATE() AND t.clientFk = ? AND ts.alertLevel = 0
AND t.id <> ? AND t.warehouseFk = ?
ORDER BY t.shipped
SELECT
t.id,
t.shipped,
a.name AS agencyName,
w.name AS warehouseName,
ad.nickname AS nickname,
ad.city AS city,
ad.postalCode AS postalCode,
ad.street AS street,
pr.name AS name
FROM ticket t
JOIN vn.ticketState ts ON t.id = ts.ticketFk
JOIN vn.agencyMode a ON t.agencyModeFk = a.id
JOIN vn.warehouse w ON t.warehouseFk = w.id
JOIN vn.address ad ON t.addressFk = ad.id
JOIN vn.province pr ON ad.provinceFk = pr.id
WHERE t.shipped >= CURDATE() AND t.clientFk = ? AND ts.alertLevel = 0
AND t.id <> ? AND t.warehouseFk = ?
ORDER BY t.shipped
LIMIT 10`;
return Self.rawSql(query, [id, ticketId, ticket.warehouseFk]);

View File

@ -0,0 +1,18 @@
const app = require('vn-loopback/server/server');
describe('Client last active tickets', () => {
it('should receive an array of last active tickets of Bruce Wayne', async() => {
const ticketId = 22;
const clientId = 109;
const warehouseId = 5;
const result = await app.models.Client.lastActiveTickets(clientId, ticketId, warehouseId);
const length = result.length;
const anyResult = result[Math.floor(Math.random() * Math.floor(length))];
const properties = Object.keys(anyResult);
expect(properties.length).toEqual(9);
expect(result.length).toEqual(3);
});
});

View File

@ -41,7 +41,7 @@ module.exports = Self => {
async function download() {
const image = await Self.findOne({
where: {url: {neq: null}, attempts: {lt: maxAttempts}},
order: 'attempts, updated'
order: 'priority, attempts, updated'
});
if (!image) return;

View File

@ -1,6 +1,10 @@
{
"name": "SupplierAccount",
"base": "VnModel",
"base": "Loggable",
"log": {
"model":"SupplierLog",
"relation": "supplier"
},
"options": {
"mysql": {
"table": "supplierAccount"
@ -45,6 +49,11 @@
"type": "belongsTo",
"model": "Supplier",
"foreignKey": "supplierFk"
},
"bankEntity": {
"type": "belongsTo",
"model": "BankEntity",
"foreignKey": "bankEntityFk"
}
}
}

View File

@ -0,0 +1,67 @@
<vn-crud-model
vn-id="model"
url="SupplierAccounts"
fields="['id', 'supplierFk', 'iban', 'bankEntityFk']"
link="{supplierFk: $ctrl.$params.id}"
include="$ctrl.include"
data="$ctrl.supplierAccounts"
auto-load="true">
</vn-crud-model>
<vn-watcher
vn-id="watcher"
data="$ctrl.supplierAccounts"
form="form">
</vn-watcher>
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal ng-repeat="supplierAccount in $ctrl.supplierAccounts">
<vn-textfield vn-three
ng-show="supplierAccount.iban || supplierAccount.iban == undefined"
label="Iban"
ng-model="supplierAccount.iban"
rule>
</vn-textfield>
<vn-autocomplete vn-two
label="Bank entity"
ng-model="supplierAccount.bankEntityFk"
url="BankEntities"
show-field="name"
rule>
</vn-autocomplete>
<append>
<vn-icon-button
icon="add_circle"
vn-tooltip="New bank entity"
ng-click="$ctrl.showBankEntity($event, $index)">
</vn-icon-button>
</append>
<vn-none>
<vn-icon-button
vn-tooltip="Remove account"
icon="delete"
ng-click="model.remove($index)"
tabindex="-1">
</vn-icon-button>
</vn-none>
</vn-horizontal>
<vn-one>
<vn-icon-button
vn-bind="+"
vn-tooltip="Add account"
icon="add_circle"
ng-click="$ctrl.add()">
</vn-icon-button>
</vn-one>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
</vn-button-bar>
</form>
<!-- New bankentity dialog -->
<vn-new-bank-entity
vn-id="bankEntity"
on-response="$ctrl.onResponse($response)">
</vn-new-bank-entity>

View File

@ -0,0 +1,56 @@
import ngModule from '../module';
import Section from 'salix/components/section';
class Controller extends Section {
constructor($element, $) {
super($element, $);
this.include = {
relation: 'bankEntity',
scope: {
fields: ['countryFk', 'id', 'name', 'bic']
}
};
}
add() {
this.$.model.insert({
supplierFk: this.$params.id
});
}
onResponse(response) {
const data = this.$.model.data;
const supplierAccount = data[this.currentRowIndex];
supplierAccount.bankEntityFk = response.id;
}
showBankEntity(event, $index) {
if (event.defaultPrevented) return;
event.preventDefault();
this.currentRowIndex = $index;
this.$.bankEntity.open();
}
onBankEntityAccept() {
const query = `SupplierAccounts/${this.$params.id}/createBankEntity`;
return this.$http.patch(query, this.newBankEntity)
.then(res => this.supplierAccount.bankEntityFk = res.data.id);
}
onSubmit() {
this.$.watcher.check();
this.$.model.save().then(() => {
this.$.watcher.notifySaved();
this.$.watcher.updateOriginalData();
this.card.reload();
});
}
}
ngModule.vnComponent('vnSupplierAccount', {
template: require('./index.html'),
controller: Controller,
require: {
card: '^vnSupplierCard'
}
});

View File

@ -0,0 +1,71 @@
import './index.js';
describe('Supplier Component vnSupplierAccount', () => {
let $scope;
let $element;
let controller;
let $httpBackend;
beforeEach(ngModule('supplier'));
beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
$scope = $rootScope.$new();
$scope.bankEntity = {
open: () => {}
};
$element = angular.element('<vn-supplier-accounts></supplier-accounts>');
controller = $componentController('vnSupplierAccount', {$element, $scope});
controller.supplierAccount = {
supplierFk: 442,
name: 'Verdnatura'
};
}));
describe('showBankEntity()', () => {
it('should do nothing if it default is prevented', () => {
const event = {
defaultPrevented: true,
preventDefault: () => {}
};
jest.spyOn(event, 'preventDefault');
jest.spyOn(controller.$.bankEntity, 'open');
controller.showBankEntity(event);
expect(event.preventDefault).not.toHaveBeenCalledWith();
expect(controller.$.bankEntity.open).not.toHaveBeenCalledWith();
});
it('should call preventDefault() and open() when the default is not prevented', () => {
const event = {
defaultPrevented: false,
preventDefault: () => {}
};
jest.spyOn(event, 'preventDefault');
jest.spyOn(controller.$.bankEntity, 'open');
controller.showBankEntity(event);
expect(event.preventDefault).toHaveBeenCalledWith();
expect(controller.$.bankEntity.open).toHaveBeenCalledWith();
});
it('should request to create a new bank entity', () => {
controller.bankEntity = {
name: 'My new bank entity',
bic: 'ES1234',
countryFk: 1,
id: 2200
};
const query = `SupplierAccounts/${controller.$.bankEntity.id}/createBankEntity`;
$httpBackend.expectPATCH(query).respond({id: 2200});
controller.onBankEntityAccept();
$httpBackend.flush();
expect(controller.supplierAccount.bankEntityFk).toEqual(controller.bankEntity.id);
});
});
});

View File

@ -0,0 +1,3 @@
Bank entity: Entidad bancaria
swift: Swift BIC
Add account: Añadir cuenta

View File

@ -8,6 +8,7 @@ import './search-panel';
import './summary';
import './basic-data';
import './fiscal-data';
import './account';
import './contact';
import './log';
import './consumption';

View File

@ -9,6 +9,7 @@
{"state": "supplier.index", "icon": "icon-supplier"}
],
"card": [
{"state": "supplier.card.account", "icon": "contact_support"},
{"state": "supplier.card.basicData", "icon": "settings"},
{"state": "supplier.card.fiscalData", "icon": "account_balance"},
{"state": "supplier.card.billingData", "icon": "icon-payment"},
@ -99,6 +100,15 @@
"supplier": "$ctrl.supplier"
},
"acl": ["administrative"]
},{
"url": "/account",
"state": "supplier.card.account",
"component": "vn-supplier-account",
"description": "Account",
"params": {
"supplier": "$ctrl.supplier"
},
"acl": ["administrative"]
}
]
}

View File

@ -326,31 +326,49 @@
icon="info">
</vn-icon>
</vn-horizontal>
<vn-table class="destinationTable">
<vn-thead>
<vn-tr>
<vn-th number>Id</vn-th>
<vn-th number>Shipped</vn-th>
<vn-th number>Agency</vn-th>
<vn-th number>Warehouse</vn-th>
<vn-th number>Address</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-data-viewer data="$ctrl.transfer.lastActiveTickets">
</vn-data-viewer>
<vn-tr
<table class="destinationTable vn-table">
<thead>
<tr>
<th translate shrink>Id</th>
<th translate>Shipped</th>
<th translate shrink>Agency</th>
<th translate expand>Address</th>
</tr>
</thead>
<tbody>
<tr
class="clickable"
ng-repeat="ticket in $ctrl.transfer.lastActiveTickets track by ticket.id"
ng-click="$ctrl.transferSales(ticket.id)">
<vn-td number>{{::ticket.id}}</vn-td>
<vn-td number>{{::ticket.shipped | date: 'dd/MM/yyyy'}}</vn-td>
<vn-td number>{{::ticket.agencyName}}</vn-td>
<vn-td number>{{::ticket.warehouseName}}</vn-td>
<vn-td number>{{::ticket.address}}</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
<td shrink>{{::ticket.id}}</td>
<td>{{::ticket.shipped | date: 'dd/MM/yyyy'}}</td>
<td shrink>{{::ticket.agencyName}}</td>
<td expand>{{::ticket.address}}
<span vn-tooltip="
{{::ticket.nickname}}
{{::ticket.name}}
{{::ticket.street}}
{{::ticket.postalCode}}
{{::ticket.city}}">
{{::ticket.nickname}}
{{::ticket.name}}
{{::ticket.street}}
{{::ticket.postalCode}}
{{::ticket.city}}
</span>
</td>
</tr>
<tr>
<td
ng-if="!$ctrl.transfer.lastActiveTickets.length"
class="empty-rows"
colspan="4"
translate>
No results
</td>
</tr>
</tbody>
</table>
<form name="form">
<vn-horizontal class="vn-py-md">
<vn-input-number vn-one

32633
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -33,7 +33,7 @@
"request": "^2.88.0",
"request-promise-native": "^1.0.8",
"require-yaml": "0.0.1",
"sharp": "^0.25.4",
"sharp": "^0.27.1",
"smbhash": "0.0.1",
"soap": "^0.35.0",
"strong-error-handler": "^2.3.2",