Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 4649-bookEntriesIncorrectly_notification

This commit is contained in:
Vicent Llopis 2022-12-20 10:52:23 +01:00
commit 1ddaf7ff08
43 changed files with 365 additions and 147 deletions

View File

@ -43,6 +43,9 @@ module.exports = Self => {
if (!recipient) if (!recipient)
throw new Error(`Could not send message "${message}" to worker id ${recipientId} from user ${userId}`); throw new Error(`Could not send message "${message}" to worker id ${recipientId} from user ${userId}`);
if (process.env.NODE_ENV == 'test')
message = `[Test:Environment to user ${userId}] ` + message;
await models.Chat.create({ await models.Chat.create({
senderFk: sender.id, senderFk: sender.id,
recipient: `@${recipient.name}`, recipient: `@${recipient.name}`,

View File

@ -12,14 +12,9 @@ BEGIN
* @param vAuthorFk The notification author or %NULL if there is no author * @param vAuthorFk The notification author or %NULL if there is no author
* @return The notification id * @return The notification id
*/ */
DECLARE vNotificationFk INT;
SELECT id INTO vNotificationFk
FROM `notification`
WHERE `name` = vNotificationName;
INSERT INTO notificationQueue INSERT INTO notificationQueue
SET notificationFk = vNotificationFk, SET notificationFk = vNotificationName,
params = vParams, params = vParams,
authorFk = vAuthorFk; authorFk = vAuthorFk;

View File

View File

@ -0,0 +1,12 @@
CREATE TABLE `vn`.`invoiceInConfig` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`retentionRate` int(3) NOT NULL,
`retentionName` varchar(25) NOT NULL,
`sageWithholdingFk` smallint(6) NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `invoiceInConfig_sageWithholdingFk` FOREIGN KEY (`sageWithholdingFk`) REFERENCES `sage`.`TiposRetencion`(`CodigoRetencion`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
INSERT INTO `vn`.`invoiceInConfig` (`id`, `retentionRate`, `retentionName`, `sageWithholdingFk`)
VALUES
(1, -2, 'Retención 2%', 2);

View File

@ -0,0 +1,46 @@
DROP TRIGGER IF EXISTS `vn`.`supplier_beforeUpdate`;
USE `vn`;
DELIMITER $$
$$
CREATE DEFINER=`root`@`localhost` TRIGGER `vn`.`supplier_beforeUpdate`
BEFORE UPDATE ON `supplier`
FOR EACH ROW
BEGIN
DECLARE vHasChange BOOL;
DECLARE vPayMethodChanged BOOL;
DECLARE vPayMethodHasVerified BOOL;
DECLARE vParams JSON;
DECLARE vOldPayMethodName VARCHAR(20);
DECLARE vNewPayMethodName VARCHAR(20);
SELECT hasVerified INTO vPayMethodHasVerified
FROM payMethod
WHERE id = NEW.payMethodFk;
SET vPayMethodChanged = NOT(NEW.payMethodFk <=> OLD.payMethodFk);
IF vPayMethodChanged THEN
SELECT name INTO vOldPayMethodName
FROM payMethod
WHERE id = OLD.payMethodFk;
SELECT name INTO vNewPayMethodName
FROM payMethod
WHERE id = NEW.payMethodFk;
SET vParams = JSON_OBJECT(
'name', NEW.name,
'oldPayMethod', vOldPayMethodName,
'newPayMethod', vNewPayMethodName
);
SELECT util.notification_send('supplier-pay-method-update', vParams, NULL) INTO @id;
END IF;
SET vHasChange = NOT(NEW.payDemFk <=> OLD.payDemFk AND NEW.payDay <=> OLD.payDay) OR vPayMethodChanged;
IF vHasChange AND vPayMethodHasVerified THEN
SET NEW.isPayMethodChecked = FALSE;
END IF;
END$$
DELIMITER ;

View File

@ -2689,7 +2689,8 @@ INSERT INTO `util`.`notificationConfig`
INSERT INTO `util`.`notification` (`id`, `name`, `description`) INSERT INTO `util`.`notification` (`id`, `name`, `description`)
VALUES VALUES
(1, 'print-email', 'notification fixture one'); (1, 'print-email', 'notification fixture one'),
(3, 'supplier-pay-method-update', 'A supplier pay method has been updated');
INSERT INTO `util`.`notificationAcl` (`notificationFk`, `roleFk`) INSERT INTO `util`.`notificationAcl` (`notificationFk`, `roleFk`)
VALUES VALUES
@ -2747,3 +2748,4 @@ INSERT INTO `salix`.`url` (`appName`, `environment`, `url`)
INSERT INTO `vn`.`payDemDetail` (`id`, `detail`) INSERT INTO `vn`.`payDemDetail` (`id`, `detail`)
VALUES VALUES
(1, 1); (1, 1);

View File

@ -1,7 +1,8 @@
import selectors from '../../helpers/selectors'; import selectors from '../../helpers/selectors';
import getBrowser from '../../helpers/puppeteer'; import getBrowser from '../../helpers/puppeteer';
describe('Login path', async() => { // https://redmine.verdnatura.es/issues/4995 fix login
xdescribe('RecoverPassword path', async() => {
let browser; let browser;
let page; let page;

View File

@ -1,8 +1,9 @@
<vn-layout <vn-layout
ng-if="$ctrl.showLayout"> ng-if="$ctrl.showLayout">
</vn-layout> </vn-layout>
<vn-out-layout <ui-view
name="login"
ng-if="!$ctrl.showLayout"> ng-if="!$ctrl.showLayout">
</vn-out-layout> </ui-view>
<vn-snackbar vn-id="snackbar"></vn-snackbar> <vn-snackbar vn-id="snackbar"></vn-snackbar>
<vn-debug-info></vn-debug-info> <vn-debug-info></vn-debug-info>

View File

@ -21,7 +21,7 @@ export default class App extends Component {
get showLayout() { get showLayout() {
const state = this.$state.current.name || this.$location.$$path.substring(1).replace('/', '.'); const state = this.$state.current.name || this.$location.$$path.substring(1).replace('/', '.');
const outLayout = ['login', 'recoverPassword', 'resetPassword']; const outLayout = ['login', 'recoverPassword', 'resetPassword', 'reset-password'];
return state && !outLayout.some(ol => ol == state); return state && !outLayout.some(ol => ol == state);
} }

View File

@ -1,27 +1,32 @@
<vn-textfield <div class="box">
label="User" <img src="./logo.svg"/>
ng-model="$ctrl.user" <form name="form" ng-submit="$ctrl.submit()">
vn-id="userField" <vn-textfield
vn-focus> label="User"
</vn-textfield> ng-model="$ctrl.user"
<vn-textfield vn-id="userField"
label="Password" vn-focus>
ng-model="$ctrl.password" </vn-textfield>
type="password"> <vn-textfield
</vn-textfield> label="Password"
<vn-check ng-model="$ctrl.password"
label="Do not close session" type="password">
ng-model="$ctrl.remember" </vn-textfield>
name="remember"> <vn-check
</vn-check> label="Do not close session"
<div class="footer"> ng-model="$ctrl.remember"
<vn-submit label="Enter" ng-click="$ctrl.submit()"></vn-submit> name="remember">
<div class="spinner-wrapper"> </vn-check>
<vn-spinner enable="$ctrl.loading"></vn-spinner> <div class="footer">
</div> <vn-submit label="Enter" ng-click="$ctrl.submit()"></vn-submit>
<div class="vn-pt-lg"> <div class="spinner-wrapper">
<a ui-sref="recoverPassword" translate> <vn-spinner enable="$ctrl.loading"></vn-spinner>
I do not remember my password </div>
</a> <!--<div class="vn-pt-lg">
</div> <a ui-sref="recoverPassword" translate>
I do not remember my password
</a>
</div>-->
</div>
</form>
</div> </div>

View File

@ -25,7 +25,7 @@ vn-recover-password{
} }
} }
vn-out-layout{ vn-login{
position: absolute; position: absolute;
height: 100%; height: 100%;
width: 100%; width: 100%;

View File

@ -9,7 +9,9 @@ function config($stateProvider, $urlRouterProvider) {
.state('login', { .state('login', {
url: '/login?continue', url: '/login?continue',
description: 'Login', description: 'Login',
template: '<vn-login></vn-login>' views: {
'login': {template: '<vn-login></vn-login>'},
}
}) })
.state('recoverPassword', { .state('recoverPassword', {
url: '/recover-password', url: '/recover-password',

View File

@ -66,9 +66,9 @@
"MESSAGE_INSURANCE_CHANGE": "I have changed the insurence credit of client [{{clientName}} ({{clientId}})]({{{url}}}) to *{{credit}} €*", "MESSAGE_INSURANCE_CHANGE": "I have changed the insurence credit of client [{{clientName}} ({{clientId}})]({{{url}}}) to *{{credit}} €*",
"Changed client paymethod": "I have changed the pay method for client [{{clientName}} ({{clientId}})]({{{url}}})", "Changed client paymethod": "I have changed the pay method for client [{{clientName}} ({{clientId}})]({{{url}}})",
"Sent units from ticket": "I sent *{{quantity}}* units of [{{concept}} ({{itemId}})]({{{itemUrl}}}) to *\"{{nickname}}\"* coming from ticket id [{{ticketId}}]({{{ticketUrl}}})", "Sent units from ticket": "I sent *{{quantity}}* units of [{{concept}} ({{itemId}})]({{{itemUrl}}}) to *\"{{nickname}}\"* coming from ticket id [{{ticketId}}]({{{ticketUrl}}})",
"Claim will be picked": "The product from the claim [({{claimId}})]({{{claimUrl}}}) from the client *{{clientName}}* will be picked", "Claim will be picked": "The product from the claim [{{claimId}}]({{{claimUrl}}}) from the client *{{clientName}}* will be picked",
"Claim state has changed to incomplete": "The state of the claim [({{claimId}})]({{{claimUrl}}}) from client *{{clientName}}* has changed to *incomplete*", "Claim state has changed to incomplete": "The state of the claim [{{claimId}}]({{{claimUrl}}}) from client *{{clientName}}* has changed to *incomplete*",
"Claim state has changed to canceled": "The state of the claim [({{claimId}})]({{{claimUrl}}}) from client *{{clientName}}* has changed to *canceled*", "Claim state has changed to canceled": "The state of the claim [{{claimId}}]({{{claimUrl}}}) from client *{{clientName}}* has changed to *canceled*",
"Customs agent is required for a non UEE member": "Customs agent is required for a non UEE member", "Customs agent is required for a non UEE member": "Customs agent is required for a non UEE member",
"Incoterms is required for a non UEE member": "Incoterms is required for a non UEE member", "Incoterms is required for a non UEE member": "Incoterms is required for a non UEE member",
"Client checked as validated despite of duplication": "Client checked as validated despite of duplication from client id {{clientId}}", "Client checked as validated despite of duplication": "Client checked as validated despite of duplication from client id {{clientId}}",
@ -136,7 +136,7 @@
"Password does not meet requirements": "Password does not meet requirements", "Password does not meet requirements": "Password does not meet requirements",
"You don't have privileges to change the zone": "You don't have privileges to change the zone or for these parameters there are more than one shipping options, talk to agencies", "You don't have privileges to change the zone": "You don't have privileges to change the zone or for these parameters there are more than one shipping options, talk to agencies",
"Not enough privileges to edit a client": "Not enough privileges to edit a client", "Not enough privileges to edit a client": "Not enough privileges to edit a client",
"Claim pickup order sent": "Claim pickup order sent [({{claimId}})]({{{claimUrl}}}) to client *{{clientName}}*", "Claim pickup order sent": "Claim pickup order sent [{{claimId}}]({{{claimUrl}}}) to client *{{clientName}}*",
"You don't have grant privilege": "You don't have grant privilege", "You don't have grant privilege": "You don't have grant privilege",
"You don't own the role and you can't assign it to another user": "You don't own the role and you can't assign it to another user", "You don't own the role and you can't assign it to another user": "You don't own the role and you can't assign it to another user",
"Email verify": "Email verify", "Email verify": "Email verify",

View File

@ -134,9 +134,9 @@
"MESSAGE_INSURANCE_CHANGE": "He cambiado el crédito asegurado del cliente [{{clientName}} ({{clientId}})]({{{url}}}) a *{{credit}} €*", "MESSAGE_INSURANCE_CHANGE": "He cambiado el crédito asegurado del cliente [{{clientName}} ({{clientId}})]({{{url}}}) a *{{credit}} €*",
"Changed client paymethod": "He cambiado la forma de pago del cliente [{{clientName}} ({{clientId}})]({{{url}}})", "Changed client paymethod": "He cambiado la forma de pago del cliente [{{clientName}} ({{clientId}})]({{{url}}})",
"Sent units from ticket": "Envio *{{quantity}}* unidades de [{{concept}} ({{itemId}})]({{{itemUrl}}}) a *\"{{nickname}}\"* provenientes del ticket id [{{ticketId}}]({{{ticketUrl}}})", "Sent units from ticket": "Envio *{{quantity}}* unidades de [{{concept}} ({{itemId}})]({{{itemUrl}}}) a *\"{{nickname}}\"* provenientes del ticket id [{{ticketId}}]({{{ticketUrl}}})",
"Claim will be picked": "Se recogerá el género de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}*", "Claim will be picked": "Se recogerá el género de la reclamación [{{claimId}}]({{{claimUrl}}}) del cliente *{{clientName}}*",
"Claim state has changed to incomplete": "Se ha cambiado el estado de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}* a *incompleta*", "Claim state has changed to incomplete": "Se ha cambiado el estado de la reclamación [{{claimId}}]({{{claimUrl}}}) del cliente *{{clientName}}* a *incompleta*",
"Claim state has changed to canceled": "Se ha cambiado el estado de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}* a *anulado*", "Claim state has changed to canceled": "Se ha cambiado el estado de la reclamación [{{claimId}}]({{{claimUrl}}}) del cliente *{{clientName}}* a *anulado*",
"Client checked as validated despite of duplication": "Cliente comprobado a pesar de que existe el cliente id {{clientId}}", "Client checked as validated despite of duplication": "Cliente comprobado a pesar de que existe el cliente id {{clientId}}",
"ORDER_ROW_UNAVAILABLE": "No hay disponibilidad de este producto", "ORDER_ROW_UNAVAILABLE": "No hay disponibilidad de este producto",
"Distance must be lesser than 1000": "La distancia debe ser inferior a 1000", "Distance must be lesser than 1000": "La distancia debe ser inferior a 1000",
@ -238,7 +238,7 @@
"Modifiable password only via recovery or by an administrator": "Contraseña modificable solo a través de la recuperación o por un administrador", "Modifiable password only via recovery or by an administrator": "Contraseña modificable solo a través de la recuperación o por un administrador",
"Not enough privileges to edit a client": "No tienes suficientes privilegios para editar un cliente", "Not enough privileges to edit a client": "No tienes suficientes privilegios para editar un cliente",
"This route does not exists": "Esta ruta no existe", "This route does not exists": "Esta ruta no existe",
"Claim pickup order sent": "Reclamación Orden de recogida enviada [({{claimId}})]({{{claimUrl}}}) al cliente *{{clientName}}*", "Claim pickup order sent": "Reclamación Orden de recogida enviada [{{claimId}}]({{{claimUrl}}}) al cliente *{{clientName}}*",
"You don't have grant privilege": "No tienes privilegios para dar privilegios", "You don't have grant privilege": "No tienes privilegios para dar privilegios",
"You don't own the role and you can't assign it to another user": "No eres el propietario del rol y no puedes asignarlo a otro usuario", "You don't own the role and you can't assign it to another user": "No eres el propietario del rol y no puedes asignarlo a otro usuario",
"Ticket merged": "Ticket [{{id}}]({{{fullPath}}}) ({{{originDated}}}) fusionado con [{{tfId}}]({{{fullPathFuture}}}) ({{{futureDated}}})", "Ticket merged": "Ticket [{{id}}]({{{fullPath}}}) ({{{originDated}}}) fusionado con [{{tfId}}]({{{fullPathFuture}}}) ({{{futureDated}}})",

View File

@ -2,7 +2,7 @@
"InvoiceIn": { "InvoiceIn": {
"dataSource": "vn" "dataSource": "vn"
}, },
"InvoiceInTax": { "InvoiceInConfig": {
"dataSource": "vn" "dataSource": "vn"
}, },
"InvoiceInDueDay": { "InvoiceInDueDay": {
@ -13,5 +13,8 @@
}, },
"InvoiceInLog": { "InvoiceInLog": {
"dataSource": "vn" "dataSource": "vn"
},
"InvoiceInTax": {
"dataSource": "vn"
} }
} }

View File

@ -0,0 +1,35 @@
{
"name": "InvoiceInConfig",
"base": "VnModel",
"options": {
"mysql": {
"table": "invoiceInConfig"
}
},
"properties": {
"id": {
"id": true,
"type": "number",
"description": "Identifier"
},
"retentionRate": {
"type": "number"
},
"retentionName": {
"type": "string"
}
},
"relations": {
"sageWithholding": {
"type": "belongsTo",
"model": "SageWithholding",
"foreignKey": "sageWithholdingFk"
}
},
"acls": [{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}]
}

View File

@ -1,3 +1,10 @@
<vn-crud-model
url="InvoiceInConfigs"
data="$ctrl.config"
filter="{fields: ['sageWithholdingFk']}"
id-value="1"
auto-load="true">
</vn-crud-model>
<vn-descriptor-content <vn-descriptor-content
module="invoiceIn" module="invoiceIn"
description="$ctrl.invoiceIn.supplierRef" description="$ctrl.invoiceIn.supplierRef"
@ -26,13 +33,13 @@
Clone Invoice Clone Invoice
</vn-item> </vn-item>
<vn-item <vn-item
ng-if="false" ng-if="$ctrl.isAgricultural()"
ng-click="$ctrl.showPdfInvoice()" ng-click="$ctrl.showPdfInvoice()"
translate> translate>
Show agricultural invoice as PDF Show agricultural invoice as PDF
</vn-item> </vn-item>
<vn-item <vn-item
ng-if="false" ng-if="$ctrl.isAgricultural()"
ng-click="sendPdfConfirmation.show({email: $ctrl.entity.supplierContact[0].email})" ng-click="sendPdfConfirmation.show({email: $ctrl.entity.supplierContact[0].email})"
translate> translate>
Send agricultural invoice as PDF Send agricultural invoice as PDF

View File

@ -110,6 +110,10 @@ class Controller extends Descriptor {
recipientId: this.entity.supplier.id recipientId: this.entity.supplier.id
}); });
} }
isAgricultural() {
return this.invoiceIn.supplier.sageWithholdingFk == this.config[0].sageWithholdingFk;
}
} }
ngModule.vnComponent('vnInvoiceInDescriptor', { ngModule.vnComponent('vnInvoiceInDescriptor', {

View File

@ -13,6 +13,7 @@ describe('InvoiceOut downloadZip()', () => {
}; };
it('should return part of link to dowloand the zip', async() => { it('should return part of link to dowloand the zip', async() => {
pending('https://redmine.verdnatura.es/issues/4875');
const tx = await models.InvoiceOut.beginTransaction({}); const tx = await models.InvoiceOut.beginTransaction({});
try { try {
@ -30,7 +31,6 @@ describe('InvoiceOut downloadZip()', () => {
}); });
it('should return an error if the size of the files is too large', async() => { it('should return an error if the size of the files is too large', async() => {
pending('https://redmine.verdnatura.es/issues/4875');
const tx = await models.InvoiceOut.beginTransaction({}); const tx = await models.InvoiceOut.beginTransaction({});
let error; let error;

View File

@ -8,7 +8,6 @@ describe('loopback model Supplier', () => {
beforeAll(async() => { beforeAll(async() => {
supplierOne = await models.Supplier.findById(1); supplierOne = await models.Supplier.findById(1);
supplierTwo = await models.Supplier.findById(442); supplierTwo = await models.Supplier.findById(442);
const activeCtx = { const activeCtx = {
accessToken: {userId: 9}, accessToken: {userId: 9},
http: { http: {
@ -23,71 +22,106 @@ describe('loopback model Supplier', () => {
}); });
}); });
afterAll(async() => {
await supplierOne.updateAttribute('payMethodFk', supplierOne.payMethodFk);
await supplierTwo.updateAttribute('payMethodFk', supplierTwo.payMethodFk);
});
describe('payMethodFk', () => { describe('payMethodFk', () => {
it('should throw an error when attempting to set an invalid payMethod id in the supplier', async() => { it('should throw an error when attempting to set an invalid payMethod id in the supplier', async() => {
let error; const tx = await models.Supplier.beginTransaction({});
const expectedError = 'You can not select this payment method without a registered bankery account'; const options = {transaction: tx};
const supplier = await models.Supplier.findById(1);
await supplier.updateAttribute('payMethodFk', 8) try {
.catch(e => { let error;
error = e; const expectedError = 'You can not select this payment method without a registered bankery account';
expect(error.message).toContain(expectedError); await supplierOne.updateAttribute('payMethodFk', 8, options)
}); .catch(e => {
error = e;
expect(error).toBeDefined(); expect(error.message).toContain(expectedError);
});
expect(error).toBeDefined();
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should not throw if the payMethod id is valid', async() => { it('should not throw if the payMethod id is valid', async() => {
let error; const tx = await models.Supplier.beginTransaction({});
const supplier = await models.Supplier.findById(442); const options = {transaction: tx};
await supplier.updateAttribute('payMethodFk', 4)
.catch(e => {
error = e;
});
expect(error).not.toBeDefined(); try {
let error;
await supplierTwo.updateAttribute('payMethodFk', 4, options)
.catch(e => {
error = e;
});
expect(error).not.toBeDefined();
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should have checked isPayMethodChecked for payMethod hasVerfified is false', async() => { it('should have checked isPayMethodChecked for payMethod hasVerfified is false', async() => {
const supplier = await models.Supplier.findById(442); const tx = await models.Supplier.beginTransaction({});
await supplier.updateAttribute('isPayMethodChecked', true); const options = {transaction: tx};
await supplier.updateAttribute('payMethodFk', 5);
const result = await models.Supplier.findById(442); try {
await supplierTwo.updateAttribute('isPayMethodChecked', true, options);
await supplierTwo.updateAttribute('payMethodFk', 5, options);
expect(result.isPayMethodChecked).toEqual(true); const result = await models.Supplier.findById(442, null, options);
expect(result.isPayMethodChecked).toEqual(true);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should have unchecked isPayMethodChecked for payMethod hasVerfified is true', async() => { it('should have unchecked isPayMethodChecked for payMethod hasVerfified is true', async() => {
const supplier = await models.Supplier.findById(442); const tx = await models.Supplier.beginTransaction({});
await supplier.updateAttribute('isPayMethodChecked', true); const options = {transaction: tx};
await supplier.updateAttribute('payMethodFk', 2);
const result = await models.Supplier.findById(442); try {
await supplierTwo.updateAttribute('isPayMethodChecked', true, options);
await supplierTwo.updateAttribute('payMethodFk', 2, options);
expect(result.isPayMethodChecked).toEqual(false); const result = await models.Supplier.findById(442, null, options);
expect(result.isPayMethodChecked).toEqual(false);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should have unchecked isPayMethodChecked for payDay and peyDemFk', async() => { it('should have unchecked isPayMethodChecked for payDay and peyDemFk', async() => {
const supplier = await models.Supplier.findById(442); const tx = await models.Supplier.beginTransaction({});
const options = {transaction: tx};
await supplier.updateAttribute('isPayMethodChecked', true); try {
await supplier.updateAttribute('payDay', 5); await supplierTwo.updateAttribute('payMethodFk', 2, options);
const firstResult = await models.Supplier.findById(442); await supplierTwo.updateAttribute('isPayMethodChecked', true, options);
await supplierTwo.updateAttribute('payDay', 5, options);
const firstResult = await models.Supplier.findById(442, null, options);
await supplier.updateAttribute('isPayMethodChecked', true); await supplierTwo.updateAttribute('isPayMethodChecked', true, options);
await supplier.updateAttribute('payDemFk', 1); await supplierTwo.updateAttribute('payDemFk', 1, options);
const secondResult = await models.Supplier.findById(442); const secondResult = await models.Supplier.findById(442, null, options);
expect(firstResult.isPayMethodChecked).toEqual(false); expect(firstResult.isPayMethodChecked).toEqual(false);
expect(secondResult.isPayMethodChecked).toEqual(false); expect(secondResult.isPayMethodChecked).toEqual(false);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });
}); });

View File

@ -256,7 +256,7 @@ class Controller extends Section {
this.$http.post(`NotificationQueues`, { this.$http.post(`NotificationQueues`, {
notificationFk: 'invoiceElectronic', notificationFk: 'invoiceElectronic',
authorFk: client.id, authorFk: client.id,
}).then(a => { }).then(() => {
this.vnApp.showSuccess(this.$t('Invoice sent')); this.vnApp.showSuccess(this.$t('Invoice sent'));
}); });
} }

View File

@ -0,0 +1,11 @@
const Stylesheet = require(`vn-print/core/stylesheet`);
const path = require('path');
const vnPrintPath = path.resolve('print');
module.exports = new Stylesheet([
`${vnPrintPath}/common/css/spacing.css`,
`${vnPrintPath}/common/css/misc.css`,
`${vnPrintPath}/common/css/layout.css`,
`${vnPrintPath}/common/css/email.css`])
.mergeStyles();

View File

@ -0,0 +1,3 @@
subject: Pay method updated
title: Pay method updated
description: The pay method of the supplier {0} has been updated from {1} to {2}

View File

@ -0,0 +1,3 @@
subject: Método de pago actualizado
title: Método de pago actualizado
description: Se ha actualizado el método de pago del proveedor {0} de {1} a {2}

View File

@ -0,0 +1,8 @@
<email-body v-bind="$props">
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p v-html="$t('description', [name, oldPayMethod, newPayMethod])"></p>
</div>
</div>
</email-body>

View File

@ -0,0 +1,23 @@
const Component = require(`vn-print/core/component`);
const emailBody = new Component('email-body');
module.exports = {
name: 'supplier-pay-method-update',
components: {
'email-body': emailBody.build(),
},
props: {
name: {
type: String,
required: true
},
oldPayMethod: {
type: String,
required: true
},
newPayMethod: {
type: String,
required: true
}
}
};

View File

@ -20,7 +20,7 @@
</tr> </tr>
<tr> <tr>
<td class="font gray uppercase">{{$t('ref')}}</td> <td class="font gray uppercase">{{$t('ref')}}</td>
<th>{{entry.ref}}</th> <th>{{entry.invoiceNumber}}</th>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@ -1,10 +1,10 @@
SELECT SELECT
e.id, e.id,
e.ref, e.invoiceNumber,
e.notes, e.notes,
c.code companyCode, c.code companyCode,
t.landed t.landed
FROM entry e FROM entry e
JOIN travel t ON t.id = e.travelFk JOIN travel t ON t.id = e.travelFk
JOIN company c ON c.id = e.companyFk JOIN company c ON c.id = e.companyFk
WHERE e.id = ? WHERE e.id = ?

View File

@ -49,7 +49,7 @@
<tbody> <tbody>
<tr v-for="entry in travel.entries"> <tr v-for="entry in travel.entries">
<td>{{entry.supplierName}}</td> <td>{{entry.supplierName}}</td>
<td>{{entry.ref}}</td> <td>{{entry.reference}}</td>
<td class="number">{{entry.volumeKg | number($i18n.locale)}}</td> <td class="number">{{entry.volumeKg | number($i18n.locale)}}</td>
<td class="number">{{entry.loadedKg | number($i18n.locale)}}</td> <td class="number">{{entry.loadedKg | number($i18n.locale)}}</td>
<td class="number">{{entry.stickers}}</td> <td class="number">{{entry.stickers}}</td>

View File

@ -1,7 +1,7 @@
SELECT SELECT
e.id, e.id,
e.travelFk, e.travelFk,
e.ref, e.reference,
s.name AS supplierName, s.name AS supplierName,
SUM(b.stickers) AS stickers, SUM(b.stickers) AS stickers,
CAST(SUM(b.weight * b.stickers) as DECIMAL(10,0)) as loadedKg, CAST(SUM(b.weight * b.stickers) as DECIMAL(10,0)) as loadedKg,
@ -15,4 +15,4 @@ SELECT
JOIN supplier s ON s.id = e.supplierFk JOIN supplier s ON s.id = e.supplierFk
JOIN vn.volumeConfig vc JOIN vn.volumeConfig vc
WHERE t.id IN(?) WHERE t.id IN(?)
GROUP BY e.id GROUP BY e.id

View File

@ -5,9 +5,8 @@
<div class="grid-row"> <div class="grid-row">
<div class="grid-block"> <div class="grid-block">
<div class="columns vn-mb-lg"> <div class="columns vn-mb-lg">
<div class="size50"> <div class="size75">
<div class="size75 vn-mt-ml"> <div class="size100 vn-mt-ml">
<h1 class="title uppercase">{{$t('title')}}</h1>
<table class="row-oriented ticket-info"> <table class="row-oriented ticket-info">
<tbody> <tbody>
<tr> <tr>
@ -16,7 +15,7 @@
</tr> </tr>
<tr> <tr>
<td class="font gray uppercase">{{$t('invoiceId')}}</td> <td class="font gray uppercase">{{$t('invoiceId')}}</td>
<th>{{invoice.id}}</th> <th>{{invoice.supplierRef}}</th>
</tr> </tr>
<tr> <tr>
<td class="font gray uppercase">{{$t('date')}}</td> <td class="font gray uppercase">{{$t('date')}}</td>
@ -26,7 +25,7 @@
</table> </table>
</div> </div>
</div> </div>
<div class="size50"> <div class="size25">
<div class="panel"> <div class="panel">
<div class="header">{{$t('invoiceData')}}</div> <div class="header">{{$t('invoiceData')}}</div>
<div class="body"> <div class="body">
@ -43,7 +42,7 @@
<div class="vn-mt-lg" v-for="entry in entries"> <div class="vn-mt-lg" v-for="entry in entries">
<div class="table-title clearfix"> <div class="table-title clearfix">
<div class="pull-left"> <div class="pull-left">
<h2>{{$t('invoiceId')}}</h2> <h2>{{$t('entry')}}</h2>
</div> </div>
<div class="pull-left vn-mr-md"> <div class="pull-left vn-mr-md">
<div class="field rectangle"> <div class="field rectangle">
@ -64,7 +63,7 @@
</div> </div>
<div class="pull-left"> <div class="pull-left">
<div class="field rectangle"> <div class="field rectangle">
<span>{{entry.ref}}</span> <span>{{entry.reference}}</span>
</div> </div>
</div> </div>
</span> </span>
@ -82,7 +81,7 @@
<tr> <tr>
<td width="50%">{{buy.name}}</td> <td width="50%">{{buy.name}}</td>
<td class="number">{{buy.quantity}}</td> <td class="number">{{buy.quantity}}</td>
<td class="number">{{buy.buyingValue}}</td> <td class="number">{{buy.buyingValue | currency('EUR', $i18n.locale)}}</td>
<td class="number">{{buyImport(buy) | currency('EUR', $i18n.locale)}}</td> <td class="number">{{buyImport(buy) | currency('EUR', $i18n.locale)}}</td>
</tr> </tr>
<tr class="description font light-gray"> <tr class="description font light-gray">
@ -103,27 +102,31 @@
</tfoot> </tfoot>
</table> </table>
</div> </div>
<div class="columns vn-mt-xl"> <div class="columns vn-mt-xl">
<div id="taxes" class="size50 pull-right no-page-break" v-if="taxes"> <div id="signature" class="size50 pull-left no-page-break vn-pr-xs">
<div class="panel">
<div class="header">{{$t('payMethod')}}: {{invoice.payMethod}}</div>
<div class="body">
<div class="vn-mt-md">{{$t('signer.received')}}:</div>
<div class="vn-my-md">{{$t('signer.signed')}}:</div>
</div>
</div>
</div>
<div id="taxes" class="size50 pull-right no-page-break vn-pl-xs vn-mt-md" v-if="taxes">
<table class="column-oriented"> <table class="column-oriented">
<thead> <thead>
<tr> <tr>
<th colspan="4">{{$t('taxBreakdown')}}</th> <th colspan="4">{{$t('taxBreakdown')}}</th>
</tr> </tr>
</thead> </thead>
<thead class="light">
<tr>
<th width="45%">{{$t('type')}}</th>
<th width="25%" class="number">{{$t('taxBase')}}</th>
<th>{{$t('tax')}}</th>
<th class="number">{{$t('fee')}}</th>
</tr>
</thead>
<tbody> <tbody>
<tr v-for="tax in taxes"> <tr v-for="tax in taxes">
<td width="45%">{{tax.name}}</td> <td width="45%">{{tax.name}}</td>
<td width="25%" class="number">{{tax.taxableBase | currency('EUR', $i18n.locale)}}</td> <td width="25%" class="number">{{tax.taxableBase | currency('EUR', $i18n.locale)}}
<td>{{tax.rate | percentage}}</td> </td>
<td>{{(tax.rate / 100) | percentage}}</td>
<td class="number">{{tax.vat | currency('EUR', $i18n.locale)}}</td> <td class="number">{{tax.vat | currency('EUR', $i18n.locale)}}</td>
</tr> </tr>
</tbody> </tbody>
@ -150,28 +153,16 @@
</div> </div>
</div> </div>
</div> </div>
<div class="columns vn-mt-xl"> </div>
<div class="size50 pull-left no-page-break">
<div class="panel"> <div id="footer" class="vn-mt-xl">
<div class="header">{{$t('observations')}}</div> <h2 class="centered bold">{{$t('footer')}}</h2>
<div class="body">
<div>{{$t('payMethod')}}</div>
<div>{{invoice.payMethod}}</div>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
<template v-slot:footer> <template v-slot:footer>
<report-footer <report-footer id="pageFooter" v-bind:company-code="invoice.companyCode" v-bind:left-text="$t('invoiceId')"
id="pageFooter" v-bind:center-text="invoice.name" v-bind="$props">
v-bind:company-code="invoice.companyCode"
v-bind:left-text="$t('invoiceId')"
v-bind:center-text="invoice.name"
v-bind="$props"
>
</report-footer> </report-footer>
</template> </template>
</report-body> </report-body>

View File

@ -9,6 +9,16 @@ module.exports = {
this.invoice = await this.fetchInvoice(this.id); this.invoice = await this.fetchInvoice(this.id);
this.taxes = await this.fetchTaxes(this.id); this.taxes = await this.fetchTaxes(this.id);
let defaultTax = await this.fetchDefaultTax();
if (defaultTax) {
defaultTax = Object.assign(defaultTax, {
taxableBase: 0,
vat: (this.taxTotal() * defaultTax.rate / 100)
});
this.taxes.push(defaultTax);
}
if (!this.invoice) if (!this.invoice)
throw new Error('Something went wrong'); throw new Error('Something went wrong');
@ -43,6 +53,9 @@ module.exports = {
fetchBuy(id) { fetchBuy(id) {
return this.rawSqlFromDef('buy', [id]); return this.rawSqlFromDef('buy', [id]);
}, },
fetchDefaultTax() {
return this.findOneFromDef('defaultTax');
},
async fetchTaxes(id) { async fetchTaxes(id) {
const taxes = await this.rawSqlFromDef(`taxes`, [id]); const taxes = await this.rawSqlFromDef(`taxes`, [id]);
return this.taxVat(taxes); return this.taxVat(taxes);

View File

@ -1,6 +1,6 @@
reportName: invoice reportName: agricultural receip
title: Agricultural invoice title: Agricultural receip
invoiceId: Agricultural invoice invoiceId: Agricultural receip
supplierId: Proveedor supplierId: Proveedor
invoiceData: Invoice data invoiceData: Invoice data
reference: Reference reference: Reference
@ -23,3 +23,8 @@ subtotal: Subtotal
taxBreakdown: Tax breakdown taxBreakdown: Tax breakdown
observations: Observations observations: Observations
payMethod: Pay method payMethod: Pay method
entry: Entry
signer:
received: Received
signed: Signature
footer: Passive subject covered by the special agrarian regime. Please send this duly signed and sealed copy. Thanks.

View File

@ -1,6 +1,6 @@
reportName: factura reportName: recibo agrícola
title: Factura Agrícola title: Recibo Agrícola
invoiceId: Factura Agrícola invoiceId: Recibo Agrícola
supplierId: Proveedor supplierId: Proveedor
invoiceData: Datos de facturación invoiceData: Datos de facturación
reference: Referencia reference: Referencia
@ -23,3 +23,8 @@ subtotal: Subtotal
taxBreakdown: Desglose impositivo taxBreakdown: Desglose impositivo
observations: Observaciones observations: Observaciones
payMethod: Método de pago payMethod: Método de pago
entry: Entrada
signer:
received: Recibí
signed: Firma y sello
footer: Sujeto pasivo acogido al régimen especial agrario. Les rogamos remitan esta copia debidamente firmada y sellada. Gracias.

View File

@ -0,0 +1,5 @@
SELECT
id,
retentionRate rate,
retentionName name
FROM invoiceInConfig;

View File

@ -1,7 +1,7 @@
SELECT SELECT
e.id, e.id,
t.landed, t.landed,
e.ref e.reference
FROM entry e FROM entry e
JOIN invoiceIn i ON i.id = e.invoiceInFk JOIN invoiceIn i ON i.id = e.invoiceInFk
JOIN travel t ON t.id = e.travelFk JOIN travel t ON t.id = e.travelFk

View File

@ -1,5 +1,5 @@
SELECT SELECT
i.id, i.supplierRef,
s.id supplierId, s.id supplierId,
i.created, i.created,
s.name, s.name,

View File

@ -5,4 +5,5 @@ SELECT
FROM invoiceIn ii FROM invoiceIn ii
JOIN invoiceInTax iit ON ii.id = iit.invoiceInFk JOIN invoiceInTax iit ON ii.id = iit.invoiceInFk
JOIN sage.TiposIva ti ON ti.CodigoIva = iit.taxTypeSageFk JOIN sage.TiposIva ti ON ti.CodigoIva = iit.taxTypeSageFk
WHERE ii.id = ?; WHERE ii.id = ?
ORDER BY name DESC;

View File

@ -1,6 +1,6 @@
SELECT SELECT
e.id, e.id,
e.ref, e.reference,
e.supplierFk, e.supplierFk,
t.shipped t.shipped
FROM vn.entry e FROM vn.entry e

View File

@ -39,7 +39,7 @@
<h2> <h2>
<span>{{$t('entry')}} {{entry.id}}</span> <span>{{$t('entry')}} {{entry.id}}</span>
<span>{{$t('dated')}} {{entry.shipped | date('%d-%m-%Y')}}</span> <span>{{$t('dated')}} {{entry.shipped | date('%d-%m-%Y')}}</span>
<span class="pull-right">{{$t('reference')}} {{entry.ref}}</span> <span class="pull-right">{{$t('reference')}} {{entry.reference}}</span>
</h2> </h2>
<table class="column-oriented repeatable"> <table class="column-oriented repeatable">
<thead> <thead>