Merge branch 'dev' into 3623-reportInvoice_intrastat
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
commit
d2871ff3a6
|
@ -6,20 +6,23 @@ CREATE DEFINER=`root`@`%` PROCEDURE `vn`.`ticket_getMovable`(vTicketFk INT, vDat
|
|||
BEGIN
|
||||
/**
|
||||
* Cálcula el stock movible para los artículos de un ticket
|
||||
* vDatedNew debe ser menor que vDatedOld, en los otros casos se
|
||||
* asume que siempre es posible
|
||||
*
|
||||
* @param vTicketFk -> Ticket
|
||||
* @param vDatedNew -> Nueva fecha
|
||||
* @return Sales con Movible
|
||||
*/
|
||||
DECLARE vDatedOld DATETIME;
|
||||
|
||||
SET vDatedNew = DATE_ADD(vDatedNew, INTERVAL 1 DAY);
|
||||
|
||||
SELECT t.shipped INTO vDatedOld
|
||||
FROM ticket t
|
||||
WHERE t.id = vTicketFk;
|
||||
|
||||
CALL itemStock(vWarehouseFk, DATE_SUB(vDatedNew, INTERVAL 1 DAY), NULL);
|
||||
CALL item_getMinacum(vWarehouseFk, vDatedNew, DATEDIFF(vDatedOld, vDatedNew), NULL);
|
||||
|
||||
CALL itemStock(vWarehouseFk, vDatedNew, NULL);
|
||||
CALL item_getMinacum(vWarehouseFk, vDatedNew, DATEDIFF(DATE_SUB(vDatedOld, INTERVAL 1 DAY), vDatedNew), NULL);
|
||||
|
||||
SELECT s.id,
|
||||
s.itemFk,
|
||||
s.quantity,
|
|
@ -1,7 +1,13 @@
|
|||
|
||||
module.exports = Self => {
|
||||
Self.getSynchronizer = async function() {
|
||||
return await Self.findOne({fields: ['id']});
|
||||
let NODE_ENV = process.env.NODE_ENV;
|
||||
if (!NODE_ENV || NODE_ENV == 'development')
|
||||
return null;
|
||||
|
||||
return await Self.findOne({
|
||||
fields: ['id', 'rolePrefix', 'userPrefix', 'userHost']
|
||||
});
|
||||
};
|
||||
|
||||
Object.assign(Self.prototype, {
|
||||
|
@ -14,17 +20,16 @@ module.exports = Self => {
|
|||
},
|
||||
|
||||
async syncUser(userName, info, password) {
|
||||
const mysqlHost = '%';
|
||||
|
||||
let mysqlUser = userName;
|
||||
if (this.dbType == 'MySQL') mysqlUser = `!${mysqlUser}`;
|
||||
if (this.dbType == 'MySQL')
|
||||
mysqlUser = this.userPrefix + mysqlUser;
|
||||
|
||||
const [row] = await Self.rawSql(
|
||||
`SELECT COUNT(*) AS nRows
|
||||
FROM mysql.user
|
||||
WHERE User = ?
|
||||
AND Host = ?`,
|
||||
[mysqlUser, mysqlHost]
|
||||
[mysqlUser, this.userHost]
|
||||
);
|
||||
let userExists = row.nRows > 0;
|
||||
|
||||
|
@ -35,11 +40,10 @@ module.exports = Self => {
|
|||
FROM mysql.global_priv
|
||||
WHERE User = ?
|
||||
AND Host = ?`,
|
||||
[mysqlUser, mysqlHost]
|
||||
[mysqlUser, this.userHost]
|
||||
);
|
||||
const priv = row && JSON.parse(row.priv);
|
||||
const role = priv && priv.default_role;
|
||||
isUpdatable = !row || (role && role.startsWith('z-'));
|
||||
isUpdatable = !row || (priv && priv.autogenerated);
|
||||
}
|
||||
|
||||
if (!isUpdatable) {
|
||||
|
@ -51,31 +55,27 @@ module.exports = Self => {
|
|||
if (password) {
|
||||
if (!userExists) {
|
||||
await Self.rawSql('CREATE USER ?@? IDENTIFIED BY ?',
|
||||
[mysqlUser, mysqlHost, password]
|
||||
);
|
||||
[mysqlUser, this.userHost, password]);
|
||||
userExists = true;
|
||||
} else {
|
||||
switch (this.dbType) {
|
||||
case 'MariaDB':
|
||||
await Self.rawSql('ALTER USER ?@? IDENTIFIED BY ?',
|
||||
[mysqlUser, mysqlHost, password]
|
||||
);
|
||||
[mysqlUser, this.userHost, password]);
|
||||
break;
|
||||
default:
|
||||
await Self.rawSql('SET PASSWORD FOR ?@? = PASSWORD(?)',
|
||||
[mysqlUser, mysqlHost, password]
|
||||
);
|
||||
[mysqlUser, this.userHost, password]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (userExists && this.dbType == 'MariaDB') {
|
||||
let role = `z-${info.user.role().name}`;
|
||||
let role = `${this.rolePrefix}${info.user.role().name}`;
|
||||
|
||||
try {
|
||||
await Self.rawSql('REVOKE ALL, GRANT OPTION FROM ?@?',
|
||||
[mysqlUser, mysqlHost]
|
||||
);
|
||||
[mysqlUser, this.userHost]);
|
||||
} catch (err) {
|
||||
if (err.code == 'ER_REVOKE_GRANTS')
|
||||
console.warn(`${err.code}: ${err.sqlMessage}: ${err.sql}`);
|
||||
|
@ -83,21 +83,18 @@ module.exports = Self => {
|
|||
throw err;
|
||||
}
|
||||
await Self.rawSql('GRANT ? TO ?@?',
|
||||
[role, mysqlUser, mysqlHost]
|
||||
);
|
||||
[role, mysqlUser, this.userHost]);
|
||||
|
||||
if (role) {
|
||||
await Self.rawSql('SET DEFAULT ROLE ? FOR ?@?',
|
||||
[role, mysqlUser, mysqlHost]
|
||||
);
|
||||
[role, mysqlUser, this.userHost]);
|
||||
} else {
|
||||
await Self.rawSql('SET DEFAULT ROLE NONE FOR ?@?',
|
||||
[mysqlUser, mysqlHost]
|
||||
);
|
||||
[mysqlUser, this.userHost]);
|
||||
}
|
||||
}
|
||||
} else if (userExists)
|
||||
await Self.rawSql('DROP USER ?@?', [mysqlUser, mysqlHost]);
|
||||
await Self.rawSql('DROP USER ?@?', [mysqlUser, this.userHost]);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
@ -16,6 +16,18 @@
|
|||
},
|
||||
"mysqlPassword": {
|
||||
"type": "string"
|
||||
},
|
||||
"rolePrefix": {
|
||||
"type": "string"
|
||||
},
|
||||
"userPrefix": {
|
||||
"type": "string"
|
||||
},
|
||||
"userHost": {
|
||||
"type": "string"
|
||||
},
|
||||
"tplUser": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
url="Workers/activeWithRole"
|
||||
search-function="{firstName: $search}"
|
||||
value-field="id"
|
||||
where="{role: {inq: ['salesPerson', 'officeBoss']}}"
|
||||
where="{role: {inq: ['salesBoss', 'salesPerson', 'officeBoss']}}"
|
||||
label="Salesperson">
|
||||
<tpl-item>{{firstName}} {{name}}</tpl-item>
|
||||
</vn-autocomplete>
|
||||
|
@ -38,7 +38,7 @@
|
|||
url="Workers/activeWithRole"
|
||||
search-function="{firstName: $search}"
|
||||
value-field="id"
|
||||
where="{role: 'salesPerson'}"
|
||||
where="{role: {inq: ['salesBoss', 'salesPerson']}}"
|
||||
label="Attended by">
|
||||
<tpl-item>{{firstName}} {{name}}</tpl-item>
|
||||
</vn-autocomplete>
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
const soap = require('soap');
|
||||
|
||||
describe('client sendSms()', () => {
|
||||
// #3673 sendSms tests excluded
|
||||
xdescribe('client sendSms()', () => {
|
||||
it('should now send a message and log it', async() => {
|
||||
spyOn(soap, 'createClientAsync').and.returnValue('a so fake client');
|
||||
const tx = await models.Client.beginTransaction({});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
|
||||
describe('sms send()', () => {
|
||||
// #3673 sendSms tests excluded
|
||||
xdescribe('sms send()', () => {
|
||||
it('should not return status error', async() => {
|
||||
const ctx = {req: {accessToken: {userId: 1}}};
|
||||
const result = await app.models.Sms.send(ctx, 1105, '123456789', 'My SMS Body');
|
||||
|
|
|
@ -3,13 +3,21 @@
|
|||
url="Items/getWasteByWorker"
|
||||
data="details">
|
||||
</vn-crud-model>
|
||||
|
||||
<vn-data-viewer model="model">
|
||||
<vn-card>
|
||||
<section ng-repeat="detail in details" class="vn-pa-md">
|
||||
<vn-horizontal class="header">
|
||||
<h5><span><span translate>{{detail.buyer}}</span></h5>
|
||||
</vn-horizontal>
|
||||
<section ng-repeat="detail in details" class="vn-pa-md">
|
||||
<vn-horizontal class="header">
|
||||
<h5><span translate>{{detail.buyer}}</span></h5>
|
||||
<vn-none>
|
||||
<vn-icon
|
||||
ng-class="{'hidden': !$ctrl.wasteConfig[detail.buyer].hidden}"
|
||||
class="arrow pointer"
|
||||
icon="keyboard_arrow_up"
|
||||
vn-tooltip="Minimize/Maximize"
|
||||
ng-click="$ctrl.toggleHidePanel(detail)">
|
||||
</vn-icon>
|
||||
</vn-none>
|
||||
</vn-horizontal>
|
||||
<vn-card ng-class="{'hidden': !$ctrl.wasteConfig[detail.buyer].hidden}">
|
||||
<vn-table>
|
||||
<vn-thead>
|
||||
<vn-tr>
|
||||
|
@ -21,7 +29,7 @@
|
|||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<a ng-repeat="waste in detail.lines" class="clickable vn-tr"
|
||||
ui-sref="item.waste.detail({buyer: waste.buyer, family: waste.family})" >
|
||||
ui-sref="item.waste.detail({buyer: waste.buyer, family: waste.family})">
|
||||
<vn-td class="waste-family">{{::waste.family}}</vn-td>
|
||||
<vn-td number>{{::(waste.percentage / 100) | percentage: 2}}</vn-td>
|
||||
<vn-td number>{{::waste.dwindle | currency: 'EUR'}}</vn-td>
|
||||
|
@ -29,6 +37,6 @@
|
|||
</vn-tr>
|
||||
</vn-tbody>
|
||||
</vn-table>
|
||||
</section>
|
||||
</vn-card>
|
||||
</vn-data-viewer>
|
||||
</vn-card>
|
||||
</section>
|
||||
</vn-data-viewer>
|
|
@ -2,7 +2,34 @@ import ngModule from '../../module';
|
|||
import Section from 'salix/components/section';
|
||||
import './style.scss';
|
||||
|
||||
export default class Controller extends Section {
|
||||
constructor($element, $) {
|
||||
super($element, $);
|
||||
|
||||
this.getWasteConfig();
|
||||
}
|
||||
|
||||
getWasteConfig() {
|
||||
return this.wasteConfig = JSON.parse(localStorage.getItem('wasteConfig')) || {};
|
||||
}
|
||||
|
||||
setWasteConfig() {
|
||||
localStorage.setItem('wasteConfig', JSON.stringify(this.wasteConfig));
|
||||
}
|
||||
|
||||
toggleHidePanel(detail) {
|
||||
if (!this.wasteConfig[detail.buyer]) {
|
||||
this.wasteConfig[detail.buyer] = {
|
||||
hidden: true
|
||||
};
|
||||
} else
|
||||
this.wasteConfig[detail.buyer].hidden = !this.wasteConfig[detail.buyer].hidden;
|
||||
|
||||
this.setWasteConfig();
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnItemWasteIndex', {
|
||||
template: require('./index.html'),
|
||||
controller: Section
|
||||
controller: Controller
|
||||
});
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
import './index.js';
|
||||
import crudModel from 'core/mocks/crud-model';
|
||||
|
||||
describe('Item', () => {
|
||||
describe('Component vnItemWasteIndex', () => {
|
||||
let $scope;
|
||||
let controller;
|
||||
|
||||
beforeEach(ngModule('item'));
|
||||
|
||||
beforeEach(inject(($componentController, $rootScope) => {
|
||||
$scope = $rootScope.$new();
|
||||
$scope.model = crudModel;
|
||||
const $element = angular.element('<vn-item-waste-index></vn-item-waste-index>');
|
||||
controller = $componentController('vnItemWasteIndex', {$element, $scope});
|
||||
}));
|
||||
|
||||
describe('getWasteConfig / setWasteConfig', () => {
|
||||
it('should return the local storage wasteConfig', () => {
|
||||
const result = controller.getWasteConfig();
|
||||
|
||||
expect(result).toEqual({});
|
||||
});
|
||||
|
||||
it('should set and return the local storage wasteConfig', () => {
|
||||
controller.wasteConfig = {salesPerson: {hidden: true}};
|
||||
controller.setWasteConfig();
|
||||
|
||||
const result = controller.getWasteConfig();
|
||||
|
||||
expect(result).toEqual(controller.wasteConfig);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toggleHidePanel()', () => {
|
||||
it('should make details hidden by default', () => {
|
||||
controller.wasteConfig = {};
|
||||
|
||||
controller.toggleHidePanel({buyer: 'salesPerson'});
|
||||
|
||||
expect(controller.wasteConfig.salesPerson.hidden).toEqual(true);
|
||||
});
|
||||
|
||||
it('should toggle hidden false', () => {
|
||||
controller.wasteConfig = {salesPerson: {hidden: true}};
|
||||
|
||||
controller.toggleHidePanel({buyer: 'salesPerson'});
|
||||
|
||||
expect(controller.wasteConfig.salesPerson.hidden).toEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,21 +1,24 @@
|
|||
@import "variables";
|
||||
@import "effects";
|
||||
|
||||
vn-item-waste-index,
|
||||
vn-item-waste-detail {
|
||||
.header {
|
||||
margin-bottom: 16px;
|
||||
text-transform: uppercase;
|
||||
font-size: 1.25rem;
|
||||
line-height: 1;
|
||||
padding: 7px;
|
||||
padding-bottom: 7px;
|
||||
padding-bottom: 4px;
|
||||
font-weight: lighter;
|
||||
background-color: $color-bg;
|
||||
border-bottom: 1px solid #f7931e;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding: 12px 0 5px 0;
|
||||
color: gray;
|
||||
font-size: 1.2rem;
|
||||
border-bottom: $border;
|
||||
margin-bottom: 10px;
|
||||
|
||||
& > vn-none > vn-icon {
|
||||
@extend %clickable-light;
|
||||
color: $color-button;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
vn-none > .arrow {
|
||||
transition: transform 200ms;
|
||||
}
|
||||
}
|
||||
|
||||
vn-table vn-th.waste-family,
|
||||
|
@ -23,4 +26,12 @@ vn-item-waste-detail {
|
|||
max-width: 64px;
|
||||
width: 64px
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
|
||||
}
|
||||
.header > vn-none > .arrow.hidden {
|
||||
display: block;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
Family: Familia
|
||||
Percentage: Porcentaje
|
||||
Dwindle: Mermas
|
||||
Dwindle: Mermas
|
||||
Minimize/Maximize: Minimizar/Maximizar
|
|
@ -86,8 +86,8 @@ describe('sale priceDifference()', () => {
|
|||
const firstItem = result.items[0];
|
||||
const secondtItem = result.items[1];
|
||||
|
||||
expect(firstItem.movable).toEqual(440);
|
||||
expect(secondtItem.movable).toEqual(1980);
|
||||
expect(firstItem.movable).toEqual(410);
|
||||
expect(secondtItem.movable).toEqual(1870);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
const soap = require('soap');
|
||||
|
||||
describe('ticket sendSms()', () => {
|
||||
// #3673 sendSms tests excluded
|
||||
xdescribe('ticket sendSms()', () => {
|
||||
it('should send a message and log it', async() => {
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
|
||||
|
|
|
@ -18,7 +18,14 @@
|
|||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<vn-tr ng-repeat="sale in $ctrl.ticket.sale.items track by sale.id">
|
||||
<vn-td number>{{("000000"+sale.itemFk).slice(-6)}}</vn-td>
|
||||
<vn-td number>
|
||||
<span
|
||||
title="{{::sale.item.name}}"
|
||||
vn-click-stop="itemDescriptor.show($event, sale.itemFk, sale.id)"
|
||||
class="link">
|
||||
{{::sale.itemFk | zeroFill:6}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td vn-fetched-tags>
|
||||
<div>
|
||||
<vn-one title="{{::sale.item.name}}">{{::sale.item.name}}</vn-one>
|
||||
|
@ -83,5 +90,9 @@
|
|||
</div>
|
||||
</div>
|
||||
</vn-side-menu>
|
||||
|
||||
<vn-item-descriptor-popover
|
||||
vn-id="item-descriptor"
|
||||
warehouse-fk="$ctrl.ticket.warehouseFk"
|
||||
ticket-fk="$ctrl.ticket.id">
|
||||
</vn-item-descriptor-popover>
|
||||
|
||||
|
|
|
@ -12,8 +12,6 @@ module.exports = {
|
|||
const failedtickets = [];
|
||||
for (const ticket of tickets) {
|
||||
try {
|
||||
await db.rawSql('START TRANSACTION');
|
||||
|
||||
await db.rawSql(`CALL vn.ticket_closeByTicket(?)`, [ticket.id]);
|
||||
|
||||
const invoiceOut = await db.findOne(`
|
||||
|
@ -91,9 +89,7 @@ module.exports = {
|
|||
const email = new Email('delivery-note-link', args);
|
||||
await email.send();
|
||||
}
|
||||
await db.rawSql('COMMIT');
|
||||
} catch (error) {
|
||||
await db.rawSql('ROLLBACK');
|
||||
// Domain not found
|
||||
if (error.responseCode == 450)
|
||||
return invalidEmail(ticket);
|
||||
|
|
|
@ -5,7 +5,7 @@ h2 {
|
|||
|
||||
.table-title {
|
||||
margin-bottom: 15px;
|
||||
font-size: 0.8rem
|
||||
font-size: .8rem
|
||||
}
|
||||
|
||||
.table-title h2 {
|
||||
|
@ -16,9 +16,12 @@ h2 {
|
|||
font-size: 22px
|
||||
}
|
||||
|
||||
|
||||
#nickname h2 {
|
||||
max-width: 400px;
|
||||
word-wrap: break-word
|
||||
max-width: 400px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#phytosanitary {
|
||||
|
|
|
@ -85,7 +85,7 @@
|
|||
<!-- End of rectified invoices block -->
|
||||
|
||||
<!-- Sales block -->
|
||||
<div class="vn-mt-lg no-page-break" v-for="ticket in tickets">
|
||||
<div class="vn-mt-lg" v-for="ticket in tickets">
|
||||
<div class="table-title clearfix">
|
||||
<div class="pull-left">
|
||||
<h2>{{$t('deliveryNote')}}</strong>
|
||||
|
|
Loading…
Reference in New Issue